SYSTEM14 SYSTEM 14 IS A CP/M SIMULATOR THAT USES THE KAYPRO II DISK FORMAT, WRITTEN FOR THE Z80 PROCESSOR. THE RULE: *********DO NOT CHANGE FLOPPY UNLESS THE PROMPT APPEARS******** I would prefer a disk drive that would not let go of the floppy, but do not know if such a drive exist. This is the only rule and violating it is asking for trouble and getting it. The format is single sided, double density, 40 tracks, 10 sectors/track (0 to 9). In order to increase disk capacity, sidea and sideb commands were added, making it possible to use double sided disks as two separate disks. No boot sector is used in any way, cylinder 0 is free. Directory size is 2kB, allowing for 64 32 byte directory entries. The directory is located at cylinder 1 sector 0 to 3 inclusive. The system boots cold from EPROM. The hardware saves CCP and BDOS for warm boot in the upper 4kB of the video ram. Due to the fact that I never had access to a CP/M machine or a CP/M manual, there are small differences between SYSTEM 14 and CP/M. The only references used by me are the following publications: Introductie in CP/M from CME TWENTE, order 5409.375 by Ir. J.W.Meerma, Ir A.F.van Veen, and Anita Lentfert. 8080/Z80 programming by Prof. Alan R.Miller ISBN 0 471 08124-8 And much later, when it was all written, for modification and correction: Inside CP/M by David E. Cortesi ISBN 0-03-059558-4 And last but not least, one floppy disk in Kaypro II format: Volume 1140 from CP/M user group Holland, for testing the disk read. BIOS services supported: offset: cmd: 0 boot 3 warm boot 6 constat 9 conin 12 conout 15 list 18 punch 21 reader 24 home 27 seldrive 30 setrack 33 setsector 36 setdma 39 read 42 write 45 lststat 48 xlate record ;not implemented 51 save CCP + BDOS in video ram ;not in CP/M 54 get CCP + BDOS from video ram ;not in CP/M 57 setbaud ;not in CP/M Due to lack of information on the subject all BIOS jumps are shifted by 3 bytes. This could be easily corrected, but at the moment only BDOS call are recommended. That is the right way to do it anyway. BDOS services supported: 0 reset SYSTEM 14 1 con in 2 con out 3 reader 4 punch 5 list 6 con i/o direct, no echo 7 get i/o byte 8 set i/o byte 9 con line out 10 con line in 11 con stat 12 get CP/M version ;returns 2.2 13 reset disk 14 select dirve 15 open file 16 close file 17 find first occurrence filename 18 find next occurrence filename 19 delete file 20 read sequential record 21 write sequential record 22 create file 23 rename file 24 get login vector ;not supported 25 set drive 26 set dma address 27 get allocation list address ;not supported 28 write on protected disk ;not supported 29 get read only vector address ;not supported 30 fill file attributes ;not supported 31 get disk param. table address ;not supported 32 get/fill user code ;not supported 33 read random access record 34 write random access record 35 calculate file length ;not supported 36 fill random access field ;not supported 107 make allocation list ;not in CP/M Calculation from fcb to cylinder and sector on disk is completely done by BDOS, no calls are made to BIOS for this, and so only this disk format is supported. Very limited random access was added later, only services 33 and 34 are supported, in order to be able to use the program CCONFIGF.COM, the C compiler configuration program. No guarantees for other use, I have seen errors when using it with other programs, but that could have other reasons. CCP services supported: The following commands are available from the command line, where: < > stands for optional. byte is value from 0 to 255 inclusive. filename is any filename (8 characters), with or without .ext (3 characters). wildcards are * for any name or extention, and ? for any character. Important differences with CP/M: Spaces in the command line are treated as characters, so: save 211 (211 followed by 2 spaces) creates a file . (space.space), it will appear in the directory as . (a full stop only). It can be loaded using load . (load space space optional full stop). Here the first space is the separator, the second space the filename. Since at time of writing a single drive system, references to byte 0 of the file control block setting a drive different then the present one have NO EFFECT. >>SINCE ONLY AN ALLOCATION LIST IS MADE ON THE PRESENT DRIVE, WRITING TO ANOTHER DRIVE WILL MESS UP THE DATA THERE. READING FROM ANY OTHER DRIVE IS OK, BUT BE AWARE THAT BECAUSE OF DISK SECTOR BUFFERING MODIFIED DATA IS FIRST WRITTEN BACK TO DISK. (THAT DISK MUST BE THE ONE THE DATA CAME FROM) To copy files from one to another disk use utility get. That was written in c and works up to the maximum file length. It must be located on the present drive and "gets" a file from the other drive. The syntax is get filename. SYSTEM 14 will not let you make duplicate filenames from the command line. An error message will appear if you try. This also holds true for rename (a file in an existing one or itself). dir __if no arg shows directory of selected drive and side, displays kilobytes left on disk. if argument shows only files specified, and displays kilobytes left on disk if only files specified were on disk. type filename __type (slowly) ASCII file stops if ^z found cls __clears screen, puts input prompt at top iobyte __if no argument displays iobyte, else sets it baud __if no argument displays baudrate settings of serial channel 0 to 2, else sets baudrate of specified channel format __formats floppy disk 2 sides, 40 tracks, 10 sectors/track (0 to 9), 512 bytes sector If side b is selected, only side b is formatted, else both sides a: __selects drive a (floppy drive 5 1/4 inch DD) floppy may be single or double sided) b: __selects ram disk 256 KB, able to hold one disk side c: __not implemented d: __not implemented load __load file specified in TPA, returns number of 256 byte blocks loaded sidea __selects side a of the floppy in drive a sideb __selects side b of the floppy in drive a user __not implemented (sidea and sideb do this) era filename __erases file specified from directory of selected drive and side ren filename filename2 __renames filename with filename2 save byte filename __saves byte 256 byte blocks from TPA on disk under filename , returns 0 if no error. backuprd __makes a (mirror like) copy of the ram disk on the selected side of drive a floppytord __makes a (mirror like) copy of the selected side of drive a on the ram disk Commands filenames and extentions may be in upper or lower case, but filenames and extentions are internally converted to upper case. Only one space MUST separate commands and arguments. On power up the system will try to load the file autoexec.bat in the console buffer, a flag is set and all commands are read from that file (if present) as if they were typed from the console. ^Z returns to con. .bat files are files that are executed as if the commands in these files were typed from the console. IMPORTANT: .bat files cannot be interrupted in any way from the console. Bat files can be chained, one bat file referring to itself, will cause a continuous loop. The length of bat files is limited to 128 bytes. Files with the extention .com are loaded in the TPA at 100h and executed, by a call to 100h. The stack pointer is saved, also bc, de, hl, ix. iy, and the alternate registers are not used. A small stack space is provided if SP is not initialized, but larger programs must set the stack pointer to point to a large enough safe place. User programs must end with jp 0. No interrupts are used in any way by system 14. autoexec.com will work. Control characters: ^C return to system (jump 0), and make an allocation list on present drive and side. ^R repeat last command. ^S stop console output, any key will start it again, but ^S followed by ^C will cause a jump to memory location 0 (warm boot). The system makes an allocation list every time a command is typed from the console, if this command requires a write to disk. 512 byte disk i/o buffers are used, and buffers are written back to disk only if modified. This results in very fast disk i/o. All variables in the system are stored with reference to the IX register. The system initializes IX to 65535. Both registers and (IX-..) variables are used to interchange parameters between FDC driver, BIOS, BDOS, and CCP. The FDC driver is a separate routine that has a fixed entry point for all functions and a fixed address, it was the first routine written. It support the Intel 8072A floppy disk controller in a polling mode. It supports single- and double density, only double density is used. The only functions used by SYSTEM 14 are: format a side read sector write sector List of variables and their use: IX - .. use used by (IX - 0) CCP stack pointer save during call 100h CCP (IX - 1) "" (IX - 2) FDC driver stack pointer save (IX - 3) "" (IX - 4) __ (IX - 5) blocks to be saved CCP (IX - 6) H command line cnt CCP (IX - 7) L "" (IX - 8) H command line pointer CCP (IX - 9) L "" (IX - 10) old drive BDOS (IX - 11) old sector BDOS (IX - 12) old track BDOS (IX - 13) user code BDOS (IX - 14) old head BDOS (IX - 15) temp. variable BDOS (IX - 16) H allocation list address BDOS (IX - 17) L "" (IX - 18) H random access record BDOS (IX - 19) L "" (IX - 20) L FCB start BDOS (IX - 21) H "" (IX - 22) record cnt BDOS (IX - 23) L WRDB cnt BDOS (IX - 24) H "" (IX - 25) L RDDB cnt (pointer in buffer to record) (IX - 26) H "" (IX - 27) L dir ptr, used by get dir BDOS (IX - 28) H "" (IX - 29) __ (IX - 30) __ (IX - 31) __ (IX - 32) __ (IX - 33) __ (IX - 34) L i/o buffer start for dma, used by set dma BDOS (IX - 35) H "" (IX - 36) block cnt read sequential BDOS (IX - 37) horizontal position print head (IX - 38) vertical position printhead (IX - 39) characters / line (IX - 40) last key (IX - 41) data received bytes (IX - 42) checksum (IX - 43) entry cnt extends dir, used by make allocation list BDOS (IX - 44) offset in fcb for extend BDOS (IX - 45) H screen start crtc driver BIOS (IX - 46) L "" (IX - 47) ctc0 reload BIOS (IX - 48) ctc1 reload BIOS (IX - 49) ctc2 reload BIOS (IX - 50) serial channel select for baud rate set BIOS (IX - 51) H baud rate request and return BIOS (IX - 52) L "" (IX - 53) system flags: bit 0: first ??? bit 1: same sector bit 2: extend check zzz bit 3: buffer modified BDOS bit 4: leading 0 CCP bit 5: command CCP bit 6: scroll BIOS bit 7: autoexec.bat CCP BIOS (IX - 54) system flags: bit 0: __ bit 1: __ bit 2: __ bit 3: __ bit 4: bat flag CCP bit 5: printer on BIOS bit 6: __ bit 7: __ (IX - 55) __ (IX - 56) system flags: bit 0: __ bit 1: __ bit 2: __ bit 3: __ bit 4: __ bit 5: __ bit 6: __ bit 7: __ (IX - 57) __ (IX - 58) character code to be displayed BIOS (IX - 59) system flags crtc driver: bit 0: 0 = 80 char. / line BIOS bit 1: 0 = white, 1 = black BIOS bit 2: 0 = hrg, 1 = alfa BIOS bit 3: inverse off bit 2 BIOS bit 4: 0 = crct to video ram, 1 = proc. to v. ram BIOS bit 5: white BIOS bit 6: command arg CCP bit 7: start / stop BIOS (IX - 60) system flags IBM keyboard driver: bit 0: .com file not keyboard BIOS bit 1: autoexec entry not keyboard BIOS bit 2: scroll lock BIOS bit 3: num lock BIOS bit 4: caps lock BIOS bit 5: shift BIOS bit 6: ctrl BIOS bit 7: alt BIOS (IX - 61) L bat buffer ptr CCP BIOS (IX - 62) H "" (IX - 63) horizontal position screen BIOS (IX - 64) vertical position screen BIOS The following variables are used by the fdc driver: (IX - 65) temp, 1 = no motor off (IX - 66) fdc driver one entry function (IX - 67) drive (IX - 68) flags: bit 0: __ bit 1: __ bit 2: HDS bit 3: __ bit 4: __ bit 5: SK skip deleted data address mark bit 6: density bit 7: multitrack (IX - 69) tracks per disk (IX - 70) L bytes total bytes transferred (IX - 71) H "" (IX - 72) interleave (IX - 73) STP scan sector increment (IX - 74) calculating purposes commands (IX - 75) program number (dir menu) old system version (IX - 76) basic flag (16443) CD flag zx81 version ____________________________________________________________________ (IX - 77) C |read (106), write(105), format(113) (IX - 78) H | (IX - 79) R | (IX - 80) EOT | ________________________________________________________________ (IX - 81) __ (IX - 82) L bytes per sector (IX - 83) H "" (IX - 84) IBM diskette (1, 2, 3, 2D, 2D') _____________________________________________________________ (IX - 85) __ (IX - 86) __ (IX - 87) kilo bytes left on disk, used by CCP in dir ____________________________________________________________________ (IX - 88) MT MFM SK COMMAND |sense drive | |read- (IX - 89) 00000 HDS DS1 DS0 |status |seek (15d) |data ________________________________________ |read (6) |write- (IX - 90) C |read ID |data ________________________________________________________ |rd del (IX - 91) H |data (IX - 92) R |wr del (IX - 93) N |data (IX - 94) EOT |read a (IX - 95) GPL (gap 3 size) |track (IX - 96) DTL (special sector size, must be 0FFh for N <> 0) | ____________________________________________________________________________ (IX - 97) COMMAND |specify (3) (IX - 98) SRT HUT step rate interval, head unload time |sense int.- (IX - 99) HLT ND head load time, non dma |status (8) _______________________________________________________________________________ (IX - 100) ST0 (ST3 after 'sense drive status') |disk- (IX - 101) ST1 (C after 'sense interrupt status') |opera- (IX - 102) ST2 |tion (IX - 103) C (IX-94) is SC set by calculate sectors / track, |result (IX - 104) H this is not equal to EOT if first sector is 0. |para- (IX - 105) R |meters (IX - 106) N | (IX - 107) spare disk result parameter _______________________________________________________________________________ (IX - 108) 0 MFM COMMAND | | (IX - 109) HDS drive |recalibrate (7) | ________________________________ | (IX - 110) N sector size code | (IX - 111) SC sectors per track | (IX - 112) GPLF gap 3 length format | (IX - 113) D filler byte |format a track (13d) ________________________________________________________ (IX - 114) L read data buffer pointer (IX - 115) H "" (IX - 116) L write data buffer pointer (IX - 117) H "" (IX - 118) L stack pointer save in fdc driver (IX - 119) H "" (IX - 120) error code returned by fdc driver (IX - 121) DR fdc data register for testing purposes (IX - 122) MSR fdc master status register for testing purposes (IX - 123) data written to fdc dataregister DR for testing purposes (IX - 124) xx (IX - 125) xx (IX - 126) xx (IX - 127) xx Addresses: VAR SPACE 65279d block 254 ALLOCATION LIST 64512d block 252 size 256 bytes DISKIOBUF 64000d block 250 size 512 bytes FDCDRV 0ef00h 61440d block 240 BIOS 0e800h 58880d block 230 BDOS 0e000h 56832d block 222 size 2048 bytes CCP 0d800h 54784d block 214 size 2048 bytes FCB default 56h 92d IOBUF default 80h 128d The file control block and directory entry: FCB+0 drive FCB+1 filename FCB+2 filename FCB+3 filename FCB+4 filename FCB+5 filename FCB+6 filename FCB+7 filename FCB+8 filename FCB+9 ext FCB+10 ext FCB+11 ext FCB+12 extend FCB+13 intern CP/M not used by SYSTEM 14 FCB+14 intern CP/M not used by SYSTEM 14 FCB+15 records in this extend FCB+16 allocation unit (0 to 255) FCB+17 allocation unit (0 to 255) FCB+18 allocation unit (0 to 255) FCB+19 allocation unit (0 to 255) FCB+20 allocation unit (0 to 255) FCB+21 allocation unit (0 to 255) FCB+22 allocation unit (0 to 255) FCB+23 allocation unit (0 to 255) FCB+24 allocation unit (0 to 255) FCB+25 allocation unit (0 to 255) FCB+26 allocation unit (0 to 255) FCB+27 allocation unit (0 to 255) FCB+28 allocation unit (0 to 255) FCB+29 allocation unit (0 to 255) FCB+30 allocation unit (0 to 255) FCB+31 allocation unit (0 to 255) FCB+32 next sequential record FCB+33 L random acces record (bit 0 to 7 inclusive) FCB+34 H "" (bit 8 to 15 inclusive) FCB+35 VH "" (bit 16 to 23 inclusive) System capacity: On one side of a floppy disk 40 * 10 sectors = 400 sectors This is 400 * 512 = 200 Kbyte. Cylinder 0 is not used: 10 sectors = 5 Kbyte, left 195 Kbyte. Directory size is 2 Kbyte, left for data 193 Kbyte. Record size: 128 byte Allocation unit size: 1024 byte (8 records) Extend size: 16 Kbyte (16 allocation units) File size: 256 Kbyte (256 allocation units) The largest file that can be used is 193 Kbyte due to floppy size. The file size is not 256 extends (256 * 16 Kbyte) because the allocation units can only have a number between 0 and 255 in the extend (file control block/ directory entry). Total data capacity on a flopy disk is 2 (sides) * 193 kBytes is 386 Kbytes. THE KEY TO ALL THE DATA The calculation of the absolute cylinder, head, and sector on a disk from the file control block is entirely done by BDOS and performed as follows: Get random access record: RA=(FCB+33)+256*(FCB+34) Calculate extend: E=int(RA/128) get FCB from directory This is also the entry point for read sequential and write sequential, in that case RE (next sequential record) = (FCB+32) The following calculations are performed by routine l162 in BDOS Calculate records into extend: RE=RA-128*E Calculate allocation unit: AE=int(RE/8) Calculate records into allocation unit: RAE=RE-AE*8 In case of read get allocation unit from dir entry: A=(FCB+16+AE) In case of write, search allocation list for free allocation unit and mark in allocation list as used, then store at directory entry (FCB+16+RAE) Calculate record absolute: RA=A*8+RAE Now cylinder (C), start sector (R), and end sector (EOT) can be calculated. EOT is always R, since only one sector at the time is read. The following calculations are performed by routine l228 in BDOS. Calculate cylinder absolute: CA=int(RA/40) Calculate cylinder: C=CA-1 Calculate records into cylinder: RC=RA-40*CA (always < 40) Calculate sectors into cylinder, start sector R, end sector EOT R=int(RC/4) EOT=R Calculate records into sector: RS=RC-R*4 (always < 4) Calculate bytes into sector: BS=128*RS Calculate data pointer into disk iobuffer RDDPTR=DATABUF+BS Then finally 128 bytes (one record) is moved from the address pointed to by RDDPTR to the address specified by setdma (BDOS 26) in read, or the other way in write. HISTORY OF THIS SYSTEM: SIR CLIVE SINCLAIR MANUFACTURED A SMALL Z80 BASED COMPUTER, THE ZX80. First I build an eprom programmer board, with serial io and an EPROM socket. Then, using tape save and load, a floppy disk controller was developed. The software for this was written using the BUGBYTE assembler. Since this assembler hardly provides for comments, there are no comments in the source file. It is all on paper. What is there was added later. Because the ZX80 does not have complete address decoding, funny addresses had to be selected for all io. Also since all interrupts are tight up in the ZX80, no interrupts could safely be used. At first the driver was located at 12288, below the Sinclair BASIC and special save and load routines were included in the driver so that BASIC programs could be saved on and loaded from disk. The Bugbyte assembler has the assembly source code in BASIC REM statements. Next a 80 column card was developed, the character set was hand copied from the Brother EP44 printer. It was at this time the system was written, for one reason that I wanted the programs that I could download via FIDO net, and also programs from the CP/M user group on floppy disk to run on the system. After going over the available formats, and since I at that time only had a single sided floppy drive, the KAYPRO II format was chosen. The system was written in a summer holiday of 4 weeks, week 1: BIOS week 2: BDOS week 3: CCP week 4: probably testing and debugging (until now). Funny enough I have never been able to improve in any way on BDOS. The code runs flawless. An IBM keyboard was added, and a driver for this was written. Later the ZX80 was replaced by a separate processor board, was only using the keyboard anyway, to start the system up. The old sources from the Bugbyte assembler were converted by a conversion program and comments were added here and there, but not everywhere!! Then the ram disk was build and several other cards: sound card teletext processing 27 MHz packet radio interface Due to lack of information about CP/M at first the routine for con line in (BDOS 10) was written using '$' as a delimiter. This was modified for compatibility with CP/M later. Autoexec and bat file were also added. Due to the fact that there are no comments in the BDOS source file, here is the documentation: Most BDOS functions have a label equal to the function number. This label is jumped to via a jump table at the start of BDOS. Exceptions to this are documented in the source file (were added later). label: used description: remark: by: l255: exit l181: rename file l184: jump BIOS BIOS address l179: find extend (n) |a=0 found, l113: F17 find first extend (0) |0ffh=not found l116: F18 find next extend (n+1) | l139: del find first occurrence filename l162: del find next occurrence filename l125: print hex at cupos hl l254: jump BIOS + C, code in E l228: calc C,R,EOT from record absolute l180: multiply de*hl result in hl source ZILOG l229: divide hl/de result in hl source ZILOG l102: calc record absolute from next sequential l112: compare string, ? is wild card a=0 fnd, ff not l117: F15 open file de pnts to fcb l120: F20 read sequential l1??: code to fcb l130: make allocation list l131: F0 warm boot l134: find allocation unit and move to list called by l130 l137: clear allocation list, fills with 255 called by l130 l140: find free alloc. unit in list and mark it used result in a l141: search alloc. list called by l140 l144: find free entry in dir, hl points to entry a=0 fnd, 7fh nt l147: fcb to dir space, extend not affected de pnts to fcb l148: F22 create extend de pnts to fcb l149: F22 make fcb de pnts to fcb l151: F19 delete file de pnts to fcb l153: F16 close file de pnts to fcb l157: compare called by l112 l161: F21 write sequential l170: allocation list update end of doc 29-3-1993 System14 was written around 1986...