Jump to content
IGNORED

CPM/Z-80 card for the 1090XL -- Calling anyone with Z-80 experience!


Recommended Posts

Posted (edited)
7 minutes ago, kheller2 said:

I didn't realize they were that unclean.   I see the page header command in there but it looks like all the returns and line feeds have been removed.  I few quick Unix scripts could clean them up.  Is there something you want me to look at first?

Not your fault.  I am really glad you posted the files as there are some good clues as to how this was supposed to work.  I just didn't spend much time trying to figure it all out.

 

I don't know what should be cleaned up first.  If a few Unix scripts could clean them up that would be great!

Edited by reifsnyderb
Link to comment
Share on other sites

I took a quick look at cleaning them up. It looks like cr/lf's were replaced globally with tabs. So now, some tabs are supposed to be tabs, and some tabs are supposed to be line feeds. I hope I am wrong because it would be difficult/impossible for a script or command to be able to tell the difference and clean them up. Or maybe like someone else said, all cr/lfs were just stripped out, which would be equally hard to replace by script or command.

Edited by invisible kid
Link to comment
Share on other sites

27 minutes ago, invisible kid said:

I took a quick look at cleaning them up. It looks like cr/lf's were replaced globally with tabs. So now, some tabs are supposed to be tabs, and some tabs are supposed to be line feeds. I hope I am wrong because it would be difficult/impossible for a script or command to be able to tell the difference and clean them up. Or maybe like someone else said, all cr/lfs were just stripped out, which would be equally hard to replace by script or command.

Yeah, looks like all CR/LFs are missing. Replacing tab by newline+tab makes it sort of readable (sed 's/\t/\n\t/g') but not all. Some files have single CRs at some lines. But with some manual intervention it could be turned into proper source code. Except for the binary blobs (BIOS, BDOS, CCP) which are .byte statement hexdumps of 8080/Z80 code.

 

IMHO it would be easier to write your own BIOS and the Atari side of things. If you know how CP/M works, it's one to two days work to get that working, provided that the card allows the Z80 to be halted after a request is made and the 6502 can access the RAM memory again.

Edited by ivop
  • Like 1
Link to comment
Share on other sites

49 minutes ago, ivop said:

IMHO it would be easier to write your own BIOS and the Atari side of things. If you know how CP/M works, it's one to two days work to get that working, provided that the card allows the Z80 to be halted after a request is made and the 6502 can access the RAM memory again.

That sounds like what will have to be done.

 

I am working on a new card that uses a lot of PLDs and has it's own SRAM on board.  It will be controlled by $D1F0 in a manner similar to the schematic, have the capability to bank the memory, have it's own clock for the Z-80, permit DMA by the 6502, and use about 10 chips.

  • Like 2
Link to comment
Share on other sites

2 hours ago, reifsnyderb said:

That sounds like what will have to be done.

 

I am working on a new card that uses a lot of PLDs and has it's own SRAM on board.  It will be controlled by $D1F0 in a manner similar to the schematic, have the capability to bank the memory, have it's own clock for the Z-80, permit DMA by the 6502, and use about 10 chips.

That sounds good!

 

Basically it comes down to this:

 

6502 sets up the Z80 memory map:

$0000-$0007 (W)BOOT vector and BDOS vectors

($e400 CCP optional, should reload on (W)BOOT anyway)

$ec00 BDOS

$fa00 BIOS (2kB should be more than enough)

 

BIOS needs entry points for each call, and a disk parameter block (also called DPH). For BDOS to find BIOS, you have to hardcode all addresses in bdos.asm

The disk parameter block is what SELDSK returns in HL. BDOS calls that to know where it is and to know how the disk is formatted.

 

start Z80 (which starts running at $0000, which points to BOOT, changed to WBOOT after that during BOOT, WBOOT always reloads CCP.SYS)

 

each BIOS call does:

  • save parameter A, BC, and HL to a fixed location (say $ff00, ignore DE as we don't do sector translation (sector skew))
  • signal 6502 we want BIOS call number x
  • busy wait for ACK
  • reload A, BC and HL from memory
  • RET

 

6502 does:

  • wait for BIOS request
  • stop Z80
  • dispatch on request number X
  • read Z80's C or BC (depends on call) from memory
  • execute bios call (CONIN, CONOUT, READ/WRITE sector directly in to/out of Z80 memory, etc...)
  • set return values in Z80 memory (A, C, HL, whatever applies)
  • start Z80
  • signal ACK

 

Short description of BIOS calls: https://www.seasip.info/Cpm/bios.html

You can ignore LIST, PUNCH, READER, SECTRAN and LISTST. You could save a copy of CCP outside of the Z80 memory so you can reload it very fast when WBOOT is called. You can start with the Atari E: device as CONOUT, switch to a proper dumb terminal later (so Atari special characters are printed as characters instead), and after that something like ADM-3a or VT-52 in 40 and/or 80 columns. For inspiration you could look at the CP/M-65 terminal implementations.

 

As dmsc would say, have fun! :)

Edited by ivop
  • Like 3
  • Thanks 1
Link to comment
Share on other sites

1 hour ago, ivop said:

That sounds good!

 

Basically it comes down to this:

 

6502 sets up the Z80 memory map:

$0000-$0007 (W)BOOT vector and BDOS vectors

($e400 CCP optional, should reload on (W)BOOT anyway)

$ec00 BDOS

$fa00 BIOS (2kB should be more than enough)

 

BIOS needs entry points for each call, and a disk parameter block (also called DPH). For BDOS to find BIOS, you have to hardcode all addresses in bdos.asm

The disk parameter block is what SELDSK returns in HL. BDOS calls that to know where it is and to know how the disk is formatted.

 

start Z80 (which starts running at $0000, which points to BOOT, changed to WBOOT after that during BOOT, WBOOT always reloads CCP.SYS)

 

each BIOS call does:

  • save parameter A, BC, and HL to a fixed location (say $ff00, ignore DE as we don't do sector translation (sector skew))
  • signal 6502 we want BIOS call number x
  • busy wait for ACK
  • reload A, BC and HL from memory
  • RET

 

6502 does:

  • wait for BIOS request
  • stop Z80
  • dispatch on request number X
  • read Z80's C or BC (depends on call) from memory
  • execute bios call (CONIN, CONOUT, READ/WRITE sector directly in to/out of Z80 memory, etc...)
  • set return values in Z80 memory (A, C, HL, whatever applies)
  • start Z80
  • signal ACK

 

Short description of BIOS calls: https://www.seasip.info/Cpm/bios.html

You can ignore LIST, PUNCH, READER, SECTRAN and LISTST. You could save a copy of CCP outside of the Z80 memory so you can reload it very fast when WBOOT is called. You can start with the Atari E: device as CONOUT, switch to a proper dumb terminal later (so Atari special characters are printed as characters instead), and after that something like ADM-3a or VT-52 in 40 and/or 80 columns. For inspiration you could look at the CP/M-65 terminal implementations.

 

As dmsc would say, have fun! :)

It sounds like you are ready to implement this!

 

I've found this, which has a skeletal BIOS and I think there is some BIOS code in the files that were posted:   http://www.cpm.z80.de/manuals/cpm22-m.pdf

 

 

 

 

 

Link to comment
Share on other sites

12 hours ago, reifsnyderb said:

It sounds like you are ready to implement this!

I could probably do that, if I had the hardware ;) Thinking about it some more, the communication between the 6502 and the Z80 needs to be a little more robust to avoid running into a race condition or executing the same BIOS command twice. Perhaps more like:

 

Z80: check if 6502 is ready for command

6502: signal ready for command

Z80: send command

6502: recv command

Z80: wait for done

6502: halt Z80, do command, signal done, start Z80

Z80: wait for done, recv done, signal ack

6502: wait for ack, recv ack, loop to signal ready for command

Z80: continue

 

12 hours ago, reifsnyderb said:

I've found this, which has a skeletal BIOS and I think there is some BIOS code in the files that were posted:   http://www.cpm.z80.de/manuals/cpm22-m.pdf

http://github.com/ivop/atari8080 contains a 8080 emulator I wrote in C and in 6502 assembly. In the C version I did a full BIOS implementation (around 160 lines of C code). The 130XE 6502 version contains just enough BIOS to output the test suite results via CONOUT. I was working on porting it to CP/M-65 to run on top of its BIOS, i.e. translating the 8080 BIOS calls to CP/M-65 BIOS calls. That's mostly finished, but then I got sidetracked by another project ;) It still needs the CP/M-65 overlay loader and read/write sector translation. Then you basically have 95% of the code you need to get your card running if you were to run it on top of CP/M-65. If you look in the cpm22 directory of the aforementioned repo, you'll find a much simpler BIOS skeleton (bios.asm) and the adapted bdos.asm with the needed hardcoded BIOS addresses.

 

If you wanted to to stay closer to what Atari did, you could try extracting the hexdumps from the .MAC files (CPMBIOS, CPMBDOS and CPMCCP, highest .number extension) and run them through a disassembler. BDOS and CCP are unmodified from Digital's sources, except for the hardcoded BIOS entries. Perhaps you can deduct from CPMBOOT where in the Z80 memory they are loaded. Looking at the hexdumps, BDOS does some calls with 0CDH (=CALL) 006H 04AH, or 009H 04AH, etc. (low byte increasing by 3 for each next BIOS call) and the BIOS starts with jumps for each BIOS call (0C3H (=JMP) and then an address in the $4Axx range, total of three bytes per entry). BDOS starts at offset +6 (after the six byte "serial") with JMP BDOSE which is 0C3H and the address 03C11H. BDOS at $3c00 and BIOS at $4a00. Pretty weird numbers if the Z80 had 64kB of RAM. CCP starts with JMP CCPSTART (which is three pages in), .Byte 0C3,05C,037, so CCP should start at $3400. So instead of pages $e4, $ec and $fa (which I had expected and pretty standard across 64kB CP/Ms) they appear to start at pages $34, $3c and $4a. Perhaps they were debug builds? They should be at the end of the TPA (Transient Program Area, read free memory), not somewhere in the middle. TPA from 00100H to 03BFFH (CCP is allowed to be overwritten if the program ends with WBOOT instead of RET) is pretty small and cannot run Zork for example.

Edited by ivop
  • Like 2
Link to comment
Share on other sites

7 minutes ago, ivop said:

I could probably do that, if I had the hardware ;) Thinking about it some more, the communication between the 6502 and the Z80 needs to be a little more robust to avoid running into a race condition or executing the same BIOS command twice. Perhaps more like:

 

Z80: check if 6502 is ready for command

6502: signal ready for command

Z80: send command

6502: recv command

Z80: wait for done

6502: halt Z80, do command, signal done, start Z80

Z80: wait for done, recv done, signal ack

6502: wait for ack, recv ack, loop to signal ready for command

Z80: continue

 

http://github.com/ivop/atari8080 contains a 8080 emulator I wrote in C and in 6502 assembly. In the C version I did a full BIOS implementation (around 160 lines of C code). The 130XE 6502 version contains just enough BIOS to output the test suite results via CONOUT. I was working on porting it to CP/M-65 to run on top of its BIOS, i.e. translating the 8080 BIOS calls to CP/M-65 BIOS calls. That's mostly finished, but then I got sidetracked by another project ;) It still needs the CP/M-65 overlay loader and read/write sector translation. Then you basically have 95% of the code you need to get your card running if you were to run it on top of CP/M-65. If you look in the cpm22 directory of the aforementioned repo, you'll find a much simpler BIOS skeleton (bios.asm) and the adapted bdos.asm with the needed hardcoded BIOS addresses.

 

If you wanted to to stay closer to what Atari did, you could try extracting the hexdumps from the .MAC files (CPMBIOS, CPMBDOS and CPMCCP) and run them through a disassembler. BDOS and CCP are unmodified from Digital's sources, except for the hardcoded BIOS entries. Perhaps you can deduct from CPMBOOT where in the Z80 memory they are loaded. Looking at the hexdumps, BDOS does some calls with 0CDH (=CALL) 006H 04AH, or 009H 04AH, etc. (low byte increasing by 3 for each next BIOS call) and the BIOS starts with jumps for each BIOS call (0C3H (=JMP) and then an address in the $4Axx range, total of three bytes per entry). BDOS starts at offset +6 (after the six byte "serial") with JMP BDOSE which is 0C3H and the address 03C11H. BDOS at $3c00 and BIOS at $4a00. Pretty weird numbers if the Z80 had 64kB of RAM. CCP starts with JMP CCPSTART (which is three pages in), .Byte 0C3,05C,037, so CCP should start at $3400. So instead of pages $e4, $ec and $fa (which I had expected and pretty standard across 64kB CP/Ms) they appear to start at pages $34, $3c and $4a. Perhaps they were debug builds? They should be at the end of the TPA (Transient Program Area, read free memory), not somewhere in the middle. TPA from 00100H to 03BFFH (CCP is allowed to be overwritten if the program ends with WBOOT instead of RET) is pretty small and cannot run Zork for example.

It sounds like it would be just as "easy" to re-implement the BIOS.  What is the absolute minimum number of devices I need to get working?  Keyboard input, screen output, disk access?

 

I was reading that C compilers don't output the greatest Z-80 code.  I have almost zero Z-80 experience, so I really don't know.  Is this true?

 

I also found out that Wozmon has been ported to the Z-80.  I am wondering if it would be worthwhile to implement here or if it would just be a distraction....

 

How long do you think it would take to get this working if you had the hardware?

 

Link to comment
Share on other sites

2 minutes ago, reifsnyderb said:

It sounds like it would be just as "easy" to re-implement the BIOS.  What is the absolute minimum number of devices I need to get working?  Keyboard input, screen output, disk access?

Yes, that's all. You need:

 

BOOT ; read BDOS into memory, or NOP if that's part of the init procedure the 6502 does

WBOOT ;  read CCP into memory

CONST ; Console status, key available yes/no

CONIN ; Console input, read key from Atari K:

CONOUT ; Console output, write character to E:

SELDSK ; Select disc drive, fixed to drive 0 (=A) (multiple drives could be added later)

SETTRK ; Set track number, store somewhere

SETSEC ; Set sector number, store somewhere

SETDMA ; Set DMA address, store somewhere

READ ; Read a sector, calculate Atari sector number with TRK and SEC, read

WRITE ; Write a sector, idem, write

 

You can ignore:

 

HOME ; Move disc head to track 0

LIST ; Printer output

PUNCH ; Paper tape punch output

READER ; Paper tape reader input

LISTST ; Status of list device

SECTRAN ; Sector translation for skewing

 

I just found this in an old CP/M document:

 

MEMORY ALLOCATION
=================

V1.4:  b = memsize-16K
0000   - 00FF     System scratch area
0100   - 28FF+b   TPA (Transient Program Area) - COM file area
2900+b - 30FF+b   CCP - Console Command Processor
3100+b - 3DFF+b   BDOS
3E00+b - 3FFF+b   BIOS

V2.2:  b = memsize-20K
0000   - 00FF     System scratch area
0100   - 33FF+b   TPA (Transient Program Area) - COM file area
3400+b - 3BFF+b   CCP - Console Command Processor
3C00+b - 49FF+b   BDOS
4A00+b - 4FFF+b   BIOS

 

That's where the 3400 came from. The Atari builds were not adjusted for 64kB memory yet.

 

2 minutes ago, reifsnyderb said:

I was reading that C compilers don't output the greatest Z-80 code.  I have almost zero Z-80 experience, so I really don't know.  Is this true?

Yes, that's true. I was not suggesting writing it in C. The C code I referenced was just meant as an example of how small and simple a BIOS implementation can be.

 

2 minutes ago, reifsnyderb said:

I also found out that Wozmon has been ported to the Z-80.  I am wondering if it would be worthwhile to implement here or if it would just be a distraction....

I think as long as you don't have a way to interact with it (keyboard, screen) it has little value :) And once you have CP/M running, there's http://www.cpm.z80.de/randyfiles/DRI/DDT.pdf

 

2 minutes ago, reifsnyderb said:

How long do you think it would take to get this working if you had the hardware?

Probably a week or so. But considering I am sometimes unable to do stuff for weeks because of an illness, and other times I barely manage 1-2 hours a day, I'm not sure if I am the right person to take on yet another project. But I can help you or somebody else through this forum.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

@ivop

 

Thanks for the information!  I can do this, but with zero Z-80 and CP/M experience, I figure it will take me quite some time.  I am concerned about an issue whereby I need to have disk access without having a clue as to the format of the disk nor having a compatible disk.  The disk has to, obviously, be compatible with an Atari disk.  So, I figure a single density 90k disk should be a starting point.  Presently, I don't have an idea as to what the format would look like.  Is there some sort of disk format information for CP/M I can refer to?  I figure it would be possible to format a disk and just re-arrange the structures into a CP/M format.  Then, with CP/M running, it will at least have something to look at. 

 

Thanks!

 

 

 

 

 

 

 

Link to comment
Share on other sites

You can create CP/M disks with cpmtools on Linux or WSL2 on Windows. We use it to create the disk images for CP/M-65. It reads a diskdefs file and you supply the format with the -f command line option, like this:

 

mkfs.cpm -f atari90 disk.img
/usr/bin/printf '\x96\x02\x80\x16\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' > disk.atr
cat disk.img >> disk.atr

 

Or a 1MB image for use with drive emulators or mount with SIDE:

mkfs.cpm -f atarihd disk.img
/usr/bin/printf '\x96\x02\xf0\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' > disk.atr
cat disk.img >> disk.atr

 

Different ATR header for the big image. I hardcoded /usr/bin/printf because the bash builtin printf is broken.

 

In between the mkfs and printf you can copy files to the image with cpmcp, list its directory with cpmls, etc..

 

cpmfs -f atari90 disk.img foo.txt 0:FOO.TXT

cpmls -f atari90 disk.img

 

Edit: The prepended 0: means user area 0 (the default). CP/M uses user areas to separate files. Sort of a poor man's directory, but there's no PATH and no copy between them. Most of the CP/M users pretend they don't exist and put everything in 0:. For the Atari images, I put the platform specific files in user area 1 though.

Edited by ivop
  • Like 4
Link to comment
Share on other sites

4 minutes ago, ivop said:

You can create CP/M disks with cpmtools on Linux or WSL2 on Windows. We use it to create the disk images for CP/M-65. It reads a diskdefs file and you supply the format with the -f command line option, like this:

 

mkfs.cpm -f atari90 disk.img
/usr/bin/printf '\x96\x02\x80\x16\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' > disk.atr
cat disk.img >> disk.atr

 

Or a 1MB image for use with drive emulators or mount with SIDE:

mkfs.cpm -f atarihd disk.img
/usr/bin/printf '\x96\x02\xf0\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' > disk.atr
cat disk.img >> disk.atr

 

Different ATR header for the big image. I hardcoded /usr/bin/printf because the bash builtin printf is broken.

 

In between the mkfs and printf you can copy files to the image with cpmcp, list its directory with cpmls, etc..

 

cpmfs -f atari90 disk.img foo.txt 0:FOO.TXT

cpmls -f atari90 disk.img

 

I was under the impression that a disk formatted in a PC compatible floppy drive was not accessible by a 1050.  Am I wrong?  Do I need an ATR8000 and PC compatible floppy disks?

 

 

 

 

 

 

 

 

 

Link to comment
Share on other sites

17 minutes ago, reifsnyderb said:

I was under the impression that a disk formatted in a PC compatible floppy drive was not accessible by a 1050.  Am I wrong?  Do I need an ATR8000 and PC compatible floppy disks?

mkfs.cpm just creates a disk file that contains the CP/M filesystem. I just noticed (and forgot to mention) that it does not expand the file to 720*128 bytes, so it's best to first create an empty disk.img file of 720*128 bytes and then write the CP/M filesystem to it. Once converted to ATR you can use it with your favorite emulator (atari800, Altirra, atari++) or disk emulator like SDrive or RespeQt. If you want a real floppy, you can just sector copy it from there to a real drive. PC compatible drives do indeed not work correctly.

 

Edit: before the mkfs.cpm -f atari90 disk.img, you can do:

dd if=/dev/zero of=disk.img bs=128 count=720

to create the empty file.

 

If later you have files written to the ATR encapsulated image with CP/M running on the Atari, you can convert it back to a plain image with:

dd if=disk.atr of=disk.img bs=1 skip=16

And then copy files out of it with cpmcp.

Edited by ivop
  • Like 1
Link to comment
Share on other sites

3 minutes ago, ivop said:

mkfs.cpm just creates a disk file that contains the CP/M filesystem. I just noticed (and forgot to mention) that it does not expand the file to 720*128 bytes, so it's best to first create an empty disk.img file of 720*128 bytes and then write the CP/M filesystem to it. Once converted to ATR you can use it with your favorite emulator (atari800, Altirra, atari++) or disk emulator like SDrive or RespeQt. If you want a real floppy, you can just sector copy it from there to a real drive. PC compatible drives do indeed not work correctly.

 

Ok.  I'll be working with a real floppy.  A couple months ago, I stumbled across 3 boxes of floppy disks in my basement.  All unopened!  They are labelled as being DS/DD and have the hub ring.  I think they'll work.  🙂

 

Link to comment
Share on other sites

Just for fun, I partly disassembled the Z80 BIOS

bios-disasm.zip

 

I decoded the Disk Parameter Headers, but did not decode the Disk Parameter Block (shared by all four drives). CSV means checksum vector, and ALV means allocation vector.

 

It looks like it communicates with the Atari a lot simpler than I expected. It uses memory at $fbf0-$fbfa for the message it wants to send, and just toggles out ($00),a to $ff, waits until the Atari has set $fbf0 to $ff, and then toggles out ($00),a back to zero, copies a byte from $fbf1 to $4daf, reads status from $fbf9, compares with 1 and returns or prints an error (code after ret z) when it's not 1:

 

sub_4c70h:
        ld a,0ffh               ;4c70   3e ff   > . 
        out (000h),a            ;4c72   d3 00   . . 
l4c74h:
        ld a,(0fbf0h)           ;4c74   3a f0 fb        : . . 
        cp 0ffh         ;4c77   fe ff   . . 
        jr nz,l4c74h            ;4c79   20 f9     . 
        ld a,000h               ;4c7b   3e 00   > . 
        out (000h),a            ;4c7d   d3 00   . . 
        ld a,(0fbf1h)           ;4c7f   3a f1 fb        : . . 
        ld (04dfah),a           ;4c82   32 fa 4d        2 . M 
        ld a,(0fbf9h)           ;4c85   3a f9 fb        : . . 
        cp 001h         ;4c88   fe 01   . . 
        ret z                   ;4c8a   c8      . 

 

$fc00 and beyond seems to be used to exchange data. A read key is loaded from there:

 

; end of CONIN
        ld a,(0fc00h)           ;4b3c   3a 00 fc        : . . 
        and 07fh                ;4b3f   e6 7f   . ^? 			; mask out upper bits, 7-bit ASCII
        ret                     ;4b41   c9      . 

 

And even reading a sector is put there:

 

        ld a,(0fbf2h)           ;4c24   3a f2 fb        : . . 
        cp 052h         ;4c27   fe 52   . R 
        jr nz,l4c37h            ;4c29   20 0c     . 
        ld hl,(DMA_STORAGE)             ;4c2b   2a 04 4d        * . M 
        ld de,0fc00h            ;4c2e   11 00 fc        . . . 
        ex de,hl                        ;4c31   eb      . 
        ld bc,00080h            ;4c32   01 80 00        . . . 
        ldir            ;4c35   ed b0   . . 
l4c37h:
        ld a,000h               ;4c37   3e 00   > . 
        ret                     ;4c39   c9      . 

 

Compare $fbf2 with 'R', if not equal, load A with return status 0 and RET (we did a write, read and write share a lot of code). If equal, use LDIR (LDI repeat) to copy 128 (080h) bytes from $fc00 to where DMA_STORAGE points to (set with SET_DMA BIOS call).

 

I think I'll stop here. I hate z80dasm and couldn't find a GUI disassembler. Note that having the command and data at $fbf0-$fc7f would conflict when the BIOS would be moved to upper memory, starting at $fa00, but it looks like Atari didn't get that far yet. The BIOS code stops at $4cff, so they would probably moved that space to $fef0-$ff7f or something.

 

Anyway, the idea of simply toggling to $ff and back to $00 with out (0),a could be reused, and have all other communications done through shared RAM. That would simplify things a lot.

 

Edit: it would be faster if "our" code just read a sector directly to where DMA_STORAGE points instead of the extra copy that's done here.

 

Edited by ivop
  • Like 1
  • Thanks 2
Link to comment
Share on other sites

Yeah cleaning up these files is ugly!  CRs were completely nuked.  You can kind of make things out by replace \t\t with \r\t\t but that makes messes of tables.

 

I have no idea if this table I tried to restore is correct.

 

        DEVICE CODES 00-13 HEX  -  STANDARD I/O
                CPM        ATASCII/ASCII   ATARI OS  IMMEDIATE
      MASKED  DEVICE   CODE CONVERSION IOCB   DEVICE  RETURN     NOTES
      IOBYTE
        00      TTY:    0       YES     -       K:      NO      FOR INPUT
        00      TTY:    8       YES     -       E:      YES     FOR OUTPUT
        01      CRT:    1       YES     (0)     K:      NO      FOR INPUT
        01      CRT:    D       YES     -       S:      YES     FOR OUTPUT
        02      BAT:    -       -       -       -       -       NOT SUPPORTED
        03      UC1:    3       YES     (0)     K:      NO      FOR INPUT
        03      UC1:    9       YES     (8)     E:      YES     FOR OUTPUT
        00      TTY:    4       YES     (0)     K:      NO      INPUTS
        04      RDR:    5       NO      -       R1:     NO
        08      UR1:    6       NO      -       M:      NO
        0C      UR2:    7       NO      -       R2:     NO
        00      TTY:    8       YES     -       E:      YES     OUTPUTS
        10      PUN:    9       NO      (5)     R1:     YES
        20      UP1:    A       NO      (6)     M:      YES
        30      UP2:    B       NO      (7)     R2:     YES
        00      TTY:    C       YES     (8)     E:      YES      OUTPUTS
        40      CRT:    D       YES     -       S:      YES
        80      LPT:    E       YES     -       P:      YES
        C0      UL1:    F       NO      (5)     R1:     YES
                                        (x)     SEE IOCB OF CPM CODE x Most of these assignments are table driven and can be reassigned by ATSTAT.COM. TTY: is reserved to ATARI and is not user changeable.

         DEVICE CODES 80-8F HEX  -   DISK TYPE DEVICES
                 CPM            ATARI OS
              DEVICE    CODE    DEVICE
                A:      80      D1:
                B:      81      D2:
                C:      82      D3:
                D:      83      D4:
                E:      84      RESERVED FOR FUTURE DOUBLE DENSITY FLOPPY DISK
                F:      85         "      "    "      "       "      "     "
                G:      86         "      "    "      "       "      "     "
                H:      87         "      "    "      "       "      "     "
                I:      88      RESERVED FOR FUTURE HARD DISK
                J:      89         "      "    "      "   "
                K:      8A         "      "    "      "   "

 

  • Like 3
  • Thanks 2
Link to comment
Share on other sites

@kheller2  That looks a lot cleaner than my clean-up attempt.  I was also looking at it from the view of somebody who knows almost nothing about CP/M.   🙂

 

@ivop  Please continue!  Any more insights as to how this thing should work is very helpful.  With enough pieces, this puzzle can come back together.   🙂

 

Link to comment
Share on other sites

Here's some more. I made educated guesses whether constants were hex (e.g. 9BH, /100H, etc..) or decimal. Note that the original dumps said /100 when they mean /256 decimal (high byte), hence 100H.

 

Some interesting things.

  • The 64K stuff seems untested, as cold boot points to $FA00 (as expected, start of BIOS, runs BOOT), but the data transfer area is at $FC00, which would overwrite the BIOS.
  • The Atari OS loads 54 sectors of boot code, which is exactly 3*18 sectors, i.e. three tracks. The old Atari CP/M-65 used the same format until the BDOS loader was added and the boot code was reduced to 1 track.
  • CP/M directory starts at sector 55, which corresponds to 3 reserved tracks (see diskdefs for reserved tracks, currently 1 for the atari90 and atarihd formats, change to 3 to match the 1984 Atari format)
  • The Atari boot code uses self modifying code to copy the Z80 code to the memory banks.
  • Their assembler supported inline labels for that (LDA @FROM(X), which would translate to lda from: $1234,x in mads syntax)
  • The equates look like a mix between 20K and 64K equates (e.g. the location of the data transfer area and the command frame).
  • Hardware and Z80 board addresses from 6502 viewpoint!
  • Command frame in mem bank 3 07BF0H translates to 0FBF0H which corresponds to the 20K Z80 BIOS disassembly.
  • Bank numbers are numbered backwards (BANK0=3, BANK3=0)

cpmboot.asm cpmequates.inc

Edited by ivop
  • Like 3
  • Thanks 2
Link to comment
Share on other sites

1 hour ago, kheller2 said:

Yeah cleaning up these files is ugly!  CRs were completely nuked.  You can kind of make things out by replace \t\t with \r\t\t but that makes messes of tables.

 

I have no idea if this table I tried to restore is correct.

Looks good. CP/M supported remapping of devices with the STAT.COM command. Atari had their custom ATSTAT.COM version which would update the BIOS tables. You can see the tables in my disassembly in the data3 block, starting at 4b96h are different mappings for CON, LIST, and READER.

 

I think if a complete rewrite of a much simpler BIOS is done, remapping could be skipped. CP/M-65 doesn't support it either. It's pretty pointless nowadays.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

11 minutes ago, ivop said:

Looks good. CP/M supported remapping of devices with the STAT.COM command. Atari had their custom ATSTAT.COM version which would update the BIOS tables. You can see the tables in my disassembly in the data3 block, starting at 4b96h are different mappings for CON, LIST, and READER.

 

I think if a complete rewrite of a much simpler BIOS is done, remapping could be skipped. CP/M-65 doesn't support it either. It's pretty pointless nowadays.

Simple will be good!  It will be easier to implement.

 

I found the following page, which describes CP/M's extremely simple file system...

https://obsolescence.wixsite.com/obsolescence/cpm-internals

 

The more info we have, the better.   🙂

 

 

 

 

 

  • Like 1
Link to comment
Share on other sites

2 hours ago, reifsnyderb said:

Simple will be good!  It will be easier to implement.

 

I found the following page, which describes CP/M's extremely simple file system...

https://obsolescence.wixsite.com/obsolescence/cpm-internals

 

The more info we have, the better.   🙂

That's a nice overview what CP/M is. Note that the addresses of CCP, BDOS and BIOS are yet again different. It al depends on the platform, and there were a ton of them in the 70's and early 80's. And a lot of them had their own custom disk format. The diskdefs supplied with cpmtools has:

 

$ grep ^diskdef /etc/cpmtools/diskdefs | wc -l

98

 

But there were more. Nevertheless, they were all binary compatible (Intel 8080) which was the main selling point at the time, except for memory requirements for some programs. That changed once systems with Z80's were shipped. The core (BDOS and CCP) were still i8080, but the BIOS and a lot of newer programs were Z80 only (MS Basic, Turbo Pascal, Wordstar). Starting from around 1980 it was more or less a requirement to have a Z80 system to run proper productivity applications.

 

Luckily, except for the BIOS call addresses in BDOS, you can treat BDOS and CCP as binary blobs and just run them. BDOS abstracts the drive based on the Disk Parameter Block in the BIOS, and CCP is the silly command line processor (silly, because rename is REN NEWFILE=OLDFILE, and copy with PIP DEST=SRC, and more).

  • Thanks 1
Link to comment
Share on other sites

Here is some notes that i found.

 

NOTES:
1.    FRAME is set to 0FF to indicate completion of service request.
2.    All IOCB's are allocated as required.
3.    IOCB OFFSETs are 00-IOCB0 .... 70-IOCB7, if the IOCB OFFSET is
         specified as 0FF, then the next    free IOCB will be assigned,
        and the assigned offset will be returned in FCOMD.
4.    Return STATUS is 01 to indicate success, any other code indicates some
        form of error (negative) or warning (positive).
5.    The X and Y registers and the accumulator are set to the call values
        before an execute frame and the values after the execution
        are returned in the call frame.
6.    The minimum transfer is 1 byte the maximum transfer is 1024 bytes,
        except for OS STD frames for which the maximum is 128 bytes.
7.    Atari memory area 2000-38FF is available for CPM users.
8.    For DISK type OS calls, the byte count is ignored before execution, all
        disk transfers are one sector at a time.  The byte count, after
        reads, contains the number of bytes actually in that sector.
9.    FX will be set to the value of the CH (KEY) register for STD-OS calls,
    a value of FF indicates no key depressed.
10.    For Status Commands on output devices, a FSTAT return of 01 implies
         that the device can accept a character.  The DEV STATUS bytes
        will only be valid if the Device Code is not set for
        immediate return.

  • Like 3
  • Thanks 2
Link to comment
Share on other sites

Here's the Atari side of things. This is the main loop that runs to handle requests made by CP/M. Again (twice) they use self-modifying code. See JSR DUMMY. I did not convert weird syntax to mads, like LDA X,LABEL but it's readable as it is. BTW some of their tools seem to be hidden in the CPMFILES* hexdumps. No Z80 source code though.

 

 

cpmmain.asm

Edited by ivop
  • Like 2
  • Thanks 2
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...