title 'Root module of relocatable BIOS for CP/M 3.0' ; version 1.0 15 Sept 82 include config.inc include z180reg.inc ; 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 hwinit,?init ; general initialization and signon extrn ?ldccp,?rlccp ; load & reload CCP for BOOT & WBOOT ; user defined character I/O routines extrn ?ci,?co,?cist,?cost ; each take device in extrn ?cinit ; (re)initialize device in extrn @ctbl ; physical character device table ; disk communication data items extrn @dtbl ; table of pointers to XDPHs public @adrv,@rdrv,@trk,@sect ; parameters for disk I/O public @dma,@dbnk,@cnt ; '' '' '' '' ; memory control public @cbnk ; current bank extrn ?xmove,?move ; select move bank, and block move extrn ?bank ; select CPU bank ; clock support extrn ?time ; signal time operation ; general utility routines public ?pmsg,?pdec ; print message, print number from 0 to 65535 public ?pderr ; print BIOS disk error message header include modebaud.inc ; 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 public bs$stack ; 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 auxiliary output character ?auxi: jp auxin ; return auxiliary 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 ?cinit ; change baud rate of device ?drtbl: jp getdrv ; 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 bnksel ; 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 ; Initial entry point for system startup. dseg ; this part can be banked boot: ld sp,bs$stack call hwinit ; first time hardware initialisation ld c,15 ; initialize all 16 character devices c$init$loop: push bc call ?cinit pop bc dec c jp p,c$init$loop call ?init ; perform any additional system initialization ; and print signon message ld bc,16*256+0 ld hl,@dtbl ; init all 16 logical disk drives d$init$loop: push bc ; save remaining count and abs drive ld e,(hl) inc hl ld d,(hl) inc hl ; grab @drv entry ld a,e or d jr z,d$init$next ; if null, no drive push hl ; save @drv pointer if 0 ex de,hl ; XDPH address in dec hl dec hl ld b,(hl) ; get relative drive code ld (@ADRV),bc ; save absolute and relative drive code dec hl ; point to init pointer ld d,(hl) dec hl ld e,(hl) ; get init pointer ex de,hl call ipchl ; call init routine else push de pop ix ld b,(ix-2) ld (@ADRV),bc ; save absolute and relative drive code ld l,(ix-4) ld h,(ix-3) ; get init pointer call ipchl ; call init routine endif pop hl ; recover @drv pointer d$init$next: pop bc ; recover counter and drive # inc c djnz d$init$loop ; and loop for each drive jp boot$1 cseg ; following in resident memory boot$1: call set$jumps call ?ldccp ; fetch CCP for first time jp ccp ; WBOOT ; Entry for system restarts. wboot: ld sp,bs$stack call set$jumps ; initialize page zero call ?rlccp ; reload CCP jp ccp ; then reset jmp vectors and exit to ccp set$jumps: if banked ld a,1 call ?bnksl endif ld a,0C3h ; jp opcode ld (0),a ld (5),a ; set up jumps in page zero ld hl,?wboot ld (1),hl ; BIOS warm start entry ld hl,(@MXTPA) ld (6),hl ; BDOS system call entry ret ds 64 bs$stack equ $ ; DEVTBL ; Return address of character device table devtbl: ld hl,@ctbl ret ; GETDRV ; Return address of drive table getdrv: ld hl,@dtbl ret ; CONOUT ; Console Output. Send character in ; to all selected devices conout: ld hl,(@covec) ; fetch console output bit vector jr out$scan ; AUXOUT ; Auxiliary Output. Send character in ; to all selected devices auxout: ld hl,(@aovec) ; fetch aux output bit vector jr out$scan ; LIST ; List Output. Send character in ; to all selected devices. list: ld hl,(@lovec) ; fetch list output bit vector out$scan: ld b,0 ; start with device 0 co$next: add hl,hl ; shift out next bit jr nc,not$out$device push hl ; save the vector push bc ; save the count and character not$out$ready: call coster or a jp z,not$out$ready pop bc push bc ; restore and resave the character and device call ?co ; if device selected, print it pop bc ; recover count and character pop hl ; recover the rest of the vector not$out$device: inc b ; next device number ld a,h or l ; see if any devices left jr nz,co$next ; and go find them... ret ; CONOST ; Console Output Status. Return true if ; all selected console output devices ; are ready. conost: ld hl,(@covec) ; get console output bit vector jr ost$scan ; AUXOST ; Auxiliary Output Status. Return true if ; all selected auxiliary output devices ; are ready. auxost: ld hl,(@aovec) ; get aux output bit vector jr ost$scan ; LISTST ; List Output Status. Return true if ; all selected list output devices ; are ready. listst: ld hl,(@lovec) ; get list output bit vector ost$scan: ld b,0 ; start with device 0 cos$next: add hl,hl ; check next bit push hl ; save the vector push bc ; save the count ld a,0FFh ; assume device ready call c,coster ; check status for this device pop bc ; recover count pop hl ; recover bit vector or a ; see if device ready ret z ; if any not ready, return false inc b ; drop device number ld a,h or l ; see if any more selected devices jr nz,cos$next or 0FFh ; all selected were ready, return true ret coster: ; check for output device ready, including optional ; xon/xoff support ld l,b ld h,0 ; make device code 16 bits push hl ; save it in stack add hl,hl add hl,hl add hl,hl ; create offset into device characteristics tbl ld de,@ctbl+6 add hl,de ; make address of mode byte ld a,(hl) and mb$xon$xoff pop hl ; recover console number in jp z,?cost ; not a xon device, go get output status direct ld de,xofflist add hl,de ; make pointer to proper xon/xoff flag call cist1 ; see if this keyboard has character ld a,(hl) call nz,ci1 ; get flag or read key if any cp ctlq jr nz,not$q ; if its a ctl-Q, ld a,0FFh ; set the flag ready not$q: cp ctls jr nz,not$s ; if its a ctl-S, ld a,00h ; clear the flag not$s: ld (hl),a ; save the flag call cost1 ; get the actual output status, and (hl) ; and mask with ctl-Q/ctl-S flag ret ; return this as the status cist1: ; get input status with and saved push bc push hl call ?cist pop hl pop bc or a ret cost1: ; get output status, saving & push bc push hl call ?cost pop hl pop bc or a ret ci1: ; get input, saving & push bc push hl call ?ci pop hl pop bc ret ; CONST ; Console Input Status. Return true if ; any selected console input device ; has an available character. const: ld hl,(@civec) ; get console input bit vector jr ist$scan ; AUXIST ; Auxiliary Input Status. Return true if ; any selected auxiliary input device ; has an available character. auxist: ld hl,(@aivec) ; get aux input bit vector ist$scan: ld b,0 ; start with device 0 cis$next: add hl,hl ; check next bit ld a,0 ; assume device not ready call c,cist1 ; check status for this device or a ret nz ; if any ready, return true inc b ; next device number ld a,h or l ; see if any more selected devices jr nz,cis$next xor a ; all selected were not ready, return false ret ; CONIN ; Console Input. Return character from first ; ready console input device. conin: ld hl,(@civec) jr in$scan ; AUXIN ; Auxiliary Input. Return character from first ; ready auxiliary input device. auxin: ld hl,(@aivec) in$scan: push hl ; save bit vector ld b,0 ci$next: add hl,hl ; shift out next bit ld a,0 ; insure zero a (nonexistant device not ready). call c,cist1 ; see if the device has a character or a jr nz,ci$rdy ; this device has a character inc b ; else, next device ld a,h or l ; see if any more devices jr nz,ci$next ; go look at them pop hl ; recover bit vector jr in$scan ; loop til we find a character ci$rdy: pop hl ; discard extra stack jp ?ci ; Utility Subroutines ipchl: ; vectored CALL point jp (hl) ?pmsg: ; print message @ up to a null ; saves & push bc push de pmsg$loop: ld a,(hl) or a jr z,pmsg$exit ld c,a push hl call ?cono pop hl inc hl jr pmsg$loop pmsg$exit: pop de pop bc ret ?pdec: ; print binary number 0-65535 from ld bc,table10 ld de,-10000 next: ld a,'0'-1 pdecl: push hl inc a add hl,de jp nc,stoploop inc sp inc sp jp pdecl stoploop: push de push bc ld c,a call ?cono pop bc pop de nextdigit: pop hl ld a,(bc) ld e,a inc bc ld a,(bc) ld d,a inc bc ld a,e or d jp nz,next ret table10: dw -1000,-100,-10,-1,0 ?pderr: ld hl,drive$msg call ?pmsg ; error header ld a,(@adrv) add a,'A' ld c,a call ?cono ; drive code ld hl,track$msg call ?pmsg ; track header ld hl,(@trk) call ?pdec ; track number ld hl,sector$msg call ?pmsg ; sector header ld hl,(@sect) call ?pdec ; sector number ret ; BNKSEL ; Bank Select. Select CPU bank for further execution. bnksel: ld (@cbnk),a ; remember current bank jp ?bank ; and go exit through users ; physical bank select routine xofflist: db -1,-1,-1,-1,-1,-1,-1,-1 ; ctl-s clears to zero db -1,-1,-1,-1,-1,-1,-1,-1 dseg ; following resides in banked memory ; Disk I/O interface routines ; SELDSK ; Select Disk Drive. Drive code in . ; Invoke login procedure for drive ; if this is first select. Return ; address of disk parameter header ; in seldsk: ld a,c ; save drive select code ld (@adrv),a ld b,0 ; create index from drive code ld hl,@dtbl add hl,bc ; get pointer to dispatch table add hl,bc ld a,(hl) inc hl ld h,(hl) ld l,a ; point at disk descriptor ld (@xdph),hl ; save descriptor pointer or h ret z ; if no entry in table, no disk ld a,e ; examine login bit and 1 ret nz push ix ld ix,(@xdph) ld a,(ix-2) ld (@RDRV),a ; get relative drive ld l,(ix-6) ; get address of LOGIN routine ld h,(ix-5) ex (sp),ix pop de call ipchl ; call LOGIN ld hl,(@xdph) ; recover DPH pointer ret ; HOME ; Home selected drive. Treated as SETTRK(0). home: ld bc,0 ; same as set track zero ; SETTRK ; Set Track. Saves track address from ; in @TRK for further operations. settrk: ld (@trk),bc ret ; SETSEC ; Set Sector. Saves sector number from ; in @sect for further operations. setsec: ld (@sect),bc ret ; SETDMA ; Set Disk Memory Address. Saves DMA address ; from in @DMA and sets @DBNK to @CBNK ; so that further disk operations take place ; in current bank. setdma: ld (@dma),bc ld a,(@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: ld (@dbnk),a ret ; SECTRN ; Sector Translate. Indexes skew table in ; with sector in . Returns physical sector ; in . If no skew table (=0) then ; returns physical=logical. sectrn: ld l,c ld h,b ld a,d or e ret z ex de,hl add hl,bc ld l,(hl) ld h,0 ret ; READ ; Read physical record from currently selected drive. ; Finds address of proper read routine from ; extended disk parameter header (XDPH). read: ld ix,(@xdph) ; get drive descriptor pointer ld l,(ix-8) ; get read routine entry ld h,(ix-7) jp (hl) ; WRITE ; Write physical sector from currently selected drive. ; Finds address of proper write routine from ; extended disk parameter header (XDPH). write: ld ix,(@xdph) ; get drive descriptor pointer ld l,(ix-10) ; get write routine entry ld h,(ix- 9) jp (hl) ; MULTIO ; Set multiple sector count. Saves passed count in ; @CNT multio: ld (@cnt),a ret ; FLUSH ; BIOS deblocking buffer flush. Not implemented. flush: xor 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 ; do not change order. sd driver depends on this @xdph: ds 2 ; pointer to currently selected drives dph @adrv: ds 1 ; currently selected disk drive @rdrv: ds 1 ; controller relative disk drive @cnt: db 0 ; record count for multisector transfer @trk: ds 2 ; current track number @sect: ds 2 ; current sector number @dma: ds 2 ; current DMA address @dbnk: db 0 ; bank for DMA operations cseg ; common memory @cbnk: db 0 ; bank for processor operations end