diff --git a/Doc/ChangeLog.txt b/Doc/ChangeLog.txt index 601d45be..ba2a36ed 100644 --- a/Doc/ChangeLog.txt +++ b/Doc/ChangeLog.txt @@ -26,6 +26,7 @@ Version 2.9.2 - WBW: Add support for secondry SPI (SD Card) on SC126 - PMS: Add sound support to NASCOM BASIC - WBW: Updated FAT to add MD and FORMAT commands +- WBW: Add CP/M 3 (experimental) Version 2.9.1 ------------- diff --git a/ReadMe.txt b/ReadMe.txt index 82b5053b..899657a3 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -7,7 +7,7 @@ *********************************************************************** Wayne Warthen (wwarthen@gmail.com) -Version 2.9.2-pre.17, 2019-10-11 +Version 2.9.2-pre.18, 2019-10-14 https://www.retrobrewcomputers.org/ RomWBW is a ROM-based implementation of CP/M-80 2.2 and Z-System for @@ -179,6 +179,18 @@ some potentially useful improvements. Please refer to "ZSDOS Manual.pdf" and "ZCPR Manual.pdf" in the Doc directory for more information on Z-System usage. +CP/M 3 +------ + +CP/M 3 exists in an experimental state. CP/M 3 must be started +from a disk drive. In the distribution archive, in the Binary +directory, you will find a cpm_hd.img file that can be copied +over to a CF or SD Card. Start your system with this card +installed and boot to CP/M 2.2 or ZSystem as usual. Switch to +the drive containing the CP/M 3 image and use the CPMLDR command +to load CP/M. It will ask you for the disk unit number containing +the CP/M 3 system files which are on the disk image you created. + ROM Customization ----------------- diff --git a/Source/BuildShared.cmd b/Source/BuildShared.cmd index c2852206..13c23f9d 100644 --- a/Source/BuildShared.cmd +++ b/Source/BuildShared.cmd @@ -7,4 +7,7 @@ setlocal & cd ZCPR && call Build || exit /b 1 & endlocal setlocal & cd ZCPR-DJ && call Build || exit /b 1 & endlocal setlocal & cd ZSDOS && call Build || exit /b 1 & endlocal setlocal & cd CBIOS && call Build || exit /b 1 & endlocal +setlocal & cd CPM3 && call Build || exit /b 1 & endlocal +setlocal & cd ZPM3 && call Build || exit /b 1 & endlocal setlocal & cd Forth && call Build || exit /b 1 & endlocal + diff --git a/Source/CBIOS/ver.inc b/Source/CBIOS/ver.inc index ed69f866..315bcced 100644 --- a/Source/CBIOS/ver.inc +++ b/Source/CBIOS/ver.inc @@ -2,4 +2,4 @@ #DEFINE RMN 9 #DEFINE RUP 2 #DEFINE RTP 0 -#DEFINE BIOSVER "2.9.2-pre.17" +#DEFINE BIOSVER "2.9.2-pre.18" diff --git a/Source/CPM3/@banks.txt b/Source/CPM3/@banks.txt new file mode 100644 index 00000000..7fb0d406 --- /dev/null +++ b/Source/CPM3/@banks.txt @@ -0,0 +1,28 @@ +COMMON 8F ?? +CPMSYS 8E 00 +HBIOS 8D ?? +TPA 8C 01 +BUFS 8B 02 + +8D:7000 -> 8E:0300 + + +00 -> 8E +01 -> 8C +02 -> 8B +03 -> 8A +... + +if bnk = 0, then hbbnk = 8EH (BID_USR) +else hbbnk = 8DH (BID_BIOS) - bnk + + or a + jr z,bank0 + neg ; 2 -> -2 + add 8DH ; 8D - 2 = 8B + jp HBX_SETBNK +bank0: + ld a,(8EH) + jp HBX_SETBNK + ret + diff --git a/Source/CPM3/Build.cmd b/Source/CPM3/Build.cmd new file mode 100644 index 00000000..a8ce11cf --- /dev/null +++ b/Source/CPM3/Build.cmd @@ -0,0 +1,99 @@ +@echo off +setlocal + +set TOOLS=../../Tools + +set PATH=%TOOLS%\zx;%TOOLS%\cpmtools;%PATH% + +set ZXBINDIR=%TOOLS%/cpm/bin/ +set ZXLIBDIR=%TOOLS%/cpm/lib/ +set ZXINCDIR=%TOOLS%/cpm/include/ + +rem cmd + +rem CPM Loader +echo. +echo. +echo *** CPM Loader *** +echo. +zx RMAC -CPMLDR +zx Z80ASM -BIOSLDR/MF +zx LINK -CPMLDR[L100]=CPMLDR,BIOSLDR +rem pause + +rem Resident CPM3 +echo. +echo. +echo *** Resident BIOS *** +echo. +copy optres.lib options.lib +copy genres.dat gencpm.dat +zx RMAC -BIOSKRNL +zx RMAC -SCB +zx Z80ASM -BOOT/MF +zx Z80ASM -CHARIO/MF +zx Z80ASM -MOVE/MF +zx Z80ASM -DRVTBL/MF +zx Z80ASM -DISKIO/MF +zx LINK -BIOS3[OS]=BIOSKRNL,SCB,BOOT,CHARIO,MOVE,DRVTBL,DISKIO +zx GENCPM -AUTO -DISPLAY +copy cpm3.sys cpm3res.sys +rem pause + +rem Banked CPM3 +echo. +echo. +echo *** Banked BIOS *** +echo. +copy optbnk.lib options.lib +copy genbnk.dat gencpm.dat +zx RMAC -BIOSKRNL +zx RMAC -SCB +zx Z80ASM -BOOT/MF +zx Z80ASM -CHARIO/MF +zx Z80ASM -MOVE/MF +zx Z80ASM -DRVTBL/MF +zx Z80ASM -DISKIO/MF +zx LINK -BNKBIOS3[B]=BIOSKRNL,SCB,BOOT,CHARIO,MOVE,DRVTBL,DISKIO +zx GENCPM -AUTO -DISPLAY +copy cpm3.sys cpm3bnk.sys +rem pause + +rem *** Resident *** +rem copy cpm3res.sys cpm3.sys +rem copy genres.dat getcpm.dat + +rem *** Banked *** +copy cpm3bnk.sys cpm3.sys +copy genbnk.dat gencpm.dat + +rem Update cpm_hd.img +echo. +echo. +echo *** Update Disk Image *** +echo. +for %%f in ( + cpmldr.com + ccp.com + gencpm.com + genres.dat + genbnk.dat + bios3.spr + bnkbios3.spr + bdos3.spr + bnkbdos3.spr + resbdos3.spr + cpm3res.sys + cpm3bnk.sys + gencpm.dat + cpm3.sys + readme.1st + cpm3fix.pat +) do call :upd_img %%f +goto :eof + +:upd_img +echo %1... +cpmrm.exe -f wbw_hd0 ../../Binary/hd_cpm3.img 0:%1 +cpmcp.exe -f wbw_hd0 ../../Binary/hd_cpm3.img %1 0:%1 +goto :eof \ No newline at end of file diff --git a/Source/CPM3/Clean.cmd b/Source/CPM3/Clean.cmd new file mode 100644 index 00000000..d308082d --- /dev/null +++ b/Source/CPM3/Clean.cmd @@ -0,0 +1,13 @@ +@echo off +setlocal + +if exist bios3.spr del bios3.spr +if exist bnkbios3.spr del bnkbios3.spr +if exist *.rel del *.rel +if exist cpmldr.com del cpmldr.com +if exist *.err del *.err +if exist *.lst del *.lst +if exist *.sym del *.sym +if exist *.sys del *.sys +if exist gencpm.dat del gencpm.dat +if exist options.lib del options.lib diff --git a/Source/CPM3/bdos3.spr b/Source/CPM3/bdos3.spr new file mode 100644 index 00000000..728fe2af Binary files /dev/null and b/Source/CPM3/bdos3.spr differ diff --git a/Source/CPM3/bioskrnl.asm b/Source/CPM3/bioskrnl.asm new file mode 100644 index 00000000..7056e025 --- /dev/null +++ b/Source/CPM3/bioskrnl.asm @@ -0,0 +1,673 @@ + title 'Root module of relocatable BIOS for CP/M 3.0' + + ; version 1.0 15 Sept 82 + + maclib options + + +; Copyright (C), 1982 +; Digital Research, Inc +; P.O. Box 579 +; Pacific Grove, CA 93950 + + +; This is the invariant portion of the modular BIOS and is +; distributed as source for informational purposes only. +; All desired modifications should be performed by +; adding or changing externally defined modules. +; This allows producing "standard" I/O modules that +; can be combined to support a particular system +; configuration. + +cr equ 13 +lf equ 10 +bell equ 7 +ctlQ equ 'Q'-'@' +ctlS equ 'S'-'@' + +ccp equ 0100h ; Console Command Processor gets loaded into the TPA + + cseg ; GENCPM puts CSEG stuff in common memory + + + ; variables in system data page + + extrn @covec,@civec,@aovec,@aivec,@lovec ; I/O redirection vectors + extrn @mxtpa ; addr of system entry point + extrn @bnkbf ; 128 byte scratch buffer + + ; initialization + + extrn ?init ; general initialization and signon + extrn ?ldccp,?rlccp ; load & reload CCP for BOOT & WBOOT + + ; user defined character I/O routines + + extrn ?ci,?co,?cist,?cost ; each take device in + extrn ?cinit ; (re)initialize device in + extrn @ctbl ; physical character device table + + ; disk communication data items + + extrn @dtbl ; table of pointers to XDPHs + public @adrv,@rdrv,@trk,@sect ; parameters for disk I/O + public @dma,@dbnk,@cnt ; '' '' '' '' + + ; memory control + + public @cbnk ; current bank + extrn ?xmove,?move ; select move bank, and block move + extrn ?bank ; select CPU bank + + ; clock support + + extrn ?time ; signal time operation + + ; general utility routines + + public ?pmsg,?pdec ; print message, print number from 0 to 65535 + public ?pderr ; print BIOS disk error message header + + maclib modebaud ; define mode bits + + + ; External names for BIOS entry points + + public ?boot,?wboot,?const,?conin,?cono,?list,?auxo,?auxi + public ?home,?sldsk,?sttrk,?stsec,?stdma,?read,?write + public ?lists,?sctrn + public ?conos,?auxis,?auxos,?dvtbl,?devin,?drtbl + public ?mltio,?flush,?mov,?tim,?bnksl,?stbnk,?xmov + + + ; BIOS Jump vector. + + ; All BIOS routines are invoked by calling these + ; entry points. + +?boot: jmp boot ; initial entry on cold start +?wboot: jmp wboot ; reentry on program exit, warm start + +?const: jmp const ; return console input status +?conin: jmp conin ; return console input character +?cono: jmp conout ; send console output character +?list: jmp list ; send list output character +?auxo: jmp auxout ; send auxilliary output character +?auxi: jmp auxin ; return auxilliary input character + +?home: jmp home ; set disks to logical home +?sldsk: jmp seldsk ; select disk drive, return disk parameter info +?sttrk: jmp settrk ; set disk track +?stsec: jmp setsec ; set disk sector +?stdma: jmp setdma ; set disk I/O memory address +?read: jmp read ; read physical block(s) +?write: jmp write ; write physical block(s) + +?lists: jmp listst ; return list device status +?sctrn: jmp sectrn ; translate logical to physical sector + +?conos: jmp conost ; return console output status +?auxis: jmp auxist ; return aux input status +?auxos: jmp auxost ; return aux output status +?dvtbl: jmp devtbl ; return address of device def table +?devin: jmp ?cinit ; change baud rate of device + +?drtbl: jmp getdrv ; return address of disk drive table +?mltio: jmp multio ; set multiple record count for disk I/O +?flush: jmp flush ; flush BIOS maintained disk caching + +?mov: jmp ?move ; block move memory to memory +?tim: jmp ?time ; Signal Time and Date operation +?bnksl: jmp bnksel ; select bank for code execution and default DMA +?stbnk: jmp setbnk ; select different bank for disk I/O DMA operations. +?xmov: jmp ?xmove ; set source and destination banks for one operation + + jmp 0 ; reserved for future expansion + jmp 0 ; reserved for future expansion + jmp 0 ; reserved for future expansion + + + ; BOOT + ; Initial entry point for system startup. + + dseg ; this part can be banked + +boot: + lxi sp,boot$stack + mvi c,15 ; initialize all 16 character devices +c$init$loop: + push b ! call ?cinit ! pop b + dcr c ! jp c$init$loop + + call ?init ; perform any additional system initialization + ; and print signon message + + lxi b,16*256+0 ! lxi h,@dtbl ; init all 16 logical disk drives +d$init$loop: + push b ; save remaining count and abs drive + mov e,m ! inx h ! mov d,m ! inx h ; grab @drv entry + mov a,e ! ora d ! jz d$init$next ; if null, no drive + push h ; save @drv pointer + xchg ; XDPH address in + dcx h ! dcx h ! mov a,m ! sta @RDRV ; get relative drive code + mov a,c ! sta @ADRV ; get absolute drive code + dcx h ; point to init pointer + mov d,m ! dcx h ! mov e,m ; get init pointer + xchg ! call ipchl ; call init routine + pop h ; recover @drv pointer +d$init$next: + pop b ; recover counter and drive # + inr c ! dcr b ! jnz d$init$loop ; and loop for each drive + jmp boot$1 + + cseg ; following in resident memory + +boot$1: + call set$jumps + call ?ldccp ; fetch CCP for first time + jmp ccp + + + ; WBOOT + ; Entry for system restarts. + +wboot: + lxi sp,boot$stack + call set$jumps ; initialize page zero + call ?rlccp ; reload CCP + jmp ccp ; then reset jmp vectors and exit to ccp + + +set$jumps: + + if banked + mvi a,1 ! call ?bnksl + endif + + mvi a,JMP + sta 0 ! sta 5 ; set up jumps in page zero + lxi h,?wboot ! shld 1 ; BIOS warm start entry + lhld @MXTPA ! shld 6 ; BDOS system call entry + + mvi a,JMP ! sta 8 ; set up HBIOS RST 08 + lxi h,0FFF0H ! shld 9 ; jump vector + + ;mvi a,3 ! sta 4 ; default drive is C: + +; xor a +; ld hl,40h +; ld b,16 +;set$jumps1: +; ld (hl),a +; inc hl +; djnz set$jumps1 + + xra a + lxi h,40h + mvi b,10h +set$jumps1: + mov m,a + inx h + dcr b + jnz set$jumps1 + + ret + + + ds 64 +boot$stack equ $ + + + ; DEVTBL + ; Return address of character device table + +devtbl: + lxi h,@ctbl ! ret + + + ; GETDRV + ; Return address of drive table + +getdrv: + lxi h,@dtbl ! ret + + + + ; CONOUT + ; Console Output. Send character in + ; to all selected devices + +conout: + + lhld @covec ; fetch console output bit vector + jmp out$scan + + + ; AUXOUT + ; Auxiliary Output. Send character in + ; to all selected devices + +auxout: + lhld @aovec ; fetch aux output bit vector + jmp out$scan + + + ; LIST + ; List Output. Send character in + ; to all selected devices. + +list: + lhld @lovec ; fetch list output bit vector + +out$scan: + mvi b,0 ; start with device 0 +co$next: + dad h ; shift out next bit + jnc not$out$device + push h ; save the vector + push b ; save the count and character +not$out$ready: + call coster ! ora a ! jz not$out$ready + pop b ! push b ; restore and resave the character and device + call ?co ; if device selected, print it + pop b ; recover count and character + pop h ; recover the rest of the vector +not$out$device: + inr b ; next device number + mov a,h ! ora l ; see if any devices left + jnz co$next ; and go find them... + ret + + + ; CONOST + ; Console Output Status. Return true if + ; all selected console output devices + ; are ready. + +conost: + lhld @covec ; get console output bit vector + jmp ost$scan + + + ; AUXOST + ; Auxiliary Output Status. Return true if + ; all selected auxiliary output devices + ; are ready. + +auxost: + lhld @aovec ; get aux output bit vector + jmp ost$scan + + + ; LISTST + ; List Output Status. Return true if + ; all selected list output devices + ; are ready. + +listst: + lhld @lovec ; get list output bit vector + +ost$scan: + mvi b,0 ; start with device 0 +cos$next: + dad h ; check next bit + push h ; save the vector + push b ; save the count + mvi a,0FFh ; assume device ready + cc coster ; check status for this device + pop b ; recover count + pop h ; recover bit vector + ora a ; see if device ready + rz ; if any not ready, return false + inr b ; drop device number + mov a,h ! ora l ; see if any more selected devices + jnz cos$next + ori 0FFh ; all selected were ready, return true + ret + +coster: ; check for output device ready, including optional + ; xon/xoff support + mov l,b ! mvi h,0 ; make device code 16 bits + push h ; save it in stack + dad h ! dad h ! dad h ; create offset into device characteristics tbl + lxi d,@ctbl+6 ! dad d ; make address of mode byte + mov a,m ! ani mb$xonxoff + pop h ; recover console number in + jz ?cost ; not a xon device, go get output status direct + lxi d,xofflist ! dad d ; make pointer to proper xon/xoff flag + call cist1 ; see if this keyboard has character + mov a,m ! cnz ci1 ; get flag or read key if any + cpi ctlq ! jnz not$q ; if its a ctl-Q, + mvi a,0FFh ; set the flag ready +not$q: + cpi ctls ! jnz not$s ; if its a ctl-S, + mvi a,00h ; clear the flag +not$s: + mov m,a ; save the flag + call cost1 ; get the actual output status, + ana m ; and mask with ctl-Q/ctl-S flag + ret ; return this as the status + +cist1: ; get input status with and saved + push b ! push h + call ?cist + pop h ! pop b + ora a + ret + +cost1: ; get output status, saving & + push b ! push h + call ?cost + pop h ! pop b + ora a + ret + +ci1: ; get input, saving & + push b ! push h + call ?ci + pop h ! pop b + ret + + + ; CONST + ; Console Input Status. Return true if + ; any selected console input device + ; has an available character. + +const: + lhld @civec ; get console input bit vector + jmp ist$scan + + + ; AUXIST + ; Auxiliary Input Status. Return true if + ; any selected auxiliary input device + ; has an available character. + +auxist: + lhld @aivec ; get aux input bit vector + +ist$scan: + mvi b,0 ; start with device 0 +cis$next: + dad h ; check next bit + mvi a,0 ; assume device not ready + cc cist1 ; check status for this device + ora a ! rnz ; if any ready, return true + inr b ; drop device number + mov a,h ! ora l ; see if any more selected devices + jnz cis$next + xra a ; all selected were not ready, return false + ret + + + ; CONIN + ; Console Input. Return character from first + ; ready console input device. + +conin: + lhld @civec + jmp in$scan + + + ; AUXIN + ; Auxiliary Input. Return character from first + ; ready auxiliary input device. + +auxin: + lhld @aivec + +in$scan: + push h ; save bit vector + mvi b,0 +ci$next: + dad h ; shift out next bit + mvi a,0 ; insure zero a (nonexistant device not ready). + cc cist1 ; see if the device has a character + ora a + jnz ci$rdy ; this device has a character + inr b ; else, next device + mov a,h ! ora l ; see if any more devices + jnz ci$next ; go look at them + pop h ; recover bit vector + jmp in$scan ; loop til we find a character + +ci$rdy: + pop h ; discard extra stack + jmp ?ci + + +; Utility Subroutines + + +ipchl: ; vectored CALL point + pchl + + +?pmsg: ; print message @ up to a null + ; saves & + push b + push d +pmsg$loop: + mov a,m ! ora a ! jz pmsg$exit + mov c,a ! push h + call ?cono ! pop h + inx h ! jmp pmsg$loop +pmsg$exit: + pop d + pop b + ret + +?pdec: ; print binary number 0-65535 from + lxi b,table10! lxi d,-10000 +next: + mvi a,'0'-1 +pdecl: + push h! inr a! dad d! jnc stoploop + inx sp! inx sp! jmp pdecl +stoploop: + push d! push b + mov c,a! call ?cono + pop b! pop d +nextdigit: + pop h + ldax b! mov e,a! inx b + ldax b! mov d,a! inx b + mov a,e! ora d! jnz next + ret + +table10: + dw -1000,-100,-10,-1,0 + +?pderr: + lxi h,drive$msg ! call ?pmsg ; error header + lda @adrv ! adi 'A' ! mov c,a ! call ?cono ; drive code + lxi h,track$msg ! call ?pmsg ; track header + lhld @trk ! call ?pdec ; track number + lxi h,sector$msg ! call ?pmsg ; sector header + lhld @sect ! call ?pdec ; sector number + ret + + + ; BNKSEL + ; Bank Select. Select CPU bank for further execution. + +bnksel: + sta @cbnk ; remember current bank + jmp ?bank ; and go exit through users + ; physical bank select routine + + +xofflist db -1,-1,-1,-1,-1,-1,-1,-1 ; ctl-s clears to zero + db -1,-1,-1,-1,-1,-1,-1,-1 + + + + dseg ; following resides in banked memory + + + +; Disk I/O interface routines + + + ; SELDSK + ; Select Disk Drive. Drive code in . + ; Invoke login procedure for drive + ; if this is first select. Return + ; address of disk parameter header + ; in + +seldsk: + mov a,c ! sta @adrv ; save drive select code + mov l,c ! mvi h,0 ! dad h ; create index from drive code + lxi b,@dtbl ! dad b ; get pointer to dispatch table + mov a,m ! inx h ! mov h,m ! mov l,a ; point at disk descriptor + ora h ! rz ; if no entry in table, no disk + mov a,e ! ani 1 ! jnz not$first$select ; examine login bit + push h ! xchg ; put pointer in stack & + lxi h,-2 ! dad d ! mov a,m ! sta @RDRV ; get relative drive + lxi h,-6 ! dad d ; find LOGIN addr + mov a,m ! inx h ! mov h,m ! mov l,a ; get address of LOGIN routine + call ipchl ; call LOGIN + pop h ; recover DPH pointer +not$first$select: + ret + + + ; HOME + ; Home selected drive. Treated as SETTRK(0). + +home: + lxi b,0 ; same as set track zero + + + ; SETTRK + ; Set Track. Saves track address from + ; in @TRK for further operations. + +settrk: + mov l,c ! mov h,b + shld @trk + ret + + + ; SETSEC + ; Set Sector. Saves sector number from + ; in @sect for further operations. + +setsec: + mov l,c ! mov h,b + shld @sect + ret + + + ; SETDMA + ; Set Disk Memory Address. Saves DMA address + ; from in @DMA and sets @DBNK to @CBNK + ; so that further disk operations take place + ; in current bank. + +setdma: + mov l,c ! mov h,b + shld @dma + + lda @cbnk ; default DMA bank is current bank + ; fall through to set DMA bank + + ; SETBNK + ; Set Disk Memory Bank. Saves bank number + ; in @DBNK for future disk data + ; transfers. + +setbnk: + sta @dbnk + ret + + + ; SECTRN + ; Sector Translate. Indexes skew table in + ; with sector in . Returns physical sector + ; in . If no skew table (=0) then + ; returns physical=logical. + +sectrn: + mov l,c ! mov h,b + mov a,d ! ora e ! rz + xchg ! dad b ! mov l,m ! mvi h,0 + ret + + + ; READ + ; Read physical record from currently selected drive. + ; Finds address of proper read routine from + ; extended disk parameter header (XDPH). + +read: + lhld @adrv ! mvi h,0 ! dad h ; get drive code and double it + lxi d,@dtbl ! dad d ; make address of table entry + mov a,m ! inx h ! mov h,m ! mov l,a ; fetch table entry + push h ; save address of table + lxi d,-8 ! dad d ; point to read routine address + jmp rw$common ; use common code + + + ; WRITE + ; Write physical sector from currently selected drive. + ; Finds address of proper write routine from + ; extended disk parameter header (XDPH). + +write: + lhld @adrv ! mvi h,0 ! dad h ; get drive code and double it + lxi d,@dtbl ! dad d ; make address of table entry + mov a,m ! inx h ! mov h,m ! mov l,a ; fetch table entry + push h ; save address of table + lxi d,-10 ! dad d ; point to write routine address + +rw$common: + mov a,m ! inx h ! mov h,m ! mov l,a ; get address of routine + pop d ; recover address of table + dcx d ! dcx d ; point to relative drive + ldax d ! sta @rdrv ; get relative drive code and post it + inx d ! inx d ; point to DPH again + pchl ; leap to driver + + + ; MULTIO + ; Set multiple sector count. Saves passed count in + ; @CNT + +multio: + sta @cnt ! ret + + + ; FLUSH + ; BIOS deblocking buffer flush. Not implemented. + +flush: + xra a ! ret ; return with no error + + + + ; error message components +drive$msg db cr,lf,bell,'BIOS Error on ',0 +track$msg db ': T-',0 +sector$msg db ', S-',0 + + + ; disk communication data items + +@adrv ds 1 ; currently selected disk drive +@rdrv ds 1 ; controller relative disk drive +@trk ds 2 ; current track number +@sect ds 2 ; current sector number +@dma ds 2 ; current DMA address +@cnt db 0 ; record count for multisector transfer +@dbnk db 0 ; bank for DMA operations + + + cseg ; common memory + +@cbnk db 0 ; bank for processor operations + + + end diff --git a/Source/CPM3/biosldr.z80 b/Source/CPM3/biosldr.z80 new file mode 100644 index 00000000..3b7e0751 --- /dev/null +++ b/Source/CPM3/biosldr.z80 @@ -0,0 +1,480 @@ + + maclib cpm3.lib + + cseg + +; BIOS Jump vector. + +; All BIOS routines are invoked by calling these +; entry points. + +?boot: jp boot ; initial entry on cold start +?wboot: jp wboot ; reentry on program exit, warm start + +?const: jp const ; return console input status +?conin: jp conin ; return console input character +?cono: jp conout ; send console output character +?list: jp list ; send list output character +?auxo: jp auxout ; send auxilliary output character +?auxi: jp auxin ; return auxilliary input character + +?home: jp home ; set disks to logical home +?sldsk: jp seldsk ; select disk drive, return disk parameter info +?sttrk: jp settrk ; set disk track +?stsec: jp setsec ; set disk sector +?stdma: jp setdma ; set disk I/O memory address +?read: jp read ; read physical block(s) +?write: jp write ; write physical block(s) + +?lists: jp listst ; return list device status +?sctrn: jp sectrn ; translate logical to physical sector + +?conos: jp conost ; return console output status +?auxis: jp auxist ; return aux input status +?auxos: jp auxost ; return aux output status +?dvtbl: jp devtbl ; return address of device def table +?devin: jp devini ; change baud rate of device + +?drtbl: jp drvtbl ; return address of disk drive table +?mltio: jp multio ; set multiple record count for disk I/O +?flush: jp flush ; flush BIOS maintained disk caching + +?mov: jp move ; block move memory to memory +?tim: jp time ; Signal Time and Date operation +?bnksl: jp selmem ; select bank for code execution and default DMA +?stbnk: jp setbnk ; select different bank for disk I/O DMA operations. +?xmov: jp xmove ; set source and destination banks for one operation + + jp 0 ; reserved for future expansion + jp 0 ; reserved for future expansion + jp 0 ; reserved for future expansion + +boot: + ;ld bc,0F8E0h ; HBIOS func: get boot info + ;call 0FFF0h ; do it, D := boot unit + ;ld a,d ; move to A + ;ld (unit),a ; save it + ;ret + + ld (stksav),sp + ld sp,stack + +boot1: + ld de,prompt + call writestr + + call cin + call cout + + push af + ld de,crlf + call writestr + pop af + + sub '0' + jr c,boot1 + cp 10 ; !!! Need to test against max disk unit num !!! + jr nc,boot1 + + ld (unit),a + + ld bc,0F9E0h ; HBIOS func: set boot info + ld d,a ; Unit + ld e,0 ; Slice + ld l,0 ; Bank + call 0FFF0h ; do it + + + + ld a,(unit) ; Get boot unit + ld c,a ; put in C + ld b,18h ; HBIOS Media function + ld e,1 ; Enabled media check/discovery + call 0FFF0H ; HBIOS call + ld a,e ; Resultant media id to accum + or a ; Set flags + ;halt + ; + ; !!! Need to do something on error !!! + ; + ret z ; Bail out on error + + ld hl,dpb$start - dpb$sz + ld de,dpb$sz + ld b,a ; loop count +dsk$login1: + add hl,de ; next dpb + djnz dsk$login1 ; loop as needed + + ; hl is ptr to desired dpb + ld de,dph0 ; load DPH pointer + ;halt + ex de,hl ; de = DPB adr, hl = DPH adr + push de ; save DPB adr + ld de,12 ; offset of DPB in DPH + add hl,de ; hl = adr of DPB field in DPH + pop de ; recover DPB adr + ld (hl),e ; update LSB + inc hl + ld (hl),d ; udpate MSB + + + + ld sp,(stksav) + ret + +wboot: + ld a,81H + halt + +const: + ld a,82H + halt +conin: + ld bc,0000H ; unit 0, func 0 = CIN + call 0FFF0H + +conout: + ld e,c ; output character in E + ld bc,0100H ; unit 0, func 1 = COUT + ;rst 08 ; do it + call 0FFF0H + ret ; return +list: + ld a,85H + halt +auxout: + ld a,86H + halt +auxin: + ld a,87H + halt + +home: + ld hl,0 + ld (trk),hl + ret +seldsk: + ld hl,dph0 + ret +settrk: + ld (trk),bc + ret +setsec: + ld (sect),bc + ret +setdma: + ld (dma),bc + ret +read: + ; Seek + ld hl,(trk) ; get track value + ld a,l ; lsb of track to a + and 0FH ; isolate head in low 4 bits + ld d,a ; stuff it in d + ld a,(sect) ; get sector + ld e,a ; stuff it in e + ld b,4 ; prepare to shift out 4 bit head value +read1: + srl h ; shift one bit out + rr l ; ... of hl + djnz read1 ; do all 4 bits + ld b,12h ; HBIOS seek + ld a,(unit) ; get boot unit + ld c,a ; put in C + ;rst 08 ; perform seek + call 0FFF0H + ; Read Sector + ld b,13h ; HBIOS read + ld a,(unit) ; get boot unit + ld c,a ; put in C + ld hl,(dma) ; dma address + ld a,(0FFE0H) ; current bank + ld d,a ; ... to D + ld e,1 ; 1 sector + ;rst 08 + call 0FFF0H + + ret +write: + ld a,8EH + halt + +listst: + ld a,8FH + halt +sectrn: + ld h,b + ld l,c + ret + +conost: + ld a,91H + halt +auxist: + ld a,92H + halt +auxost: + ld a,93H + halt +devtbl: + ld a,94H + halt +devini: + ld a,95H + halt + +drvtbl: + ld a,96H + halt +multio: + ld a,97H + halt +flush: + ld a,98H + halt + +move: + ex de,hl ; we are passed source in DE and dest in HL + ldir ; use Z80 block move instruction + ex de,hl ; need next addresses in same regs + ret +time: + ld a,9AH + halt +selmem: + ld a,9BH + halt +setbnk: + ld a,9CH + halt +xmove: + ld a,9DH + halt + +cin: + ; save incoming registers (af is output) + push bc + push de + push hl + + ; input character from console via hbios + ld c,0D0H ; console unit to c + ld b,00H ; hbios func: input char + call 0FFF0H ; hbios reads character + ld a,e ; move character to a for return + + ; restore registers (af is output) + pop hl + pop de + pop bc + ret + +cout: + ; save all incoming registers + push af + push bc + push de + push hl + + ; output character to console via hbios + ld e,a ; output char to e + ld c,0D0H ; console unit to c + ld b,01H ; hbios func: output char + call 0FFF0H ; hbios outputs character + + ; restore all registers + pop hl + pop de + pop bc + pop af + ret + +writestr: + push af +writestr1: + ld a,(de) + cp '$' ; test for string terminator + jp z,writestr2 + call cout + inc de + jp writestr1 +writestr2: + pop af + ret + +prompt db 13,10,'Boot CP/M 3 from Disk Unit: $' +crlf db 13,10,'$' + +dpb$start: +dpb$rom: ; 384K ROM Drive + dw 64 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 1 ; exm: extent mask + dw 192 - 1 ; dsm: total storage in blocks - 1 = (384kb / 2k bls) - 1 = 191 + dw 256 - 1 ; drm: dir entries - 1 = 256 - 1 = 255 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 8000h ; cks: directory check vector size - permanent storage = 8000H + dw 0 ; off: reserved tracks = 16 trks * (16 trks * 16 heads * 16 secs * 512 bytes) = 128k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 +dpb$sz equ $ - dpb$start + +dpb$ram: ; 256K RAM Drive + dw 64 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 1 ; exm: extent mask + dw 128 - 1 ; dsm: total storage in blocks - 1 = (256kb / 2k bls) - 1 = 127 + dw 256 - 1 ; drm: dir entries - 1 = 256 - 1 = 255 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 8000h ; cks: directory check vector size - permanent storage = 8000H + dw 0 ; off: reserved tracks = 16 trks * (16 trks * 16 heads * 16 secs * 512 bytes) = 128k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb$rf: ; 4MB RAM Floppy Drive + dw 64 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 0 ; exm: extent mask + dw 2047 ; dsm: total storage in blocks - 1 = (4mb / 2k bls) - 1 = 2047 + dw 255 ; drm: dir entries - 1 = 256 - 1 = 255 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 8000h ; cks: directory check vector size - permanent storage = 8000H + dw 0 ; off: reserved tracks = 0 trks + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb$hd: ; 8MB Hard Disk Drive + dw 64 ; spt: sectors per track + db 5 ; bsh: block shift factor + db 31 ; blm: block mask + db 1 ; exm: extent mask + dw 2047 ; dsm: total storage in blocks - 1 = (8mb / 4k bls) - 1 = 2047 + dw 511 ; drm: dir entries - 1 = 512 - 1 = 511 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 8000h ; cks: directory check vector size - permanent storage = 8000H + dw 16 ; off: reserved tracks = 16 trks * (16 trks * 16 heads * 16 secs * 512 bytes) = 128k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb$fd720: ; 3.5" DS/DD Floppy Drive (720K) + dw 36 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 0 ; exm: extent mask + dw 350 ; dsm: total storage in blocks - 1 blk = ((720k - 18k off) / 2k bls) - 1 = 350 + dw 127 ; drm: dir entries - 1 = 128 - 1 = 127 + db 11000000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 32 ; cks: directory check vector size = 128 / 4 + dw 4 ; off: reserved tracks = 4 trks * (512 b/sec * 36 sec/trk) = 18k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb_fd144: ; 3.5" DS/HD Floppy Drive (1.44M) + dw 72 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 0 ; exm: extent mask + dw 710 ; dsm: total storage in blocks - 1 blk = ((1,440k - 18k off) / 2k bls) - 1 = 710 + dw 255 ; drm: dir entries - 1 = 256 - 1 = 255 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 64 ; cks: directory check vector size = 256 / 4 + dw 2 ; off: reserved tracks = 2 trks * (512 b/sec * 72 sec/trk) = 18k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb_fd360: ; 5.25" DS/DD Floppy Drive (360K) + dw 36 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 1 ; exm: extent mask + dw 170 ; dsm: total storage in blocks - 1 blk = ((360k - 18k off) / 2k bls) - 1 = 170 + dw 127 ; drm: dir entries - 1 = 128 - 1 = 127 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 32 ; cks: directory check vector size = 128 / 4 + dw 4 ; off: reserved tracks = 4 trks * (512 b/sec * 36 sec/trk) = 18k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb_fd120: ; 5.25" DS/HD Floppy Drive (1.2M) + dw 60 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 0 ; exm: extent mask + dw 591 ; dsm: total storage in blocks - 1 blk = ((1,200k - 15k off) / 2k bls) - 1 = 591 + dw 255 ; drm: dir entries - 1 = 256 - 1 = 255 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 64 ; cks: directory check vector size = 256 / 4 + dw 2 ; off: reserved tracks = 2 trks * (512 b/sec * 60 sec/trk) = 15k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb_fd111: ; 8" DS/DD Floppy Drive (1.11M) + dw 60 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 0 ; exm: extent mask + dw 569 ; dsm: total storage in blocks - 1 blk = ((1,155k - 15k off) / 2k bls) - 1 = 569 + dw 255 ; drm: dir entries - 1 = 256 - 1 = 255 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 64 ; cks: directory check vector size = 256 / 4 + dw 2 ; off: reserved tracks = 2 trks * (512 b/sec * 60 sec/trk) = 15k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dph0: dw 0 ; xlt, 0 means no translation + db 0,0,0,0,0,0,0,0,0 ; scratch (9 bytes) + db 0 ; mf: media flag + dw dpb$hd ; dpb + dw csvbuf ; csv: + dw alvbuf ; alv: + dw dirbcb ; dirbcb + dw dtabcb ; dtabcb + dw 0ffffh ; hash (disabled) + db 0 ; hbank + +dtbl: dtbl dph0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + +dirbcb: db 0ffh ; drv + db 0,0,0 ; rec# + db 0 ; wflg + db 0 ; scratch + dw 0 ; track + dw 0 ; sector + dw dirbuf ; buffad + +dtabcb: db 0ffh ; drv + db 0,0,0 ; rec# + db 0 ; wflg + db 0 ; scratch + dw 0 ; track + dw 0 ; sector + dw dtabuf ; buffad + +unit ds 1 ; HBIOS unit number +trk ds 2 ; current track +sect ds 2 ; current sector +dma ds 2 ; current DMA address + +csvbuf ds 128 ; length (CSV) = ((DRM+1)/4) +alvbuf ds 512 ; length (ALV) = ((DSM+1)/4) +dirbuf ds 512 ; sector buffer +dtabuf ds 512 ; sector buffer + + ds 32 +stack equ $ +stksav dw 0 + + end \ No newline at end of file diff --git a/Source/CPM3/bnkbdos3.spr b/Source/CPM3/bnkbdos3.spr new file mode 100644 index 00000000..97c8aee4 Binary files /dev/null and b/Source/CPM3/bnkbdos3.spr differ diff --git a/Source/CPM3/boot.z80 b/Source/CPM3/boot.z80 new file mode 100644 index 00000000..a0a7b995 --- /dev/null +++ b/Source/CPM3/boot.z80 @@ -0,0 +1,362 @@ + title 'Boot loader module for CP/M 3.0' + + maclib options.lib + + public ?init,?ldccp,?rlccp,?time + public @bootdu + extrn ?pmsg,?conin + extrn ?mvinit,?bnkxlt,?xmove,?move + extrn @civec,@covec,@aivec,@aovec,@lovec + extrn @cbnk,?bnksl,?bank + extrn @sysdr,@ccpdr + extrn dph0 + extrn @dtbl + +bdos equ 5 + + if banked +tpa$bank equ 1 + else +tpa$bank equ 0 + endif + + dseg ; init done from banked memory + +?init: + call ?mvinit +; lxi h,08000h ! shld @civec ! shld @covec ; assign console to CRT: + ld hl,8000H ; device 0 + ld (@civec),hl ; assign to console input + ld (@covec),hl ; assign to console output +; lxi h,04000h ! shld @lovec ; assign printer to LPT: +; lxi h,02000h ! shld @aivec ! shld @aovec ; assign AUX to CRT1: +; lxi h,signon$msg ! call ?pmsg ; print signon message + ld hl,signon$msg ; signon message + call ?pmsg ; print it + + if banked + + ; clone page zero from bank 0 to additional banks + ld b,2 ; last bank + ld c,0 ; src bank +init$0: + push bc ; save bank id's + call init$1 ; copy page zero + pop bc ; restore bank id's + djnz init$0 ; loop till done + jr init$2 + +init$1: + call ?xmove ; set src/dest banks + ld bc,0100h ; size is one page + ld hl,0 ; dest adr is 0 + ld de,0 ; src adr is 0 + call ?move ; do it + ret + + endif + +init$2: + ; get boot disk unit and save it + ld bc,0F8E0h ; HBIOS func: get boot info + call 0FFF0h ; do it, D := boot unit + ld a,d ; move to A + ld (@bootdu),a ; save it + + call dinit + ret + +dinit: + + ; loop through all disk devices to count hard disk units + ld b,0F8h ; SYS GET + ld c,010h ; Disk Drive Unit Count + call 0FFF0h ; e := disk unit count + ld b,e ; count to b + ld a,b ; count to a + or a ; set flags + ret z ; !!! handle zero devices (albeit poorly) !!! +; + ; loop thru devices to count total hard disk volumes + push bc ; save the device count + ld c,0 ; use c as device list index + ld e,0 ; init e for hard disk volume count +; +dinit2: + push bc ; save loop control + call dinit3 ; check drive + pop bc ; restore loop control + inc c ; next unit + djnz dinit2 ; loop + pop bc ; restore unit count in b + jr dinit4 ; continue +; +dinit3: + push de ; save de (hard disk volume counter) + ld b,017h ; hbios func: report device info + call 0FFF0h ; call hbios, unit to c + ld a,d ; device type to a + pop de ; restore de + cp 050h ; hard disk device? + ret c ; nope, return + inc e ; increment hard disk count + ret ; and return +; +dinit4: ; set slices per volume (hdspv) based on hard disk volume count + ld a,e ; hard disk volume count to a + ld e,8 ; assume 8 slices per volume + dec a ; dec accum to check for count = 1 + jr z,dinit5 ; yes, skip ahead to implement 8 hdspv + ld e,4 ; now assume 4 slices per volume + dec a ; dec accum to check for count = 2 + jr z,dinit5 ; yes, skip ahead to implement 4 hdspv + ld e,2 ; in all other cases, we use 2 hdspv +; +dinit5: + ld a,e ; slices per volume value to accum + ld (hdspv),a ; save it +; + ; setup to enumerate devices to build drvmap + ld b,0F8h ; SYS GET + ld c,010h ; Disk Drive Unit Count + call 0FFF0h ; e := disk unit count + ld b,e ; count to b + ld c,0 ; use c as device list index + ;ld hl,dph0 ; point to first dph + ld hl,0 ; dph index +; +dinit6: ; loop thru all units available + push bc ; preserve loop control + push hl ; preserve dph pointer + ld b,017h ; hbios func: report device info + call 0FFF0h ; call hbios, d := device type + pop hl ; restore dph pointer + pop bc ; get unit index back in c + push bc ; resave loop control + call dinit7 ; update dph entries + pop bc ; restore loop control + inc c ; increment list index + djnz dinit6 ; loop as needed + + ; zero out remaining dph table entries + ld a,16 ; dph table entries + sub l ; subtract entries used + ret z ; return if all entries used + ld b,a ; save as loop counter + ld a,l ; current dph to accum + rlca ; *2 for word entry + ld hl,@dtbl ; start of dtbl + call addhla ; hl now points to entry +dinit6a: + xor a ; zero accum + ld (hl),a ; zero lsb + inc hl ; next byte + ld (hl),a ; zero msb + inc hl ; next byte + djnz dinit6a + + ret ; finished +; +dinit7: ; process unit + ld e,0 ; initialize slice index + ld b,1 ; default loop counter + ld a,d ; device type to accum + ld d,c ; unit number to d + cp 050h ; hard disk device? + jr c,dinit8 ; nope, leave loop count at 1 + ld a,(hdspv) ; get slices per volume to accum + ld b,a ; move to b for loop counter +; +dinit8: + ; d=unit, e=slice, l=dph# + ld a,l ; dph # to accum + cp 16 ; dph table size + ret z ; bail out if overflow + push hl ; save dph # + rlca ; *2 for adr entry + ld hl,@dtbl ; dph table start + call addhla ; offset hl to desired entry + ld a,(hl) ; dereference + inc hl + ld h,(hl) + ld l,a + dec hl ; backup to slice field + ld (hl),e ; update slice number + dec hl ; backup to unit number + ld (hl),d ; update unit number + inc e ; next slice + pop hl ; restore dph # + inc hl ; next dph # + djnz dinit8 ; loop till done with unit + ret + +addhla: + add a,l + ld l,a + ret nc + inc h + ret + + + cseg ; boot loading most be done from resident memory + + ; This version of the boot loader loads the CCP from a file + ; called CCP.COM on the system drive (A:). + +?ldccp: + ; First time, load the A:CCP.COM file into TPA + ld a,(@sysdr) ; get system boot drive + inc a ; drive + 1 for FCB + ld (ccp$fcb),a ; stuff into FCB + add 'A' - 1 ; drive letter + ld (ccp$msg$drv),a ; save for load msg + xor a + ld (ccp$fcb+15),a + ld hl,0 + ld (fcb$nr),hl + ld de,ccp$fcb + call open + inc a + jr z,no$CCP + ld de,0100H + call setdma + ld de,128 + call setmulti + ld de,ccp$fcb + call read + + if banked + +; ; now, +; ; copy CCP to bank 0 for reloading +; lxi h,0100h ! lxi b,0C80h ; clone 3K, just in case +; lda @cbnk ! push psw ; save current bank +;ld$1: +; mvi a,tpa$bank ! call ?bnksl ; select TPA +; mov a,m ! push psw ; get a byte +; mvi a,2 ! call ?bnksl ; select extra bank +; pop psw ! mov m,a ; save the byte +; inx h ! dcx b ; bump pointer, drop count +; mov a,b ! ora c ; test for done +; jnz ld$1 +; pop psw ! call ?bnksl ; restore original bank + +; ; now, +; ; copy CCP to bank 0 for reloading + ld hl,0100h ; clone 3K, just in case + ld bc,0C80h + ld a,(@cbnk) ; save current bank + push af +ld$1: + ld a,tpa$bank ; select TPA + call ?bnksl + ld a,(hl) ; get a byte + push af + ld a,2 ; select extra bank + call ?bnksl + pop af ; save the byte + ld (hl),a + inc hl ; bump pointer, drop count + dec bc + ld a,b ; test for done + or c + jr nz,ld$1 + pop af ; restore original bank + call ?bnksl + + endif + + ; Force CCP to use system boot drive as initial default + ld a,(@sysdr) ; get system boot drive + ld (@ccpdr),a ; set CCP current drive + + ret + +no$CCP: ; here if we couldn't find the file + ld hl,ccp$msg + call ?pmsg + call ?conin + jp ?ldccp + + +?rlccp: + + if banked + +; lxi h,0100h ! lxi b,0C00h ; clone 3K +;rl$1: +; mvi a,2 ! call ?bnksl ; select extra bank +; mov a,m ! push psw ; get a byte +; mvi a,tpa$bank ! call ?bnksl ; select TPA +; pop psw ! mov m,a ; save the byte +; inx h ! dcx b ; bump pointer, drop count +; mov a,b ! ora c ; test for done +; jnz rl$1 +; ret + + ld hl,0100h ; clone 3K + ld bc,0C80h +rl$1: + ld a,2 ; select extra bank + call ?bnksl + ld a,(hl) ; get a byte + push af + ld a,tpa$bank ; select TPA + call ?bnksl + pop af ; save the byte + ld (hl),a + inc hl ; bump pointer, drop count + dec bc + ld a,b ; test for done + or c + jr nz,rl$1 + ret + + else + + jr ?ldccp + + endif + + ; No external clock. +?time: + ret + + ; CP/M BDOS Function Interfaces + +open: + ld c,15 + jp bdos + +setdma: + ld c,26 + jp bdos + +setmulti: + ld c,44 + jp bdos + +read: + ld c,20 + jp bdos + + +signon$msg db 13,10,'CP/M v3.0' + if banked + db ' [BANKED]' + endif + db ', HBIOS v2.9.2',13,10,13,10,0 + +ccp$msg db 13,10,'BIOS Err on ' +ccp$msg$drv db '?' + db ': No CCP.COM file',0 + + +ccp$fcb db 0,'CCP ','COM',0,0,0,0 + ds 16 +fcb$nr db 0,0,0 + +@bootdu db 0 +hdspv db 2 ; slices per volume for hard disks (must be >= 1) + + end diff --git a/Source/CPM3/ccp.com b/Source/CPM3/ccp.com new file mode 100644 index 00000000..3934d840 Binary files /dev/null and b/Source/CPM3/ccp.com differ diff --git a/Source/CPM3/chario.z80 b/Source/CPM3/chario.z80 new file mode 100644 index 00000000..27c78616 --- /dev/null +++ b/Source/CPM3/chario.z80 @@ -0,0 +1,193 @@ + title 'Character I/O handler for z80 chip based system' + +; Character I/O for the Modular CP/M 3 BIOS + + public ?cinit,?ci,?co,?cist,?cost + public @ctbl + +; maclib Z80 ; define Z80 op codes +; maclib ports ; define port addresses + maclib modebaud.lib ; define mode bits and baud equates + +max$devices equ 6 + + cseg + +?cinit: + ret +; mov a,c ! cpi max$devices ! jz cent$init ; init parallel printer +; rnc ; invalid device +; mov l,c ! mvi h,0 ; make 16 bits from device number +; push h ; save device in stack +; dad h ! dad h ! dad h ; *8 +; lxi d,@ctbl+7 ! dad d ! mov l,m ; get baud rate +; mov a,l ! cpi baud$600 ; see if baud > 300 +; mvi a,44h ! jnc hi$speed ; if >= 600, use *16 mode +; mvi a,0C4h ; else, use *64 mode +;hi$speed: +; sta sio$reg$4 +; mvi h,0 ! lxi d,speed$table ! dad d ; point to counter entry +; mov a,m ! sta speed ; get and save ctc count +; pop h ; recover +; lxi d,data$ports ! dad d ; point at SIO port address +; mov a,m ! inr a ! sta sio$port ; get and save port +; lxi d,baud$ports-data$ports ! dad d ; offset to baud rate port +; mov a,m ! sta ctc$port ; get and save +; lxi h,serial$init$tbl +; jmp stream$out +; +;cent$init: +; lxi h,pio$init$tbl +; +;stream$out: +; mov a,m ! ora a ! rz +; mov b,a ! inx h ! mov c,m ! inx h +; outir +; jmp stream$out + + +?ci: ; character input + ld bc,0000H ; unit 0, func 0 = CIN + rst 08 ; do it + ld a,e ; put char in A + ret ; done + +; mov a,b ! cpi 6 ! jnc null$input ; can't read from centronics +;ci1: +; call ?cist ! jz ci1 ; wait for character ready +; dcr c ! inp a ; get data +; ani 7Fh ; mask parity +; ret +; +;null$input: +; mvi a,1Ah ; return a ctl-Z for no device +; ret + +?cist: ; character input status + ld bc,0200H ; unit 0, func 2 = IST + rst 08 ; do it + or a ; set flags + ret z ; return w/ ZF set if no char ready + or 0FFH ; else signal nothing ready + ret ; done + +; mov a,b ! cpi 6 ! jnc null$status ; can't read from centronics +; mov l,b ! mvi h,0 ; make device number 16 bits +; lxi d,data$ports ! dad d ; make pointer to port address +; mov c,m ! inr c ; get SIO status port +; inp a ; read from status port +; ani 1 ; isolate RxRdy +; rz ; return with zero +; ori 0FFh +; ret + +;null$status: +; xra a ! ret + +?co: ; character output + ld e,c ; char to E + ld bc,0100H ; unit 0, func 1 = COUT + rst 08 ; do it + ret ; done + +; mov a,b ! cpi 6 ! jz centronics$out +; jnc null$output +; mov a,c ! push psw ; save character from +; push b ; save device number +;co$spin: +; call ?cost ! jz co$spin ; wait for TxEmpty +; pop h ! mov l,h ! mvi h,0 ; get device number in +; lxi d,data$ports ! dad d ; make address of port address +; mov c,m ; get port address +; pop psw ! outp a ; send data +;null$output: +; ret +; +;centronics$out: +; in p$centstat ! ani 20h ! jnz centronics$out +; mov a,c ! out p$centdata ; give printer data +; in p$centstat ! ori 1 ! out p$centstat ; set strobe +; ani 7Eh ! out p$centstat ; clear strobe +; ret + +?cost: ; character output status + ld bc,0300H ; unit 0, func 3 = OST + rst 08 ; do it + or a ; set flags + ret z ; return w/ ZF set if not ready to send + or 0FFH ; else signal nothing ready + ret ; done + + +; mov a,b ! cpi 6 ! jz cent$stat +; jnc null$status +; mov l,b ! mvi h,0 +; lxi d,data$ports ! dad d +; mov c,m ! inr c +; inp a ; get input status +; ani 4 ! rz ; test transmitter empty +; ori 0FFh ! ret ; return true if ready +; +; +;cent$stat: +; in p$centstat ! cma +; ani 20h ! rz +; ori 0FFh ! ret + +;baud$ports: ; CTC ports by physical device number +; db p$baud$con1,p$baud$lpt1,p$baud$con2,p$baud$con34 +; db p$baud$con34,p$baud$lpt2 +; +;data$ports: ; serial base ports by physical device number +; db p$crt$data,p$lpt$data,p$con2data,p$con3data +; db p$con4data,p$lpt2data + + +@ctbl db 'COM0 ' + db mb$in$out+mb$serial+baud$none + db baud$none + db 0 + +;@ctbl db 'CRT ' ; device 0, CRT port 0 +; db mb$in$out+mb$serial+mb$softbaud +; db baud$9600 +; db 'LPT ' ; device 1, LPT port 0 +; db mb$in$out+mb$serial+mb$softbaud+mb$xonxoff +; db baud$9600 +; db 'CRT1 ' ; device 2, CRT port 1 +; db mb$in$out+mb$serial+mb$softbaud +; db baud$9600 +; db 'CRT2 ' ; device 3, CRT port 2 +; db mb$in$out+mb$serial+mb$softbaud +; db baud$9600 +; db 'CRT3 ' ; device 4, CRT port 3 +; db mb$in$out+mb$serial+mb$softbaud +; db baud$9600 +; db 'VAX ' ; device 5, LPT port 1 used for VAX interface +; db mb$in$out+mb$serial+mb$softbaud +; db baud$9600 +; db 'CEN ' ; device 6, Centronics parallel printer +; db mb$output +; db baud$none +; db 0 ; table terminator + + +;speed$table db 0,255,255,255,233,208,104,208,104,69,52,35,26,17,13,7 +; +;serial$init$tbl +; db 2 ; two bytes to CTC +;ctc$port ds 1 ; port address of CTC +; db 47h ; CTC mode byte +;speed ds 1 ; baud multiplier +; db 7 ; 7 bytes to SIO +;sio$port ds 1 ; port address of SIO +; db 18h,3,0E1h,4 +;sio$reg$4 ds 1 +; db 5,0EAh +; db 0 ; terminator +; +;pio$init$tbl db 2,p$zpio$2b,0Fh,07h +; db 3,p$zpio$2a,0CFh,0F8h,07h +; db 0 + + end diff --git a/Source/CPM3/cpm3.lib b/Source/CPM3/cpm3.lib new file mode 100644 index 00000000..6b45a43d --- /dev/null +++ b/Source/CPM3/cpm3.lib @@ -0,0 +1,180 @@ +; Macro Definitions for CP/M3 BIOS Data Structures. + + ; dtbl - drive table + + ; dph translate$table, - disk parameter header + ; disk$parameter$block, + ; checksum$size, (optional) + ; alloc$size (optional) + + ; skew sectors, - skew table + ; skew$factor, + ; first$sector$number + + ; dpb physical$sector$size, - disk parameter block + ; physical$sectors$per$track, + ; number$tracks, + ; block$size, + ; number$dir$entries, + ; track$offset, + ; checksum$vec$size (optional) + + +; Drive Table. Contains 16 one word entries. + +dtbl macro ?list + local ?n +?n aset 0 + irp ?drv, +?n aset ?n+1 + dw ?drv + endm + + if ?n > 16 +.' Too many drives. Max 16 allowed' + exitm + endif + + if ?n < 16 + rept (16-?n) + dw 0 + endm + endif + endm + +dph macro ?trans,?dpb,?csize,?asize + local ?csv,?alv + dw ?trans ; translate table address + db 0,0,0,0,0,0,0,0,0 ; BDOS Scratch area + db 0 ; media flag + dw ?dpb ; disk parameter block + if not nul ?csize + dw ?csv ; checksum vector + else + dw 0FFFEh ; checksum vector allocated by + endif ; GENCPM + if not nul ?asize + dw ?alv ; allocation vector + else + dw 0FFFEh ; alloc vector allocated by GENCPM + endif + dw 0fffeh,0fffeh,0fffeh ; dirbcb, dtabcb, hash allocd + ; by GENCPM + db 0 ; hash bank + + if not nul ?csize +?csv ds ?csize ; checksum vector + endif + if not nul ?asize +?alv ds ?asize ; allocation vector + endif + + endm + +dpb macro ?psize,?pspt,?trks,?bls,?ndirs,?off,?ncks + local ?spt,?bsh,?blm,?exm,?dsm,?drm,?al0,?al1,?cks,?psh,?psm + local ?n +;; physical sector mask and physical sector shift + ?psh aset 0 + ?n aset ?psize/128 + ?psm aset ?n-1 + rept 8 + ?n aset ?n/2 + if (?n = 0) + exitm + endif + ?psh aset ?psh + 1 + endm + ?spt aset ?pspt*(?psize/128) + + ?bsh aset 3 + ?n aset ?bls/1024 + rept 8 + ?n aset ?n/2 + if (?n = 0) + exitm + endif + ?bsh aset ?bsh + 1 + endm + ?blm aset ?bls/128-1 + ?size aset (?trks-?off)*?spt + ?dsm aset ?size/(?bls/128)-1 + + ?exm aset ?bls/1024 + if ?dsm > 255 + if ?bls = 1024 +.'Error, can''t have this size disk with 1k block size' + exitm + endif + ?exm aset ?exm/2 + endif + ?exm aset ?exm-1 + ?all aset 0 + ?n aset (?ndirs*32+?bls-1)/?bls + rept ?n + ?all aset (?all shr 1) or 8000h + endm + ?al0 aset high ?all + ?al1 aset low ?all + ?drm aset ?ndirs-1 + if not nul ?ncks + ?cks aset ?ncks + else + ?cks aset ?ndirs/4 + endif + dw ?spt ; 128 byte records per track + db ?bsh,?blm ; block shift and mask + db ?exm ; extent mask + dw ?dsm ; maximum block number + dw ?drm ; maximum directory entry number + db ?al0,?al1 ; alloc vector for directory + dw ?cks ; checksum size + dw ?off ; offset for system tracks + db ?psh,?psm ; physical sector size shift + ; and mask + endm + +; +gcd macro ?m,?n + ;; greatest common divisor of m,n + ;; produces value gcdn as result + ;; (used in sector translate table generation) + ?gcdm aset ?m ;;variable for m + ?gcdn aset ?n ;;variable for n + ?gcdr aset 0 ;;variable for r + rept 65535 + ?gcdx aset ?gcdm/?gcdn + ?gcdr aset ?gcdm - ?gcdx*?gcdn + if ?gcdr = 0 + exitm + endif + ?gcdm aset ?gcdn + ?gcdn aset ?gcdr + endm + endm + +skew macro ?secs,?skf,?fsc +;; generate the translate table + ?nxtsec aset 0 ;;next sector to fill + ?nxtbas aset 0 ;;moves by one on overflow + gcd %?secs,?skf + ;; ?gcdn = gcd(?secs,skew) + ?neltst aset ?secs/?gcdn + ;; neltst is number of elements to generate + ;; before we overlap previous elements + ?nelts aset ?neltst ;;counter + rept ?secs ;;once for each sector + db ?nxtsec+?fsc + ?nxtsec aset ?nxtsec+?skf + if ?nxtsec >= ?secs + ?nxtsec aset ?nxtsec-?secs + endif + ?nelts aset ?nelts-1 + if ?nelts = 0 + ?nxtbas aset ?nxtbas+1 + ?nxtsec aset ?nxtbas + ?nelts aset ?neltst + endif + endm + endm + \ No newline at end of file diff --git a/Source/CPM3/cpm3fix.pat b/Source/CPM3/cpm3fix.pat new file mode 100644 index 00000000..b60a2720 Binary files /dev/null and b/Source/CPM3/cpm3fix.pat differ diff --git a/Source/CPM3/cpmldr.asm b/Source/CPM3/cpmldr.asm new file mode 100644 index 00000000..7849afb1 --- /dev/null +++ b/Source/CPM3/cpmldr.asm @@ -0,0 +1,1572 @@ + title 'CP/M V3.0 Loader' + + +; Copyright (C) 1982 +; Digital Research +; Box 579, Pacific Grove +; California, 93950 + +; Revised: +; 01 Nov 82 by Bruce Skidmore + +base equ $ +abase equ base-0100h + +cr equ 0dh +lf equ 0ah + +fcb equ abase+005ch ;default FCB address +buff equ abase+0080h ;default buffer address + +; +; System Equates +; +resetsys equ 13 ;reset disk system +printbuf equ 09 ;print string +open$func equ 15 ;open function +read$func equ 20 ;read sequential +setdma$func equ 26 ;set dma address +; +; Loader Equates +; +comtop equ abase+80h +comlen equ abase+81h +bnktop equ abase+82h +bnklen equ abase+83h +osentry equ abase+84h + + cseg + + lxi sp,stackbot + + call bootf ;first call is to Cold Boot + + mvi c,resetsys ;Initialize the System + call bdos + + mvi c,printbuf ;print the sign on message + lxi d,signon + call bdos + + mvi c,open$func ;open the CPM3.SYS file + lxi d,cpmfcb + call bdos + cpi 0ffh + lxi d,openerr + jz error + + lxi d,buff + call setdma$proc + + call read$proc ;read the load record + + lxi h,buff + lxi d,mem$top + mvi c,6 +cloop: + mov a,m + stax d + inx d + inx h + dcr c + jnz cloop + + call read$proc ;read display info + + mvi c,printbuf ;print the info + lxi d,buff + call bdos + +; +; Main System Load +; + +; +; Load Common Portion of System +; + lda res$len + mov h,a + lda mem$top + call load +; +; Load Banked Portion of System +; + lda bank$len + ora a + jz execute + mov h,a + lda bank$top + call load +; +; Execute System +; +execute: + lxi h,fcb+1 + mov a,m + cpi '$' + jnz execute$sys + inx h + mov a,m + cpi 'B' + cz break +execute$sys: + lxi sp,osentry$adr + ret + +; +; Load Routine +; +; Input: A = Page Address of load top +; H = Length in pages of module to read +; +load: + ora a ;clear carry + mov d,a + mvi e,0 + mov a,h + ral + mov h,a ;h = length in records of module +loop: + xchg + lxi b,-128 + dad b ;decrement dma address by 128 + xchg + push d + push h + call setdma$proc + call read$proc + pop h + pop d + dcr h + jnz loop + ret + +; +; Set DMA Routine +; +setdma$proc: + mvi c,setdma$func + call bdos + ret + +; +; Read Routine +; +read$proc: + mvi c,read$func ;Read the load record + lxi d,cpmfcb ;into address 80h + call bdos + ora a + lxi d,readerr + rz +; +; Error Routine +; +error: + mvi c,printbuf ;print error message + call bdos + di + hlt + +break: + db 0ffh + ret + +cpmfcb: + db 0,'CPM3 SYS',0,0,0,0,0,0 + dw 0,0,0,0,0,0,0,0,0 + +openerr: + db cr,lf + db 'CPMLDR error: failed to open CPM3.SYS' + db cr,lf,'$' + +readerr: + db cr,lf + db 'CPMLDR error: failed to read CPM3.SYS' + db cr,lf,'$' + +signon: + db cr + db lf,lf,lf,lf,lf,lf,lf,lf,lf,lf,lf,lf + db lf,lf,lf,lf,lf,lf,lf,lf,lf,lf,lf,lf + db 'CP/M V3.0 Loader',cr,lf + db 'Copyright (C) 1998, Caldera Inc. ' + db cr,lf,'$' + maclib makedate + @BDATE ;[JCE] Build date + db 0,0,0,0 +stackbot: + +mem$top: + ds 1 +res$len: + ds 1 +bank$top: + ds 1 +bank$len: + ds 1 +osentry$adr: + ds 2 + +; title 'CP/M 3.0 LDRBDOS Interface, Version 3.1 Nov, 1982' +;***************************************************************** +;***************************************************************** +;** ** +;** B a s i c D i s k O p e r a t i n g S y s t e m ** +;** ** +;** I n t e r f a c e M o d u l e ** +;** ** +;***************************************************************** +;***************************************************************** +; +; Copyright (c) 1978, 1979, 1980, 1981, 1982 +; Digital Research +; Box 579, Pacific Grove +; California +; +; Nov 1982 +; +; +; equates for non graphic characters +; + +rubout equ 7fh ; char delete +tab equ 09h ; tab char +cr equ 0dh ; carriage return +lf equ 0ah ; line feed +ctlh equ 08h ; backspace + + +; +serial: db 0,0,0,0,0,0 +; +; Enter here from the user's program with function number in c, +; and information address in d,e +; + +bdos: +bdose: ; Arrive here from user programs + xchg! shld info! xchg ; info=de, de=info + + mov a,c! cpi 14! jc bdose2 + sta fx ; Save disk function # + xra a! sta dircnt + lda seldsk! sta olddsk ; Save seldsk + +bdose2: + mov a,e! sta linfo ; linfo = low(info) - don't equ + lxi h,0! shld aret ; Return value defaults to 0000 + shld resel ; resel = 0 + ; Save user's stack pointer, set to local stack + dad sp! shld entsp ; entsp = stackptr + + lxi sp,lstack ; local stack setup + + lxi h,goback ; Return here after all functions + push h ; jmp goback equivalent to ret + mov a,c! cpi nfuncs! jnc high$fxs ; Skip if invalid # + mov c,e ; possible output character to c + lxi h,functab! jmp bdos$jmp + + ; look for functions 100 -> +high$fxs: + sbi 100! jc lret$eq$ff ; Skip if function < 100 + +bdos$jmp: + + mov e,a! mvi d,0 ; de=func, hl=.ciotab + dad d! dad d! mov e,m! inx h! mov d,m ; de=functab(func) + lhld info ; info in de for later xchg + xchg! pchl ; dispatched + + +; dispatch table for functions + +functab: + dw func$ret, func1, func2, func3 + dw func$ret, func$ret, func6, func$ret + dw func$ret, func9, func10, func11 +diskf equ ($-functab)/2 ; disk funcs + dw func12,func13,func14,func15 + dw func16,func17,func18,func19 + dw func20,func21,func22,func23 + dw func24,func25,func26,func27 + dw func28,func29,func30,func31 + dw func32,func33,func34,func35 + dw func36,func37,func38,func39 + dw func40,func42,func43 + dw func44,func45,func46,func47 + dw func48,func49,func50 +nfuncs equ ($-functab)/2 + + +entsp: ds 2 ; entry stack pointer + + ; 40 level stack + + dw 0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h + dw 0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h + dw 0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h + dw 0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h + dw 0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h +lstack: + + +page + title 'CP/M 3.0 LDRBDOS Interface, Version 3.1 July, 1982' +;***************************************************************** +;***************************************************************** +;** ** +;** B a s i c D i s k O p e r a t i n g S y s t e m ** +;** ** +;** C o n s o l e P o r t i o n ** +;** ** +;***************************************************************** +;***************************************************************** +; +; July, 1982 +; +; +; console handlers + +conout: + ;compute character position/write console char from C + ;compcol = true if computing column position + lda compcol! ora a! jnz compout + ;write the character, then compute the column + ;write console character from C + push b ;recall/save character + call conoutf ;externally, to console + pop b ;recall the character + compout: + mov a,c ;recall the character + ;and compute column position + lxi h,column ;A = char, HL = .column + cpi rubout! rz ;no column change if nulls + inr m ;column = column + 1 + cpi ' '! rnc ;return if graphic + ;not graphic, reset column position + dcr m ;column = column - 1 + mov a,m! ora a! rz ;return if at zero + ;not at zero, may be backspace or eol + mov a,c ;character back to A + cpi ctlh! jnz notbacksp + ;backspace character + dcr m ;column = column - 1 + ret + + notbacksp: + ;not a backspace character, eol? + cpi lf! rnz ;return if not + ;end of line, column = 0 + mvi m,0 ;column = 0 + ret +; +; +tabout: + ;expand tabs to console + mov a,c! cpi tab! jnz conout ;direct to conout if not + ;tab encountered, move to next tab pos + tab0: + mvi c,' '! call conout ;another blank + lda column! ani 111b ;column mod 8 = 0 ? + jnz tab0 ;back for another if not + ret +; +print: + ;print message until M(BC) = '$' + LXI H,OUTDELIM + ldax b! CMP M! rz ;stop on $ + ;more to print + inx b! push b! mov c,a ;char to C + call tabout ;another character printed + pop b! jmp print +; +; +func2: equ tabout + ;write console character with tab expansion +; +func9: + ;write line until $ encountered + xchg ;was lhld info + mov c,l! mov b,h ;BC=string address + jmp print ;out to console +; +sta$ret: + ;store the A register to aret + sta aret +func$ret: + ret ;jmp goback (pop stack for non cp/m functions) +; +setlret1: + ;set lret = 1 + mvi a,1! jmp sta$ret +; +func1: equ func$ret +; +func3: equ func$ret +; +func6: equ func$ret +; +func10: equ func$ret +func11: equ func$ret +; +; data areas +; + + +compcol:db 0 ;true if computing column position +; end of BDOS Console module + +;********************************************************************** +;***************************************************************** +; +; Error Messages + +md equ 24h + +err$msg: db cr,lf,'BDOS ERR: ',md +err$select: db 'Select',md +err$phys: db 'Perm.',md + +;***************************************************************** +;***************************************************************** +; +; common values shared between bdosi and bdos + + +aret: ds 2 ; address value to return +lret equ aret ; low(aret) + +;***************************************************************** +;***************************************************************** +;** ** +;** b a s i c d i s k o p e r a t i n g s y s t e m ** +;** ** +;***************************************************************** +;***************************************************************** + +; literal constants + +true equ 0ffh ; constant true +false equ 000h ; constant false +enddir equ 0ffffh ; end of directory +byte equ 1 ; number of bytes for "byte" type +word equ 2 ; number of bytes for "word" type + +; fixed addresses in low memory + +tbuff equ 0080h ; default buffer location + +; error message handlers + +sel$error: + ; report select error + lxi b,err$msg + call print + lxi b,err$select + jmp goerr1 + +goerr: + lxi b,err$msg + call print + lxi b,err$phys +goerr1: + call print + di ! hlt + +bde$e$bde$m$hl: + mov a,e! sub l! mov e,a + mov a,d! sbb h! mov d,a + rnc! dcr b! ret + +bde$e$bde$p$hl: + mov a,e! add l! mov e,a + mov a,d! adc h! mov d,a + rnc! inr b! ret + +shl3bv: + inr c +shl3bv1: + dcr c! rz + dad h! adc a! jmp shl3bv1 + +compare: + ldax d! cmp m! rnz + inx h! inx d! dcr c! rz + jmp compare + +; +; local subroutines for bios interface +; + +move: + ; Move data length of length c from source de to + ; destination given by hl + inr c ; in case it is zero + move0: + dcr c! rz ; more to move + ldax d! mov m,a ; one byte moved + inx d! inx h ; to next byte + jmp move0 + +selectdisk: + ; Select the disk drive given by register D, and fill + ; the base addresses curtrka - alloca, then fill + ; the values of the disk parameter block + mov c,d ; current disk# to c + ; lsb of e = 0 if not yet logged - in + call seldskf ; hl filled by call + ; hl = 0000 if error, otherwise disk headers + mov a,h! ora l! rz ; Return with C flag reset if select error + ; Disk header block address in hl + mov e,m! inx h! mov d,m! inx h ; de=.tran + inx h ! inx h + shld curtrka! inx h! inx h ; hl=.currec + shld curreca! inx h! inx h ; hl=.buffa + inx h! inx h + inx h! inx h + ; de still contains .tran + xchg! shld tranv ; .tran vector + lxi h,dpbaddr ; de= source for move, hl=dest + mvi c,addlist! call move ; addlist filled + ; Now fill the disk parameter block + lhld dpbaddr! xchg ; de is source + lxi h,sectpt ; hl is destination + mvi c,dpblist! call move ; data filled + ; Now set single/double map mode + lhld maxall ; largest allocation number + mov a,h ; 00 indicates < 255 + lxi h,single! mvi m,true ; Assume a=00 + ora a! jz retselect + ; high order of maxall not zero, use double dm + mvi m,false + retselect: + ; C flag set indicates successful select + stc + ret + +home: + ; Move to home position, then offset to start of dir + call homef + xra a ; constant zero to accumulator + lhld curtrka! mov m,a! inx h! mov m,a ; curtrk=0000 + lhld curreca! mov m,a! inx h! mov m,a ; currec=0000 + inx h! mov m,a ; currec high byte=00 + + ret + +pass$arecord: + lxi h,arecord + mov e,m! inx h! mov d,m! inx h! mov b,m + ret + +rdbuff: + ; Read buffer and check condition + call pass$arecord + call readf ; current drive, track, sector, dma + + +diocomp: ; Check for disk errors + ora a! rz + mov c,a + cpi 3! jc goerr + mvi c,1! jmp goerr + +seekdir: + ; Seek the record containing the current dir entry + + lhld dcnt ; directory counter to hl + mvi c,dskshf! call hlrotr ; value to hl + + mvi b,0! xchg + + lxi h,arecord + mov m,e! inx h! mov m,d! inx h! mov m,b + ret + +seek: + ; Seek the track given by arecord (actual record) + + lhld curtrka! mov c,m! inx h! mov b,m ; bc = curtrk + push b ; s0 = curtrk + lhld curreca! mov e,m! inx h! mov d,m + inx h! mov b,m ; bde = currec + lhld arecord! lda arecord+2! mov c,a ; chl = arecord +seek0: + mov a,l! sub e! mov a,h! sbb d! mov a,c! sbb b + push h ; Save low(arecord) + jnc seek1 ; if arecord >= currec then go to seek1 + lhld sectpt! call bde$e$bde$m$hl ; currec = currec - sectpt + pop h! xthl! dcx h! xthl ; curtrk = curtrk - 1 + jmp seek0 +seek1: + lhld sectpt! call bde$e$bde$p$hl ; currec = currec + sectpt + pop h ; Restore low(arecord) + mov a,l! sub e! mov a,h! sbb d! mov a,c! sbb b + jc seek2 ; if arecord < currec then go to seek2 + xthl! inx h! xthl ; curtrk = curtrk + 1 + push h ; save low (arecord) + jmp seek1 +seek2: + xthl! push h ; hl,s0 = curtrk, s1 = low(arecord) + lhld sectpt! call bde$e$bde$m$hl ; currec = currec - sectpt + pop h! push d! push b! push h ; hl,s0 = curtrk, + ; s1 = high(arecord,currec), s2 = low(currec), + ; s3 = low(arecord) + xchg! lhld offset! dad d + mov b,h! mov c,l! shld track + call settrkf ; call bios settrk routine + ; Store curtrk + pop d! lhld curtrka! mov m,e! inx h! mov m,d + ; Store currec + pop b! pop d! + lhld curreca! mov m,e! inx h! mov m,d + inx h! mov m,b ; currec = bde + pop b ; bc = low(arecord), de = low(currec) + mov a,c! sub e! mov l,a ; hl = bc - de + mov a,b! sbb d! mov h,a + call shr$physhf + mov b,h! mov c,l + + lhld tranv! xchg ; bc=sector#, de=.tran + call sectran ; hl = tran(sector) + mov c,l! mov b,h ; bc = tran(sector) + shld sector + call setsecf ; sector selected + lhld curdma! mov c,l! mov b,h! jmp setdmaf + +shr$physhf: + lda physhf! mov c,a! jmp hlrotr + + +; file control block (fcb) constants + +empty equ 0e5h ; empty directory entry +recsiz equ 128 ; record size +fcblen equ 32 ; file control block size +dirrec equ recsiz/fcblen ; directory fcbs / record +dskshf equ 2 ; log2(dirrec) +dskmsk equ dirrec-1 +fcbshf equ 5 ; log2(fcblen) + +extnum equ 12 ; extent number field +maxext equ 31 ; largest extent number +ubytes equ 13 ; unfilled bytes field + +namlen equ 15 ; name length +reccnt equ 15 ; record count field +dskmap equ 16 ; disk map field +nxtrec equ fcblen + +; utility functions for file access + +dm$position: + ; Compute disk map position for vrecord to hl + lxi h,blkshf! mov c,m ; shift count to c + lda vrecord ; current virtual record to a + dmpos0: + ora a! rar! dcr c! jnz dmpos0 + ; a = shr(vrecord,blkshf) = vrecord/2**(sect/block) + mov b,a ; Save it for later addition + mvi a,8! sub m ; 8-blkshf to accumulator + mov c,a ; extent shift count in register c + lda extval ; extent value ani extmsk + dmpos1: + ; blkshf = 3,4,5,6,7, c=5,4,3,2,1 + ; shift is 4,3,2,1,0 + dcr c! jz dmpos2 + ora a! ral! jmp dmpos1 + dmpos2: + ; Arrive here with a = shl(ext and extmsk,7-blkshf) + add b ; Add the previous shr(vrecord,blkshf) value + ; a is one of the following values, depending upon alloc + ; bks blkshf + ; 1k 3 v/8 + extval * 16 + ; 2k 4 v/16+ extval * 8 + ; 4k 5 v/32+ extval * 4 + ; 8k 6 v/64+ extval * 2 + ; 16k 7 v/128+extval * 1 + ret ; with dm$position in a + +getdma: + lhld info! lxi d,dskmap! dad d! ret + +getdm: + ; Return disk map value from position given by bc + call getdma + dad b ; Index by a single byte value + lda single ; single byte/map entry? + ora a! jz getdmd ; Get disk map single byte + mov l,m! mov h,b! ret ; with hl=00bb + getdmd: + dad b ; hl=.fcb(dm+i*2) + ; double precision value returned + mov a,m! inx h! mov h,m! mov l,a! ret + +index: + ; Compute disk block number from current fcb + call dm$position ; 0...15 in register a + sta dminx + mov c,a! mvi b,0! call getdm ; value to hl + shld arecord! mov a,l! ora h! ret + +atran: + ; Compute actual record address, assuming index called + +; arecord = shl(arecord,blkshf) + + lda blkshf! mov c,a + lhld arecord! xra a! call shl3bv + shld arecord! sta arecord+2 + + shld arecord1 ; Save low(arecord) + +; arecord = arecord or (vrecord and blkmsk) + + lda blkmsk! mov c,a! lda vrecord! ana c + mov b,a ; Save vrecord & blkmsk in reg b & blk$off + sta blk$off + lxi h,arecord! ora m! mov m,a! ret + + +getexta: + ; Get current extent field address to hl + lhld info! lxi d,extnum! dad d ; hl=.fcb(extnum) + ret + +getrcnta: + ; Get reccnt address to hl + lhld info! lxi d,reccnt! dad d! ret + +getfcba: + ; Compute reccnt and nxtrec addresses for get/setfcb + call getrcnta! xchg ; de=.fcb(reccnt) + lxi h,(nxtrec-reccnt)! dad d ; hl=.fcb(nxtrec) + ret + +getfcb: + ; Set variables from currently addressed fcb + call getfcba ; addresses in de, hl + mov a,m! sta vrecord ; vrecord=fcb(nxtrec) + xchg! mov a,m! sta rcount ; rcount=fcb(reccnt) + call getexta ; hl=.fcb(extnum) + lda extmsk ; extent mask to a + ana m ; fcb(extnum) and extmsk + sta extval + ret + +setfcb: + ; Place values back into current fcb + call getfcba ; addresses to de, hl + mvi c,1 + + lda vrecord! add c! mov m,a ; fcb(nxtrec)=vrecord+seqio + xchg! lda rcount! mov m,a ; fcb(reccnt)=rcount + ret + +hlrotr: + ; hl rotate right by amount c + inr c ; in case zero + hlrotr0: dcr c! rz ; return when zero + + mov a,h! ora a! rar! mov h,a ; high byte + mov a,l! rar! mov l,a ; low byte + jmp hlrotr0 + +hlrotl: + ; Rotate the mask in hl by amount in c + inr c ; may be zero + hlrotl0: dcr c! rz ; return if zero + + dad h! jmp hlrotl0 + +set$cdisk: + ; Set a "1" value in curdsk position of bc + lda seldsk + push b ; Save input parameter + mov c,a ; Ready parameter for shift + lxi h,1 ; number to shift + call hlrotl ; hl = mask to integrate + pop b ; original mask + mov a,c! ora l! mov l,a + mov a,b! ora h! mov h,a ; hl = mask or rol(1,curdsk) + ret + +test$vector: + lda seldsk + mov c,a! call hlrotr + mov a,l! ani 1b! ret ; non zero if curdsk bit on + +getdptra: + ; Compute the address of a directory element at + ; positon dptr in the buffer + + lhld buffa! lda dptr + ; hl = hl + a + add l! mov l,a! rnc + ; overflow to h + inr h! ret + +clr$ext: + ; fcb ext = fcb ext & 1fh + + call getexta! mov a,m! ani 0001$1111b! mov m,a! + ret + + +subdh: + ; Compute hl = de - hl + mov a,e! sub l! mov l,a! mov a,d! sbb h! mov h,a + ret + +get$buffa: + push d! lxi d,10! dad d + mov e,m! inx h! mov d,m + xchg! pop d! ret + + +rddir: + ; Read a directory entry into the directory buffer + call seek$dir + lda phymsk! ora a! jz rddir1 + mvi a,3 + call deblock$dir! jmp setdata + +rddir1: + call setdir ; directory dma + shld buffa! call seek + call rdbuff ; directory record loaded + +setdata: + ; Set data dma address + lhld dmaad! jmp setdma ; to complete the call + +setdir: + ; Set directory dma address + + lhld dirbcba + call get$buffa + +setdma: + ; hl=.dma address to set (i.e., buffa or dmaad) + shld curdma! ret + +end$of$dir: + ; Return zero flag if at end of directory, non zero + ; if not at end (end of dir if dcnt = 0ffffh) + lxi h,dcnt + mov a,m ; may be 0ffh + inx h! cmp m ; low(dcnt) = high(dcnt)? + rnz ; non zero returned if different + ; high and low the same, = 0ffh? + inr a ; 0ffh becomes 00 if so + ret + +set$end$dir: + ; Set dcnt to the end of the directory + lxi h,enddir! shld dcnt! ret + + +read$dir: + ; Read next directory entry, with c=true if initializing + + lhld dirmax! xchg ; in preparation for subtract + lhld dcnt! inx h! shld dcnt ; dcnt=dcnt+1 + + ; while(dirmax >= dcnt) + call subdh ; de-hl + jc set$end$dir + ; not at end of directory, seek next element + ; initialization flag is in c + + lda dcnt! ani dskmsk ; low(dcnt) and dskmsk + mvi b,fcbshf ; to multiply by fcb size + + read$dir1: + add a! dcr b! jnz read$dir1 + ; a = (low(dcnt) and dskmsk) shl fcbshf + sta dptr ; ready for next dir operation + ora a! rnz ; Return if not a new record + + push b ; Save initialization flag c + call rd$dir ; Read the directory record + pop b ; Recall initialization flag + ret +compext: + ; Compare extent# in a with that in c, return nonzero + ; if they do not match + push b ; Save c's original value + push psw! lda extmsk! cma! mov b,a + ; b has negated form of extent mask + mov a,c! ana b! mov c,a ; low bits removed from c + pop psw! ana b ; low bits removed from a + sub c! ani maxext ; Set flags + pop b ; Restore original values + ret + +get$dir$ext: + ; Compute directory extent from fcb + ; Scan fcb disk map backwards + call getfcba ; hl = .fcb(vrecord) + mvi c,16! mov b,c! inr c! push b + ; b=dskmap pos (rel to 0) +get$de0: + pop b + dcr c + xra a ; Compare to zero +get$de1: + dcx h! dcr b ; Decr dskmap position + cmp m! jnz get$de2 ; fcb(dskmap(b)) ~= 0 + dcr c! jnz get$de1 + ; c = 0 -> all blocks = 0 in fcb disk map +get$de2: + mov a,c! sta dminx + lda single! ora a! mov a,b + jnz get$de3 + rar ; not single, divide blk idx by 2 +get$de3: + push b! push h ; Save dskmap position & count + mov l,a! mvi h,0 ; hl = non-zero blk idx + ; Compute ext offset from last non-zero + ; block index by shifting blk idx right + ; 7 - blkshf + lda blkshf! mov d,a! mvi a,7! sub d + mov c,a! call hlrotr! mov b,l + ; b = ext offset + lda extmsk! cmp b! pop h! jc get$de0 + ; Verify computed extent offset <= extmsk + call getexta! mov c,m + cma! ani maxext! ana c! ora b + ; dir ext = (fcb ext & (~ extmsk) & maxext) | ext offset + pop b ; Restore stack + ret ; a = directory extent + + +search: + ; Search for directory element of length c at info + lhld info! shld searcha ; searcha = info + mov a,c! sta searchl ; searchl = c + + call set$end$dir ; dcnt = enddir + call home ; to start at the beginning + +searchn: + ; Search for the next directory element, assuming + ; a previous call on search which sets searcha and + ; searchl + + mvi c,false! call read$dir ; Read next dir element + call end$of$dir! jz lret$eq$ff + ; not end of directory, scan for match + lhld searcha! xchg ; de=beginning of user fcb + + call getdptra ; hl = buffa+dptr + lda searchl! mov c,a ; length of search to c + mvi b,0 ; b counts up, c counts down + + mov a,m! cpi empty! jz searchn + + searchloop: + mov a,c! ora a! jz endsearch + ; Scan next character if not ubytes + mov a,b! cpi ubytes! jz searchok + ; not the ubytes field, extent field? + cpi extnum ; may be extent field + jz searchext ; Skip to search extent + ldax d + sub m! ani 7fh ; Mask-out flags/extent modulus + jnz searchn ; Skip if not matched + jmp searchok ; matched character + searchext: + ldax d + ; Attempt an extent # match + push b ; Save counters + mov c,m ; directory character to c + call compext ; Compare user/dir char + pop b ; Recall counters + ora a ; Set flag + jnz searchn ; Skip if no match + searchok: + ; current character matches + inx d! inx h! inr b! dcr c + jmp searchloop + endsearch: + ; entire name matches, return dir position + xra a + sta lret ; lret = 0 + ; successful search - + ; return with zero flag reset + mov b,a! inr b + ret + lret$eq$ff: + ; unsuccessful search - + ; return with zero flag set + ; lret,low(aret) = 0ffh + mvi a,255 ! mov b,a ! inr b ! jmp sta$ret + +open: + ; Search for the directory entry, copy to fcb + mvi c,namlen! call search + rz ; Return with lret=255 if end + + ; not end of directory, copy fcb information +open$copy: + call getexta ! mov a,m ! push a ; save extent to check for extent + ; folding - move moves entire dir FCB + call getdptra! xchg ; hl = .buff(dptr) + lhld info ; hl=.fcb(0) + mvi c,nxtrec ; length of move operation + call move ; from .buff(dptr) to .fcb(0) + + ; Note that entire fcb is copied, including indicators + + call get$dir$ext! mov c,a + pop a ! mov m,a ; restore extent + + ; hl = .user extent#, c = dir extent# + ; above move set fcb(reccnt) to dir(reccnt) + ; if fcb ext < dir ext then fcb(reccnt) = fcb(reccnt) | 128 + ; if fcb ext = dir ext then fcb(reccnt) = fcb(reccnt) + ; if fcb ext > dir ext then fcb(reccnt) = 0 + +set$rc: ; hl=.fcb(ext), c=dirext + mvi b,0 + xchg! lxi h,(reccnt-extnum)! dad d + ldax d! sub c! jz set$rc2 + mov a,b! jnc set$rc1 + mvi a,128! mov b,m + + set$rc1: + mov m,a! mov a,b! sta actual$rc! ret + set$rc2: + sta actual$rc + mov a,m! ora a! rnz ; ret if rc ~= 0 + lda dminx! ora a! rz ; ret if no blks in fcb + lda fx! cpi 15! rz ; ret if fx = 15 + mvi m,128 ; rc = 128 + ret + +restore$rc: + ; hl = .fcb(extnum) + ; if actual$rc ~= 0 then rcount = actual$rc + push h + lda actual$rc! ora a! jz restore$rc1 + lxi d,(reccnt-extnum)! dad d + mov m,a! xra a! sta actual$rc + +restore$rc1: + pop h! ret + +open$reel: + ; Close the current extent, and open the next one + ; if possible. + + call getexta + mov a,m! mov c,a + inr c! call compext + jz open$reel3 + + mvi a,maxext! ana c! mov m,a ; Incr extent field + mvi c,namlen! call search ; Next extent found? + ; not end of file, open + call open$copy + + open$reel2: + call getfcb ; Set parameters + xra a! sta vrecord! jmp sta$ret ; lret = 0 + open$reel3: + inr m ; fcb(ex) = fcb(ex) + 1 + call get$dir$ext! mov c,a + ; Is new extent beyond dir$ext? + cmp m! jnc open$reel4 ; no + dcr m ; fcb(ex) = fcb(ex) - 1 + jmp set$lret1 + open$reel4: + call restore$rc + call set$rc! jmp open$reel2 + +seqdiskread: + ; Sequential disk read operation + ; Read the next record from the current fcb + + call getfcb ; sets parameters for the read + + lda vrecord! lxi h,rcount! cmp m ; vrecord-rcount + ; Skip if rcount > vrecord + jc recordok + + ; not enough records in the extent + ; record count must be 128 to continue + cpi 128 ; vrecord = 128? + jnz setlret1 ; Skip if vrecord<>128 + call open$reel ; Go to next extent if so + ; Check for open ok + lda lret! ora a! jnz setlret1 ; Stop at eof + + recordok: + ; Arrive with fcb addressing a record to read + + call index ; Z flag set if arecord = 0 + + jz setlret1 ; Reading unwritten data + + ; Record has been allocated + call atran ; arecord now a disk address + + lda phymsk! ora a ; if not 128 byte sectors + jnz read$deblock ; go to deblock + + call setdata ; Set curdma = dmaad + call seek ; Set up for read + call rdbuff ; Read into (curdma) + jmp setfcb ; Update FCB + +curselect: + lda seldsk! inr a! jz sel$error + dcr a! lxi h,curdsk! cmp m! rz + + ; Skip if seldsk = curdsk, fall into select +select: + ; Select disk info for subsequent input or output ops + mov m,a ; curdsk = seldsk + + mov d,a ; Save seldsk in register D for selectdisk call + lhld dlog! call test$vector ; test$vector does not modify DE + mov e,a! push d ; Send to seldsk, save for test below + call selectdisk! pop h ; Recall dlog vector + jnc sel$error ; returns with C flag set if select ok + ; Is the disk logged in? + dcr l ; reg l = 1 if so + rz ; yes - drive previously logged in + + lhld dlog! mov c,l! mov b,h ; call ready + call set$cdisk! shld dlog ; dlog=set$cdisk(dlog) + ret + +set$seldsk: + lda linfo! sta seldsk! ret + +reselectx: + xra a! sta high$ext! jmp reselect1 +reselect: + ; Check current fcb to see if reselection necessary + mvi a,80h! mov b,a! dcr a! mov c,a ; b = 80h, c = 7fh + lhld info! lxi d,7! xchg! dad d + mov a,m! ana b + ; fcb(7) = fcb(7) & 7fh + mov a,m! ana c! mov m,a + ; high$ext = 80h & fcb(8) + inx h! mov a,m! ana b! sta high$ext + ; fcb(8) = fcb(8) & 7fh + mov a,m! ana c! mov m,a + ; fcb(ext) = fcb(ext) & 1fh + call clr$ext + + ; if fcb(rc) & 80h + ; then fcb(rc) = 80h, actual$rc = fcb(rc) & 7fh + ; else actual$rc = 0 + + call getrcnta! mov a,m! ana b! jz reselect1 + mov a,m! ana c! mov m,b + +reselect1: + sta actual$rc + + lxi h,0 + shld fcbdsk ; fcbdsk = 0 + mvi a,true! sta resel ; Mark possible reselect + lhld info! mov a,m ; drive select code + ani 1$1111b ; non zero is auto drive select + dcr a ; Drive code normalized to 0..30, or 255 + sta linfo ; Save drive code + cpi 0ffh! jz noselect + ; auto select function, seldsk saved above + mov a,m! sta fcbdsk ; Save drive code + call set$seldsk + + noselect: + call curselect + mvi a,0 ! lhld info ! mov m,a + ret + +; +; individual function handlers +; + +func12 equ func$ret + +func13: + + ; Reset disk system - initialize to disk 0 + lxi h,0! shld dlog + + xra a! sta seldsk + dcr a! sta curdsk + + lxi h,tbuff! shld dmaad ; dmaad = tbuff + jmp setdata ; to data dma address + +func14: + ; Select disk info + call set$seldsk ; seldsk = linfo + jmp curselect + +func15: + ; Open file + call reselectx + call open! call openx ; returns if unsuccessful, a = 0 + ret + +openx: + call end$of$dir! rz + call getfcba! mov a,m! inr a! jnz openxa + dcx d! dcx d! ldax d! mov m,a +openxa: + ; open successful + pop h ; Discard return address + mvi c,0100$0000b + ret + +func16 equ func$ret + +func17 equ func$ret + +func18 equ func$ret + +func19 equ func$ret + +func20: + ; Read a file + call reselect + jmp seqdiskread + +func21 equ func$ret + +func22 equ func$ret + +func23 equ func$ret + +func24 equ func$ret + +func25: lda seldsk ! jmp sta$ret + +func26: xchg ! shld dmaad + jmp setdata + +func27 equ func$ret + +func28: equ func$ret + +func29 equ func$ret + +func30 equ func$ret + +func31 equ func$ret + +func32 equ func$ret + +func33 equ func$ret + +func34 equ func$ret + +func35 equ func$ret + +func36 equ func$ret + +func37 equ func$ret + +func38 equ func$ret + +func39 equ func$ret + +func40 equ func$ret + +func42 equ func$ret + +func43 equ func$ret + +func44 equ func$ret + +func45 equ func$ret + +func46 equ func$ret + +func47 equ func$ret + +func48 equ func$ret + +func49 equ func$ret + +func50 equ func$ret + +func100 equ func$ret + +func101 equ func$ret + +func102 equ func$ret + +func103 equ func$ret + +func104 equ func$ret + +func105 equ func$ret + +func106 equ func$ret + +func107 equ func$ret + +func108 equ func$ret + +func109 equ func$ret + + +goback: + ; Arrive here at end of processing to return to user + lda fx! cpi 15! jc retmon + lda olddsk! sta seldsk ; Restore seldsk + lda resel! ora a! jz retmon + + lhld info! mvi m,0 ; fcb(0)=0 + lda fcbdsk! ora a! jz goback1 + ; Restore fcb(0) + mov m,a ; fcb(0)=fcbdsk + goback1: + ; fcb(8) = fcb(8) | high$ext + inx h! lda high$ext! ora m! mov m,a + ; fcb(rc) = fcb(rc) | actual$rc + call getrcnta! lda actual$rc! ora m! mov m,a + ; return from the disk monitor +retmon: + lhld entsp! sphl + lhld aret! mov a,l! mov b,h + ret +; +; data areas +; +dlog: dw 0 ; logged-in disks +curdma ds word ; current dma address +buffa: ds word ; pointer to directory dma address + +; +; curtrka - alloca are set upon disk select +; (data must be adjacent, do not insert variables) +; (address of translate vector, not used) +cdrmaxa:ds word ; pointer to cur dir max value (2 bytes) +curtrka:ds word ; current track address (2) +curreca:ds word ; current record address (3) +drvlbla:ds word ; current drive label byte address (1) +lsn$add:ds word ; login sequence # address (1) + ; +1 -> bios media change flag (1) +dpbaddr:ds word ; current disk parameter block address +checka: ds word ; current checksum vector address +alloca: ds word ; current allocation vector address +dirbcba:ds word ; dir bcb list head +dtabcba:ds word ; data bcb list head +hash$tbla: + ds word + ds byte + +addlist equ $-dpbaddr ; address list size + +; +; buffer control block format +; +; bcb format : drv(1) || rec(3) || pend(1) || sequence(1) || +; 0 1 4 5 +; +; track(2) || sector(2) || buffer$add(2) || +; 6 8 10 +; +; link(2) +; 12 +; + +; sectpt - offset obtained from disk parm block at dpbaddr +; (data must be adjacent, do not insert variables) +sectpt: ds word ; sectors per track +blkshf: ds byte ; block shift factor +blkmsk: ds byte ; block mask +extmsk: ds byte ; extent mask +maxall: ds word ; maximum allocation number +dirmax: ds word ; largest directory number +dirblk: ds word ; reserved allocation bits for directory +chksiz: ds word ; size of checksum vector +offset: ds word ; offset tracks at beginning +physhf: ds byte ; physical record shift +phymsk: ds byte ; physical record mask +dpblist equ $-sectpt ; size of area +; +; local variables +; +blk$off: ds byte ; record offset within block +dir$cnt: ds byte ; direct i/o count + +tranv: ds word ; address of translate vector +linfo: ds byte ; low(info) +dminx: ds byte ; local for diskwrite + +actual$rc: + ds byte ; directory ext record count + +single: ds byte ; set true if single byte allocation map + + +olddsk: ds byte ; disk on entry to bdos +rcount: ds byte ; record count in current fcb +extval: ds byte ; extent number and extmsk + +vrecord:ds byte ; current virtual record + +curdsk: + +adrive: db 0ffh ; current disk +arecord:ds word ; current actual record + ds byte + +arecord1: ds word ; current actual block# * blkmsk + +;******** following variable order critical ***************** + +high$ext: ds byte ; fcb high ext bits +;xfcb$read$only: ds byte + +; local variables for directory access +dptr: ds byte ; directory pointer 0,1,2,3 + +; +; local variables initialized by bdos at entry +; +fcbdsk: ds byte ; disk named in fcb + +phy$off: ds byte +curbcba: ds word + +track: ds word +sector: ds word + +read$deblock: + mvi a,1! call deblock$dta + jmp setfcb + +column db 0 +outdelim: db '$' + +dmaad: dw 0080h +seldsk: db 0 +info: dw 0 +resel: db 0 +fx: db 0 +dcnt: dw 0 +searcha: dw 0 +searchl: db 0 + + +; ************************** +; Blocking/Deblocking Module +; ************************** + +deblock$dir: + + lhld dirbcba + + jmp deblock + +deblock$dta: + lhld dtabcba + +deblock: + + ; BDOS Blocking/Deblocking routine + ; a = 1 -> read command + ; a = 2 -> write command + ; a = 3 -> locate command + ; a = 4 -> flush command + ; a = 5 -> directory update + + push a ; Save z flag and deblock fx + + ; phy$off = low(arecord) & phymsk + ; low(arecord) = low(arecord) & ~phymsk + call deblock8 + lda arecord! mov e,a! ana b! sta phy$off + mov a,e! ana c! sta arecord + + shld curbcba! call getbuffa! shld curdma + + call deblock9 + ; Is command flush? + pop a! push a! cpi 4 + jnc deblock1 ; yes + ; Is referenced physical record + ;already in buffer? + call compare! jz deblock45 ; yes + xra a +deblock1: + call deblock10 + ; Read physical record buffer + mvi a,2! call deblock$io + + call deblock9 ; phypfx = adrive || arecord + call move! mvi m,0 ; zero pending flag + +deblock45: + ; recadd = phybuffa + phy$off*80h + lda phy$off! inr a! lxi d,80h! lxi h,0ff80h +deblock5: + dad d! dcr a! jnz deblock5 + xchg! lhld curdma! dad d + ; If deblock command = locate + ; then buffa = recadd; return + pop a! cpi 3! jnz deblock6 + shld buffa! ret +deblock6: + xchg! lhld dmaad! lxi b,80h + ; If deblock command = read + jmp move$tpa ; then move to dma + +deblock8: + lda phymsk! mov b,a! cma! mov c,a! ret + +deblock9: + lhld curbcba! lxi d,adrive! mvi c,4! ret + +deblock10: + lxi d,4 +deblock11: + lhld curbcba! dad d! ret + +deblock$io: + ; a = 0 -> seek only + ; a = 1 -> write + ; a = 2 -> read + push a! call seek + pop a! dcr a + cp rdbuff + ; Move track & sector to bcb + call deblock10! inx h! inx h + lxi d,track! mvi c,4! jmp move + + org base+((($-base)+255) and 0ff00h)-1 + db 0 + +; Bios equates + +bios$pg equ $ + +bootf equ bios$pg+00 ; 00. cold boot +conoutf equ bios$pg+12 ; 04. console output function +homef equ bios$pg+24 ; 08. disk home function +seldskf equ bios$pg+27 ; 09. select disk function +settrkf equ bios$pg+30 ; 10. set track function +setsecf equ bios$pg+33 ; 11. set sector function +setdmaf equ bios$pg+36 ; 12. set dma function +sectran equ bios$pg+48 ; 16. sector translate +movef equ bios$pg+75 ; 25. memory move function +readf equ bios$pg+39 ; 13. read disk function +move$out equ movef +move$tpa equ movef + + end + \ No newline at end of file diff --git a/Source/CPM3/diskio.z80 b/Source/CPM3/diskio.z80 new file mode 100644 index 00000000..91613e73 --- /dev/null +++ b/Source/CPM3/diskio.z80 @@ -0,0 +1,781 @@ + title 'HBIOS disk handler' + +; CP/M-80 Version 3 -- Modular BIOS + + maclib options.lib + + dseg + + ; Disk drive dispatching tables for linked BIOS + + public dph0,dph1,dph2,dph3,dph4,dph5,dph6,dph7 + public dph8,dph9,dph10,dph11,dph12,dph13,dph14,dph15 + + ; Linked BIOS variables + + public @sysdr + extrn @bootdu + + ; Variables containing parameters passed by BDOS + + extrn @adrv,@rdrv + extrn @dma,@trk,@sect + extrn @dbnk + + ; System Control Block variables + + extrn @ermde ; BDOS error mode + + ; Utility routines in standard BIOS + + extrn ?wboot ; warm boot vector + extrn ?pmsg ; print message @ up to 00, saves & + extrn ?pdec ; print binary number in from 0 to 99. + extrn ?pderr ; print BIOS disk error header + extrn ?conin,?cono ; con in and out + extrn ?const ; get console status + + extrn ?bnkxlt + + + ; CP/M 3 Disk definition macros + + maclib cpm3.lib + + ; common control characters + +cr equ 13 +lf equ 10 +bell equ 7 + + + ; Extended Disk Parameter Headers (XPDHs) + + ; All DPH entries below are generic. They are updated during + ; boot to point to available HBIOS disk unit/slices dynamically. + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph0: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph1: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph2: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph3: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph4: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph5: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph6: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph7: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph8: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph9: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph10: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph11: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph12: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph13: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph14: dph 0,dpb$max ; Real DPB filled in at disk login + + dw dsk$write + dw dsk$read + dw dsk$login + dw dsk$init + db 0,0 ; HBIOS Disk Unit/Slice (filled in at boot) +dph15: dph 0,dpb$max ; Real DPB filled in at disk login + + cseg ; DPB must be resident + +dpb$max: + dw 64 ; spt: sectors per track + db 5 ; bsh: block shift factor + db 31 ; blm: block mask + db 1 ; exm: extent mask + dw 2047 ; dsm: total storage in blocks - 1 = (8mb / 4k bls) - 1 = 2047 + dw 511 ; drm: dir entries - 1 = 512 - 1 = 511 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 64 ; cks: directory check vector size - 256 / 4 + dw 0 ; off: reserved tracks = 16 trks * (16 trks * 16 heads * 16 secs * 512 bytes) = 128k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb$start: +dpb$rom: ; 384K ROM Drive + dw 64 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 1 ; exm: extent mask + dw 192 - 1 ; dsm: total storage in blocks - 1 = (384kb / 2k bls) - 1 = 191 + dw 256 - 1 ; drm: dir entries - 1 = 256 - 1 = 255 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 8000h ; cks: directory check vector size - permanent storage = 8000H + dw 0 ; off: reserved tracks = 16 trks * (16 trks * 16 heads * 16 secs * 512 bytes) = 128k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 +dpb$sz equ $ - dpb$start + +dpb$ram: ; 256K RAM Drive + dw 64 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 1 ; exm: extent mask + dw 128 - 1 ; dsm: total storage in blocks - 1 = (256kb / 2k bls) - 1 = 127 + dw 256 - 1 ; drm: dir entries - 1 = 256 - 1 = 255 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 8000h ; cks: directory check vector size - permanent storage = 8000H + dw 0 ; off: reserved tracks = 16 trks * (16 trks * 16 heads * 16 secs * 512 bytes) = 128k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb$rf: ; 4MB RAM Floppy Drive + dw 64 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 0 ; exm: extent mask + dw 2047 ; dsm: total storage in blocks - 1 = (4mb / 2k bls) - 1 = 2047 + dw 255 ; drm: dir entries - 1 = 256 - 1 = 255 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 8000h ; cks: directory check vector size - permanent storage = 8000H + dw 0 ; off: reserved tracks = 0 trks + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb$hd: ; 8MB Hard Disk Drive + dw 64 ; spt: sectors per track + db 5 ; bsh: block shift factor + db 31 ; blm: block mask + db 1 ; exm: extent mask + dw 2047 ; dsm: total storage in blocks - 1 = (8mb / 4k bls) - 1 = 2047 + dw 511 ; drm: dir entries - 1 = 512 - 1 = 511 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 8000h ; cks: directory check vector size - permanent storage = 8000H + dw 16 ; off: reserved tracks = 16 trks * (16 trks * 16 heads * 16 secs * 512 bytes) = 128k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb$fd720: ; 3.5" DS/DD Floppy Drive (720K) + dw 36 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 0 ; exm: extent mask + dw 350 ; dsm: total storage in blocks - 1 blk = ((720k - 18k off) / 2k bls) - 1 = 350 + dw 127 ; drm: dir entries - 1 = 128 - 1 = 127 + db 11000000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 32 ; cks: directory check vector size = 128 / 4 + dw 4 ; off: reserved tracks = 4 trks * (512 b/sec * 36 sec/trk) = 18k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb_fd144: ; 3.5" DS/HD Floppy Drive (1.44M) + dw 72 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 0 ; exm: extent mask + dw 710 ; dsm: total storage in blocks - 1 blk = ((1,440k - 18k off) / 2k bls) - 1 = 710 + dw 255 ; drm: dir entries - 1 = 256 - 1 = 255 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 64 ; cks: directory check vector size = 256 / 4 + dw 2 ; off: reserved tracks = 2 trks * (512 b/sec * 72 sec/trk) = 18k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb_fd360: ; 5.25" DS/DD Floppy Drive (360K) + dw 36 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 1 ; exm: extent mask + dw 170 ; dsm: total storage in blocks - 1 blk = ((360k - 18k off) / 2k bls) - 1 = 170 + dw 127 ; drm: dir entries - 1 = 128 - 1 = 127 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 32 ; cks: directory check vector size = 128 / 4 + dw 4 ; off: reserved tracks = 4 trks * (512 b/sec * 36 sec/trk) = 18k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb_fd120: ; 5.25" DS/HD Floppy Drive (1.2M) + dw 60 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 0 ; exm: extent mask + dw 591 ; dsm: total storage in blocks - 1 blk = ((1,200k - 15k off) / 2k bls) - 1 = 591 + dw 255 ; drm: dir entries - 1 = 256 - 1 = 255 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 64 ; cks: directory check vector size = 256 / 4 + dw 2 ; off: reserved tracks = 2 trks * (512 b/sec * 60 sec/trk) = 15k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + +dpb_fd111: ; 8" DS/DD Floppy Drive (1.11M) + dw 60 ; spt: sectors per track + db 4 ; bsh: block shift factor + db 15 ; blm: block mask + db 0 ; exm: extent mask + dw 569 ; dsm: total storage in blocks - 1 blk = ((1,155k - 15k off) / 2k bls) - 1 = 569 + dw 255 ; drm: dir entries - 1 = 256 - 1 = 255 + db 11110000b ; al0: dir blk bit map, first byte + db 00000000b ; al1: dir blk bit map, second byte + dw 64 ; cks: directory check vector size = 256 / 4 + dw 2 ; off: reserved tracks = 2 trks * (512 b/sec * 60 sec/trk) = 15k + db 2 ; psh: 2 for 512 byte sectors + db 3 ; phm: (512 / 128) - 1 + + dseg ; rest is banked + + + + ; Disk I/O routines for standardized BIOS interface + +; Initialization entry point. + +; called for first time initialization. + +dsk$init: + inc de ; point to slice in XDPH + inc de + inc de + ld a,(de) ; get slice + or a ; set flags + ret nz ; done if not zero + + ld a,(@rdrv) ; unit being initialized + ld hl,@bootdu + cp (hl) + ret nz ; done if no match + + ld a,(@adrv) ; get cp/m drive + ld (@sysdr),a ; and save it + ret + +; lxi h,init$table +;fd$init$next: +; mov a,m ! ora a ! rz +; mov b,a ! inx h ! mov c,m ! inx h +; outir +; jmp fd$init$next +; +;fd$init1: ; all initialization done by drive 0 +; ret + +;init$table db 4,p$zpio$1A +; db 11001111b, 11000010b, 00010111b,11111111b +; db 4,p$zpio$1B +; db 11001111b, 11011101b, 00010111b,11111111b +; db 0 + + +dsk$login: + ; This entry is called when a logical drive is about to + ; be logged into for the purpose of density determination. + + ; It may adjust the parameters contained in the disk + ; parameter header pointed at by + + ;ret ; we have nothing to do in + ; simple single density only environment. + + + push de ; save DPH ptr + + ; check media + ld a,(@rdrv) ; get disk unit + ;halt + ld c,a ; put in C + ld b,18h ; HBIOS Media function + ld e,1 ; Enabled media check/discovery + call 0FFF0H ; HBIOS call + ld a,e ; Resultant media id to accum + or a ; Set flags + ;halt + ; + ; !!! Need to do something on error !!! + ; + ret z ; Bail out on error + + ld hl,dpb$start - dpb$sz + ld de,dpb$sz + ld b,a ; loop count +dsk$login1: + add hl,de ; next dpb + djnz dsk$login1 ; loop as needed + + ; hl is ptr to desired dpb + pop de ; restore DPH ptr + ;halt + ex de,hl ; de = DPB adr, hl = DPH adr + push de ; save DPB adr + ld de,12 ; offset of DPB in DPH + add hl,de ; hl = adr of DPB field in DPH + pop de ; recover DPB adr + ld (hl),e ; update LSB + inc hl + ld (hl),d ; udpate MSB + ret ; done + + +; disk READ and WRITE entry points. + + ; these entries are called with the following arguments: + + ; relative drive number in @rdrv (8 bits) + ; absolute drive number in @adrv (8 bits) + ; disk transfer address in @dma (16 bits) + ; disk transfer bank in @dbnk (8 bits) + ; disk track address in @trk (16 bits) + ; disk sector address in @sect (16 bits) + ; pointer to XDPH in + + ; they transfer the appropriate data, perform retries + ; if necessary, then return an error code in + +dsk$read: +; ld ix,30H +; halt + + push de ; save XDPH pointer + call dsk$seek ; disk seek + pop hl ; restore pointer to HL + ret nz ; abort on seek error +; + dec hl ; point to unit field of XDPH + dec hl + ld c,(hl) ; BIOS Disk Unit in C + ld b,13H ; HBIOS READ function + ld hl,(@dma) ; Dest buffer adr + if banked + ld a,(@dbnk) ; destination bank + call ?bnkxlt + else + ld a,(0FFE0H) ; get current bank + endif + ld d,a ; set desk bank + ld e,1 ; 1 sector + ;rst 08 ; do it + call 0FFF0H + ret ; return + +; lxi h,read$msg ; point at " Read " +; mvi a,88h ! mvi b,01h ; 1797 read + Z80DMA direction +; jmp rw$common + +dsk$write: + ;ld ix,32H + ;halt + + push de ; save XDPH pointer + call dsk$seek ; disk seek + pop hl ; restore pointer to XDPH + ret nz ; abort on seek error +; + dec hl ; point to unit field of XDPH + dec hl + ld c,(hl) ; BIOS Disk Unit in C + ld b,14H ; HBIOS WRITE function + ld hl,(@dma) ; Dest buffer adr + if banked + ld a,(@dbnk) ; destination bank + call ?bnkxlt + else + ld a,(0FFE0H) ; get current bank + endif + ld d,a ; set desk bank + ld e,1 ; 1 sector + ;rst 08 ; do it + call 0FFF0H + ret ; return + +; lxi h,write$msg ; point at " Write " +; mvi a,0A8h ! mvi b,05h ; 1797 write + Z80DMA direction +; ; jmp wr$common + +dsk$seek: + dec de ; point to unit field of XDPH + dec de + ld a,(de) ; get it + ld c,a ; BIOS Disk Unit in C + ld b,12H ; HBIOS SEEK function + push bc ; save it + + inc de ; point to slice field of XDPH + ld a,(de) ; get it + ld e,a ; slice to E + ld h,65 ; number of tracks per slice + call mult8 ; HL now has track offset for slice + push hl ; save it for now + + ld hl,(@trk) ; get track value + ld a,l ; lsb of track to a + and 0FH ; isolate head in low 4 bits + ld d,a ; stuff it in d + ld a,(@sect) ; get sector + ld e,a ; stuff it in e + ld b,4 ; prepare to shift out 4 bit head value +seek1: + srl h ; shift one bit out + rr l ; ... of hl + djnz seek1 ; do all 4 bits + + ex de,hl ; de=track, hl=head/sect + ex (sp),hl ; save head/sect, hl = offset + add hl,de ; hl has final track value + pop de ; recover head/sect to de + + pop bc ; recover function & unit + ;rst 08 ; perform seek + call 0FFF0H + ret + +; +; multiply 8-bit values +; in: multiply h by e +; out: hl = result, e = 0, b = 0 +; +mult8: + ld d,0 + ld l,d + ld b,8 +mult8_loop: + add hl,hl + jr nc,mult8_noadd + add hl,de +mult8_noadd: + djnz mult8_loop + ret + +;rw$common: ; seek to correct track (if necessary), +; ; initialize DMA controller, +; ; and issue 1797 command. +; +; shld operation$name ; save message for errors +; sta disk$command ; save 1797 command +; mov a,b ! sta zdma$direction ; save Z80DMA direction code +; lhld @dma ! shld zdma$dma ; get and save DMA address +; lda @rdrv ! mov l,a ! mvi h,0 ; get controller-relative disk drive +; lxi d,select$table ! dad d ; point to select mask for drive +; mov a,m ! sta select$mask ; get select mask and save it +; out p$select ; select drive +;more$retries: +; mvi c,10 ; allow 10 retries +;retry$operation: +; push b ; save retry counter +; +; lda select$mask ! lxi h,old$select ! cmp m +; mov m,a +; jnz new$track ; if not same drive as last, seek +; +; lda @trk ! lxi h,old$track ! cmp m +; mov m,a +; jnz new$track ; if not same track, then seek +; +; in p$fdmisc ! ani 2 ! jnz same$track ; head still loaded, we are OK +; +;new$track: ; or drive or unloaded head means we should . . . +; call check$seek ; . . read address and seek if wrong track +; +; lxi b,16667 ; 100 ms / (24 t states*250 ns) +;spin$loop: ; wait for head/seek settling +; dcx b +; mov a,b ! ora c +; jnz spin$loop +; +;same$track: +; lda @trk ! out p$fdtrack ; give 1797 track +; lda @sect ! out p$fdsector ; and sector +; +; lxi h,dma$block ; point to dma command block +; lxi b,dmab$length*256 + p$zdma ; command block length and port address +; outir ; send commands to Z80 DMA +; +; in p$bankselect ; get old value of bank select port +; ani 3Fh ! mov b,a ; mask off DMA bank and save +; lda @dbnk ! rrc ! rrc ; get DMA bank to 2 hi-order bits +; ani 0C0h ! ora b ; merge with other bank stuff +; out p$bankselect ; and select the correct DMA bank +; +; lda disk$command ; get 1797 command +; call exec$command ; start it then wait for IREQ and read status +; sta disk$status ; save status for error messages +; +; pop b ; recover retry counter +; ora a ! rz ; check status and return to BDOS if no error +; +; ani 0001$0000b ; see if record not found error +; cnz check$seek ; if a record not found, we might need to seek +; +; dcr c ! jnz retry$operation +; +; ; suppress error message if BDOS is returning errors to application... +; +; lda @ermde ! cpi 0FFh ! jz hard$error +; +; ; Had permanent error, print message like: +; +; ; BIOS Err on d: T-nn, S-mm, , Retry ? +; +; call ?pderr ; print message header +; +; lhld operation$name ! call ?pmsg ; last function +; +; ; then, messages for all indicated error bits +; +; lda disk$status ; get status byte from last error +; lxi h,error$table ; point at table of message addresses +;errm1: +; mov e,m ! inx h ! mov d,m ! inx h ; get next message address +; add a ! push psw ; shift left and push residual bits with status +; xchg ! cc ?pmsg ! xchg ; print message, saving table pointer +; pop psw ! jnz errm1 ; if any more bits left, continue +; +; lxi h,error$msg ! call ?pmsg ; print ", Retry (Y/N) ? " +; call u$conin$echo ; get operator response +; cpi 'Y' ! jz more$retries ; Yes, then retry 10 more times +;hard$error: ; otherwise, +; mvi a,1 ! ret ; return hard error to BDOS +; +;cancel: ; here to abort job +; jmp ?wboot ; leap directly to warmstart vector +; +; +; ; subroutine to seek if on wrong track +; ; called both to set up new track or drive +; +;check$seek: +; push b ; save error counter +; call read$id ; try to read ID, put track in +; jz id$ok ; if OK, we're OK +; call step$out ; else step towards Trk 0 +; call read$id ; and try again +; jz id$ok ; if OK, we're OK +; call restore ; else, restore the drive +; mvi b,0 ; and make like we are at track 0 +;id$ok: +; mov a,b ! out p$fdtrack ; send current track to track port +; lda @trk ! cmp b ! pop b ! rz ; if its desired track, we are done +; out p$fddata ; else, desired track to data port +; mvi a,00011010b ; seek w/ 10 ms. steps +; jmp exec$command +; +; +; +;step$out: +; mvi a,01101010b ; step out once at 10 ms. +; jmp exec$command +; +;restore: +; mvi a,00001011b ; restore at 15 ms +; ; jmp exec$command +; +; +;exec$command: ; issue 1797 command, and wait for IREQ +; ; return status +; out p$fdcmnd ; send 1797 command +;wait$IREQ: ; spin til IREQ +; in p$fdint ! ani 40h ! jz wait$IREQ +; in p$fdstat ; get 1797 status and clear IREQ +; ret +; +;read$id: +; lxi h,read$id$block ; set up DMA controller +; lxi b,length$id$dmab*256 + p$zdma ; for READ ADDRESS operation +; outir +; mvi a,11000100b ; issue 1797 read address command +; call exec$command ; wait for IREQ and read status +; ani 10011101b ; mask status +; lxi h,id$buffer ! mov b,m ; get actual track number in +; ret ; and return with Z flag true for OK +; +; +;u$conin$echo: ; get console input, echo it, and shift to upper case +; call ?const ! ora a ! jz u$c1 ; see if any char already struck +; call ?conin ! jmp u$conin$echo ; yes, eat it and try again +;u$c1: +; call ?conin ! push psw +; mov c,a ! call ?cono +; pop psw ! cpi 'a' ! rc +; sui 'a'-'A' ; make upper case +; ret +; +; +;disk$command ds 1 ; current wd1797 command +;select$mask ds 1 ; current drive select code +;old$select ds 1 ; last drive selected +;old$track ds 1 ; last track seeked to +; +;disk$status ds 1 ; last error status code for messages +; +;select$table db 0001$0000b,0010$0000b ; for now use drives C and D +; +; +; ; error message components +; +;read$msg db ', Read',0 +;write$msg db ', Write',0 +; +;operation$name dw read$msg +; +; ; table of pointers to error message strings +; ; first entry is for bit 7 of 1797 status byte +; +;error$table dw b7$msg +; dw b6$msg +; dw b5$msg +; dw b4$msg +; dw b3$msg +; dw b2$msg +; dw b1$msg +; dw b0$msg +; +;b7$msg db ' Not ready,',0 +;b6$msg db ' Protect,',0 +;b5$msg db ' Fault,',0 +;b4$msg db ' Record not found,',0 +;b3$msg db ' CRC,',0 +;b2$msg db ' Lost data,',0 +;b1$msg db ' DREQ,',0 +;b0$msg db ' Busy,',0 +; +;error$msg db ' Retry (Y/N) ? ',0 +; +; +; +; ; command string for Z80DMA device for normal operation +; +;dma$block db 0C3h ; reset DMA channel +; db 14h ; channel A is incrementing memory +; db 28h ; channel B is fixed port address +; db 8Ah ; RDY is high, CE/ only, stop on EOB +; db 79h ; program all of ch. A, xfer B->A (temp) +;zdma$dma ds 2 ; starting DMA address +; dw 128-1 ; 128 byte sectors in SD +; db 85h ; xfer byte at a time, ch B is 8 bit address +; db p$fddata ; ch B port address (1797 data port) +; db 0CFh ; load B as source register +; db 05h ; xfer A->B +; db 0CFh ; load A as source register +;zdma$direction ds 1 ; either A->B or B->A +; db 0CFh ; load final source register +; db 87h ; enable DMA channel +;dmab$length equ $-dma$block + + + +;read$id$block db 0C3h ; reset DMA channel +; db 14h ; channel A is incrementing memory +; db 28h ; channel B is fixed port address +; db 8Ah ; RDY is high, CE/ only, stop on EOB +; db 7Dh ; program all of ch. A, xfer A->B (temp) +; dw id$buffer ; starting DMA address +; dw 6-1 ; Read ID always xfers 6 bytes +; db 85h ; byte xfer, ch B is 8 bit address +; db p$fddata ; ch B port address (1797 data port) +; db 0CFh ; load dest (currently source) register +; db 01h ; xfer B->A +; db 0CFh ; load source register +; db 87h ; enable DMA channel +;length$id$dmab equ $-read$id$block + + cseg ; easier to put ID buffer in common + +;id$buffer ds 6 ; buffer to hold ID field +; ; track +; ; side +; ; sector +; ; length +; ; CRC 1 +; ; CRC 2 + + cseg + +@sysdr db 0 ; system boot drive + + end diff --git a/Source/CPM3/drvtbl.z80 b/Source/CPM3/drvtbl.z80 new file mode 100644 index 00000000..27a4fda5 --- /dev/null +++ b/Source/CPM3/drvtbl.z80 @@ -0,0 +1,12 @@ + public @dtbl + extrn dph0,dph1,dph2,dph3,dph4,dph5,dph6,dph7 + extrn dph8,dph9,dph10,dph11,dph12,dph13,dph14,dph15 + + maclib cpm3.lib + + cseg + +@dtbl dw dph0,dph1,dph2,dph3,dph4,dph5,dph6,dph7 + dw dph8,dph9,dph10,dph11,dph12,dph13,dph14,dph15 + + end diff --git a/Source/CPM3/genbnk.dat b/Source/CPM3/genbnk.dat new file mode 100644 index 00000000..1e9f59df --- /dev/null +++ b/Source/CPM3/genbnk.dat @@ -0,0 +1,158 @@ +PRTMSG = Y +PAGWID = 4F +PAGLEN = 17 +BACKSPC = N +RUBOUT = N +BOOTDRV = C +MEMTOP = FD +BNKSWT = Y +COMBAS = 80 +LERROR = Y +NUMSEGS = 02 +MEMSEG00 = 01,43,00 +MEMSEG01 = 0E,72,02 +MEMSEG02 = 01,7F,03 +MEMSEG03 = 01,7F,04 +MEMSEG04 = 00,C0,05 +MEMSEG05 = 00,C0,06 +MEMSEG06 = 00,C0,07 +MEMSEG07 = 00,C0,08 +MEMSEG08 = 00,C0,09 +MEMSEG09 = 00,C0,0A +MEMSEG0A = 00,C0,0B +MEMSEG0B = 00,C0,0C +MEMSEG0C = 00,C0,0D +MEMSEG0D = 00,C0,0E +MEMSEG0E = 00,C0,0F +MEMSEG0F = 00,C0,10 +HASHDRVA = Y +HASHDRVB = Y +HASHDRVC = Y +HASHDRVD = Y +HASHDRVE = Y +HASHDRVF = Y +HASHDRVG = Y +HASHDRVH = Y +HASHDRVI = Y +HASHDRVJ = Y +HASHDRVK = Y +HASHDRVL = Y +HASHDRVM = Y +HASHDRVN = Y +HASHDRVO = Y +HASHDRVP = Y +ALTBNKSA = Y +ALTBNKSB = Y +ALTBNKSC = Y +ALTBNKSD = Y +ALTBNKSE = Y +ALTBNKSF = Y +ALTBNKSG = Y +ALTBNKSH = Y +ALTBNKSI = Y +ALTBNKSJ = Y +ALTBNKSK = Y +ALTBNKSL = Y +ALTBNKSM = Y +ALTBNKSN = Y +ALTBNKSO = Y +ALTBNKSP = Y +NDIRRECA = 02 +NDIRRECB = 00 +NDIRRECC = 00 +NDIRRECD = 00 +NDIRRECE = 00 +NDIRRECF = 00 +NDIRRECG = 00 +NDIRRECH = 00 +NDIRRECI = 00 +NDIRRECJ = 00 +NDIRRECK = 00 +NDIRRECL = 00 +NDIRRECM = 00 +NDIRRECN = 00 +NDIRRECO = 00 +NDIRRECP = 00 +NDTARECA = 02 +NDTARECB = 00 +NDTARECC = 00 +NDTARECD = 00 +NDTARECE = 00 +NDTARECF = 00 +NDTARECG = 00 +NDTARECH = 00 +NDTARECI = 00 +NDTARECJ = 00 +NDTARECK = 00 +NDTARECL = 00 +NDTARECM = 00 +NDTARECN = 00 +NDTARECO = 00 +NDTARECP = 00 +ODIRDRVA = A +ODIRDRVB = A +ODIRDRVC = A +ODIRDRVD = A +ODIRDRVE = A +ODIRDRVF = A +ODIRDRVG = A +ODIRDRVH = A +ODIRDRVI = A +ODIRDRVJ = A +ODIRDRVK = A +ODIRDRVL = A +ODIRDRVM = A +ODIRDRVN = A +ODIRDRVO = A +ODIRDRVP = A +ODTADRVA = A +ODTADRVB = A +ODTADRVC = A +ODTADRVD = A +ODTADRVE = A +ODTADRVF = A +ODTADRVG = A +ODTADRVH = A +ODTADRVI = A +ODTADRVJ = A +ODTADRVK = A +ODTADRVL = A +ODTADRVM = A +ODTADRVN = A +ODTADRVO = A +ODTADRVP = A +OVLYDIRA = Y +OVLYDIRB = Y +OVLYDIRC = Y +OVLYDIRD = Y +OVLYDIRE = Y +OVLYDIRF = Y +OVLYDIRG = Y +OVLYDIRH = Y +OVLYDIRI = Y +OVLYDIRJ = Y +OVLYDIRK = Y +OVLYDIRL = Y +OVLYDIRM = Y +OVLYDIRN = Y +OVLYDIRO = Y +OVLYDIRP = Y +OVLYDTAA = Y +OVLYDTAB = Y +OVLYDTAC = Y +OVLYDTAD = Y +OVLYDTAE = Y +OVLYDTAF = Y +OVLYDTAG = Y +OVLYDTAH = Y +OVLYDTAI = Y +OVLYDTAJ = Y +OVLYDTAK = Y +OVLYDTAL = Y +OVLYDTAM = Y +OVLYDTAN = Y +OVLYDTAO = Y +OVLYDTAP = Y +CRDATAF = N +DBLALV = Y + \ No newline at end of file diff --git a/Source/CPM3/gencpm.com b/Source/CPM3/gencpm.com new file mode 100644 index 00000000..d9d67499 Binary files /dev/null and b/Source/CPM3/gencpm.com differ diff --git a/Source/CPM3/genres.dat b/Source/CPM3/genres.dat new file mode 100644 index 00000000..3622d946 --- /dev/null +++ b/Source/CPM3/genres.dat @@ -0,0 +1,158 @@ +PRTMSG = Y +PAGWID = 4F +PAGLEN = 17 +BACKSPC = N +RUBOUT = N +BOOTDRV = C +MEMTOP = FD +BNKSWT = N +COMBAS = 00 +LERROR = Y +NUMSEGS = 03 +MEMSEG00 = 00,80,00 +MEMSEG01 = 00,C0,02 +MEMSEG02 = 00,C0,03 +MEMSEG03 = 00,C0,04 +MEMSEG04 = 00,C0,05 +MEMSEG05 = 00,C0,06 +MEMSEG06 = 00,C0,07 +MEMSEG07 = 00,C0,08 +MEMSEG08 = 00,C0,09 +MEMSEG09 = 00,C0,0A +MEMSEG0A = 00,C0,0B +MEMSEG0B = 00,C0,0C +MEMSEG0C = 00,C0,0D +MEMSEG0D = 00,C0,0E +MEMSEG0E = 00,C0,0F +MEMSEG0F = 00,C0,10 +HASHDRVA = N +HASHDRVB = N +HASHDRVC = N +HASHDRVD = N +HASHDRVE = N +HASHDRVF = N +HASHDRVG = N +HASHDRVH = N +HASHDRVI = N +HASHDRVJ = N +HASHDRVK = N +HASHDRVL = N +HASHDRVM = N +HASHDRVN = N +HASHDRVO = N +HASHDRVP = N +ALTBNKSA = N +ALTBNKSB = N +ALTBNKSC = N +ALTBNKSD = N +ALTBNKSE = N +ALTBNKSF = N +ALTBNKSG = N +ALTBNKSH = N +ALTBNKSI = N +ALTBNKSJ = N +ALTBNKSK = N +ALTBNKSL = N +ALTBNKSM = N +ALTBNKSN = N +ALTBNKSO = N +ALTBNKSP = N +NDIRRECA = 01 +NDIRRECB = 00 +NDIRRECC = 00 +NDIRRECD = 00 +NDIRRECE = 00 +NDIRRECF = 00 +NDIRRECG = 00 +NDIRRECH = 00 +NDIRRECI = 00 +NDIRRECJ = 00 +NDIRRECK = 00 +NDIRRECL = 00 +NDIRRECM = 00 +NDIRRECN = 00 +NDIRRECO = 00 +NDIRRECP = 00 +NDTARECA = 01 +NDTARECB = 00 +NDTARECC = 00 +NDTARECD = 00 +NDTARECE = 00 +NDTARECF = 00 +NDTARECG = 00 +NDTARECH = 00 +NDTARECI = 00 +NDTARECJ = 00 +NDTARECK = 00 +NDTARECL = 00 +NDTARECM = 00 +NDTARECN = 00 +NDTARECO = 00 +NDTARECP = 00 +ODIRDRVA = A +ODIRDRVB = A +ODIRDRVC = A +ODIRDRVD = A +ODIRDRVE = A +ODIRDRVF = A +ODIRDRVG = A +ODIRDRVH = A +ODIRDRVI = A +ODIRDRVJ = A +ODIRDRVK = A +ODIRDRVL = A +ODIRDRVM = A +ODIRDRVN = A +ODIRDRVO = A +ODIRDRVP = A +ODTADRVA = A +ODTADRVB = A +ODTADRVC = A +ODTADRVD = A +ODTADRVE = A +ODTADRVF = A +ODTADRVG = A +ODTADRVH = A +ODTADRVI = A +ODTADRVJ = A +ODTADRVK = A +ODTADRVL = A +ODTADRVM = A +ODTADRVN = A +ODTADRVO = A +ODTADRVP = A +OVLYDIRA = Y +OVLYDIRB = Y +OVLYDIRC = Y +OVLYDIRD = Y +OVLYDIRE = Y +OVLYDIRF = Y +OVLYDIRG = Y +OVLYDIRH = Y +OVLYDIRI = Y +OVLYDIRJ = Y +OVLYDIRK = Y +OVLYDIRL = Y +OVLYDIRM = Y +OVLYDIRN = Y +OVLYDIRO = Y +OVLYDIRP = Y +OVLYDTAA = Y +OVLYDTAB = Y +OVLYDTAC = Y +OVLYDTAD = Y +OVLYDTAE = Y +OVLYDTAF = Y +OVLYDTAG = Y +OVLYDTAH = Y +OVLYDTAI = Y +OVLYDTAJ = Y +OVLYDTAK = Y +OVLYDTAL = Y +OVLYDTAM = Y +OVLYDTAN = Y +OVLYDTAO = Y +OVLYDTAP = Y +CRDATAF = N +DBLALV = N + \ No newline at end of file diff --git a/Source/CPM3/genres.old b/Source/CPM3/genres.old new file mode 100644 index 00000000..75b45a49 --- /dev/null +++ b/Source/CPM3/genres.old @@ -0,0 +1,158 @@ +PRTMSG = Y +PAGWID = 4F +PAGLEN = 17 +BACKSPC = N +RUBOUT = N +BOOTDRV = A +MEMTOP = FD +BNKSWT = N +COMBAS = 00 +LERROR = Y +NUMSEGS = 03 +MEMSEG00 = 00,80,00 +MEMSEG01 = 00,C0,02 +MEMSEG02 = 00,C0,03 +MEMSEG03 = 00,C0,04 +MEMSEG04 = 00,C0,05 +MEMSEG05 = 00,C0,06 +MEMSEG06 = 00,C0,07 +MEMSEG07 = 00,C0,08 +MEMSEG08 = 00,C0,09 +MEMSEG09 = 00,C0,0A +MEMSEG0A = 00,C0,0B +MEMSEG0B = 00,C0,0C +MEMSEG0C = 00,C0,0D +MEMSEG0D = 00,C0,0E +MEMSEG0E = 00,C0,0F +MEMSEG0F = 00,C0,10 +HASHDRVA = N +HASHDRVB = Y +HASHDRVC = Y +HASHDRVD = Y +HASHDRVE = Y +HASHDRVF = Y +HASHDRVG = Y +HASHDRVH = Y +HASHDRVI = Y +HASHDRVJ = Y +HASHDRVK = Y +HASHDRVL = Y +HASHDRVM = Y +HASHDRVN = Y +HASHDRVO = Y +HASHDRVP = Y +ALTBNKSA = N +ALTBNKSB = N +ALTBNKSC = N +ALTBNKSD = N +ALTBNKSE = N +ALTBNKSF = N +ALTBNKSG = N +ALTBNKSH = N +ALTBNKSI = N +ALTBNKSJ = N +ALTBNKSK = N +ALTBNKSL = N +ALTBNKSM = N +ALTBNKSN = N +ALTBNKSO = N +ALTBNKSP = N +NDIRRECA = 01 +NDIRRECB = 01 +NDIRRECC = 01 +NDIRRECD = 01 +NDIRRECE = 01 +NDIRRECF = 01 +NDIRRECG = 01 +NDIRRECH = 01 +NDIRRECI = 01 +NDIRRECJ = 01 +NDIRRECK = 01 +NDIRRECL = 01 +NDIRRECM = 01 +NDIRRECN = 01 +NDIRRECO = 01 +NDIRRECP = 01 +NDTARECA = 01 +NDTARECB = 01 +NDTARECC = 01 +NDTARECD = 01 +NDTARECE = 01 +NDTARECF = 01 +NDTARECG = 01 +NDTARECH = 01 +NDTARECI = 01 +NDTARECJ = 01 +NDTARECK = 01 +NDTARECL = 01 +NDTARECM = 01 +NDTARECN = 01 +NDTARECO = 01 +NDTARECP = 01 +ODIRDRVA = A +ODIRDRVB = A +ODIRDRVC = A +ODIRDRVD = A +ODIRDRVE = A +ODIRDRVF = A +ODIRDRVG = A +ODIRDRVH = A +ODIRDRVI = A +ODIRDRVJ = A +ODIRDRVK = A +ODIRDRVL = A +ODIRDRVM = A +ODIRDRVN = A +ODIRDRVO = A +ODIRDRVP = A +ODTADRVA = A +ODTADRVB = A +ODTADRVC = A +ODTADRVD = A +ODTADRVE = A +ODTADRVF = A +ODTADRVG = A +ODTADRVH = A +ODTADRVI = A +ODTADRVJ = A +ODTADRVK = A +ODTADRVL = A +ODTADRVM = A +ODTADRVN = A +ODTADRVO = A +ODTADRVP = A +OVLYDIRA = Y +OVLYDIRB = Y +OVLYDIRC = Y +OVLYDIRD = Y +OVLYDIRE = Y +OVLYDIRF = Y +OVLYDIRG = Y +OVLYDIRH = Y +OVLYDIRI = Y +OVLYDIRJ = Y +OVLYDIRK = Y +OVLYDIRL = Y +OVLYDIRM = Y +OVLYDIRN = Y +OVLYDIRO = Y +OVLYDIRP = Y +OVLYDTAA = Y +OVLYDTAB = Y +OVLYDTAC = Y +OVLYDTAD = Y +OVLYDTAE = Y +OVLYDTAF = Y +OVLYDTAG = Y +OVLYDTAH = Y +OVLYDTAI = Y +OVLYDTAJ = Y +OVLYDTAK = Y +OVLYDTAL = Y +OVLYDTAM = Y +OVLYDTAN = Y +OVLYDTAO = Y +OVLYDTAP = Y +CRDATAF = N +DBLALV = N + \ No newline at end of file diff --git a/Source/CPM3/makedate.lib b/Source/CPM3/makedate.lib new file mode 100644 index 00000000..3fe3f25b --- /dev/null +++ b/Source/CPM3/makedate.lib @@ -0,0 +1,16 @@ +; +; [JCE] Have the date and copyright messages in only one source file +; +@BDATE MACRO + db '101198' + ENDM + +@LCOPY MACRO + db 'Copyright 1998, ' + db 'Caldera, Inc. ' + ENDM + +@SCOPY MACRO + db '(c) 98 Caldera' + ENDM + diff --git a/Source/CPM3/modebaud.lib b/Source/CPM3/modebaud.lib new file mode 100644 index 00000000..29853d35 --- /dev/null +++ b/Source/CPM3/modebaud.lib @@ -0,0 +1,32 @@ + ; equates for mode byte bit fields +; +mb$input equ 00000001b ; device may do input +mb$output equ 00000010b ; device may do output +mb$in$out equ mb$input+mb$output +; +mb$soft$baud equ 00000100b ; software selectable + ; baud rates +; +mb$serial equ 00001000b ; device may use protocol +mb$xonxoff equ 00010000b ; XON/XOFF protocol + ; enabled +; +baud$none equ 0 ; no baud rate associated + ; with this device +baud$50 equ 1 ; 50 baud +baud$75 equ 2 ; 75 baud +baud$110 equ 3 ; 110 baud +baud$134 equ 4 ; 134.5 baud +baud$150 equ 5 ; 150 baud +baud$300 equ 6 ; 300 baud +baud$600 equ 7 ; 600 baud +baud$1200 equ 8 ; 1200 baud +baud$1800 equ 9 ; 1800 baud +baud$2400 equ 10 ; 2400 baud +baud$3600 equ 11 ; 3600 baud +baud$4800 equ 12 ; 4800 baud +baud$7200 equ 13 ; 7200 baud +baud$9600 equ 14 ; 9600 baud +baud$19200 equ 15 ; 19.2k baud +; +; end of file diff --git a/Source/CPM3/move.z80 b/Source/CPM3/move.z80 new file mode 100644 index 00000000..885eecb2 --- /dev/null +++ b/Source/CPM3/move.z80 @@ -0,0 +1,116 @@ + title 'bank & move module for CP/M3 linked BIOS' + + cseg + + public ?move,?xmove,?bank,?bnkxlt + public ?mvinit,@hbbio,@hbusr + extrn @cbnk + +?mvinit: + ld bc,0F8F2H ; HBIOS GET BNKINFO + ;rst 08 ; D: BIOS Bank, E: User Bank + call 0FFF0H + ld a,d + ld (@hbbio),a + ld a,e + ld (@hbusr),a + ret + +?xmove: + ld (movbnk),bc ; save source & dest banks + or 0FFH ; flag interbank move type + ld (movtyp),a ; save it + ret + +?move: + ld a,(movtyp) ; get move type flag + or a ; set flags + jr nz,xbnkmov ; if so, go to interbank move + + ; Intrabank move + ex de,hl ; we are passed source in DE and dest in HL + ldir ; use Z80 block move instruction + ex de,hl ; need next addresses in same regs + ret + +xbnkmov: + ;ld ix,8888H + ;halt + ; Interbank move + xor a ; zero + ld (movtyp),a ; clear move type flag + push de + push hl + push bc + pop hl + ld a,(srcbnk) + call ?bnkxlt + ld e,a + ld a,(dstbnk) + call ?bnkxlt + ld d,a + ld b,0F4H ; SETCPY + ;rst 08 + call 0FFF0H + pop hl + pop de + ex de,hl ; swap address regs for call + ld b,0F5H ; BNKCPY + ;rst 08 + call 0FFF0H + ex de,hl ; next addresses in same regs + ;ld ix,9999H + ;halt + ret + +?bank: + call ?bnkxlt ; xlat to HBIOS bank id + jp 0FFF3H ; do it and return + +; +; Convert from CPM3 bank id to HBIOS bank id. +; CPM3 wants TPA for it's bank 0, so that is special +; case mapping to HBIOS BID_USR (8EH). Otherwise, we index +; down below BID_HBIOS (8DH). So CPM3 bank usage grows +; downward. +; +; CPM3 HBIOS +; ------------- ------------------- +; COMMON 8FH - BID_COM +; 0 - OS/BUFS 8EH - BID_USR +; 8DH - BID_BIOS +; 1 - TPA 8CH - BID_AUX +; 2 - BUFS 8BH - BID_AUX-1 +; 3 - BUFS 8AH - BID_AUX-2 +; ... +; +; N.B., Below BID_AUX is considered RAM disk bank. Need to +; make sure RAM disk is kept small enough to stay below +; banks used for OS buffers. +; +?bnkxlt: + ;ld ix,5555H + ;halt + ;cp 2 + ;jr c,xxx + ;ld ix,6666H + ;halt +;xxx: + or a + jr z,bank0 + neg ; 2 -> -2 + add a,08DH ; 8DH - 2 = 8BH +@hbbio equ $ - 1 ; BID_BIOS + ret +bank0: + ld a,08EH ; 0 -> 8EH +@hbusr equ $ - 1 ; BID_USR + ret + +movtyp db 0 ; non-zero for interbank move + +movbnk: +srcbnk db 0 +dstbnk db 0 + + end diff --git a/Source/CPM3/optbnk.lib b/Source/CPM3/optbnk.lib new file mode 100644 index 00000000..a5bf221d --- /dev/null +++ b/Source/CPM3/optbnk.lib @@ -0,0 +1,6 @@ + ; global assembler options for BANKED BIOS + +true equ -1 +false equ not true + +banked equ true diff --git a/Source/CPM3/optres.lib b/Source/CPM3/optres.lib new file mode 100644 index 00000000..5b94dab5 --- /dev/null +++ b/Source/CPM3/optres.lib @@ -0,0 +1,6 @@ + ; global assembler options for NONBANKED BIOS + +true equ -1 +false equ not true + +banked equ false diff --git a/Source/CPM3/readme.1st b/Source/CPM3/readme.1st new file mode 100644 index 00000000..1277e897 --- /dev/null +++ b/Source/CPM3/readme.1st @@ -0,0 +1,39 @@ +CP/M 3 +====== + + This archive contains an almost complete build of CP/M 3. + + If you have the source distribution, the file MAKING.DOC explains how to +set up the build environment on your computer. + +Differences from Digital Research CP/M 3 +======================================== + + All the CP/M 3 patches described in the document CPM3FIX.PAT have been +applied to the source code, except those to INITDIR. Patches 1-18 (except +nos. 5 and 9) were applied. + + CP/M 3 is now fully Year 2000 compliant. This affects the programs +DATE.COM, DIR.COM and SHOW.COM. + + Dates can be displayed in US, UK or Year-Month-Day format. This is set by +SETDEF: + + SETDEF [US] + SETDEF [UK] + SETDEF [YMD] respectively. + + The CCP has a further bug fix: A command sequence such as: + + C1 + :C2 + :C3 + + will now not execute the command C3 if the command C1 failed. + +What's missing? +=============== +INITDIR.COM - because it is written in PL/I and I can't make the + PL/I compiler at compile it. + Apparently a more recent version of the compiler is + required. \ No newline at end of file diff --git a/Source/CPM3/resbdos3.spr b/Source/CPM3/resbdos3.spr new file mode 100644 index 00000000..a5f21969 Binary files /dev/null and b/Source/CPM3/resbdos3.spr differ diff --git a/Source/CPM3/scb.asm b/Source/CPM3/scb.asm new file mode 100644 index 00000000..814deef6 --- /dev/null +++ b/Source/CPM3/scb.asm @@ -0,0 +1,50 @@ + title 'System Control Block Definition for CP/M3 BIOS' + + public @civec, @covec, @aivec, @aovec, @lovec, @bnkbf + public @crdma, @crdsk, @vinfo, @resel, @fx, @usrcd + public @mltio, @ermde, @erdsk, @media, @bflgs + public @date, @hour, @min, @sec, ?erjmp, @mxtpa + public @ccpdr + + +scb$base equ 0FE00H ; Base of the SCB + +@CCPDR equ scb$base+13h ; CCP Current Drive +@CIVEC equ scb$base+22h ; Console Input Redirection + ; Vector (word, r/w) +@COVEC equ scb$base+24h ; Console Output Redirection + ; Vector (word, r/w) +@AIVEC equ scb$base+26h ; Auxiliary Input Redirection + ; Vector (word, r/w) +@AOVEC equ scb$base+28h ; Auxiliary Output Redirection + ; Vector (word, r/w) +@LOVEC equ scb$base+2Ah ; List Output Redirection + ; Vector (word, r/w) +@BNKBF equ scb$base+35h ; Address of 128 Byte Buffer + ; for Banked BIOS (word, r/o) +@CRDMA equ scb$base+3Ch ; Current DMA Address + ; (word, r/o) +@CRDSK equ scb$base+3Eh ; Current Disk (byte, r/o) +@VINFO equ scb$base+3Fh ; BDOS Variable "INFO" + ; (word, r/o) +@RESEL equ scb$base+41h ; FCB Flag (byte, r/o) +@FX equ scb$base+43h ; BDOS Function for Error + ; Messages (byte, r/o) +@USRCD equ scb$base+44h ; Current User Code (byte, r/o) +@MLTIO equ scb$base+4Ah ; Current Multi-Sector Count + ; (byte,r/w) +@ERMDE equ scb$base+4Bh ; BDOS Error Mode (byte, r/o) +@ERDSK equ scb$base+51h ; BDOS Error Disk (byte,r/o) +@MEDIA equ scb$base+54h ; Set by BIOS to indicate + ; open door (byte,r/w) +@BFLGS equ scb$base+57h ; BDOS Message Size Flag (byte,r/o) +@DATE equ scb$base+58h ; Date in Days Since 1 Jan 78 + ; (word, r/w) +@HOUR equ scb$base+5Ah ; Hour in BCD (byte, r/w) +@MIN equ scb$base+5Bh ; Minute in BCD (byte, r/w) +@SEC equ scb$base+5Ch ; Second in BCD (byte, r/w) +?ERJMP equ scb$base+5Fh ; BDOS Error Message Jump + ; (word, r/w) +@MXTPA equ scb$base+62h ; Top of User TPA + ; (address at 6,7)(word, r/o) + end diff --git a/Source/CPM3/zpmldr.com b/Source/CPM3/zpmldr.com new file mode 100644 index 00000000..d355f574 Binary files /dev/null and b/Source/CPM3/zpmldr.com differ diff --git a/Source/Clean.cmd b/Source/Clean.cmd index 63f011c0..8f681c22 100644 --- a/Source/Clean.cmd +++ b/Source/Clean.cmd @@ -7,6 +7,8 @@ setlocal & cd ZCPR && call Clean.cmd & endlocal setlocal & cd ZCPR-DJ && call Clean.cmd & endlocal setlocal & cd ZSDOS && call Clean.cmd & endlocal setlocal & cd CBIOS && call Clean.cmd & endlocal +setlocal & cd CPM3 && call Clean.cmd & endlocal +setlocal & cd ZPM3 && call Clean.cmd & endlocal setlocal & cd Forth && call Clean.cmd & endlocal setlocal & cd BPBIOS && call Clean.cmd & endlocal setlocal & cd HBIOS && call Clean.cmd & endlocal diff --git a/Source/HBIOS/dbgmon.asm b/Source/HBIOS/dbgmon.asm index a75898e7..e93e6c38 100644 --- a/Source/HBIOS/dbgmon.asm +++ b/Source/HBIOS/dbgmon.asm @@ -828,7 +828,7 @@ COUT: LD E,A ; OUTPUT CHAR TO E LD C,CIODEV_CONSOLE ; CONSOLE UNIT TO C LD B,BF_CIOOUT ; HBIOS FUNC: OUTPUT CHAR - RST 08 ; HBIOS OUTPUTS CHARACTDR + RST 08 ; HBIOS OUTPUTS CHARACTER ; ; RESTORE ALL REGISTERS POP HL @@ -851,7 +851,7 @@ CIN: ; INPUT CHARACTER FROM CONSOLE VIA HBIOS LD C,CIODEV_CONSOLE ; CONSOLE UNIT TO C LD B,BF_CIOIN ; HBIOS FUNC: INPUT CHAR - RST 08 ; HBIOS READS CHARACTDR + RST 08 ; HBIOS READS CHARACTER LD A,E ; MOVE CHARACTER TO A FOR RETURN ; ; RESTORE REGISTERS (AF IS OUTPUT) diff --git a/Source/HBIOS/hbios.asm b/Source/HBIOS/hbios.asm index bb8b0965..b518104d 100644 --- a/Source/HBIOS/hbios.asm +++ b/Source/HBIOS/hbios.asm @@ -512,45 +512,24 @@ HBX_STACK .EQU $ ; ; HBIOS INTERRUPT SLOT ASSIGNMENTS ; -; # SBC ZETA N8,MK4,RCZ180 -; --- -------------- -------------- -------------- -; 0 CTC0A Z180/INT1 -; 1 CTC0B Z180/INT2 -; 2 CTC0C Z180/TIM0 -; 3 CTC0D Z180/TIM1 -; 4 Z180/DMA0 -; 5 Z180/DMA1 -; 6 Z180/CSIO -; 7 SIO0A/B Z180/SER0 -; 8 SIO1A/B Z180/SER1 -; 9 PIO0A PIO0A -; 10 PIO0B PIO0B -; 11 PIO1A PIO1A -; 12 PIO1B PIO1B -; 13 -; 14 -; 15 -; 16 -; -; # RCZ80 EZZ80 ZETA2 -; --- -------------- ------------- -------------- -; 0 CTC0A CTC0A/SIO0CLK CTC0A/PRESCL -; 1 CTC0B CTC0B/SIO1CLK CTC0B/TIMER -; 2 CTC0C CTC0C/PRESCL CTC0C/UART -; 3 CTC0D CTC0D/TIMER CTC0D/FDC -; 4 -; 5 -; 6 -; 7 SIO0A/B SIO0A/B -; 8 SIO1A/B SIO1A/B +; # Z80 Z180 +; --- -------------- -------------- +; 0 CTC0A INT1 -+ +; 1 CTC0B INT2 | +; 2 CTC0C TIM0 | +; 3 CTC0D TIM1 | +; 4 DMA0 +- Z180 INTERNAL +; 5 DMA1 | +; 6 CSIO | +; 7 SIO0 SER0 | +; 8 SIO1 SER1 -+ ; 9 PIO0A PIO0A ; 10 PIO0B PIO0B ; 11 PIO1A PIO1A ; 12 PIO1B PIO1B -; 13 -; 14 +; 13 SIO0 +; 14 SIO1 ; 15 -; 16 ; HBX_IVT: .DW HBX_IV00 diff --git a/Source/HBIOS/ver.inc b/Source/HBIOS/ver.inc index ed69f866..315bcced 100644 --- a/Source/HBIOS/ver.inc +++ b/Source/HBIOS/ver.inc @@ -2,4 +2,4 @@ #DEFINE RMN 9 #DEFINE RUP 2 #DEFINE RTP 0 -#DEFINE BIOSVER "2.9.2-pre.17" +#DEFINE BIOSVER "2.9.2-pre.18" diff --git a/Source/ZCCP/ccp.com b/Source/ZCCP/ccp.com new file mode 100644 index 00000000..72a01c68 Binary files /dev/null and b/Source/ZCCP/ccp.com differ diff --git a/Source/ZCCP/diskinfo.com b/Source/ZCCP/diskinfo.com new file mode 100644 index 00000000..068cc97a Binary files /dev/null and b/Source/ZCCP/diskinfo.com differ diff --git a/Source/ZCCP/loadseg.com b/Source/ZCCP/loadseg.com new file mode 100644 index 00000000..503d7679 Binary files /dev/null and b/Source/ZCCP/loadseg.com differ diff --git a/Source/ZCCP/read.me b/Source/ZCCP/read.me new file mode 100644 index 00000000..83dd11e6 --- /dev/null +++ b/Source/ZCCP/read.me @@ -0,0 +1,103 @@ + + + + + Z C C P by Simeon Cran + ======================= + + +February 1993. +ZCCP has been improved slightly from the original release. It +should run much the same as before however modifications have +been made in an attempt to allow it to work successfully from +systems which boot from other than the A: drive. Also, the +terminal dimensions are now automatically taken from the SCB (as +set up by GENCPM). Special thanks go to Randy Winchester who is +sharing his easy to follow instructions about ZCCP. Read ZCCP.TXT +in this package for the expert help. + + + + +ZCCP Quick Instructions +======================= + +ZCCP is a direct replacement for the CP/M 3.0 CCP. It requires +ZPM3 and provides a ZCPR compatible system. There are some minor +incompatibilities with ZCPR however the advantages of ZCCP more +than outweigh these. + +You must replace your current CCP with the ZCCP.COM. How you do +this will depend on the setup of your computer so no details are +given here. + +When ZCCP starts, it reads ZINSTAL.ZPM (which must be on the +default boot drive (usually A:) in user area 0). This file can +be patched to set initial values for MHZ (speed of the computer), +MAXU (maximum user number) and MAXD (highest disk drive). These +values are set near the start of the ZINSTAL.ZPM file and there +are ASCII pointers to them (which can be seen with a debugger +such as SID.COM). + +After ZINSTAL.ZPM has been read, ZCCP attempts to run the command +STARTZPM.COM. This is usually an "alias" containing a number of +commands used to put the system into a useful state. Typically +STARTZPM.COM would contain commands to load a TCAP and named +directory file, to set the path, and to set an error handler. + +LOADSEG.COM is a ZCCP utility used to load RSXes, TCAPs and Named +Directory files. It contains some online help. + +RSXDIR.COM is a ZCPR utility which displays RSXes in memory. It +has been included to help you understand how LOADSEG places +various segments in high memory. + +DISKINFO.COM is a ZCPR utility which gives information about your +disks. It has been included for your interest. + +Note that ZCCP has very few resident commands. If you are +switching from a standard CP/M 3.0 CCP you will find that you are +unable to perform certain simple functions (such as directory +displays!). You should therefore obtain replacements for the CCP +resident commands before installing ZCCP. The Z-Node bulletin +boards in Australia and the USA are a good source. Note that ZCCP +will not load Type-4 ZCPR programs. + +If you are familiar with ZCPR systems you should have little +trouble in getting the most from ZCCP. If you are new to ZCPR, I +suggest that you learn as much as you can. ZCPR is a complex and +powerful system which does very little without proper setup. + +RCP's and IOP's are not implemented (which saves TPA space) and +FCP's are not implemented although flow control is. To use flow +control you must have an IF.COM program to handle IF commands. +ZCCP handles the standard flow control commands internally (XIF, +FI, ELSE, AND, OR). There are two other internal commands +implemented: NOTE (a do-nothing command) and CLS (to clear the +screen). + + +The ZCCP package presented here may be freely distributed and +used for your own private use. No commercial or institutional use +is allowed without prior written agreement. + + +The ZCCP package is copyright 1992,1993 by Simeon Cran, Brisbane, +Australia. + + +The Z-Node 62 BBS in Perth, Western Australia may be contacted +from Australia on 09 450 0200 or from overseas on +619 4500200. +Z-Node 62 also is a distribution point for ZPM3 and MYZ80. MYZ80 +is a high performance Z80 emulator for IBM AT (and better) +computers. MYZ80 is able to run ZPM3. + + +No support for ZCCP is being offered at this stage. Use at your +own risk. + + +Simeon Cran. +February 1993. + + \ No newline at end of file diff --git a/Source/ZCCP/rsxdir.com b/Source/ZCCP/rsxdir.com new file mode 100644 index 00000000..dd880a61 Binary files /dev/null and b/Source/ZCCP/rsxdir.com differ diff --git a/Source/ZCCP/startzpm.com b/Source/ZCCP/startzpm.com new file mode 100644 index 00000000..721b588b Binary files /dev/null and b/Source/ZCCP/startzpm.com differ diff --git a/Source/ZCCP/zccp.txt b/Source/ZCCP/zccp.txt new file mode 100644 index 00000000..fb4efce9 --- /dev/null +++ b/Source/ZCCP/zccp.txt @@ -0,0 +1,308 @@ +The following is used by permission and remains copyright Randy Winchester. +=========================================================================== + +ZCCP Documentation, Version 1.0 +by Randy Winchester + +ZCCP Features + +This documentation is provided to assist the user in getting a +ZCCP system up and running. It is not an exhaustive course on Z- +System or ZCPR. The following list details which ZCPR features +are provided with ZCCP, and which ones aren't. + +* ZCPR 3.3 compatibility. ZCCP can run a wide range of utilities + an applications created for ZCPR 3.3 and ZCPR 3.4. + +* TCAP. A Z3T termcap file describing terminal characteristics + can be loaded into the system. Z-System programs make use of the + TCAP for output to the screen - a big improvement over the old + method of patching individual programs with terminal control + codes. TCAP files are loaded by the ZCCP LOADSEG command. + +* Named directories. Up to 12 user areas can be assigned names. + Named Directory Registers (*.NDR files) are loaded by the ZCCP + LOADSEG command. + +* Command Search Path. ZCCP will search for commands along a + user defined search path. Up to six path elements + (directories) can be defined. + +* Environment block. Contains TCAP, Named Directory, and Path + information. Also includes a map of active disk drives and + other system information. The environment block can be viewed + with the Z-System SHOW utility. + +* Flow control. Conditional processing for batch files. Relies + on Z-System IF.COM for setting the flow state. Other flow + control commands (FI, ELSE, XIF, OR, AND) are resident. + +* Multiple commands can be entered on the command line. The + command line buffer will hold up to 225 characters. Commands + should be separated by semicolons. + +* Extended Command Processor. If a command is not a built-in + flow command, resident command, or located on disk along the + search path, the command line is passed to an extended command + processor. A typical extended command processor is ARUNZ, a + sophisticated batch file executor with alias features. To use + a program as an extended command processor, rename it to + CMDRUN.COM and place it in the ROOT directory of your boot disk. + +* Error handler. In the event that the extended command + processor can't handle a command, control is passed to an error + handler. Error handlers give information about the error + (instead of the useless CP/M "?" message) and allow the command + line to be edited and reused. + +* Resident commands. The following commands are built in: + CLS - clears the screen + NOTE - text following the NOTE command is treated as a + comment. + FI - Flow control: terminate the current IF level + ELSE - Flow control: toggle the flow state + XIF - Flow control: exit all pending IF levels + OR - Flow control: OR IF tests to set flow state + AND - Flow control: AND IF tests to set flow state + +* Shell stack. Up to four shell levels can be defined. Z-System + provides a choice of several different shells. Applications such + as terminal programs and word processors can also be assigned + shell status. + +* ZCCP uses the LOADSEG command for direct loading of RSX files + that have not been GENCOMed. Example: LOADSEG SAVE.RSX loads + SAVE.RSX. + +There are some things that Z3Plus will do that ZCCP won't do. + +- ZCCP does not support a Flow Command Package (FCP). It relies + on the transient IF command. Other flow commands (FI, ELSE, XIF, + OR, AND) are resident in ZCCP. + +- A Resident Command Package (RCP) is not implemented. CLS and + NOTE are resident in ZCCP. All other commands must be loaded + from disk. This isn't as much of a handicap as it might sound + if you have a fast RAM drive to store frequently used commands. + +- ZCCP can not load type 4 programs (used with ZCPR 3.4). It + loads standard COM files at 100H, and type 3 programs that load + in high memory. Most type 4 programs have type 3 or COM + equivalents, so this should not be a problem. + +- ZCCP can not reexecute loaded programs. This trick is usually + performed on Z-Systems with a GO command that jumps to 100H. + Since ZCCP also loads at 100H, a GO command would only restart + ZCCP. + +ZCCP Files + +Three files are included in ZCCP.ARK: + + File name Size Description + ============ ==== ========================================== + CCP .COM 3k ZCCP replacement for CCP.COM + LOADSEG .COM 3k Loader for named directories and termcaps + ZINSTAL .ZPM 1k Segment containing environment information + +Getting Started - Preparing a Boot Disk + +Format a system boot disk using the same proceedure that you normally +would. + +Copy the files from ZCCP.ARK to user area 0 of the newly +formatted disk. + +Copy CPM+.SYS (some systems may use a slightly different name for this +file) to user 0 of the boot disk. The CPM+.SYS must include the BDOS +segments from ZPM3. Use the ZPM3 MAKEDOS utility to overlay your +system file with ZPM3. (Commodore 128 users must generate a new +system using the ZPM3 BDOS segments. The MAKEDOS utility does not +work properly on a C128.) + +Locate a copy of a Z-System alias utility. A good one is +SALIAS16, although others should work also. Copy it to user 0 of +the boot disk. + +At this point, reboot the system with the new system disk. After the +system boots, you won't be able to do much with it. The only resident +commands are CLS and NOTE, and ZCCP can only locate commands if they +are prefixed with the drive and user number. + +The next step is to create a startup alias. When ZCCP boots, it +looks for a file named STARTZPM.COM and executes commands from +it. STARTZPM.COM is created with a ZCPR alias utility. Here is +a listing of a STARTZPM.COM created with SALIAS: + + ============================================================= + + A0>SALIAS STARTZPM + + 15: ; Logs the ROOT directory (A15) on the + ; current drive. + + LOADSEG NAMES.NDR TCAP.Z3T + ; LOADSEG loads the Named Directory Register + ; and TCAP. + ; Directories can now be referred to by + ; name, as in the next command: + + SETPTH10 /C COMMANDS RAM2 WORK $$$$ $$0 ROOT + ; SETPTH sets the command search path. + ; The /c option first clears any existing path. + ; Directories are then listed in the + ; order searched. In this case, COMMANDS + ; is a 64K ramdisk (drive/user F0) where + ; frequently used commands are stored. RAM2 is + ; an additional RAM disk. (drive/user M0). + ; WORK is a standard 3.5" floppy disk + ; drive, (drive/user C15) where some 700K + ; of utilities and applications are + ; located. $$$$ refers to the currently + ; logged drive and user area. $$0 refers + ; to user area 0 of the current drive. + ; The ROOT directory is on drive A, user + ; 15, where startup utilities and system + ; files can be found. + + AUTOTOG ON ; Turns on keyboard control of ZPM3 Auto + ; Command Prompting. Auto Command + ; Prompting is toggled by entering CTRL-Q. + + COMMANDS: ; Logs the commands directory. + + IF ~EXIST CP.* ; Test to see if commands are loaded. + ; This line reads: "If the CP command + ; does not exist . . ." and sets the flow + ; state to true if the file doesn't exist. + C1:CP C1:*.* F0: + ; ". . . copy all of the commands in + ; drive/user C1 to the commands (F0) + ; directory . . ." + FI ; ". . . end if." + + ROOT: ; Log the root directory (A15). + + CP C:ZF*.* M0: ; Copy ZFILER.COM and ZFILER.CMD to the + ; REU2 directory (M0). + + VERROR ; Install VERROR error handler. + + DATE S ; Set the system time and date. + + ZF ; Invoke ZFILER as a shell. + + ============================================================= + +Of course, your STARTZPM alias will vary depending on the +hardware you need to support, your software preferences, and your +work habits. This alias is close to the upward size limit that +ZCCP can handle based on the capacity of the multiple command +buffer. At the very least, I recommend an alias that will set up +a search path and load a TCAP. + +Actually, I put the cart before the horse in this example. If +you try to reboot your system with the LOADSEG command as listed, +you'll notice that you don't have a NAMES.NDR file. There isn't +one distributed with ZCCP either. Z-System utilities won't let +you edit the NDR either, since the buffer for it hasn't been +created yet. This turned out to be a nasty chicken/egg +situation, hopefully solved by the inclusion of a sample +NAMES.NDR file containing simply A0:SYSTEM and A15:ROOT. + +At this point, you should have a mostly functioning ZCCP system disk. +Reboot the system with the new disk. You might want to correct any +problems with it or tweak it to perfection before moving on. + +List of Z-System Utilities for ZCCP + +Some of the following utilities are essential, others are nice to +have. The version numbers listed are the latest known versions at the +time that this documentation was written. Utilities can be found on +ZNode BBSs, and some of them are available on Internet anonymous ftp +sites (Simtel20 or its mirror sites). + +SALIAS16 - already mentioned in the example above. SALIAS (or +one of the other ZCPR alias utilities) are essential. + +SD138B - excellent DIRectory utility. SD offers many +different types of sorts, list formats, etc., displays date +stamps, and supports output to a file. + +MKDIR32 - utility for manipulating directory names and Named +Directory Register (*.NDR) files. + +ERASE57 - erases files. + +ZFILER10 - a file management shell that can launch applications. +It is programmable in that it can execute user defined macros +from a file. Multiple files can be "tagged" and operated on by +other programs. ZFILER is an excellent program, sort of a GUI +desktop without the slow graphics. + +SETPTH10 - used to set the command search path. Essential! + +VERROR17 - error handler that displays the command line for +reediting. VERROR17 is the only error handler that I found that +works with ZCCP. + +ZEX50 - Z-System EXecutive is a powerful batch file processor +that replaces the CP/M SUBMIT command. + +LBRHLP22 - Z-System Help utility displays help files. Help +files can be crunched (*.HZP), and/or loaded from a HELP.LBR +library. + +ARUNZ09 - runs an alias script from a text file. ARUNZ is +frequently used as an extended command processor. To use ARUNZ +(or any other executable utility) as an extended command +processor, rename it to CMDRUN.COM. + +VLU102 - Video Library Utility views or extracts files from +libraries. Versions of VLU above 1.02 do not work reliably with +ZPM3/ZCCP. + +Z33IF16 - is the IF.COM discussed in the section on flow control. + +SHOW14 - displays an immense amount of information about your +Z-System. SHOW also includes a memory patching function. + +ZCNFG24 - configures Z-System program options. Most Z-System +programs are distributed with a configuration (*.CFG) file that +produces a menu of configuration options when run with ZCNFG. + +ZP17 - Z-System Patch utility edits files, disk sectors, or +memory, and includes a built-in RPN calculator and number base +converter. + +ZMAN-NEW - This is a manual describing Z-System features in +depth. It is based on earlier versions of Z-System, and is a +little dated, but otherwise contains information that you won't +find anywhere else. Not everything in the manual applies to +operation of ZPM3/ZCCP, but with the documentation presented +here, you should be able to get a good idea of what works and +what doesn't. + +A TCAP termcap file for your system - This file is essential if you +want to use any ZCPR programs that need a TCAP. + +ZCCP Technical Notes + +ZCCP is a replacement CCP that implements ZCPR 3.3. It loads at +100H and is stored in the bank 0 CCP buffer for fast reloading as +does the standard CCP. By contrast, Z3Plus loads into high +memory and can be overwritten by transient commands, requiring +reloading Z3Plus from disk. Because ZCCP replaces the CCP, a +ZCCP system has more TPA (transient program area) than a Z3Plus +system. A ZCCP system on the C128 has more than 57K of TPA, +almost the same amount as a standard C128 CP/M system. + +This should be enough information to get started with ZPM3/ZCCP. +Set up a boot disk, experiment with some Z-System utilities, read +ZMAN-NEW, and get some applications running. You'll agree that +ZPM3/ZCCP breaths new life into CP/M. + +******************************************************************************* +* Randy Winchester * randy@mit.edu * PO Box 1074, Cambridge, MA 02142 * +******************************************************************************* + \ No newline at end of file diff --git a/Source/ZCCP/zinstal.zpm b/Source/ZCCP/zinstal.zpm new file mode 100644 index 00000000..1a6b2c4c Binary files /dev/null and b/Source/ZCCP/zinstal.zpm differ diff --git a/Source/ZPM3/Build.cmd b/Source/ZPM3/Build.cmd new file mode 100644 index 00000000..1e195248 --- /dev/null +++ b/Source/ZPM3/Build.cmd @@ -0,0 +1,61 @@ +@echo off +setlocal + +set TOOLS=../../Tools + +set PATH=%TOOLS%\zx;%TOOLS%\cpmtools;%PATH% + +set ZXBINDIR=%TOOLS%/cpm/bin/ +set ZXLIBDIR=%TOOLS%/cpm/lib/ +set ZXINCDIR=%TOOLS%/cpm/include/ + +copy ..\ZCCP\ccp.com zccp.com +copy ..\ZCCP\zinstal.zpm . +copy ..\ZCCP\startzpm.com +copy ..\CPM3\genbnk.dat . +rem copy ..\CPM3\bios3.spr . +copy ..\CPM3\bnkbios3.spr . +copy ..\CPM3\gencpm.com . +copy ..\CPM3\biosldr.rel + +rem ZPM Loader +echo. +echo. +echo *** ZPM Loader *** +echo. +zx LINK -ZPMLDR[L100]=ZPM3LDR,BIOSLDR +rem pause + +rem Banked CPM3 +echo. +echo. +echo *** Banked ZPM3 *** +echo. +copy genbnk.dat gencpm.dat +zx gencpm -auto -display +if exist zpm3.sys del zpm3.sys +ren cpm3.sys zpm3.sys +rem pause + +rem Update cpm_hd.img +echo. +echo. +echo *** Update Disk Image *** +echo. +for %%f in ( + zpmldr.com + autotog.com + clrhist.com + setz3.com + zpm3.sys + zccp.com + zinstal.zpm + startzpm.com +) do call :upd_img %%f +goto :eof + +:upd_img +echo %1... +cpmrm.exe -f wbw_hd0 ../../Binary/hd_cpm3.img 0:%1 +cpmcp.exe -f wbw_hd0 ../../Binary/hd_cpm3.img %1 0:%1 +goto :eof \ No newline at end of file diff --git a/Source/ZPM3/Clean.cmd b/Source/ZPM3/Clean.cmd new file mode 100644 index 00000000..f4b6eab1 --- /dev/null +++ b/Source/ZPM3/Clean.cmd @@ -0,0 +1,18 @@ +@echo off +setlocal + +if exist ccp.com del ccp.com +if exist *.sys del *.sys +if exist gencpm.dat del gencpm.dat +if exist loader.cim del loader.cim +if exist bnkbios3.spr del bnkbios3.spr +if exist system.epr del system.epr +if exist system.evn del system.evn +if exist system.odd del system.odd +if exist biosldr.rel del biosldr.rel +if exist *.sym del *.sym +if exist zpmldr.com del zpmldr.com +if exist zccp.com del zccp.com +if exist startzpm.com del startzpm.com +if exist gencpm.com del gencpm.com +if exist *.dat del *.dat diff --git a/Source/ZPM3/autotog.com b/Source/ZPM3/autotog.com new file mode 100644 index 00000000..3c7a5980 Binary files /dev/null and b/Source/ZPM3/autotog.com differ diff --git a/Source/ZPM3/autotog.z80 b/Source/ZPM3/autotog.z80 new file mode 100644 index 00000000..b59a6663 --- /dev/null +++ b/Source/ZPM3/autotog.z80 @@ -0,0 +1,131 @@ + +;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +; A U T O T O G +; for ZPM3 +; by Simeon Cran +; 30/3/92 +; +; This program toggles the Auto Command Prompting facility of ZPM3. It is +; presented in source code form to inform users about how the facility is +; manipulated. + +; Be aware that when Auto Command Prompting is enabled with this program, +; it won't actually operate until turned on at the keyboard with ^Q. This +; program simply enables the ^Q toggling of Auto Command Prompting. + +; When ZPM3 is booted, Auto Command Prompting is disabled. Usually, a +; startup file would include the AUTOTOG command to turn it on unless it +; is felt that the facility could confuse the operator (as may happen with +; a remote ZPM3 system). +;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +; SYNTAX: +; AUTOTOG Toggles the state of the Auto Command Prompting +; AUTOTOG ON Enables Auto Command Prompting +; AUTOTOG OFF Disables Auto Command Prompting +; AUTOTOG // Displays a brief help message + +;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +; +; Automatic Command Prompting is enabled and disabled by a bit 6 of offset +; 85h of the SCB page. This offset can not be directly accessed by the SCB +; function (31h). Instead, we get the SCB base page with function 31h, and +; then find the offset from there. No other bits in the byte may be touched. +; +;=============================================================================== + +BDOS equ 5 +deffcb equ 5ch +SCBfunc equ 31h ; Get/Set SCB function number +SCBoff equ 3bh ; Offset in SCB to get SCB base page +ACPoff equ 85h ; Offset in SCB base page of Auto Command Prompting bit + + jp start ; Jump over general data + +HELPmsg: + db ' SYNTAX:' + db 10,13 + db ' AUTOTOG Toggles the state of the Auto Command Prompting' + db 10,13 + db ' AUTOTOG ON Enables Auto Command Prompting' + db 10,13 + db ' AUTOTOG OFF Disables Auto Command Prompting' + db 10,13 + db ' AUTOTOG // Displays a brief help message' + db '$' +ONmsg: + db 'ZPM3 Auto Command Prompting is now enabled. Toggle with ^Q.' + db '$' +OFFmsg: + db 'ZPM3 Auto Command Prompting is now disabled.' + db '$' + +ONword: ; Word to match to turn Auto Command Prompting on + db 'ON ' +OFFword: ; Word to match to turn Auto Command Prompting off + db 'OFF ' +TOGword: ; Word to match to toggle Auto Command Prompting + db ' ' + +HELP: ld de,HELPmsg +MSGexit: + ld c,9 + call bdos + rst 0 + +start: ; Get the address of the bit which controls Auto Command Prompting. + ld c,SCBfunc + ld de,SCBPB + call bdos ; Get base page of SCB + ld h,a + ld l,ACPoff ; HL is now the address of the byte + ld (ACPaddr),hl ; Save it + +; Find out what the user wants to do. If the argument matches +; any of the three control words, act accordingly, otherwise +; show the help message and exit. + ld hl,ONword + call matchWord + jr z,TurnON + ld hl,OFFword + call matchWord + jr z,TurnOFF + ld hl,TOGword + call matchWord + jr nz,HELP +; Toggle the Auto Command Prompting. + ld hl,(ACPaddr) + bit 6,(hl) + jr z,TurnON +TurnOFF: ; Turn the Auto Command Prompting off. + ld hl,(ACPaddr) + res 6,(hl) + ld de,OFFmsg + jr MSGexit + +TurnON: ; Turn the Auto Command Prompting on. + ld hl,(ACPaddr) + set 6,(hl) + ld de,ONmsg + jr MSGexit + + + +matchWord: ; Compare the string at HL with the string at defFCB+1 for + ; 8 bytes. Return Z if it matches. + ld de,defFCB + ld bc,8 +matchW1: + inc de + ld a,(de) + cpi + ret nz + jp pe,matchW1 + ret + +SCBPB: ; System control block function parameter block. +ACPaddr: ; Save the address of the ACP bit here too. + db 03bh + db 0 ; Get operation + + \ No newline at end of file diff --git a/Source/ZPM3/bios.txt b/Source/ZPM3/bios.txt new file mode 100644 index 00000000..f9a6ccd7 --- /dev/null +++ b/Source/ZPM3/bios.txt @@ -0,0 +1,230 @@ + NOTES on your CP/M 3.0 BIOS and ZPM3 + ==================================== + Last updated 19/4/92 + +ZPM3 will work fine with your current CP/M 3.0 BIOS. This +document is not meant to tell you how to change your BIOS for +ZPM3, but rather to point out some interesting and useful facts +about the way ZPM3 uses the BIOS, and how you should configure +your BIOS. + + + +XMOVE routine. +~~~~~~~~~~~~~~ +If you have 128 byte physical sectors, or your BIOS does all the +deblocking so that it appears to the BDOS that you have 128 byte +physical sectors, XMOVE does not get used at all by ZPM3. Such +was not the case with CP/M 3.0 which would make redundant calls +to XMOVE. Make sure XMOVE is implemented and working anyhow as +applications may attempt to use it. + +When the BDOS is operating in the system bank (bank 0) and it +needs to move data in the TPA bank, it switches to the TPA bank +and does an ordinary LDIR. As such, XMOVE will never get called +by the BDOS with B=C (source bank and destination bank the same). + +In the CP/M 3.0 manuals, there are two differing opinions about +XMOVE as far as whether B is the source or the destination. The +truth is that C is the source and B is the destination. Anything +you see to the contrary is a misprint. + +MOVE routine. +~~~~~~~~~~~~~ +When CP/M 3.0 was released, it was made 8080 compatible simply +because CP/M 2.2 was 8080 compatible. I have never heard of an +8080 machine running CP/M 3.0, and it is likely that there has +never been one. Digital Research knew that the Z80 was the CPU of +choice for modern PC's, and while they wrote their code for the +8080, they recognised the Z80 with the MOVE routine (which a Z80 +BIOS could implement in just three instructions). + +ZPM3 uses the MOVE routine much less than CP/M 3.0 does. In fact, +the only time ZPM3 uses MOVE is with an XMOVE call directly +preceding it. If you have 128 byte physical sectors (or the BIOS +does the sector deblocking), MOVE will never get called. + +Always remember that MOVE must return with HL and DE pointing to +the end of the moved data. If they don't, you will have trouble. + + +TIME routine. +~~~~~~~~~~~~~ +Be aware that the DATE program supplied with CP/M 3.0 will not +work properly if your BIOS does not update the SCB with +interrupts. There have been replacements since then that are +available in the public domain. + +One common trap for BIOS writers is forgetting that HL and DE +must be saved by the TIME routine. There is no obvious reason for +it, and really they should be saved in the BDOS. + +ZPM3 does not expect HL to be saved. If you have had trouble with +your CP/M 3.0 clock things might work now. It was decided that +seeing as TIME was the only routine (apart from MOVE) which +required HL to be saved, it was too easy to overlook, and a real +pain to implement (some systems use HL to switch banks on entry +to the BIOS. MOVE is always accounted for, but TIME sometimes +isn't (Morrow MD11 owners take note!)). + +Ideally, there should be no reason to save HL in your BIOS, +unless you intend to run CP/M 3.0 sometimes (although I can't +imagine why). Any applications which attempt to use TIME through +the function 50 are not guaranteed that HL will be saved anyhow. + +Buffers. +~~~~~~~~ +CP/M 3.0 (and therefore ZPM3) keeps special disk buffers. The +system is rather complex. The directory is buffered separately +from the rest of the disk (and in the case of 128 byte sectors +the rest of the disk isn't buffered anyhow). + +You decide how many buffers to give to each disk's directory and +data, and you may choose to have buffers shared by different +drives. All these choices can make for lots of fun for the +hacker, but without knowing much about the internal workings of +the BDOS how do you best set the buffer up? + +There are many cases to consider depending on how much RAM you +have available to allocate to buffers. If you have virtually +unlimited RAM, you might as well allocate as many buffers as +GENCPM will allow. The only catch to this is that more buffers +implies the BDOS will take more time to look through them all +before coming to the decision that a disk read is required. The +good news is that the ZPM3 searching algorithm is particularly +fast. Empty buffers are discovered even faster than buffers +which are valid but don't match, so large numbers of empty +buffers pose very little problem. In general, even with the +maximum number of buffers, the advantages they give outweigh the +disadvantages. + +Of course, few people have unlimited RAM. If you have very little +room available, spend most of it on the directory buffers. These +buffers act like a cache of the directory, and can save the disk +heads from moving back to the directory tracks to find out where +the next block is stored. Even on very fast hard disks, the +advantages that decent directory buffers give are great. + +When dividing up directory buffers between a number of drives, +consider which drive holds the most files and which drive does +the most work. A drive which holds a lot of files but is rarely +accessed is not worth wasting buffers on. If you have a system +with one hard drive and one floppy drive, and you don't intend to +use the floppy drive very much, give only one buffer to the +floppy and all the rest to your hard drive. This will penalise +the floppy's performance somewhat, but the improvement it gives +to the hard drive will make it worthwhile. + +Data buffers, like directory buffers, perform two tasks: +deblocking of physical sectors, and cacheing. For data buffers +however the cacheing is the less important job, unless you have a +lot of data buffers available. The reason for this is that the +buffer algorithms work by taking the least recently used buffer +and using it for deblocking. If you are working on a file which +is 8k long, but you only have 4k of buffers, the BDOS will run +out of buffers before it has read the whole file and will grab +the least recently used one even though it contains valid data +from the file which could be required later on. The result is +that the BDOS does much searching through its 4k of buffers, but +rarely finds anything which matches and must read from the disk +anyhow. + +In practice the system works a little better than that because of +the way files are used by most programs, so data buffers are +still worthwhile, but to take real advantage of their cacheing +ability you must have more room in the data buffers than the size +of the file you are working with. With word processors such as +Wordstar and NewWord creating extra files as they work, you +really need more than twice as much room in the buffers than the +size of the file. + +So you can see why data buffers are less important than directory +buffers. Something else you should be aware of concerns multi- +sector i/o and the data buffers. When the BDOS is told to read a +file it searches its buffers and if it can't find the data there +it reads it from the disk. Normally it deblocks the data one +record at a time through its data buffers, leaving the data in +the buffers in case it is required again. However multi-sector +i/o does not usually need to deblock its data, so the data is +sent straight to the TPA without going through the data buffers. +If any of that data is required again, it will not be in the data +buffers and must be read from the disk. So two reads of the same +data using multi-sector i/o might actually be slower than reads +that are done a sector at a time! + +And the really important thing about all this is that the CCP +uses multi-sector i/o to load programs. So if you thought that +implementing large numbers of data buffers would give you faster +loading of programs, you were wrong. The data buffers won't help +program loading unless the data can be put into the buffers +first. + +If you use ZCCP, you will find there is a facility to prevent the +data buffers from being bypassed on program loads. It involves +simply setting the f1' bit of the file. The idea is that you set +f1' on all the files which are small enough not to clog up your +buffers, and then they run as if they are on a ram disk, but one +in which you can never lose data. The system is quite wonderful +in that the RAM used to hold the files is available to buffer +other data if required. Unlike a ram disk, the RAM is dynamically +allocated and the data is completely safe. But you must be using +ZCCP, and you must have at least 6k of data buffers before it +does anything useful. If you currently have a ram disk but few +data buffers, consider taking a chunk of your ram disk for data +buffers and switching to ZCCP. + +CPMLDR bug. +~~~~~~~~~~~ +This is closely related to the subject of buffers because you +will find that if you increase your buffers past a certain point, +the system will not boot. Almost certainly you will suspect a +problem with your BIOS code (you normally should), however the +CPMLDR.REL code supplied by DRI has a bug in it. + +You may be wondering if everything DRI did with CP/M 3.0 was +buggy! I must say that what they achieved was terrific, but it +had its faults as well. Hopefully ZPM3 has addressed them all. + +The CPMLDR problem occurs when your CPM3.SYS grows from being 16k +or less, to over 16k (and therefore two logical extents). You may +not have this problem under certain drive configurations, but if +you do, the symptom is that described above. + +There really is no way of patching around this, but if you have +your loader BIOS, you can certainly use the (somewhat superior) +ZPM3LDR.REL code instead. This works very similarly to the DRI +code, except that it works properly. Unlike the DRI code, you +will find that ZPM3LDR has all its messages at the head of the +file so that you can patch them and change them if you wish. + +ZPM3LDR does not clear the screen on boot up (CPMLDR does by +sending multiple linefeeds), but you could patch this if you +like. ZPM3LDR.REL will directly replace CPMLDR.REL. ZPM3LDR +however does not use the MOVE routine that CPMLDR requires +(although there is nothing much to be gained by removing it from +your loader bios code). + + +GENCPM bugs. +~~~~~~~~~~~~ +GENCPM has bugs in it. If you can, try and set up all your +buffers manually. That way you'll know where they are and you are +in complete control. + +The biggest fault I have found with GENCPM is that it will +allocate allocation vectors incorrectly. CP/M 3.0 can use double +bit allocation vectors, but doesn't necessarily. Sometimes (and I +think it is mainly with big disks), GENCPM will only allocate +enough room for single bit allocation vectors when double bit +vectors had been specified. The symptoms of this are varied, but +often, you can use your A: drive for a while, but as soon as you +use your B: drive funny things happen. If you only use A: and C: +drives, things appear to work OK. + +The first thing to try if you suspect a GENCPM induced problem is +setting up with only one drive and a single buffer. If that fixes +it, the problem could well be with GENCPM. + +Naturally, your BIOS code could still be the problem, so look out +for that too! + \ No newline at end of file diff --git a/Source/ZPM3/bnkbdos3.spr b/Source/ZPM3/bnkbdos3.spr new file mode 100644 index 00000000..cd31a6a4 Binary files /dev/null and b/Source/ZPM3/bnkbdos3.spr differ diff --git a/Source/ZPM3/clrhist.com b/Source/ZPM3/clrhist.com new file mode 100644 index 00000000..02a39c83 Binary files /dev/null and b/Source/ZPM3/clrhist.com differ diff --git a/Source/ZPM3/clrhist.z80 b/Source/ZPM3/clrhist.z80 new file mode 100644 index 00000000..9b149d24 --- /dev/null +++ b/Source/ZPM3/clrhist.z80 @@ -0,0 +1,49 @@ + +;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +; C L R H I S T +; for ZPM3 +; by Simeon Cran +; 26/4/92 +; +; This program clears the ZPM3 function 10 history buffer. It is presented in +; source code form to inform users about how the facility is manipulated. + +; The only real use for clearing the history buffer is as a security feature +; on RZPM3 systems (remote ZPM3 systems (such as BBSes)). Note that individual +; commands may be cleared from the history buffer with control-V. +;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +; SYNTAX: +; CLRHIST Clears the history buffer + +;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +; +; The history buffer is cleared by setting bit 7 of offset 85h of the SCB page. +; This offset can not be directly accessed by the SCB function (31h). Instead +; we get the SCB base page with function 31h, and then find the offset from +; there. No other bits in the byte may be touched. +;=============================================================================== + +BDOS equ 5 +deffcb equ 5ch +SCBfunc equ 31h ; Get/Set SCB function number +SCBoff equ 3bh ; Offset in SCB to get SCB base page +CLHoff equ 85h ; Offset in SCB base page of Clear History buffer bit. + + jp start ; Jump over general data + +start: ; Get the address of the bit which controls History buffer clearing + ld c,SCBfunc + ld de,SCBPB + call bdos ; Get base page of SCB + ld h,a + ld l,CLHoff ; HL is now the address of the byte + + set 7,(hl) ; All to do + rst 0 + +SCBPB: ; System control block function parameter block. + db 03bh + db 0 ; Get operation + + \ No newline at end of file diff --git a/Source/ZPM3/makedos.com b/Source/ZPM3/makedos.com new file mode 100644 index 00000000..5ca35e74 Binary files /dev/null and b/Source/ZPM3/makedos.com differ diff --git a/Source/ZPM3/makedos.txt b/Source/ZPM3/makedos.txt new file mode 100644 index 00000000..a55b1e36 --- /dev/null +++ b/Source/ZPM3/makedos.txt @@ -0,0 +1,90 @@ + Using the MAKEDOS utility. + ========================== + +In an ideal world, MAKEDOS would not be required as every +computer manufacturer would have provided the source code for +your BIOS and the GENCPM.COM utility. This however is not an +ideal world. + +If you have all the appropriate files, use GENCPM with the new +ZPM3 BNKBDOS3.SPR and RESBDOS3.SPR to make a new CP/M system. If +you only have your CP/M 3.0 system file, then read on to learn +how to use MAKEDOS.COM to convert it from CP/M 3.0 to ZPM3. + + +You CP/M 3.0 system file could be called one of a number of +things. Ideally it will be called CPM3.SYS. But it might have +another name such as J14CPM3.EMS (Amstrad computers). Even if you +find it, you must know how the system uses it. For example does +it load it from A0: when you boot your computer? If it does then +you are going to have to return your modified file to A0:. Or +does it keep the file hidden in system tracks of your disk? If +that is the case you will have to find out how to change the +system tracks. Chances are though that the file is read from A0: +on each cold boot. + +Before continuing, make sure you have a backup bootable disk. If +you overwrite your only system file and it fails to work you are +going to be pretty unhappy... so don't let it happen! + +Put your system file, MAKEDOS.COM, BNKBDOS3.SPR and RESBDOS3.SPR +onto the same disk and user area. Note that you must do this ON +THE SAME COMPUTER RUNNING CP/M 3.0 as the system is intended for. +This is most important because MAKEDOS gets information from its +host computer, and if the computer is different from the one the +system is intended for, it will get the wrong information. + +Type MAKEDOS SYSTEM.FIL at the prompt (replacing the SYSTEM.FIL +in the above command with the actual name of your system file +(such as MAKEDOS CPM3.SYS)). MAKEDOS will churn away for a while +and tell you some information. If it doesn't come up with an +error message, all is well and you can proceed. + +MAKEDOS makes three files. RES.DAT, BNK.DAT and another file with +the same name as your original but with the tail .NEW (eg +CPM3.SYS becomes CPM3.NEW). Your original file has not been +touched. Obviously, you have to rename the .NEW file so that it +has the correct name as the system. MAKEDOS doesn't do this for +you just in case something goes wrong... until you rename the new +file, you will still have a copy of the original. So, rename the +new file, put it where it needs to be for it to become the +system, and reboot the computer. All being well, you will come up +running ZPM3. + +Possible problems: +~~~~~~~~~~~~~~~~~~ +A few things may cause a failure and should be checked if you get +an error message. + +You must have enough disk space for all the files. Figure on +having enough for the new system (same size as the old system), +plus about another 16k. + +Another problem is that your serial number gets overwritten. +MAKEDOS uses your CP/M 3.0 serial number to find the BDOS in your +system file. It checks the serial number in the file against the +serial number on the host machine. However, it is possible for +your serial number to become corrupted. In such a case you should +reboot and try again. + +It is unlikely to affect anyone, but MAKEDOS may fail with +system files larger than 48k. If you need to convert such a file, +please contact me. + +Be aware that running MAKEDOS on a machine different from the +machine that the system file is intended for may not result in +any error messages, but will most likely cause the file to not be +converted properly. Always use the host computer, and make sure +you are running CP/M 3.0 or ZPM3. + + +Once you have successfully installed ZPM3, it may not be obvious +that it is running. ZPM3 will act just like CP/M 3.0 for the most +part. The easiest way to check is to enter a few commands, then +press control-W a few times. Unlike CP/M 3.0, ZPM3 remembers more +than one previous command, and you should see them presented to +you with control-W. + +If you have any further trouble, all you can really do is talk to +me, via Z-Node 62 in Perth, Western Australia (09 450 0200). Good +luck. \ No newline at end of file diff --git a/Source/ZPM3/resbdos3.spr b/Source/ZPM3/resbdos3.spr new file mode 100644 index 00000000..2908ed3e Binary files /dev/null and b/Source/ZPM3/resbdos3.spr differ diff --git a/Source/ZPM3/scb.txt b/Source/ZPM3/scb.txt new file mode 100644 index 00000000..78c65af8 --- /dev/null +++ b/Source/ZPM3/scb.txt @@ -0,0 +1,435 @@ +The following has been taken direct from the ZPM3 source and is +provided as a reference. No guarantees are made with regard to +its accuracy. The only SCB entries that you should manipulate in +CP/M 3.0 are the ones published by its authors. + + +;------------------------------------------------------------------------------- +;68 TRAPS FOR WARM BOOT and CONSOLE FUNCTIONS +;------------------------------------------------------------------------------- +; This table allows you to replace certain BIOS functions with new ones in the +; TPA. Because of the banked nature of CP/M 3, simply changing the BIOS +; vector could cause a problem as some BIOS functions need to be in the +; system bank. If you redirect any of these functions, you should replace +; the first jump (0c3h) in each group with a LD HL, (21h). When restoring +; the jumps check first that the BIOS vectors you are restoring are in +; fact to the BIOS and not to another redirection. To do this, make sure +; that the BIOS jumps are pointing above themselves, not down into TPA. + +; For ZPM, the need for the traps has been eliminated. +; Because pre-ZPM programs may attempt to write to the first byte of each +; trap, these bytes can only be used for other things with some caution! + +; Warm boot trap + jp ?wboot ;68 + jp dotpa ;6b +; Console status trap + jp ?const ;6e + jp dotpa ;71 +; Console input trap + jp ?conin ;74 + jp dotpa ;77 +; Console output trap + jp ?conout ;7a + jp dotpa ;7d + +;;; List output trap +;; jp ?list ;80 +;; jp dotpa ;83 + + + db 0c3h ;80 This first byte should not be used as it + ; could get changed by programs which attempt to + ; redirect the printer + + db 0 ;81 Not used yet + + db 0 ;82 Function 59 load user number. Normally + ; function 59 loads from the current user + ; however by setting this byte to a user + ; number + 1, function 59 will load from + ; that user area instead (only works with + ; the ZCCP loader RSX). 0=current user number. + + dw 0 ; -19 83 + ; This word is set to the address of the + ; ZCPR system environment. If it is 0000h + ; the BDOS assumes that the normal CP/M 3 + ; CCP system is running. Otherwise, the BDOS + ; will perform certain functions differently. + ; For example function 152 will use named + ; directories if available, function 10 + ; will the use message buffers for CCP running + ; flag and wheel protection of files is enabled. + + ; Note that Z3PLUS users can set this word + ; once Z3PLUS is installed to enable the + ; extra functions. ZCCP users need not worry + ; as it will be done automatically. + + db 0 ; -17 85 + ; This byte holds control flags for various ZPM3 + ; functions. + ; bit 7: Setting this bit will clear the function + ; 10 history buffer. Write only. + ; bit 6: Controls enabling of the function 10 AUTO + ; COMMAND facility. If set, control-Q toggles + ; the facility on and off. If clear, control-Q + ; has no effect. Read/Write. + ; bit 5: After function 152 has been called, if a DU: + ; D:,U: or DIR: spec has been found, this + ; bit is set and the drive and user is + ; set in the FCB. + ; bit 4: This flag is for system use only. It is + ; set after a function 55 call, but is reset + ; after any other call. + ; bit 3: After function 152 has been called, if a + ; DIR: spec has been parsed, this bit is set + ; and the user and drive is set in the FCB. + ; bits 2-0: Not used yet +;------------------------------------------------------------------------------- +; SYSTEM CONTROL BLOCK (unofficial) +;------------------------------------------------------------------------------- +; None of these is accessed by the resident BDOS or the user + dw 0 ; -16 86 + dw 0 ; -14 88 + dw 0 ; -12 8a + dw 0 ; -10 8c + dw 0 ; -e 8e + + dw 0 ; Bit mapped vector of drives -c 90 + ; with open files since last warm boot. + dw 0 ; Bit mapped vector of drives -a 92 + ; accessed since last warm boot. + + dw 0 ; -8 94 + dw 07h ; -6 96 + dw base+6 ; This word is the address -4 98 + ; of the entry to the BDOS. + ; It can be used to find the + ; actual BDOS as opposed to + ; the top of TPA. + db 0 ; -2 9a + db 0 ; -1 9b +;------------------------------------------------------------------------------ +;9c SYSTEM CONTROL BLOCK +;------------------------------------------------------------------------------ +; The official system control block starts here. In reality, the control block +; begins before this point, but this is the data section that we are +; told about in the DRI literature +; In this section, a code is used to signify which sections of the code +; access the bytes: a * means that that user may read and write the bytes +; a + means that the resident portion of the BDOS accesses the bytes +; a ~ means the banked portion of the BDOS accesses the bytes +; a ~~ means the banked portion of ZPM3 accesses the bytes, but CPM doesn't +scb: + db 0 ;+ Reserved 0 9c + dw 0 ;+ Reserved 1 9d + db 0 ; Reserved 3 9f + db 0 ; Reserved 4 a0 + + db 31h ; BDOS version number (in BCD) 5 a1 + + ; The following four bytes may be used for any purpose. + ; Note that CCP104 used 8 and 9. ZCCP and ZPM3 do not + ; affect these bytes at all. + db 0 ;* Reserved for user 6 a2 + db 0 ;* Reserved for user 7 a3 + db 0 ;* Reserved for user 8 a4 + db 0 ;* Reserved for user 9 a5 + + db 0 ; Reserved 0a a6 + db 0 ; Reserved 0b a7 + db 0 ; Reserved 0c a8 + db 0 ; Reserved 0d a9 + db 0 ; Reserved 0e aa + db 0 ; Reserved 0f ab + + dw 0 ;* Program Error Return Code. 10 ac + ; This 2-byte field can be used by a program to pass + ; an error code or value to chained programs. CP/M 3's + ; conditional command facility also uses this field to + ; determine if a program executes successfuly. The + ; BDOS Function 108 (Get/SET Program Return Code) is + ; used to get/set this value + + ; Following byte holds the base page of the top + ; multiple command RSX (only used by CCP). + db 0 ; Reserved 12 ae + + ; The following bytes are the default disk and user + ; of the CCP. When the CCP is run, the disk and user + ; is restored to these values unless flagged not to + ; by the chain command. + db 0 ; CCP disk 13 af + db 0 ; CCP user number 14 b0 + + ; The following word holds the address of the next + ; command to get if running multiple commands or + ; shells. It should not be set by the user. + + dw 0 ; Multiple command pointer. CCP 15 b1 + + db 0 ; System flag CCP use 17 b3 + ; This byte is bit mapped as follows: + ; Bit 0 Submit flag (set if a file beginning with '$' + ; is found, cleared by CCP) + ; 1 RSX flag (set by loader when it loads a null + ; file with RSXs attached (indicates to CCP + ; not to attempt to remove the RSXs until the + ; second warm boot). May be set by RSXs + ; 2-5 unknown (probably used by utilities) + ; 6 Change default DU to last program's DU + ; when chaining. + ; 7 Chain flag. Set to indicate to CCP that + ; there is a command to chain to at 080h. + + db 0 ; System flag CCP use 18 b4 + ; This byte is bit mapped as follows: + ; Bit 0 Display command flag + ; 1 Display command flag + ; 2 Unknown + ; 3 File type search order + ; 4 File type search order + ; 5 Reset disk system + ; 6 "GET" RSX flag (set if GET RSX is redirecting) + ; 7 CCP running flag + ; Bit 7 is the only one used by the BDOS (in function 10) + + db 0 ; System flag CCP use 19 b5 + ; This byte is bit mapped as follows: + ; Bit 0 Unknown + ; 1 Cold boot flag + ; 2-7 Unknown + + db 0 ;* Console Width 1a b6 + ; This byte contains the number of columns + ; (characters) per line on your console relative + ; to zero. Most systems default this value to + ; 79. You can set this default value by using + ; GENCPM or the DEVICE utility. The console width + ; value is used by CP/M 3 in BDOS function 10. It + ; is not used by ZPM3. Typing a character into the + ; last position of the screen, as specified by the + ; Console Width field, must not cause the terminal + ; to advance to the next line. + + db 0 ; Console Column Position 1b b7 + ; This byte contains the current console column postion + + db 0 ;* Console Page Length 1c b8 + ; This byte contains the number of lines (rows) on your + ; console relative to zero. Most systems default this + ; value to 23. This default value may be changed by + ; using GENCPM or the DEVICE utility. + + db 0 ; Reserved 1d b9 + ; The following word is used by function 10 and points + ; to the next character to get in an initialised + ; function 10 buffer. If a ^C termination occurs or + ; if a null terminator is found before a CR or LF, + ; this word is set 0. By setting DE NZ and pointing + ; this word to a buffer before calling it, you + ; can have it initialize buffers other than + ; the default DMA. + + dw 0 ;+~Reserved 1e ba + ; The following word is used by multiple commands and + ; shells. When function 10 retrieves information from + ; an initialised buffer, it stores the next character + ; position at offset 1e and here at 20. If a ^C + ; termination occurs, 1e is set to 00, but 20 is left + ; as it was so that the next command can be retrieved. + ; Therefore, if 1e is 00 and 20 is NZ it means that + ; a ^C termination happened + + dw 0 ;~ Reserved 20 bc + + + ; Redirection flags (following) for each of the five + ; logical character devices. If your system's BIOS + ; supports assignment of logical devices to physical + ; devices, you can direct each of the five logical + ; character devices to any combination of up to 12 + ; physical devices. The 16 bit word for each device + ; represents the following: + ; + ; Each bit represents a physical device where bit 15 + ; corresponds to device zero and bit 4 corresponds to + ; device 11. Bits zero through 3 are reserved for + ; system use and are used for redirection to disk files. + ; + dw 0 ;* CONIN Redirection Flag 22 be + dw 0 ;* CONOUT Redirection Flag 24 c0 + dw 0 ;* AUXIN Redirection Flag 26 c2 + dw 0 ;* AUXOUT Redirection Flag 28 c4 + dw 0 ;* LIST Redirection Flag 2a c6 + + db 0 ;* Page Mode 2c c8 + ; If this byte is set to zero, some CP/M 3 utilities + ; and CCP built in commands display one page of data + ; at a time; you display the next page by pressing + ; any key. If this byte is not set to zero, the system + ; displays data on the screen without stopping. To + ; stop and start the display, you can press CTRL-S and + ; CTRL-Q respectively. + + db 0 ; Default page mode 2d c9 + + db 0 ;* ~~ 2e ca + ; Determines if CTRL-H is interpreted as a rub/del + ; character. If this byte is set to 0, then CTRL-H is + ; a backspace character (moves back and deletes). If + ; this byte is set to 0ffh, then CTRL-H is a rub/del + ; character, echoes the deleted character. + ; Under ZPM3, the byte has no effect. It should not + ; be used however as it may be written to by + ; applications. + + db 0 ;* 2f cb + ; Determines if rub/del is interpreted as CTRL-H + ; character. If this byte is set to 0, then rub/del + ; echoes the deleted character. If this byte is + ; set to 0ffh, then rub/del is interpreted as a + ; CTRL-H character (moves back and deletes). + ; Under ZPM3, the byte has no effect. It should not + ; be used however as it may be written to by + ; applications. + + db 0 ;~ Reserved 30 cc + + ; Following two bytes are probably used by CP/M3 utilities + db 0 ; Reserved 31 cd + db 0 ; Reserved 32 ce + + dw 0 ;*+ Console Mode 33 cf + ; This is a 16 bit system parameter that determines + ; the action of certain BDOS Console I/O functions. + + dw bnkbuf ; Address of 128 byte buffer 35 d1 + + db '$' ;*+ Output delimiter character. 37 d3 + ; The default output delimiter character is $, but + ; you can change this value by using the BDOS Function + ; 110 Get/Set Output Delimiter. + + db 0 ;* List Output Flag 38 d4 + ; If this byte is reset to 0, console output is not + ; echoed to the list device. If this byte is set + ; to 1, console output is echoed to the list device. + + db 0 ; Scroll flag 39 d5 + ; Following bits set when in system bank and: + ; Bit 7 is set when function 11 is checking the status. + ; Bit 6 is set when function 2 is checking input. + ; Note that raw input (function 6 and function 2 raw) + ; will not set these bits. + + dw scb ; Holds the address of the SCB 3a d6 + + dw 0080h ;+ Current DMA Address. 3c d8 + ; This address can be set by BDOS Function 26. The + ; CCP initializes this value to 0080h. BDOS Function + ; 13, Reset Disk System also sets the DMA address to + ; 0080h. + + db 0 ; Current Disk. 3e da + ; This byte contains the currently selected default + ; disk number. This value ranges from 0-15 + ; corresponding to drives a-p, respectively. BDOS + ; Function 24, Return Current Disk, can be used to + ; determine the current disk value. + + dw 0 ; BDOS variable 'INFO' 3f db + ; This word is used by the banked portion of the + ; BDOS. It is normally an entry parameter. + + db 0 ; FCB flag 41 dd + ; If this byte = 0ffh, the word at 03fh is a valid + ; FCB address. + + db 0 ; Same drive flag 42 de + + db 0 ;+ BDOS function for error 43 df + + db 0 ; Current User Number. 44 e0 + ; This byte contains the current user number. This + ; value ranges from 0-15. BDOS Function 32, + ; Get/Set User Code can change or interrogate + ; the currently active user number. Under ZPM3 you may + ; change the currently active user number directly + ; by writing to this byte. + + dw 0 ;+ Reserved 45 e1 + ; Holds the current directory entry number. Lower + ; two bits are the search return code. + + dw 0 ;+ Search FCB address 47 e3 + ; Holds the FCB address of the last search for + ; first/next operation. + + db 0 ;+ Search type flag 49 e5 + ; 0=? in drive code search. + ; 0fh=normal search. + + db 01 ;* BDOS Multi-Sector Count. 4a e6 + ; This field is set by BDOS Function 44, Set Multi- + ; Sector Count. + + db 0 ;* BDOS Error Mode. 4b e7 + ; This field is set by BDOS Function 45, Set BDOS + ; Error Mode. If this byte is set to 0ffh, the + ; system returns to the current program without + ; displaying any error messages. + + + db 0 ;* Drive Search Chain 1 4c e8 + db 0ffh ;* Drive Search Chain 2 4d e9 + db 0ffh ;* Drive Search Chain 3 4e ea + db 0ffh ;* Drive Search Chain 4 4f eb + + db 0 ;* Temporary File Drive 50 ec + + db 0 ; Error Drive. 51 ed + ; This byte contains the drive number of the selected + ; drive when the last physical or extended error + ; occured. + + db 0 ; Reserved 52 ee + db 0 ; Reserved 53 ef + + db 0 ; Media Flag 54 f0 + ; This flag may be set by the BIOS to indicate that + ; a drive door has opened thus signalling the BDOS + ; to relog the drive if required. + db 0 ; Reserved 55 f1 + db 0 ; Reserved 56 f2 + + db 080h ; BDOS Flags. 57 f3 + ; bit 7= expanded error messages + ; 6= single byte allocation vectors + + dw 0ffffh ;* Date in days in binary since 1 Jan 78 58 f4 + db 0ffh ;* Hour in BCD 5a f6 + db 0ffh ;* Minutes in BCD 5b f7 + db 0ffh ;* Seconds in BCD 5c f8 + +COMBASE: + dw 0 ; Common Memory Base address 5d f9 + ; This value is zero for nonbanked systems, and + ; nonzero for banked systems. Because the base + ; address must reside on a page boundary, the + ; first byte will always be 0. The second byte + ; is the important one being the common memory + ; base page. + + jp bnkdos2 ; Pointer to second entry in banked DOS 5f fb + ; This entry handles the displaying of errors to + ; the user + + dw start ; Top of user TPA 62 fe + ; This word always reflects the top of TPA and + ; should be the same as the word at 0006h unless a + ; transient changes (0006h) without knowing about + ; this word + \ No newline at end of file diff --git a/Source/ZPM3/setz3.com b/Source/ZPM3/setz3.com new file mode 100644 index 00000000..4ddb374f Binary files /dev/null and b/Source/ZPM3/setz3.com differ diff --git a/Source/ZPM3/setz3.z80 b/Source/ZPM3/setz3.z80 new file mode 100644 index 00000000..947f3006 --- /dev/null +++ b/Source/ZPM3/setz3.z80 @@ -0,0 +1,80 @@ + +;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +; S E T Z 3 +; for ZPM3 +; by Simeon Cran +; 30/3/92 +; +; This program automatically sets the system environment address in the ZPM3 +; SCB for Z3PLUS users. Certain advanced ZCPR facilities such as wheel +; protection of files will then be activated. +; +; Z3PLUS users should run SETZ3.COM once when they start up Z3PLUS and again +; when returning to regular (non-Z-System) operation. When run after Z3PLUS +; is started, the SCB environment address word is set with the ZCPR environment +; address. When run after returning to regular operation, the SCB environment +; address word is cleared to 0000h. +; +;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +;=============================================================================== + +BDOS equ 5 +deffcb equ 5ch +SCBfunc equ 31h ; Get/Set SCB function number +SCBoff equ 3bh ; Offset in SCB to get SCB base page +Z3ENVoff equ 83h ; Offset in SCB base page of the ZCPR system + ; environment pointer. + + jp start ; Jump over general data + db 'Z3ENV' + db 1 +Z3ENV: dw 0 + dw 0 +HELPmsg: + db ' SYNTAX:' + db 10,13 + db ' SETZ3' + db 10,13 + db ' Sets the ZCPR environment address in the SCB,' + db 10,13 + db ' or else clears it if not running ZCPR.' + db 10,13 + db ' SETZ3 // Displays this brief help message' + db '$' + +HELP: ld de,HELPmsg +MSGexit: + ld c,9 + call bdos + rst 0 + +start: ; Get the address of the SCB environment address pointer. + ld de,(Z3ENV) + ld a,d + or e ; Has it been set by Z3PLUS? + jr nz,Zinstall ; Jump if it has + ex de,hl + ld a,d + or e ; Was it in HL instead? + jr nz,Zinstall +Zinstall: ; DE holds 0 if uninstalling, otherwise the address of the + ; ZCPR evironment descriptor. + push de + ld c,SCBfunc + ld de,SCBPB + call bdos ; Get base page of SCB + ld h,a + ld l,Z3ENVoff ; HL is now the address of the word + pop de + ld (hl),e + inc hl + ld (hl),d ; Set it appropriately + rst 0 ; And exit. + + +SCBPB: ; System control block function parameter block. + db 03bh + db 0 ; Get operation + + \ No newline at end of file diff --git a/Source/ZPM3/version.not b/Source/ZPM3/version.not new file mode 100644 index 00000000..644d01e0 --- /dev/null +++ b/Source/ZPM3/version.not @@ -0,0 +1,35 @@ + ZPM3 by Simeon Cran. + ==================== +ZPM3 is a Z80 coded BDOS replacement for banked CP/M 3.0 systems. + +This is release 0010 on 1/2/93. + +Since version 0009 the .SPR files have been updated. They are +slightly faster and will possibly work on the Bondwell or other +machines that they seemed to fail on before. ZPM3LDR.REL has also +been modified. The main improvements throughout should be in +speed. The differences may not be obvious, but you should upgrade +to this latest version. SJC. + +Files in this release are: + +VERSION.NOT +ZPM3.TXT +RESBDOS3.SPR +BNKBDOS3.SPR +AUTOTOG.COM +AUTOTOG.Z80 +SETZ3.COM +SETZ3.Z80 +MAKEDOS.COM +MAKEDOS.TXT +BIOS.TXT +SCB.TXT +ZPM3LDR.TXT +ZPM3LDR.REL +CLRHIST.COM +CLRHIST.Z80 +ECHOTERM.COM +============ +17 files +============ \ No newline at end of file diff --git a/Source/ZPM3/zinstal.zpm b/Source/ZPM3/zinstal.zpm new file mode 100644 index 00000000..1a6b2c4c Binary files /dev/null and b/Source/ZPM3/zinstal.zpm differ diff --git a/Source/ZPM3/zpm3.txt b/Source/ZPM3/zpm3.txt new file mode 100644 index 00000000..7df5cb8f --- /dev/null +++ b/Source/ZPM3/zpm3.txt @@ -0,0 +1,483 @@ + + Z P M 3 by Simeon Cran + ======================== + + A Z80 coded CP/M 3.0 compatible BDOS replacement. + + The first public release: 27/3/92 + This document dated: 16/6/92 + + Distributed at: Z-Node 62 (Perth, Western Australia) + V21,V22,V22bis 09 450 0200 + + +WELCOME TO ZPM3 +~~~~~~~~~~~~~~~ +Welcome to the best CP/M compatible operating system for Z80 +based computers with banked memory. The best? Yes, we believe so. +CP/M 3.0 has had bad press, but the fact is that it is faster +than CP/M 2.2 ever was, and it offered more integrated +facilities. Perhaps it was all the Z80 replacement BDOSes for +CP/M 2.2 which stole the limelight from CP/M 3.0, or was it just +that few computers had the required banked memory? + +Whatever the reason for CP/M 3.0's lack of success in the +marketplace, there are still plenty of users who will stand by +its wonderful facilities and speed. For those users ZPM3 provides +the long awaited Z80 coded update. + +ZPM3 offers all the good things that CP/M 3.0 does, and then it +offers more. Because ZPM3 is written in Z80 code rather than the +8080 code of CP/M 3.0, it can do everything that CP/M 3.0 does, +but in much less space. With the extra space recovered, ZPM3 +packs in a number of new facilities. Yet the whole package fits +in exactly the same space as CP/M 3.0 so you can directly replace +your old CP/M 3.0 BDOS with ZPM3 without a worry. + +ZPM3 is also fast. Faster, in fact, than CP/M 3.0. This is +possible because the rich Z80 instruction set allows many +algorithms to be implemented more efficiently. In addition, the +extra space available in ZPM3 has been put to use to further +optimise the code. Lots of small optimisations smooth the +execution flow, so ZPM3 becomes the fastest operating system on +most banked CP/M computers. + + +THE FEATURES +~~~~~~~~~~~~ +ZPM3, in addition to complete CP/M 3.0 compatibility, offers the +following features: + + +Random Read Bug fixed. +++++++++++++++++++++++ +Maybe you didn't know, but CP/M 3.0 has a bug. It affects random +reads under very specific circumstances, and can result in a +program thinking that you don't have some pieces of data in a +file when in fact you do. The bug would occur very, very rarely, +but it is real. ZPM3 finally squashes it. + + +Protected SCB User code ++++++++++++++++++++++++ +The System Control Block of CP/M 3.0 was a revolution at the +time. ZCPR has a system environment and most other operating +systems have other similar structures, but the SCB of CP/M 3.0 +was one of the very first. + +Unfortunately, Digital Research never properly documented it, and +some programmers found things out about it that weren't quite +true and started programming accordingly. As well, because it is +available in the TPA bank, runaway programs can overwrite it +causing problems. + +Mostly though, the SCB will survive, or at least any problems +will be so obvious that the user will realise that a crash has +occurred and will reboot. A real problem exists with the CP/M 3.0 +code however when the user value is written over with a value +above 15. Many programs now directly write to this byte, and if +they put a value in that is above 15, all sorts of havoc can +happen with the disk system. Actually, CP/M 3.0 will handle user +areas above 15 with this method, and all seems ok until the +operating system mistakes one of these directory entries as an +XFCB. Simply put, user areas above 15 must not be used with CP/M +3.0. + +ZPM3 has code which prevents these problems, making the system +even more stable. + + +Obsoleted Trap system. +++++++++++++++++++++++ +One of the problems of the banked operating system was that it +was possible to redirect the BIOS to code below common memory, in +which case the banked BDOS could not access it. One solution is +to call all BIOS code from common memory, but this involves a +bank switch for every BIOS call, and this slows things down +considerably. + +CP/M 3.0 got around the problem by providing special code just +below the SCB. If you redirected the BIOS, you also had to change +this code which caused a bank switch when your new BIOS routine +was called. When you removed the redirection, you also had to +restore the special code. + +This system has major drawbacks. For a start, if you redirect the +BIOS, then another program redirects your redirection, then you +remove your first redirection (along with the special code), the +bank switch won't happen for the second redirection and the +system will crash. + +If a CP/M 2.2 program tried to do the redirection, it would know +nothing about CP/M 3.0 and would not adjust the special code, so +a crash would result in that case too. + +The special code was called the "Trap System" as it was meant to +trap redirection (as long as you set the trap). ZPM3 has +eliminated the need for the traps. They are still there, and +programs can still fiddle with them, but it doesn't matter how +they are set, they are ignored. There is simply no need for them +anymore. And this has been achieved without a performance +penalty. In fact, in the case of a program which sets the traps +but forgets to restore them, performance is now much better. + + +Semi-Permanent Read Only status for drives. ++++++++++++++++++++++++++++++++++++++++++++ +In recent years, a trend in CP/M 2.2 is to make drives which have +been set read only to remain that way until explicitly changed by +function 37. ZPM3 now adopts this logic. Previously a control-C +would return a read only drive to read write. The advantage is +that a program can now make a drive read only for a session and +know that it will stay that way. + + +ZCPR compatible function 152 +++++++++++++++++++++++++++++ +Function 152 is the CP/M 3.0 parser. It was a great innovation at +the time as parsing is one of the more tedious aspects of +programming for CP/M. Unfortunately, almost as soon as it +appeared, it was made obsolete by the fact that it didn't handle +references to user number (DU references). A line such as +A:FILE.TYP would be correctly parsed, but A3:FILE.TYP would not. +CP/M 3.0 programs would often parse the drive and user +separately, then give function 152 the line without the DU: +reference. All this extra work should not have been necessary if +CP/M 3.0 had included user number parsing. + +ZPM3 parses the user number, and goes even further by handling +named directories for ZCPR. This is possible as long as you set a +special word in the SCB which tells ZPM3 where to find the ZCPR +system environment descriptor. ZCCP, a companion CCP for ZPM3, +handles this automatically, but for Z3PLUS users, a special +utility is available which automatically sets this word. + +The result is that CP/M 3.0 programs will not balk at DU: +references and ZPM3 aware programs can use the full DU: and DIR: +facilities of function 152. It has also made the brilliant ZCCP +code possible. + + +New Functions 54 and 55 ++++++++++++++++++++++++ +Datestamps in CP/M 3.0 are wonderful, but difficult to +manipulate. Two new functions make them easier to handle and at +the same time give compatibility to Z80DOS aware programs. + +Function 54 (Get Stamp) returns a Z80DOS compatible datestamp. +Any program (such as many directory programs) which recognise the +Z80DOS standard can make use of function 54. There is only one +slight difference between Z80DOS datestamps and ZPM3's which you +should be aware of. Z80DOS will return a correct datestamp after +any successful open or search of any extent. ZPM3 can only return +a correct datestamp after a successful open or search of the +first extent of the file. This is because CP/M 3.0 datestamps are +only saved for the first extents of each file, in order to +provide the highest performance. + +Even more interesting is Function 55 (Use Stamp) which provides a +mechanism for changing datestamps on files. Trying to do this +with CP/M 3.0 was virtually impossible because it involved direct +sector writes. With Function 55 you can simply set the stamp and +then write. + + +Wheel protected files ++++++++++++++++++++++ +If you are using a ZCPR system (ZCCP or Z3PLUS), ZPM3 has access +to the wheel byte and supports wheel protected files. Such files +act normally if the wheel is set (signifying a priveleged user), +but if the wheel is not set, the files can not be changed. This +is of most benefit to BBS systems. The implementation is +virtually the same as most current Z80 CP/M 2.2 compatible +BDOSes. + + +Better error messages ++++++++++++++++++++++ +CP/M 3.0 introduced the best error messages that CP/M had ever +had. ZPM3 goes further. The main difference you will notice is +that the user number as well as the drive is shown in the error +message. This is invaluable in helping you identify which file +might have caused a problem. + + +Function 10 history buffer and improved editing. +++++++++++++++++++++++++++++++++++++++++++++++++ +Function 10 is used by the CCP to input command lines. Many other +programs use function 10 for input. + +CP/M 3.0 introduced a history buffer for function 10. You press +control-W and you were returned the last command. It is a great +facility, but because it only remembers one command it is rather +limited. There have been RSXes written which give a much larger +history buffer, but RSXes take up extra program memory so are +undesirable. + +ZPM3 gives a large (approximately 250 bytes) history buffer which +can store multiple commands. It also makes very intelligent use +of the buffer so that identical commands are not stored twice, +and commands of less than three characters are not stored. The +history buffer takes up no additional memory, and is always +available. + +For security, it is possible to clear the history buffer so that +other users can not see what commands you have used. + +The ZPM3 history buffer feature is so good, that for many users, +the ZPM3 upgrade is completely justified by it. + +As part of the history buffer system, ZPM3 also offers a facility +called Automatic Command Prompting. This can be disabled, or can +be made switchable from the keyboard. When it is on, ZPM3 tries +to fill in the rest of your command based on what commands you +used most recently. It is like magic, and can save you typing out +complicated commands many times. In effect, it looks through the +history buffer for you and finds the command it thinks you want. +As you keep typing, if it turns out that the command doesn't +match anymore, it will try to match another command, and if it +can't, it lets you make the command by yourself. This facility is +quite amazing to watch. + +And to integrate the history buffer and the automatic command +prompting, function 10 has the best command line editing you'll +find anywhere. Most of the control keys do something when you are +editing a function 10 line, and for the most part they mimic the +standard WordStar/NewWord/ZDE functions. You can jump to +different words in the command, delete individual words, delete +individual letters, insert letters, and a whole lot more. + + +Here is a list of what the various control keys do for function +10: + +A Move left one word +B Go to the beginning or end of the line +C Warm boot if at start of line, otherwise nothing +D Go right one character +E Go backwards one command in the history buffer +F Go right one word +G Delete current character +H Destructive backspace +I +J Enter line +K Delete all to the right +L +M Enter line +N +O +P Toggle printing +Q Toggle automatic command prompting (if enabled) +R +S Go left one character +T Delete current word +U Add current line to history buffer +V Clear line and delete from history buffer +W Go forwards one command in the history buffer +X Delete all to the left +Y Clear the whole line +Z + + +CPMLDR.REL bug fixed. ++++++++++++++++++++++ +If you have ever tried to use the CPMLDR.REL code supplied with +CP/M 3.0 to load a CPM3.SYS file larger than 16k, you have +probably come across the CPMLDR.REL bug. The computer probably +crashed, and you were left wondering what you did wrong in your +bios. + +Well CPMLDR.REL has a bug. To solve this for you ZPM3 comes with +ZPM3LDR.REL which directly replaces CPMLDR.REL. It is also +somewhat better in that all the messages, and the fcb for loading +CPM3.SYS, are at the start of the file along with plenty of spare +room. As a result you can easily patch the signon and error +messages to say whatever you like and even change the FCB to load +a file called something other than CPM3.SYS. + + + + +All About the Random Read Bug. +============================== +Never heard of it? Well it's there in CP/M 3.0. I spent a lot of +time trying to work out what it was and just why it was +happening, and if you are interested, here are the details. + +CP/M 3.0 uses the Record Count byte of an active FCB a little +differently from the way CP/M 2.2 does. It is mentioned in the +CP/M 3.0 manuals that the record count may contain numbers +greater than 128, but in such a case it implies that the record +count is really 128. CP/M 2.2 would not return record counts +greater than 128. + +The reason for the use of the record count in this way is to help +speed up some of the logic used to find records in a file. It +works very well for sequential access. When it comes to random +access, the system has some failings. + +The idea behind CP/M 3.0's unusual use of the record count is to +keep the record count of the last logical extent of the current +physical extent always in the Record Count byte. When accessing +extents before the last one, bit 7 of the byte is set. That way +it will always be at least 128 for logical extents before the +last (which CP/M 3.0 translates to mean equal to 128), and the +lower 7 bits are used as convenient storage for the record count +of the last logical extent. This is particularly convenient +because it means there is no need to go and read the directory +entry again when it comes time to read the last logical extent. + +I hope you have followed that! In sequential access, this scheme +is great. The problem occurs with random access. In this case it +is possible to access a logical extent which has no records in +it. This could be any logical extent past the last one. In such a +case the record count must be returned as 0 (which is correct). +If we then go back to a previous logical extent in the same +physical extent, CP/M 3.0 gets confused and assumes that there +must be 128 records in that extent because the one we just came +from had no records and we are now accessing an earlier extent. +You're probably well and truly lost by now! + +Anyhow, the assumption that CP/M 3.0 makes is quite wrong. The +record count ends up being set to 128, a read is allowed to go +ahead as if nothing was wrong, no error is returned, and the +record count remains incorrectly set until a different physical +extent is opened. The result could be chaos, but mostly it just +means that a program returns the wrong information. + +Remember, a logical extent is always 16k. A physical extent can +be a multiple of 16k and is all the data described by one +directory entry. If your system has physical extents which are +16k, you would never have the problem because a new physical +extent would be properly opened for every new logical extent that +was accessed. + +Typically though, a physical extent is 32k, so it holds 2 logical +extents. The problem won't arise until the file grows past the +32k mark in such a case. And when the file gets over 48k the +problem can't occur again until it gets over 64k... and so on. +Even then, it can only happen if reads are attempted to +particular extents in a particular order. So you shouldn't be too +surprised if the bug hasn't been too noticeable to you. + +ZPM3 squashes the bug once and for all by using the correct +logic. In the situation where the bug would normally occur, ZPM3 +makes sure it gets the correct record count information, and the +reads return the correct record count every time. + +If you are interested in seeing a demonstration of the bug in +action (on CP/M 3.0) and comparing it with ZPM3, there is a file +floating around various bulletin boards which contains +demonstrations for the bug and an RSX to fix it. The RSX is a +less than perfect way of overcoming the bug, although it seems to +work. However, now that you have ZPM3, you don't need to worry. + + + + +Other things you should know about ZPM3 +======================================= +ZPM3 has worked on EVERY CP/M 3.0 system tried so far except one. +This is a Bondwell computer, and as yet it isn't clear why it +won't work. I will study the source code of its BIOS and come up +with a fix shortly. + +The MAKEDOS.COM utility is not perfect (as mentioned previously) +and it seems that nobody has managed to get it to work with the +Commodore C128 system. You must use the conventional method for +installing ZPM3 on such systems. + +If you have a computer that ZPM3 will not install on with MAKEDOS +and you do not have access to the files required to do a +conventional install, please contact me. I am interested in +making ZPM3 as universal as possible and will help you to install +it on your system. + +The ESCAPE key is ignored by function 10. There has been some +lively discussion about this but the decision is final: it stays +ignored. Remember what function 10 is for and you will understand +why I made it ignore the ESCAPE key. The argument against this +has been from people who control their terminals from the command +line. Apparently some people type in an escape sequence at the +command line (which CP/M 3.0 will not output correctly anyhow +(converting the escape character to ^[)) then press return to +have the CCP echo back the line including the escape character. + +Sorry folks, that is a KLUDGE in my books! Anybody using Z-System +would of course use an ALIAS and ECHO to do this properly, but +for those who will continue to complain that I have sacrificed +CP/M 3.0 compatibility I am now including ECHOTERM.COM to solve +your problems. Run it and whatever you type will be sent to the +terminal correctly after you press RETURN. Press RETURN twice to +exit the program. + +And a reminder that the ability to put control characters into +function 10 lines was always limited by the fact that some +control keys were used to edit the command line. CP/M 3.0 added +even more, and ZPM3 uses virtually all the control keys. The few +that aren't used are ignored, and this is in fact a FEATURE which +guarantees that unusable characters can't get into function 10 +lines by accident. + + + + +LEGALS and SUCH +=============== +The ZPM3 package is supplied free of charge, on the condition +that you don't use it to make money. If you want to use it +commercially you must contact me to get the OK (and negotiate our +fee). + +If you find anyone (except myself) charging money for ZPM3, +please inform me! + +Nobody is making any guarantees about this software. None at all. +If it causes your house to burn down, or a divorce, or just a bad +day, this is unfortunate, regrettable, but there is nothing that +I can or will do about it. You have been warned. + +The ZPM3 package must only be distributed in the form that you +found it. Do not change or add anything. Don't even change it +into a different type of archive. Just leave it alone. However +you are free to distribute it to as many places and people that +you can. Just don't charge for it. + + + +If in using ZPM3 you find that it doesn't act as described, +please forward the details to me so that either the ZPM3 code or +the documentation can be changed. If you would like further +details, please forward your specific questions to me. SJC. + + + + +As a service to all our ZPM3 fans, the latest version of the ZPM3 +package can now be ordered. At this stage we can only supply IBM +formatted 3.5 inch 720k disks, however if you are keen enough +that shouldn't matter. ZPM3 remains free, however this service +will cost you $15 Australian (for the disk, copying, postage and +packing) to most places in the Western World (others by +arrangement). + +This is a good way to guarantee you have the latest version, and +to guarantee that your package has not been corrupted by some +unscrupulous person. + +When we fill your order, we will make sure to include the latest +demonstration copy of MYZ80 - the fastest and best Z80 emulator +for IBM AT (and better) compatibles. MYZ80 can run ZPM3 with +ease. It also handles ZCPR and CP/M 2.2. And yes, we do mean +FASTEST. + +Send your international money order to: + + Software by Simeon + ZPM3 Package + 2 Maytone Ave + Killara NSW + Australia 2071 + +Your order will be promptly filled. + \ No newline at end of file diff --git a/Source/ZPM3/zpm3ldr.rel b/Source/ZPM3/zpm3ldr.rel new file mode 100644 index 00000000..bca9c4be Binary files /dev/null and b/Source/ZPM3/zpm3ldr.rel differ diff --git a/Source/ZPM3/zpm3ldr.txt b/Source/ZPM3/zpm3ldr.txt new file mode 100644 index 00000000..9307b039 --- /dev/null +++ b/Source/ZPM3/zpm3ldr.txt @@ -0,0 +1,68 @@ + + + ZPM3LDR.REL + =========== + + A CPMLDR.REL replacement for CP/M 3.0 and ZPM3 systems. + + +CPMLDR.REL, as supplied by DRI, has a bug on some systems which +prevents the loading of CPM3.SYS files larger than 16k. This is a +significant problem especially if you intend to enlarge your bios +or increase the number of buffers allocated to your system. + +ZPM3LDR.REL was developed primarily to overcome this bug. +ZPM3LDR.REL is able to load CPM3.SYS files up to the maximum +possible system size without any problem. ZPM3LDR.REL also offers +some convenient enhancements. + + +The usual way to use ZPM3LDR.REL is exactly as you would use +CPMLDR.REL: link it to your loader bios and SCB.REL files to make +the loader program which must be installed onto your system +tracks. Before you install the program however, you may choose to +patch the file at locations provided for in ZPM3LDR.REL. + +The messages issued by ZPM3LDR can be changed. They take a +standard '$' terminated form (as used by BDOS function 9). Using +a debugger such as SID.COM, you should be able to view these +messages and note that they have extra '$' terminators at then +end of each. This is the room in which you may expand or alter +the ZPM3LDR messages. Just remember not to overwrite the next +message. + +The CPM3.SYS FCB will be visible there too, allowing you to +change it so that ZPM3LDR will load a file of a different name +instead. + +The copyright message is there but not to show that DRI has +copyright on ZPM3LDR.REL (which it doesn't!). That is part of an +advanced feature of ZPM3LDR.REL which allows it to check for +valid CPM3.SYS files. CPMLDR.REL would attempt to load any file +called CPM3.SYS, even if it wasn't really a CP/M 3.0 system file. +The results could be catastrophic. ZPM3LDR.REL will always check +for the 112 byte signature at the start of the file, and will +refuse to load CPM3.SYS unless the signature is correct. + +This has another advantage. You may patch this signature to +whatever you wish. Then, after generating your CPM3.SYS file +(using GENCPM.COM) you should patch it too. The patch might be to +put in the version of your BIOS or some such thing. On MYZ80, I +use this system whenever I change the MYZ80 80x86 bios in such a +way that the CPM3.SYS files won't work properly anymore. That +way, any old CPM3.SYS files that are not valid anymore, won't get +loaded accidentally. + +For your information, the first 128 bytes of CPM3.SYS always +begin with 6 bytes which tell CPMLDR where to load each section +and where the cold boot entry is. After that there are 10 bytes +of 0. ZPM3LDR does not check these bytes against anything so you +can patch them with whatever you like. + +The next 112 bytes would normally contain the DRI copyright +message, the serial number of your system, then a fill of 0 bytes +to the next record. Because ZPM3LDR looks for the copyright +message as a signature of a valid CPM3.SYS, if it is changed, you +will have to change ZPM3LDR as well. + + \ No newline at end of file