Browse Source

Preliminary CP/M 3

patch
Wayne Warthen 6 years ago
parent
commit
57c87db9a1
  1. 1
      Doc/ChangeLog.txt
  2. 14
      ReadMe.txt
  3. 3
      Source/BuildShared.cmd
  4. 2
      Source/CBIOS/ver.inc
  5. 28
      Source/CPM3/@banks.txt
  6. 99
      Source/CPM3/Build.cmd
  7. 13
      Source/CPM3/Clean.cmd
  8. BIN
      Source/CPM3/bdos3.spr
  9. 673
      Source/CPM3/bioskrnl.asm
  10. 480
      Source/CPM3/biosldr.z80
  11. BIN
      Source/CPM3/bnkbdos3.spr
  12. 362
      Source/CPM3/boot.z80
  13. BIN
      Source/CPM3/ccp.com
  14. 193
      Source/CPM3/chario.z80
  15. 180
      Source/CPM3/cpm3.lib
  16. BIN
      Source/CPM3/cpm3fix.pat
  17. 1572
      Source/CPM3/cpmldr.asm
  18. 781
      Source/CPM3/diskio.z80
  19. 12
      Source/CPM3/drvtbl.z80
  20. 158
      Source/CPM3/genbnk.dat
  21. BIN
      Source/CPM3/gencpm.com
  22. 158
      Source/CPM3/genres.dat
  23. 158
      Source/CPM3/genres.old
  24. 16
      Source/CPM3/makedate.lib
  25. 32
      Source/CPM3/modebaud.lib
  26. 116
      Source/CPM3/move.z80
  27. 6
      Source/CPM3/optbnk.lib
  28. 6
      Source/CPM3/optres.lib
  29. 39
      Source/CPM3/readme.1st
  30. BIN
      Source/CPM3/resbdos3.spr
  31. 50
      Source/CPM3/scb.asm
  32. BIN
      Source/CPM3/zpmldr.com
  33. 2
      Source/Clean.cmd
  34. 4
      Source/HBIOS/dbgmon.asm
  35. 47
      Source/HBIOS/hbios.asm
  36. 2
      Source/HBIOS/ver.inc
  37. BIN
      Source/ZCCP/ccp.com
  38. BIN
      Source/ZCCP/diskinfo.com
  39. BIN
      Source/ZCCP/loadseg.com
  40. 103
      Source/ZCCP/read.me
  41. BIN
      Source/ZCCP/rsxdir.com
  42. BIN
      Source/ZCCP/startzpm.com
  43. 308
      Source/ZCCP/zccp.txt
  44. BIN
      Source/ZCCP/zinstal.zpm
  45. 61
      Source/ZPM3/Build.cmd
  46. 18
      Source/ZPM3/Clean.cmd
  47. BIN
      Source/ZPM3/autotog.com
  48. 131
      Source/ZPM3/autotog.z80
  49. 230
      Source/ZPM3/bios.txt
  50. BIN
      Source/ZPM3/bnkbdos3.spr
  51. BIN
      Source/ZPM3/clrhist.com
  52. 49
      Source/ZPM3/clrhist.z80
  53. BIN
      Source/ZPM3/makedos.com
  54. 90
      Source/ZPM3/makedos.txt
  55. BIN
      Source/ZPM3/resbdos3.spr
  56. 435
      Source/ZPM3/scb.txt
  57. BIN
      Source/ZPM3/setz3.com
  58. 80
      Source/ZPM3/setz3.z80
  59. 35
      Source/ZPM3/version.not
  60. BIN
      Source/ZPM3/zinstal.zpm
  61. 483
      Source/ZPM3/zpm3.txt
  62. BIN
      Source/ZPM3/zpm3ldr.rel
  63. 68
      Source/ZPM3/zpm3ldr.txt

1
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
-------------

14
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
-----------------

3
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

2
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"

28
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

99
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

13
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

BIN
Source/CPM3/bdos3.spr

Binary file not shown.

673
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 <B>
extrn ?cinit ; (re)initialize device in <C>
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 <HL>
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 <C>
; to all selected devices
conout:
lhld @covec ; fetch console output bit vector
jmp out$scan
; AUXOUT
; Auxiliary Output. Send character in <C>
; to all selected devices
auxout:
lhld @aovec ; fetch aux output bit vector
jmp out$scan
; LIST
; List Output. Send character in <C>
; 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 <HL>
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 <BC> and <HL> saved
push b ! push h
call ?cist
pop h ! pop b
ora a
ret
cost1: ; get output status, saving <BC> & <HL>
push b ! push h
call ?cost
pop h ! pop b
ora a
ret
ci1: ; get input, saving <BC> & <HL>
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 @<HL> up to a null
; saves <BC> & <DE>
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 <HL>
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 <C>.
; Invoke login procedure for drive
; if this is first select. Return
; address of disk parameter header
; in <HL>
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 & <DE>
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 <BC>
; in @TRK for further operations.
settrk:
mov l,c ! mov h,b
shld @trk
ret
; SETSEC
; Set Sector. Saves sector number from <BC>
; in @sect for further operations.
setsec:
mov l,c ! mov h,b
shld @sect
ret
; SETDMA
; Set Disk Memory Address. Saves DMA address
; from <BC> 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 <DE>
; with sector in <BC>. Returns physical sector
; in <HL>. If no skew table (<DE>=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

480
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

BIN
Source/CPM3/bnkbdos3.spr

Binary file not shown.

362
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

BIN
Source/CPM3/ccp.com

Binary file not shown.

193
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 <C>
; 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 <HL>
; 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

180
Source/CPM3/cpm3.lib

@ -0,0 +1,180 @@
; Macro Definitions for CP/M3 BIOS Data Structures.
; dtbl <dph0,dph1,...> - 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,<?list>
?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


BIN
Source/CPM3/cpm3fix.pat

Binary file not shown.

1572
Source/CPM3/cpmldr.asm

File diff suppressed because it is too large

781
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 @<HL> up to 00, saves <BC> & <DE>
extrn ?pdec ; print binary number in <A> 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 <DE>
;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 <DE>
; they transfer the appropriate data, perform retries
; if necessary, then return an error code in <A>
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, <operation> <type>, 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 "<BEL>, 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 <B>
; 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 <B>
; 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

12
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

158
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


BIN
Source/CPM3/gencpm.com

Binary file not shown.

158
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


158
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


16
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

32
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

116
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

6
Source/CPM3/optbnk.lib

@ -0,0 +1,6 @@
; global assembler options for BANKED BIOS
true equ -1
false equ not true
banked equ true

6
Source/CPM3/optres.lib

@ -0,0 +1,6 @@
; global assembler options for NONBANKED BIOS
true equ -1
false equ not true
banked equ false

39
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 <http://cdl.uta.edu/cpm> compile it.
Apparently a more recent version of the compiler is
required.

BIN
Source/CPM3/resbdos3.spr

Binary file not shown.

50
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

BIN
Source/CPM3/zpmldr.com

Binary file not shown.

2
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

4
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)

47
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

2
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"

BIN
Source/ZCCP/ccp.com

Binary file not shown.

BIN
Source/ZCCP/diskinfo.com

Binary file not shown.

BIN
Source/ZCCP/loadseg.com

Binary file not shown.

103
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.


BIN
Source/ZCCP/rsxdir.com

Binary file not shown.

BIN
Source/ZCCP/startzpm.com

Binary file not shown.

308
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 <randy@mit.edu>
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 *
*******************************************************************************


BIN
Source/ZCCP/zinstal.zpm

Binary file not shown.

61
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

18
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

BIN
Source/ZPM3/autotog.com

Binary file not shown.

131
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


230
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!


BIN
Source/ZPM3/bnkbdos3.spr

Binary file not shown.

BIN
Source/ZPM3/clrhist.com

Binary file not shown.

49
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


BIN
Source/ZPM3/makedos.com

Binary file not shown.

90
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.

BIN
Source/ZPM3/resbdos3.spr

Binary file not shown.

435
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


BIN
Source/ZPM3/setz3.com

Binary file not shown.

80
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


35
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
============

BIN
Source/ZPM3/zinstal.zpm

Binary file not shown.

483
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.


BIN
Source/ZPM3/zpm3ldr.rel

Binary file not shown.

68
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.

Loading…
Cancel
Save