I chose to use the modular approach, with separate files for BIOSKRNL.asm, SCB3.asm, BOOT3.asm, CHARIO3.asm, MOVE3.asm, DRVTBL3.asm, FLP3.asm and IDE3.asm. All but the last three are files distributed by Digital Research. The last three have the hardware-specific code in them.
The files all assemble and link properly using RMAC and LINK. There are no assembly errors or undefined symbols. But it still fails to boot, giving the following message:
CP/M V3.0 Loader
Copyright (C) 1982, Digital Research
CPMLDR error: failed to read CPM3.SYS
As an attempt to reduce system complexity, I removed the IDE3.asm file and did a BIOS3.SPR build. When IDE3 is in place, the GENCPM utility asks questions to setup the blocking/deblocking buffers for drives C: and D:, which is what I assigned for the IDE drives. But when the IDE3 code is removed, GENCPM doesn't ask about them. So I'm working that way for starters, just to get a modular BIOS running with only Mike's FDC code in it. After I get that working, I will put the IDE3 code back in place.
My next step is to debug the BIOS, using the method described in the CP/M 3.0 System Guide. It suggests setting a break point at the jump table, and to start with the boot function. I'll do that.
I'm sure I left stuff out of the FLP3.asm file. I copied the FDC diskette functions from Mike's 3.0 BIOS code, but probably missed some stuff.
This is what my files look like at this point:
BIOSKRNL.asm
- Code: Select all
title 'Root module of relocatable BIOS for CP/M 3.0'
; version 1.0 15 Sept 82
true equ -1
false equ not true
banked equ FALSE ;<------ BANKED Version
; 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
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
SCB3.asm
- Code: Select all
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
SCB$BASE EQU 0FE00H ; BASE OF THE SCB
@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
BOOT3.asm
- Code: Select all
TITLE 'BOOT LOADER MODULE FOR CP/M 3.0'
; DEFINE LOGICAL VALUES:
TRUE EQU -1
FALSE EQU NOT TRUE
; DETERMINE IF FOR BANK SELECT OR NOT:
BANKED EQU FALSE ;<--------------- BANKED VERSION
; DEFINE PUBLIC LABELS:
PUBLIC ?INIT,?LDCCP,?RLCCP,?TIME
PUBLIC OUT$BLOCKS
; EXTERNALLY DEFINED ENTRY POINTS AND LABELS:
EXTRN ?PMSG,?CONIN
EXTRN @CIVEC,@COVEC,@AIVEC,@AOVEC,@LOVEC
EXTRN @CBNK,?BNKSL
IF BANKED
EXTRN BANKBUF ;128 BYTE BUFFER IN MOVE MODULE FOR USE
; DURING COLD AND WARM BOOTS
ENDIF
EXTRN @SEC,@MIN,@HOUR,@DATE ;FIELDS HOLDING CURRENT TIME AND DATE
; INCLUDE Z-80 MACROS:
MACLIB Z80
; SOME MISCELLANEOUS EQUATES:
BDOS EQU 5
CR EQU 13 ;ASCII CARRIAGE RETURN
LF EQU 10 ;ASCII LINEFEED
;
;
; WE CAN DO INITIALIZATION FROM BANKED MEMORY (IF WE HAVE IT):
IF BANKED
DSEG ; INIT DONE FROM BANKED MEMORY
ELSE
CSEG ; INIT TO BE DONE FROM COMMON MEMORY
ENDIF
;;;;; ?INIT
; HARDWARE INITIALIZATION OTHER THAN CHARACTER AND DISK I/O:
?INIT:
; ASSIGN CONSOLE INPUT AND OUTPUT TO CRT:
LXI H,8000H ;SIGNIFIES DEVICE 0
SHLD @CIVEC ;CONSOLE INPUT VECTOR
SHLD @COVEC ;CONSOLE OUTPUT VECTOR
; ASSIGN PRINTER TO LPT:
LXI H,4000H ;SIGNIFIES DEVICE 1
SHLD @LOVEC ;LIST OUTPUT VECTOR
; ASSIGN AUX TO CRT1:
LXI H,02000H ;SIGNIFIES DEVICE 2
SHLD @AIVEC ;AUXILLIARY INPUT VECTOR
SHLD @AOVEC ;AUXILLIARY OUTPUT VECTOR
; PRINT THE SIGN-ON MESSAGE:
LXI H,SIGNON$MSG ;POINT TO IT
JMP ?PMSG ;AND PRINT IT
;
;
;;;;; OUT$BLOCKS
; ROUTINE OUTPUTS SPECIFIED # BYTES TO SPECIFIED OUTPUT PORTS:
IF BANKED
CSEG ;WE WANT THIS ROUTINE IN COMMON MEMORY
ENDIF
OUT$BLOCKS:
MOV A,M ;GET A BYTE FROM THE BLOCK
ORA A ;END OF OUTPUT BLOCK ?
RZ ;THEN DONE!!
MOV B,A ;ELSE PUT # BYTES TO SEND OUT IN [B]
INX H ;POINT TO PORT TO SEND TO
MOV C,M ;GET IT TO [C]
INX H ;POINT TO 1ST BYTE OF BLOCK TO SEND OUT
OUTIR ;Z-80 BLOCK OUTPUT
JR OUT$BLOCKS
;;;;; ?LDCCP
; THIS ROUTINE IS ENTERED TO LOAD THE CCP.COM FILE INTO THE TPA BANK
; AT SYSTEM COLD START:
?LDCCP:
; SET UP THE FCB FOR THE FILE OPERATION:
XRA A ;ZERO EXTENT
STA CCP$FCB+15
LXI H,0 ;START AT BEGINNING OF FILE
SHLD FCB$NR
; TRY TO OPEN THE CCP.COM FILE:
LXI D,CCP$FCB ;POINT TO FCB
CALL OPEN ;ATTEMPT THE OPEN OPERATION
INR A ;WAS IT ON THE DISK ?
JRNZ CCP$FOUND ;YES -- GO LOAD IT
; WE ARRIVE HERE WHEN CCP.COM FILE WASN'T FOUND:
LXI H,CCP$MSG ;REPORT THE ERROR
CALL ?PMSG
CALL ?CONIN ;GET A RESPONSE
JR ?LDCCP ;AND TRY AGAIN
; FILE WAS OPENED OK -- READ IT IN:
CCP$FOUND:
LXI D,0100H ;LOAD AT BOTTOM OF TPA
CALL SETDMA ;BY SETTING THE NEXT DMA ADDRESS
LXI D,128 ;SET MULTI SECTOR I/O COUNT
CALL SETMULTI ; TO ALLOW UP TO 16K BYTES IN ONE OPERATION
LXI D,CCP$FCB ;POINT TO THE FCB
CALL READ ;AND READ THE CCP IN
; FOLLOWING CODE FOR BANKED SYSTEMS -- MOVES CCP IMAGE TO BANK 2
; FOR LATER RELOADING AT WARM STARTS:
IF BANKED
LXI H,0100H ;GET CCP IMAGE FROM START OF TPA
MVI B,25 ;TRANSFER 25 LOGICAL SECTORS
LDA @CBNK ;GET CURRENT BANK
PUSH PSW ;AND SAVE IT
LD$1:
PUSH B ;SAVE SECTOR COUNT
MVI A,1 ;SELECT TPA BANK
CALL ?BNKSL
LXI B,128 ;TRANSFER 128 BYTES TO TEMPORARY BUFFER
LXI D,BANKBUF ;TEMPORARY BUFFER ADDR IN [DE]
PUSH H ;SAVE SOURCE ADDRESS
PUSH D ;AND DESTINATION
PUSH B ;AND COUNT
LDIR ;BLOCK MOVE SECTOR TO TEMPORARY BUFFER
MVI A,2 ;SELECT BANK TO SAVE CCP IN
CALL ?BNKSL
POP B ;GET BACK COUNT
POP H ;LAST DESTINATION WILL BE NEW SOURCE ADDR
POP D ;LAST SOURCE WILL BE NEW DESTINATION
LDIR ;BLOCK MOVE SECTOR FROM BUFFER TO ALTERNATE
; BANK
XCHG ;NEXT ADDR WILL BE NEW SOURCE ADDR
POP B ;GET BACK SECTOR COUNT
DJNZ LD$1 ;DROP SECTOR COUNT AND LOOP TILL DONE...
POP PSW ;WHEN DONE -- RESTORE ORIGINAL BANK
JMP ?BNKSL
ELSE
; IF NON-BANKED WE RETURN THROUGH HERE:
RET
ENDIF
;;;;; ?RLCCP
; ROUTINE RELOADS CCP IMAGE FROM BANK 2 IF BANKED SYSTEM OR FROM THE
; DISK IF NON-BANKED VERSION:
?RLCCP:
IF BANKED
; FOLLOWING CODE FOR BANKED VERSION:
LXI H,0100H ;GET CCP IMAGE FROM START OF ALTERNATE BUFFER
MVI B,25 ;TRANSFER 25 LOGICAL SECTORS
LDA @CBNK ;GET CURRENT BANK
PUSH PSW ;AND SAVE IT
RL$1:
PUSH B ;SAVE SECTOR COUNT
MVI A,2 ;SELECT ALTERNATE BANK
CALL ?BNKSL
LXI B,128 ;TRANSFER 128 BYTES TO TEMPORARY BUFFER
LXI D,BANKBUF ;TEMPORARY BUFFER ADDR IN [DE]
PUSH H ;SAVE SOURCE ADDRESS
PUSH D ;AND DESTINATION
PUSH B ;AND COUNT
LDIR ;BLOCK MOVE SECTOR TO TEMPORARY BUFFER
MVI A,1 ;PUT CCP TO TPA BANK
CALL ?BNKSL
POP B ;GET BACK COUNT
POP H ;LAST DESTINATION WILL BE NEW SOURCE ADDR
POP D ;LAST SOURCE WILL BE NEW DESTINATION
LDIR ;BLOCK MOVE SECTOR FROM BUFFER TO TPA BANK
XCHG ;NEXT ADDR WILL BE NEW SOURCE ADDR
POP B ;GET BACK SECTOR COUNT
DJNZ RL$1 ;DROP SECTOR COUNT AND LOOP TILL DONE...
POP PSW ;GET BACK LAST CURRENT BANK #
JMP ?BNKSL ;SELECT IT AND RETURN
ELSE
; FOLLOWING CODE IS FOR NON-BANKED VERSIONS:
JMP ?LDCCP ;JUST DO LOAD AS THOUGH COLD BOOT
ENDIF
;;;;; ?TIME
; ROUTINE SETS/GETS TIME:
?TIME:
RET ;Just return in this simple version
;
;
;
IF BANKED
CSEG
ENDIF
;;;;;
; CP/M BDOS FUNCTION INTERFACES
; OPEN FILE:
OPEN:
MVI C,15 ! JMP BDOS ; OPEN FILE CONTROL BLOCK
; SET DMA ADDRESS:
SETDMA:
MVI C,26 ! JMP BDOS ; SET DATA TRANSFER ADDRESS
; SET MULTI SECTOR I/O COUNT:
SETMULTI:
MVI C,44 ! JMP BDOS ; SET RECORD COUNT
; READ FILE RECORD:
READ:
MVI C,20 ! JMP BDOS ; READ RECORDS
; CCP NOT FOUND ERROR MESSAGE:
CCP$MSG:
DB CR,LF,'BIOS ERR ON A: NO CCP.COM FILE',0
; FCB FOR CCP.COM FILE LOADING:
CCP$FCB:
DB 1 ;AUTO-SELECT DRIVE A
DB 'CCP COM' ;FILE NAME AND TYPE
DB 0,0,0,0
DS 16
FCB$NR: DB 0,0,0
; SYSTEM SIGN-ON MESSAGE:
SIGNON$MSG:
db CR,LF,LF,'CP/M Plus v3.0hd',CR,LF,LF,0
END
CHARIO3.asm
- Code: Select all
TITLE 'CHARACTER I/O HANDLER FOR CP/M 3.0'
; DEFINE LOGICAL VALUES:
TRUE EQU -1
FALSE EQU NOT TRUE
; DETERMINE IF FOR BANK SELECT OR NOT:
BANKED EQU FALSE ;<------------------- BANKED VERSION
; DEFINE PUBLIC LABELS:
PUBLIC ?CINIT,?CI,?CO,?CIST,?COST
PUBLIC @CTBL
; DEFINE EXTERNAL LABELS AND ENTRY POINTS:
IF BANKED
EXTRN @CBNK
EXTRN ?BNKSL
ENDIF
EXTRN OUT$BLOCKS ;BLOCK OUTPUT ROUTINE TO I/O PORTS
EXTRN ?PMSG
; INCLUDE Z-80 MACROS:
MACLIB Z80
; EQUATES FOR MODE BYTE BIT FIELDS
MB$INPUT EQU 0000$0001B ; DEVICE MAY DO INPUT
MB$OUTPUT EQU 0000$0010B ; DEVICE MAY DO OUTPUT
MB$IN$OUT EQU MB$INPUT+MB$OUTPUT
MB$SOFT$BAUD EQU 0000$0100B ; SOFTWARE SELECTABLE BAUD RATES
MB$SERIAL EQU 0000$1000B ; DEVICE MAY USE PROTOCOL
MB$XON$XOFF EQU 0001$0000B ; 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
; MISCELLANEOUS EQUATES:
CRTSTAT EQU 0H ;STATUS PORT TO CHECK CRT OUTPUT STATUS
IOBYTE EQU 0EFH
KEYSTAT EQU 0H
KEYIN EQU 01H
KEYOUT EQU 01H
CENTSTROBE EQU 4
CENTOUT EQU 5
CENTSTAT EQU 5
; WILL START OFF IN COMMON MEMORY FOR BANKED OR NON-BANKED SYSTEMS:
CSEG
IF BANKED
; WE PROVIDE ALTERNATE DEFINITIONS OF THE ROUTINE ENTRY POINTS IF
; WE ARE RUNNING A BANKED SYSTEM VERSUS A NON-BANKED SYSTEM:
;;;;; ?CINIT
; ENTER HERE FOR BANKED SYSTEMS FOR DEVICE INITIALIZATIONS:
?CINIT:
LXI H,BCINIT ;POINT TO BANKED ROUTINE ADDRESS
JR BANKIO ;GO TO DISPATCHER
;;;;; ?CI
; ENTER HERE FOR BANKED SYSTEM DEVICE INPUT:
?CI: LXI H,BCI ;POINT TO BANKED ROUTINE ADDRESS
JR BANKIO ;GO TO DISPATCHER
;;;;; ?CO
; ENTER HERE FOR BANKED SYSTEM DEVICE OUTPUT:
?CO: LXI H,BCO ;POINT TO BANKED ROUTINE ADDRESS
JR BANKIO ;GO TO DISPATCHER
;;;;; ?CIST
; ENTER HERE FOR BANKED SYSTEM DEVICE INPUT STATUS:
?CIST: LXI H,BCIST ;POINT TO BANKED ROUTINE ADDRESS
JR BANKIO ;GO TO DISPATCHER
;;;;; ?COST
; ENTER HERE FOR BANKED SYSTEM DEVICE OUTPUT STATUS:
?COST: LXI H,BCOST ;POINT TO BANKED ROUTINE ADDRESS
;;;;; BANKIO
; ROUTINE DISPATCHES TO BANKED PORTION OF CHARACTER I/O ROUTINES:
BANKIO:
SSPD SPSAVE ;SAVE CURRENT STACK POINTER
LXI SP,IOSP ; AND USE LOCAL STACK FOR I/O
LDA @CBNK ;GET CURRENT BANK
PUSH PSW ;SAVE ON LOCAL STACK
XRA A ;WE WILL SELECT BANK 0 (OP SYS)
CALL ?BNKSL
LXI D,BIORET ;RETURN ADDRESS IN [DE]
PUSH D ;PUT IT ON STACK FOR RETURN
PCHL ;DISPATCH TO BANKED PART OF ROUTINE
; ARRIVE HERE AFTER DEVICE HANDLER FINISHED:
BIORET:
POP D ;GET PREVIOUS CURRENT BANK TO [D]
PUSH PSW ;SAVE HANDLER RETURNED RESULT (IF ANY)
MOV A,D ;RESELECT PREVIOUS CURRENT BANK
CALL ?BNKSL
POP PSW ;GET BACK RESULT CODE TO [A]
LSPD SPSAVE ;RESTORE PREVIOUS STACK
RET ;AND RETURN...
ENDIF
;;;;;
;;;;; ACTUAL DEVICE HANDLERS
;;;;;
;;;;; ?CINIT (BCINIT FOR BANKED)
; PHYSICAL CODE FOR DEVICE INITIALIZATION:
IF BANKED
DSEG ;CAN PUT IN BANKED SEGMENT IF BANKED
BCINIT:
ELSE
?CINIT:
ENDIF
MOV B,C ;ON ENTRY DEVICE # IS IN [C] BUT WE NEED
; IT IN [B]
CALL DEV$DISPATCH ;GO TO CORRECT INIT ROUTINE
DW CINIT0 ;INIT FOR DEVICE 0
DW CINIT1 ;INIT FOR DEVICE 1
DW NULL$INIT ;INIT FOR DEVICE 2
DW NULL$INIT ;INIT FOR DEVICE 3
DW NULL$INIT ;INIT FOR DEVICE 4
DW NULL$INIT ;INIT FOR DEVICE 5
DW NULL$INIT ;INIT FOR DEVICE 6
DW NULL$INIT ;INIT FOR DEVICE 7
DW NULL$INIT ;INIT FOR DEVICE 8
DW NULL$INIT ;INIT FOR DEVICE 9
DW NULL$INIT ;INIT FOR DEVICE 10
DW NULL$INIT ;INIT FOR DEVICE 11
DW NULL$INIT ;INIT FOR DEVICE 12
DW NULL$INIT ;INIT FOR DEVICE 13
DW NULL$INIT ;INIT FOR DEVICE 14
DW NULL$INIT ;INIT FOR DEVICE 15
;;;;; ?CI (BCI FOR BANKED)
; PHYSICAL CODE FOR DEVICE INPUT:
IF BANKED
BCI:
ELSE
?CI:
ENDIF
CALL DEV$DISPATCH
DW CI0 ;DEVICE 0 INPUT
DW CI1 ;DEVICE 1 INPUT
DW NULL$CI ;DEVICE 2 INPUT
DW NULL$CI ;DEVICE 3 INPUT
DW NULL$CI ;DEVICE 4 INPUT
DW NULL$CI ;DEVICE 5 INPUT
DW NULL$CI ;DEVICE 6 INPUT
DW NULL$CI ;DEVICE 7 INPUT
DW NULL$CI ;DEVICE 8 INPUT
DW NULL$CI ;DEVICE 9 INPUT
DW NULL$CI ;DEVICE 10 INPUT
DW NULL$CI ;DEVICE 11 INPUT
DW NULL$CI ;DEVICE 12 INPUT
DW NULL$CI ;DEVICE 13 INPUT
DW NULL$CI ;DEVICE 14 INPUT
DW NULL$CI ;DEVICE 15 INPUT
;;;;; ?CO (BCO FOR BANKED)
; PHYSICAL CODE FOR DEVICE OUTPUT:
IF BANKED
BCO:
ELSE
?CO:
ENDIF
CALL DEV$DISPATCH ;GO TO CORRECT DEVICE OUTPUT HANDLER
DW CO0 ;DEVICE 0 OUTPUT
DW CO1 ;DEVICE 1 OUTPUT
DW NULL$CO ;DEVICE 2 OUTPUT
DW NULL$CO ;DEVICE 3 OUTPUT
DW NULL$CO ;DEVICE 4 OUTPUT
DW NULL$CO ;DEVICE 5 OUTPUT
DW NULL$CO ;DEVICE 6 OUTPUT
DW NULL$CO ;DEVICE 7 OUTPUT
DW NULL$CO ;DEVICE 8 OUTPUT
DW NULL$CO ;DEVICE 9 OUTPUT
DW NULL$CO ;DEVICE 10 OUTPUT
DW NULL$CO ;DEVICE 11 OUTPUT
DW NULL$CO ;DEVICE 12 OUTPUT
DW NULL$CO ;DEVICE 13 OUTPUT
DW NULL$CO ;DEVICE 14 OUTPUT
DW NULL$CO ;DEVICE 15 OUTPUT
;;;;; ?CIST (BCIST FOR BANKED)
; PHYSICAL CODE FOR DEVICE INPUT STATUS:
IF BANKED
BCIST:
ELSE
?CIST:
ENDIF
CALL DEV$DISPATCH
DW CIST0 ;DEVICE 0 INPUT STATUS
DW CIST1 ;DEVICE 1 INPUT STATUS
DW NULL$CIST ;DEVICE 2 INPUT STATUS
DW NULL$CIST ;DEVICE 3 INPUT STATUS
DW NULL$CIST ;DEVICE 4 INPUT STATUS
DW NULL$CIST ;DEVICE 5 INPUT STATUS
DW NULL$CIST ;DEVICE 6 INPUT STATUS
DW NULL$CIST ;DEVICE 7 INPUT STATUS
DW NULL$CIST ;DEVICE 8 INPUT STATUS
DW NULL$CIST ;DEVICE 9 INPUT STATUS
DW NULL$CIST ;DEVICE 10 INPUT STATUS
DW NULL$CIST ;DEVICE 11 INPUT STATUS
DW NULL$CIST ;DEVICE 12 INPUT STATUS
DW NULL$CIST ;DEVICE 13 INPUT STATUS
DW NULL$CIST ;DEVICE 14 INPUT STATUS
DW NULL$CIST ;DEVICE 15 INPUT STATUS
;;;;; ?COST (BCOST FOR BANKED)
; PHYSICAL CODE FOR DEVICE OUTPUT STATUS:
IF BANKED
BCOST:
ELSE
?COST:
ENDIF
CALL DEV$DISPATCH ;GO TO CONSOLE OUTPUT STATUS HANDLER
DW COST0 ;DEVICE 0 OUTPUT STATUS
DW COST1 ;DEVICE 1 OUTPUT STATUS
DW NULL$COST ;DEVICE 2 OUTPUT STATUS
DW NULL$COST ;DEVICE 3 OUTPUT STATUS
DW NULL$COST ;DEVICE 4 OUTPUT STATUS
DW NULL$COST ;DEVICE 5 OUTPUT STATUS
DW NULL$COST ;DEVICE 6 OUTPUT STATUS
DW NULL$COST ;DEVICE 7 OUTPUT STATUS
DW NULL$COST ;DEVICE 8 OUTPUT STATUS
DW NULL$COST ;DEVICE 9 OUTPUT STATUS
DW NULL$COST ;DEVICE 10 OUTPUT STATUS
DW NULL$COST ;DEVICE 11 OUTPUT STATUS
DW NULL$COST ;DEVICE 12 OUTPUT STATUS
DW NULL$COST ;DEVICE 13 OUTPUT STATUS
DW NULL$COST ;DEVICE 14 OUTPUT STATUS
DW NULL$COST ;DEVICE 15 OUTPUT STATUS
;;;;; DEV$DISPATCH
; ROUTINE JUMPS TO CORRECT DEVICE HANDLER:
DEV$DISPATCH:
MOV A,B ;GET DEVICE # TO [A]
STA DEV$CODE ;SAVE FOR LATER USE
ADD A ;X2 FOR WORD OFFSET
POP H ;RETURN ADDRESS IS 1ST PARAMETER ADDRESS
MOV E,A ;SET UP OFFSET IN [DE]
MVI D,0
DAD D ;[HL] = PTR TO HANDLER ADDRESS
MOV E,M ;GET HANDLER ADDRESS TO [DE]
INX H
MOV D,M
XCHG ;PUT IN [HL]
PCHL ;AND DISPATCH TO IT...
;;;;;
;;;;; PHYSICAL DEVICE HANDLER CODE:
;;;;;
;
;;;;; DEVICE 0 HANDLERS (SD SYSTEMS VIDIO BOARD)
CINIT0: ; DEVICE 0 INITIALIZATION
RET
;
;
;<<<<<<<<<<<<<<<<<<< MAIN CONSOL STATUS ROUTINE >>>>>>>>>>>>>>>>>>>>>>
CIST0: ; DEVICE 0 INPUT STATUS:
CSTS: IN KEYSTAT
ANI 02H
RZ ;RETURN WITH 0 IN [A] IF NOTHING THERE
DCR A
RET ;RETURN WITH 0FFH IN [A] IF SOMETHING
;
;
;<<<<<<<<<<<<<<<<<<<< MAIN CONSOL INPUT ROUTINE >>>>>>>>>>>>>>>>>>>>
;
CI0: ;DEVICE 0 INPUT:
CI: CALL CSTS ;NEED CONSTAT TO CLEAN UP SHIFT KEYS ETC
JRZ CI
IN KEYIN
ANI 7FH
RET
;
;<<<<<<<<<<<<<<<<<<<<<< MAIN CONSOL OUTPUT ROUTINE >>>>>>>>>>>>>>>>>>>>>>>>>
;
COST0: ; DEVICE 0 OUTPUT STATUS:
IN CRTSTAT
ANI 4H
RZ
XRA A
DCR A
RET
;
CO0: ; DEVICE 0 OUTPUT:
CO: IN IOBYTE
BIT 0,A ;CHECK IF OUTPUT TO LIST IS ALSO REQ
JZ LOX
BIT 4,A ;KILL LF'S IF THIS IS 0
JRNZ SDCONO
MOV A,C
CPI 0AH ;CHECK FOR LF'S
JRZ SDCON5 ;KILL LF'S
PUSH B ;ALL OTHERE CHARACTERS SEND EOL THEN CHAR
MVI C,']'-40H ;FOR CLEAR TO END OF LINE
CALL SDCONO ;BECAUSE EOL IS SENT FOR EACH CHARACTER THE
POP B ;TYPE RATE IS NICELY SLOWED DOWN TO ~ 60 BAUD
JR SDCONO ;AT NO FURTHER EXPENSE |
SDCON5: MOV A,C
RET
;
LOX: CALL SDCONO ;OUTPUT TO BOTH PRINTER & CONSOLE
CALL LO
RET
;
SDCONO: IN 0H ;SD SYSTEMS VIDIO BOARD PORT
ANI 4H
JRZ SDCONO
MOV A,C
CPI 07H ;IS IT A BELL
JRZ BELL1
CPI 0H ;SD BOARD CANNOT TAKE A NULL!
RZ
OUT 1
IN IOBYTE
BIT 5,A ;SEE IF TIME DELAY REQ WITH CONSOL OUTPUT
JRNZ SDCON5 ;MAKE SURE TO RETURN CHARACTER SENT IN [A]
PUSH PSW
MVI A,50
CALL DELAY
POP PSW
MOV A,C
RET ;RETURN CHARACTER SENT IN [A]
;
;
BELL1: MVI A,06H ;SEND A BELL
OUT 1H
MVI A,0FH
CALL DELAY
MVI A,07H
OUT 1H
JR SDCON5
;
DELAY: DCR A ;GENERAL COUNT DOWN TIME DELAY
RZ ;LENGTH SET IN [A]
PUSH PSW
MVI A,05H
MORE: DCR A
PUSH PSW
XRA A
MORE2: DCR A
JRNZ MORE2
POP PSW
JRNZ MORE
POP PSW
JR DELAY
;
;
;;;;; DEVICE 1 HANDLERS: (Parallel port to printers).
CINIT1: ; DEVICE 1 INITIALIZATIONS
MVI A,0FFH ;NEEDED TO CLEAR PRINTER STROBE COMES UP
OUT CENTSTROBE ;AFTER A RESET LOW
RET
CIST1: ; DEVICE 1 INPUT STATUS:
XRA A
RET
COST1: ; DEVICE 1 OUTPUT STATUS:
LSTAT: IN CENTSTAT ;FIRST FIND WHICH PRINTER IS SELECTED
BIT 1,A
JRNZ CENSTAT
BIT 5,A
JRNZ TRANSTAT
XRA A ;NONE SELECTED
DCR A
RET
CENSTAT:ANI 00001111B ;XXXX0110 IS READY (BIT 3=PAPER BIT 2=FAULT
CPI 00000110B ;BIT 1=SELECT BIT 0=BUSY
JRZ LSTAT1
XRA A
RET
TRANSTAT:ANI 11110000B ;0110XXX IS READY (BIT 7=ALERT BIT 6=FAULT
CPI 01100000B ;BIT 5=SELECT BIT 4=BUSY
JRZ LSTAT1
XRA A
RET
LSTAT1: XRA A ;PUT 0FFH IN [A] IF READY & NO ZERO FLAG
DCR A
RET
;
CI1: ; DEVICE 1 INPUT:
MVI A,1AH
RET
CO1: ; DEVICE 1 OUTPUT:
;
;THIS IS THE MAIN DRIVER FOR PRINTER OUTPUT.
;
LO: CALL LSTAT
JRZ LO
MVI A,0FFH
OUT CENTSTROBE
MOV A,C
OUT CENTOUT
IN CENTSTAT
BIT 1,A
JRNZ LCENT
BIT 5,A
JRNZ LTRANS
RET ;NO STROBE SINCE NOT SELECTED
;
LCENT: MVI A,11111110B ;STROBE FOR CENTRONICS
JR OVERLS
LTRANS: MVI A,11111101B
OVERLS: OUT CENTSTROBE
MVI A,0FFH
OUT CENTSTROBE
RET
;
;
;;;;; NULL ROUTINES:
NULL$CIST:
NULL$COST:
XRA A ;RETURN A FALSE STATUS RESULT
JR NULL$RET
NULL$CI:
MVI A,1AH ;FOR INPUT RETURN A CNTL-Z (EOF)
NULL$INIT:
NULL$CO:
NULL$RET:
RET ;HARMLESS RETURN
; STORAGE FOR DEVICE CODE -- CAN RESIDE IN SAME SEGMENT AS THE BULK
; OF CHARACTER I/O ROUTINES:
DEV$CODE: DS 1
;;;;; CHRTBL
; CHARACTER DEVICE TABLE
CSEG ;MUST RESIDE IN COMMON MEMORY
@CTBL:
DB 'CRT ' ;CONSOLE (DEVICE 0)
DB MB$IN$OUT
DB BAUD$NONE
DB 'LPT ' ;PRINTER (DEVICE 1)
DB MB$OUTPUT
DB BAUD$NONE
MAX$DEVICES EQU ($-@CTBL)/8 ;# DEVICES IN TABLE
DB 0 ;TABLE TERMINATOR
; OTHER DATA AREAS:
DS 24 ;CHARACTER I/O LOCAL STACK
IOSP EQU $
SPSAVE DS 2
END
MOVE3.asm
- Code: Select all
TITLE 'BANK & MOVE MODULE FOR CP/M3 LINKED BIOS'
; DEFINE LOGICAL VALUES:
TRUE EQU -1
FALSE EQU NOT TRUE
; DETERMINE IF FOR BANK SELECT OR NOT:
BANKED EQU FALSE ;<----------------Banked VERSION
MPURR0 EQU 0D2H ;Z80 BOARD RELOCATION PORT
MPURR1 EQU 0D3H ;Z80 BOARD RELOCATION PORT
; DEFINE PUBLIC LABELS:
PUBLIC ?MOVE,?XMOVE,?BANK
; PUBLIC LABELS AND EXTERNALS FOR BANKED SYSTEMS:
IF BANKED
PUBLIC CURRR0,CURRR1 ;CURRENT WINDOW RELOCATION SEGMENTS
PUBLIC BANKBUF ;TEMPORARY 128 BYTE BUFFER OTHER ROUTINES
; MAY USE FOR TRANSIENT PURPOSES
PUBLIC WIN$LOW$2BITS ;LOWER 2 BITS FOR EACH RELOCATION REGISTER
PUBLIC DOXMOV,XMOV$BANKS ;EXTENDED BANK MOVE VARIABLES
; EXTERNALLY DEFINED ENTRY POINTS AND LABELS:
EXTRN @CBNK
EXTRN ?BNKSL
ENDIF
; INCLUDE Z-80 MACROS:
MACLIB Z80
; LOCATE CODE IN THE COMMON SEGMENT:
CSEG
;;;;; ?XMOVE
; ROUTINE SETS UP AN INTER-BANK MOVE OF 128 BYTES ON THE NEXT CALL
; TO ?MOVE:
?XMOVE:
IF BANKED
MVI A,0FFH ;SET EXTENDED MOVE FLAG
STA DOXMOV
SBCD XMOV$BANKS ;AND STORE BANKS FOR THE EXTENDED MOVE
ENDIF
RET
;;;;; ?MOVE
; ROUTINE PERFORMS INTRA-BANK MOVES IF ?XMOVE WAS NOT CALLED PRIOR TO
; THIS CALL TO ?MOVE ELSE A 128 BYTE TRANSFER IS CONDUCTED BETWEEN
; DIFFERENT BANKS:
?MOVE:
IF BANKED
LDA DOXMOV ;GET EXTENDED MOVE FLAG
ORA A ;IS IT SET ?
MVI A,0 ;RESET FOR NEXT TIME ANYWAY
STA DOXMOV
JRZ MOVE$IT
; HAVE TO IMPLEMENT INTER-BANK MOVE:
LDA @CBNK ;REMEMBER CURRENT BANK
PUSH PSW
LDA XMOV$BANKS ;GET SOURCE BANK
CALL ?BNKSL ; AND SELECT IT
PUSH H ;SAVE DESTINATION ADDRESS
PUSH B ;AND THE COUNT
XCHG ;[HL] = SOURCE
LXI D,BANKBUF ;[DE] = LOCAL TEMPORARY BUFFER
LDIR ;BLOCK MOVE TO TEMP BUFFER
POP B ;RESTORE COUNT
POP D ;[DE] = ORIGINAL DESTINATION
PUSH H ;SAVE NEXT SOURCE ADDRESS
LXI H,BANKBUF ;[HL] = SOURCE = TEMP BUFFER
LDA XMOV$BANKS+1 ;GET DESTINATION BANK
CALL ?BNKSL ;AND SELECT IT
LDIR ;BLOCK MOVE FROM TEMP BUFFER TO DESTINATION
POP H ;RESTORE NEXT SOURCE
XCHG ;RETURN SWAP
POP PSW ;GET BACK PREVIOUS CURRENT BANK
JMP ?BNKSL ;SELECT IT AND RETURN
; ARRIVE HERE FOR INTRA-BANK MEMORY MOVE:
MOVE$IT:
ENDIF
XCHG ;WE ARE PASSED SOURCE IN DE AND DEST IN HL
LDIR ;USE Z80 BLOCK MOVE INSTRUCTION
XCHG ;NEED NEXT ADDRESSES IN SAME REGS
RET
;;;;; ?BANK
; ROUTINE SWITCHES IN PHYSICAL BANK:
?BANK:
IF BANKED
CPI 2 ;BANK 2 OR HIGHER ?
JRNC BNK1$SWITCH ;GO DIRECTLY TO CODE TO CALCULATE THE
; PHYSICAL ADDRESS...
ORA A ;ELSE SWAP BANK 0 AND 1 VALUES
MVI A,1 ;IF BANK 0 MAKE BANK 1
JRZ BNK1$SWITCH
XRA A ;ELSE IF BANK 1 MAKE BANK 0
JR BNK2$SWITCH
; HERE TO GET MPU-80 SEGMENT # FOR THE BANK WE NEED:
BNK1$SWITCH:
DCR A ;NORMALIZE BANK # RELATIVE TO 0
ADD A ;X2
ADD A ;X4 FOR 16K / WINDOW
ADD A ;X8 FOR 32K / BANK
ADI 10H ;BANKS 1-15 START ABOVE 1ST 64K
; DO PHYSICAL BANK SWITCHING HERE:
BNK2$SWITCH:
PUSH B ;SAVE [BC]
LBCD WIN$LOW$2BITS ;GET LOWER 2 BITS FOR EACH RELOCATION REGISTER
DI ;CRITICAL SECTION -- NO INTERRUPTS HERE
STA CURRR0 ;SAVE SEGMENT ADDR. OF LOWER WINDOW
ORA B ;[A] = WINDOW SEG + LOW 2 BITS
OUT MPURR0 ;RELOCATE LOWER WINDOW NOW
ANI 0FCH ;TAKE OUT LOWER 2 BITS
ADI 4 ;BUMP UP SEG ADDR BY 16K FOR UPPER WINDOW
STA CURRR1 ;SAVE UPPER WINDOW SEGMENT ADDR.
ORA C ;[A] = WINDOW SEG + LOW 2 BITS
OUT MPURR1 ;RELOCATE UPPER WINDOW
;;;; EI ;CAN REENABLE INTERRUPTS NOW
POP B ;RESTORE [BC]
ENDIF
RET
IF BANKED
DOXMOV: DB 0 ;EXTENDED MOVE FLAG -- IF EQUAL TO FFH THEN
; NEXT CALL TO ?MOVE WILL BE AN INTER-BANK
; MOVE.
CURRR0: DB 10H ;CURRENT LOWER WINDOW RELOCATION REGISTER
; VALUE
CURRR1: DB 14H ;CURRENT UPPER WINDOW RELOCATION REGISTER
; VALUE
WIN$LOW$2BITS: DB 1 ;UPPER WINDOW REGISTER LOW 2 BITS (MAY CONTROL
; ADDRESSING ABOVE 1 MEGABYTE OR EPROM SELECT)
DB 1 ;LOWER WINDOW REGISTER LOW 2 BITS (MAY CONTROL
; ADDRESSING ABOVE 1 MEGABYTE OR EPROM SELECT)
XMOV$BANKS: DS 1 ;STORAGE AREA FOR DESTINATION BANK # FOR
; EXTENDED MOVES
DS 1 ;STORAGE AREA FOR SOURCE BANK # FOR
; EXTENDED MOVES
BANKBUF: DS 128 ;LOCAL TEMPORARY BUFFER FOR EXTENDED MOVES
ENDIF
END