BIOSKRN3.ASM:
- Code: Select all
title 'ROOT BIOS MODULE 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
dcx h ;convert to zero-indexed
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/M 3.0'
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
JNZ 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 'SIO AND 2SIO MODULE FOR CP/M 3.0'
; DEFINE LOGICAL VALUES:
TRUE EQU -1
FALSE EQU NOT TRUE
; Equates used by FDC for flushing during system inactivity (during char I/O)
UNDEF EQU 0FFH
MINIDSK EQU FALSE ;Set TRUE for Altair Mini-Disk (see FDC3.asm)
cHDUNLD EQU 08H ;head unload
DRVCMD EQU 09H ;drive command register (out)
; 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
; FDC external labels:
EXTRN bfDrive ;Buffered drive
EXTRN ckFlush ;Disk buffer flush routine
; INCLUDE Z-80 MACROS:
MACLIB Z80
; 2SIO Serial Board Equates
SIO1CTL EQU 10h ;1st port on 2SIO board - control register
SIO1DAT EQU 11h ;1st port on 2SIO board - data register
SIO2CTL EQU 12h ;2nd port on 2SIO board - control register
SIO2DAT EQU 13h ;2nd port on 2SIO board - data register
SIORDRF EQU 01h ;read data register full flag
SIOTDRE EQU 02h ;transmit data register empty flag
; SIO Serial Board Equates
SIOCTL EQU 00h ;SIO board at 0 - control register
SIODAT EQU 01h ;SIO board at 0 - data register
ACRCTL EQU 06h ;SIO board at 6 - control register (cassette)
ACRDAT EQU 07h ;SIO board at 6 - data register (cassette)
SIORCV EQU 01h ;data received bit (inverted)
SIOXMT EQU 80h ;read to transmit (inverted)
; 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
; 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 CINIT2 ;INIT FOR DEVICE 2
DW CINIT3 ;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
;;;;; System is relatively idle during char input, so do some FDC housecleaning
mvi a,UNDEF ;invalidate track buffer
sta bfDrive
call ckFlush ;flush the track if needed
ei ;restore interrupts
IF NOT MINIDSK
mvi a,cHDUNLD ;unload head
out DRVCMD
ENDIF
CALL DEV$DISPATCH
DW CI0 ;DEVICE 0 INPUT
DW CI1 ;DEVICE 1 INPUT
DW CI2 ;DEVICE 2 INPUT
DW CI3 ;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 CO2 ;DEVICE 2 OUTPUT
DW CO3 ;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 CIST2 ;DEVICE 2 INPUT STATUS
DW CIST3 ;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 COST2 ;DEVICE 2 OUTPUT STATUS
DW COST3 ;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
MOV A,C ;PUT CHAR TO SEND INTO A
XCHG ;PUT HANDLER ADDRESS IN [HL]
PCHL ;AND DISPATCH TO IT...
;;;;;
;;;;; PHYSICAL DEVICE HANDLER CODE:
;;;;;
;---------------------------------------------------------------------------
; Initialize serial ports
;---------------------------------------------------------------------------
CINIT0:
mvi a,03h ;reset 2SIO ports
out SIO1CTL
out SIO2CTL
mvi a,11h ;select 8N2
out SIO1CTL
out SIO2CTL
CINIT1:
CINIT2:
CINIT3:
ret
;---------------------------------------------------------------------------
; Input status routines. Return FFh if character ready, else zero
;---------------------------------------------------------------------------
CIST0:
sio1IS in SIO1CTL ;read 2SIO #1 status/control register
ani SIORDRF ;data present?
rz ;no, return zero
mvi a,0FFh ;else return FFh
ret
CIST1:
sio2IS in SIO2CTL ;read 2SIO #2 status/control register
ani SIORDRF ;data present?
rz ;no, return zero
mvi a,0FFh ;else return FFh
ret
CIST2:
sioIS in SIOCTL ;read SIO status/control register
xri 0FFh ;convert to positive logic
ani SIORCV ;data present?
rz ;no, return zero
mvi a,0FFh ;else return FFh
ret
CIST3:
acrIS in ACRCTL ;read ACR (SIO) control register
xri 0FFh ;convert to positive logic
ani SIORCV ;data present?
rz ;no, return zero
mvi a,0FFh ;else return FFh
ret
;---------------------------------------------------------------------------
; Character input routines (character ready assumed)
;---------------------------------------------------------------------------
CI0:
sio1In in SIO1DAT ;read and return the character
ret
CI1:
sio2In in SIO2DAT ;read and return the character
ret
CI2:
sioIn in SIODAT ;read and return the character
ret
CI3:
acrIn in ACRDAT ;read and return the character
ret
;---------------------------------------------------------------------------
; Output status routines. Return FFh if ready to send, else zero
;---------------------------------------------------------------------------
COST0:
sio1OS in SIO1CTL ;read 2SIO #1 status/control register
ani SIOTDRE ;0=busy
rz ;not ready, return 0
mvi a,0FFh ;else return FFh
ret
COST1:
sio2OS in SIO2CTL ;read 2SIO #2 status/control register
ani SIOTDRE ;0=busy
rz ;not ready, return 0
mvi a,0FFh ;else return FFh
ret
COST2:
sioOS in SIOCTL ;read SIO status/control register
xri 0FFh ;convert to positive logic
ani SIOXMT ;0=busy
rz ;not ready, return 0
mvi a,0FFh ;else return FFh
ret
COST3:
acrOS in ACRCTL ;read SIO status/control register
xri 0FFh ;convert to positive logic
ani SIOXMT ;0=busy
rz ;not ready, return 0
mvi a,0FFh ;else return FFh
ret
;---------------------------------------------------------------------------
; Character output routines (transmit ready assumed)
;---------------------------------------------------------------------------
CO0:
sio1Out out SIO1DAT ;send character
ret
CO1:
sio2Out out SIO2DAT ;send character
ret
CO2:
sioOut out SIODAT ;send character
ret
CO3:
acrOut out ACRDAT ;send character
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 '2SIOA ' ; device 0, 2SIO at 10h/11h
db mb$in$out+mb$serial
db baud$none
db '2SIOB ' ; device 1, 2SIO at 12h/13h
db mb$in$out+mb$serial
db baud$none
db 'SIO ' ; device 2, SIO at 0/1
db mb$in$out+mb$serial
db baud$none
db 'ACR ' ; device 3, SIO at 6/7 (ACR)
db mb$in$out+mb$serial
db baud$none
db 0 ; table terminator
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