; ; Assemble with Z80MR the z80 macro assembler from Micro Cornucopia ; though M80 can handle it if you add an ASEG and .Z80 and change ; .LOW. to LOW ; Z80MR XBIOS ; Integrate into your System Image ; MOVCPM 63 * ; save 40 xcpm.com ; ddt xcpm.com ; ixbios.hex ; or ixbios8s.hex or ixbios8d.com ; r2980 ; ^C ; sysgen (when asked for a source drive answer with a carriage ; return and then choose a destination drive) ; The new bios will be loaded on the next cold start (reset) ; .z80 false equ 0 true equ not false ; ; Set for type of assembler used ; m80 equ true z80mr equ false ; ;## Conditionals for destination ("A") drive type ######################### ; ONLY ONE OF THESE SHOULD BE TRUE Sing8 equ FALSE ; Eight inch single density Doub8 equ FALSE ; Eight inch double density Doub5 equ TRUE ; Five inch double density (Kaypro Format) qpm equ true ; TRUE if QP/M, FALSE if CP/M ferg equ false ; TRUE if Ferguson board ; system MUST come up mapped as follows: ; logical physical byte value ; 0 C 0CH ; 1 D 1DH ; 2 E 2EH ; 3 F 3FH ;NOTE: PLUS2 does this for you already ; ;!!!!!!!!!!!!! Warning !!!!!!!!!!!!!!! ; ; SYstem MUST be set at least TWO pages (512 bytes) lower ; to accomodate the Ferguson transfer buffer & other stuff ; ; It is possible to EXCEED the max BIOS size of 400h ; ;## Conditionals for printer driver ####################################### ; ; Printer driver handled from printer driver code byte ; ; If you need other printer drivers, you will need to muck around ; with the code ; ; ;## Xerox port information ; ptsioc equ 07h ; serial printer port b status/control ptsiod equ 05h ; serial printer port b data sysdat equ 01ch ; system control port bauda equ 0 ; baud rate port a baudb equ 0Ch ; baud rate port b ;## Misc Conditionals ##################################################### HiBit equ FALSE ; set to true to enable translation of ; keys with high bit set (msb). slodrv equ FALSE ; TRUE slows 'reads' down for disk drives ; with slow settling times (older 5 1/4's) user31 equ true ; TRUE for QP/M 31 user area reference ; (qpm MUST also be true) usrloc equ 000Bh ;location for 31 user location hold ;should be clear of drive/user flag & motor flg iobyte equ 3 ; logical to physical map maxdsk equ 4 ; max disk # ;## CP/M system size variables ############################################ ; ; Max size for PLUS2 is 62k ; msize equ 62 ; system memory size in k moff equ 0 ; 1/4k increment: 0, 1, 2, or 3 for ; 0.00, 0.25, 0.5, and 0.75 k bias equ (msize-20)*1024+moff*256 ; bias for systems ; larger than 20k qcp equ 3400H+bias ; start of QCP qdos equ qcp+806H ; start of QDOS bios equ qcp+1600H ; start of Basic I/O Subsystem (BIOS) qpml equ bios-qcp ; length of QP/M system in bytes (less BIOS) nsects equ qpml/128 ; length of QP/M system in sectors(less BIOS) IF Sing8 trksec equ 27 ; sectors/track + 1 nexsec equ 1 ; first sector on track 1 firsec equ 1 ; first sector on track ENDIF ;Sing8 IF Doub8 trksec equ 64 ; sectors per track nexsec equ 1 ; doesn't matter firsec equ 0 ; first sector on track ENDIF ;Doub8 IF doub5 trksec equ 40 ; sectors per track nexsec equ 16 ; first sector on track 1 firsec equ 0 ; first sector on track ENDIF ;doub5 IF m80 aseg org 100h .phase bios ELSE org bios ENDIF ;m80 jp boot ; arrive here from cold start jp wboot ; arrive here for warm start jp const ; console status return in A FF=ready, 00=not jp conin ; console char in jp conout ; console char out jp list ; listing char out jp punch ; punch char out jp reader ; reader char in jp home ; move to track 0 on selected disk drive jp seldsk ; select disk drive jp settrk ; set track # jp setsec ; set sector # jp setdma ; set DMA address jp read ; read selected sector jp write ; write selected sector jp listst ; list status (Ready to print a char) jp sectran ; sector translate jp getdt ; right in the bios - get time/date ; ioconfig: defb 00000001B ; initial value for i/o byte (may be patched) wrtsafe: defb FALSE ; write safe flag bdrata: defb 7 ;1200 for both bdratb: defb 7 serprt: db 0 ;serial printer type protocol: ; 0=none, 1=xon/xoff, 2=etx/ack parprt: db 0 ;parallel printer type: 0=normal, ; 1=single port A, 2=single port B ; IF HiBit vtab: defb ' ',3,' ' ; vector pad xlate table defb 8,' *+ -.' defb 12 ; setup for Keytronic kbd (FRANKLIN ACE) defb '0123456789 ' defb 27,' ',10 ENDIF ;HiBit ; ; Cold boot entry point, set up system pointers and pass control to the QCP boot: ld a,(ioconfig) ; init value for i/o byte ld (iobyte),a ld a,(bdrata) ; set baud rates out (bauda),a ld a,(bdratb) out (baudb),a ld a,(parprt) ;see if need to initialize parallel printer or a jr z,nopint ;initialize single port parallel printer nopint: xor a ; clear system disk number ld (4),a IF user31 ; only makes sense if user 31 ld (usrloc),a ENDIF ;user31 ; ; Ferguson code ; IF ferg call print db 1ah,'Era M?',0 call conin res 5,a cp 'Y' jr nz,noera di xor a ;swap in page 0 ld (0ffffh),a ld hl,0 ld de,1 ld (hl),0e5h ld bc,32*32 ;direc entries ldir ld a,0ch ld (0ffffh),a ;back to top page ei ENDIF ;ferg noera: call print db 26,13,10 db 'Xpro 820+2 (MICROCode) v2.6' db 13,10,0 call const ; flush boot charater from keyboard or a call nz,conin jr goccp ; ; Warm boot entry point, re-load the QCP and QDOS ; wboot: call diskint ; ; I REFUSE to put this junk in! (normally) ; call print db 13,10,'Boot',13,10,0 wb0: ld sp,100H ; re-set stack ld c,0 ; select drive A: call seldsk IF DOUB8 ld bc,1 ELSE ld bc,0 ; set track ENDIF ;DOUB8 call settrk ld hl,qcp ; first memory location to load ld bc,nsects*256+(firsec+1) wb1: push hl push bc ld b,h ld c,l call setdma pop bc push bc ; save sector count and current sector call setsec ; select sector call read pop bc pop hl or a jr nz,wb0 ; oops, error on warm boot ; ld de,128 ; new dma address add hl,de dec b jr z,goccp ; done loading inc c ; bump sector count ld a,trksec ; on to next track? cp c jr nz,wb1 ; ld c,nexsec ; first sector on next track push bc ; save counters push hl ld c,1 call settrk ; set track pop hl pop bc jr wb1 ; goccp: ld a,0C3H ; set up CP/M jumps to bdos and wboot ld hl,bios+3 ; wboot entry point ld (0),a ld (1),hl ld hl,qdos ; entry point to qdos ld (5),a ld (6),hl ld a,(4) ; last logical disk unit used ld c,a ; pass to ccp to select and 0fh ; make sure this is valid drive IF ferg cp 'm'-'a' jr z,okdsk0 ENDIF cp maxdsk jr c,okdsk0 ld a,c and 0f0h ld c,a ; okdsk0: IF qpm ;make sure clock is set call getdt ld a,(hl) or a jr nz,isokdt ; ld hl,tdprg ;time/date prog ld de,qcp+7 ld bc,tdprge-tdprg ;size ldir ;move it in jp qcp ;assume A0 ; tdprg: db 6,'SETCLK' ; set Emerald Microware clock tdprge equ $ ; MUST be capitals isokdt: ENDIF ;qpm ld a,3 ;so can do auto-boot on cold start ld hl,qcp qcpval equ $-2 ld (qcpval),a jp (hl) IF ferg dphm: dw 0 ;dphm dw 0 dw 0 dw 0 dw dirm ;dirbuf dw dpbm dw 0 dw alloc dpbm: dw 16*8 ;16k * 8 sectors/k db 3 db 7 db 0 dw (256-64)-1 ;max size dw 31 ;32 dir entries db 80h,00h ;pre-alloc for dir dw 0 ;no check dw 0 ;or offset ENDIF ;ferg ; I/O devices are selected using the IOBYTE ; logical devices are con: rdr: pun: and lst: ; physical devices are: ; crt: video and kbd ; tty: serial ; lpt: centronics parallel ; ul1: serial with cts as busy ; pun: same as ul1 ; ;con: tty, crt bits 0,1 ;rdr: tty bits 2,3 ;pun: tty, pun bits 4,5 ;lst: tty, crt, lpt, ul1 bits 6,7 getdt: ld l,4bh jr callrom const: ld l,2ah jr callrom conin: ld l,2dh jr callrom ;; call callrom IF HiBit or a ; if msb set translate character ret p ; msb not set AND 01FH ; FORM TABLE INDEX TO VTAB ld hl,vtab ld c,a ld b,0 add hl,bc ld a,(hl) ; pick up xlated character ENDIF ;HiBit ;; and 07FH ; strip bit 7 ret reader: ret punch: ret diskint: ld l,03H ; re-set disk software sub-system jr callrom home: ld l,0CH ; home disk drive rom routine jr callrom sectran: ld l,21H ; xlate logical to physical sector jr callrom settrk: IF ferg ld a,c ld (mtrack),a ENDIF ;ferg ld l,12H ; seek track jr callrom setsec: IF ferg ld a,c ld (msect),a ENDIF ;ferg ld l,15H ; set sector number jr callrom setdma: IF ferg ld (mdma),bc ENDIF ;ferg ld l,18H ; set dma address jr callrom conout: ld l,45h ; video output ; ; put here since conout is most frequently called of any ; callrom: ld (savsp),sp ; save current stack (may be under rom) ld sp,stack ; set a local stack di exx ; save cp/m arguments in a,(sysdat) ; turn rom on or 80h ; is faster than set out (sysdat),a ld de,biosret ; rom to "RET" here push de exx ; restore cp/m arguments and call loc ei ld h,0 ;ROM base at 0000h callhl: jp (hl) ; to routine specified in hl ; biosret: di ex af,af' ; save reg A ld sp,0 ; restore stack savsp equ $-2 in a,(sysdat) and 7fh out (sysdat),a ex af,af' ; restore reg A ei ret ; done with rom routine seldsk: IF ferg ld a,c ld (mdisk),a cp 'M'-'A' ld hl,dphm ret z ENDIF ;ferg IF slodrv ld a,0ffh ; set new disk flag ld (newdsk),a ; store it ENDIF ;slodrv ld l,0FH ; select disk drive jr callrom read: IF ferg ld a,0 mdisk equ $-1 sub 'M'-'A' jr z,frgio ENDIF ;ferg ; IF slodrv ld a,0ffh ; waste some time if its a new drive newdsk equ $-1 or a jr z,pass olp: ld de,80h ; delay loop ilp: dec de ld a,d or e jp nz,ilp djnz olp xor a ld (newdsk),a pass: ENDIF ;slodrv ld l,1BH ; read a logical sector jr callrom write: IF ferg ld a,(mdisk) cp 'M'-'A' jr z,frgout ENDIF ;ferg ld l,1EH ; write a logical sector ld a,(wrtsafe) ; write safe flag or a ; true or false jr z,callrom ; normal operation ld c,1 ; directory write code (forces write op) jr callrom IF ferg frgout: inc a frgio: or a call nz,m64 ;move into high mem di ld a,0 mtrack equ $-1 ld (0ffffh),a ld hl,0 msect equ $-1 ex af,af' srl h rr l ex af,af' ld de,mbuf jr z,rdw2 ex de,hl rdw2: ld bc,128 ldir ld a,0ch ;restore ld (0ffffh),a ei ret nz ;end for write m64: ld hl,mbuf ld de,0 mdma equ $-2 jr z,isrd0 ex de,hl isrd0: ld bc,128 ldir ret ENDIF ;ferg list: ld a,(iobyte) and 0C0H ; strip IOBYTE to lst: devices jr z,serial ; serial on SIO port B if lst:=tty:=00xxxxxxb pioad equ 8 pioac equ 10 piobd equ 9 cp 80h jr z,noxpl listxp: call listst jr z,listxp ld a,c out (pioad),a xor a ;Send strobe out (piobd),a dec a out (piobd),a ret ld l,3FH ; centronics with polled status cp 80H ; as lst:=lpt: (same as Kaypro) jp z,callrom noxpl: ld l,45H ; video cp 40H ; video if lst:=crt:=01xxxxxxb jp z,callrom ld l,39h jp callrom ; if lst:=ul1:=11xxxxxxb ; serial: in a,(ptsioc) ; see if SIO port is busy and 4 jr z,serial ;loop until it's ready ld a,c out (ptsiod),a ;send the character ;; ;;; XON/XOFF ;; ;; call serin ;get serial in char, else 0 if none ;; ret z ;; cp 'S'-'A'+1 ;check for ctrl-s ;; ret nz ;;serwt: call serin ;wait for any char ;; jr z,serwt ;; ;;; etxack ;; ;; cp 0ah ;lf? ;; ret nz ;nope - forget it ;;serwt1: call siost ;get status ;; jr z,serwt1 ;; ld a,3 ;send ETX ;; out (ptsiod),a ;;serwt: call serin ;; jr z,serwt ret serin: in a,(ptsioc) ;else wait for another char and 1 ret z in a,(ptsiod) ;else get data ret listst: ld a,(iobyte) ; check i/o byte and 0C0H jp z,siost ;check status on sio port b cp 80h jr z,giveup in a,(piobd) ;Xerox parallel cable and 10000b xor 10000b ld a,0 ret z ;else fall through to ready ld l,3CH ; centronics cp 80H jp z,callrom giveup: or 0ffh ; 0ffh=ready ret ; siost: in a,(ptsioc) and 4 ; is it ready ret z jr giveup ;is true print: ex (sp),hl ; pop return address, points to text to print ld a,(hl) ; get a byte of text, stop on zero byte inc hl ex (sp),hl ; save new return address or a ; is it a zero byte? ret z ld c,a ; no, so print it call conout jr print ;## I/O initialization table ############################################## ;## First byte is how many to send, next byte is where to send, following # ;## is what to send ####################################################### ;; ;; Int driven par. prnt. ;; ;; defb 3,LSTCNT ;; defb 0fh ;; defb LOW gpvec ;; defb 087h defb 0ffh defb 'MjM' ; to mark end of bios stack equ bios+400h IF ferg alloc equ $ dirm equ alloc+(256-64)/8+2 mbuf equ dirm+128 mbufe equ mbuf+128 ENDIF ;ferg biose equ $ IF (stack-64) lt biose printx / #### Problem, bozo breath #### / ENDIF IF m80 .DEPHASE ENDIF ;m80 end