page 0 ; cp/m basic input/output operating system (bios) ; ; 2.2 version of 10-27-80 ; .. copyright 1980 .. ; ; this module contains all the input/output ; routines for the cp/m system, excluding ; the disk routines. ; true equ 0ffffh ;define value of true. false equ not true ;define value of false. ; msize equ 62 ;memory size in kbytes. ndisks equ 2 ;number of disk drives vers equ 22 ;cp/m version number bvers equ 21 ;bios version number ; .18 release version of sept 80 ; .19 fixed four drv problem ; (29-Apr-81) ; .20 added support for patch 3 ; (19-May-81) ; .21 added support for mixed drvs ; (09-Jun-81 ; dubsid equ true sglsid equ not dubsid ;define type of drives drva equ sglsid drvb equ sglsid drvc equ slgsid drvd equ slgsid ; intrp equ false ;set true for intrrupts allowed. colcnt equ false ;true for console col. cntr. adm3a equ false ;true for adm term. hazltn equ true ;true for hazeltine term. netvdb equ false ;true for netronics term. pat1 equ true ;true for usr #'s in cmd prompt pat2 equ true ;true to make usr #0 public to all pat3 equ true ;true to cause CCP of CP/M 2.x ;system to look on drv A when ;you are logged into a drv other ;than A and call for a .COM file ;that does not exist on that drv. onedsk equ not((((ndisks-1)+3)and 4)shr 2) if netvdb cpl equ 64 ;characters/line delay equ 500 ;500 milisec delay for formfd llimit equ cpl ;characters/line endif if hazltn or adm3a delay equ 10 ;10 millisec for formfd cpl equ 80 llimit equ cpl ;characters/line endif if hazltn formfd equ 28 ;clear screen code endif if adm3a or netvdb formfd equ 12 ;clear screen code endif ; cstat equ 0E1H ;console status port ccom equ 0E1H ;console command port cdata equ 0E0H ;console data port lstat equ 0E3H ;list status port. lcom equ 0E3H ;list command port. ldata equ 0E2H ;list data port. ; ; ckbr equ 0000$0010b ;status bit on port 0. cptr equ 0000$0100b ;printer ready bit. lrbit equ cptr ;lister ready bit. ; iobyte equ 3 ;address of i/o byte. cdisk equ 4 ;current selected disk smsg equ 0 ;location of signon message ;after boot up cbase equ (msize-20)*1024 ;bias for larger than 20k. cpmb equ cbase+3400h ;start of cp/m 2.2 bdos equ cpmb+806h ;start of bdos 2.2 bios equ cpmb+1600h ;base of bios 2.2 org bios nsects equ ($-cpmb)/128 ;number of sectors in it. trks equ 77 ;# of tracks/disk spt equ 26 ;sectors/track off equ 2 ;# of reserved system trks ; ; i/o jump vector ; this is where cp/m calls whenever it needs ; to do any input/output operation. ; user programs may use these entry points ; also, but note that the location of this ; vector changes with the memory size. ; jmp boot ;from cold start loader. wboote: jmp wboot ;from warm boot. jmp const ;check console kb status. jmp conin ;read console char. jmp conot ;write console char. jmp list ;write listing char. jmp punch ;write punch char. jmp reader ;read reader char. jmp home ;move disk to zero. jmp seldsk ;select disk drive. jmp settrk ;seek to track in reg a. jmp setsec ;set sector number. jmp setdma ;set disk starting address. jmp read ;read selected sector. jmp write ;write selected sector. jmp ltbsy ;return list status. jmp sectran ;sector translate. ; ; fixed data tables for four drive standard ; ibm-compatible 8" disks ; ; disk parameter header for disk 00 dpbase equ $ dw xlt0,0000h dw 0000h,0000h dw dirbf,dpb0 dw chk00,all00 ; disk parameter header for disk 01 dw xlt1,0000h dw 0000h,0000h dw dirbf,dpb1 dw chk01,all01 ; disk parameter header for disk 02 dw xlt2,0000h dw 0000h,0000h dw dirbf,dpb2 dw chk02,all02 ; disk parameter header for disk 03 dw xlt3,0000h dw 0000h,0000h dw dirbf,dpb3 dw chk03,all03 ; ; ; sector translate vector ; xlt0 equ $ db 1,7,13,19 ;sectors 1,2,3,4 db 25,5,11,17 ;sectors 5,6,7,8 db 23,3,9,15 ;sectors 9,10,11,12 db 21,2,8,14 ;sectors 13,14,15,16 db 20,26,6,12 ;sectors 17,18,19,20 db 18,24,4,10 ;sectors 21,22,23,24 db 16,22 ;sectors 25,26 ; ;disk parameter block, common to all disks ;single sided dpbs: dw spt ;sectors per track db 3 ;block shift mask db 7 ;block mask db 0 ;null mask dw 242 ;disk size-1 dw 63 ;directory max db 192 ;alloc 0 db 0 ;alloc 1 dw 16 ;check size dw off ;track offset ;double sided dpbd: dw spt ;sectors per track db 4 ;block shift factor db 15 ;block mask db 0 ;null mask dw 246 ;disk size-1 dw 127 ;directory max db 192 ;alloc 0 db 0 ;alloc 1 dw 32 ;check size dw off ;track offset if drva dpb0 equ dpbd endif if not drva dpb0 equ dpbs endif if drvb dpb1 equ dpbd endif if not drvb dpb1 equ dpbs ;equivalent parameters endif if drvc dpb2 equ dpbd endif if not drvc dpb2 equ dpbs endif if drvd dpb3 equ dpbd endif if not drvd dpb3 equ dpbs endif xlt1 equ xlt0 xlt2 equ xlt0 xlt3 equ xlt0 ; ; end of fixed tables ; patch equ $ ; ; patch in the bdos to report user number >0 in command ; line prompt (i.e. xa> -- where x is >=1) ; if pat1 cout equ cpmb+8ch ;ccp console output gtusr equ cpmb+113h ;ccp get user # gtdrv equ cpmb+1d0h ;ccp get drv org cpmb+388h ;location of pat1 in bdos call patch1 org (bios-1)-15 ;patch area at end of bdos (16 bytes) patch1: call gtusr ora a jz upa2 ;don't report user #0 cpi 10 jc upa1 ;jmp if usr # = 1 thru 9 jmp patch1a org patch ;this patch finished in bios patch1a: sui 10 push psw mvi a,'1' call cout pop psw ; upa1: adi '0' call cout ; upa2: jmp gtdrv ;now print drv # endif epatch1 equ $ if pat2 and not colcnt bbdos equ bdos-6 org bbdos+75eh ;location of this patch in bdos jmp patch2 ;overlays mvi a,b ; cpi 0dh ; ; bdos patch to make user area #0 public to all user areas ; note: if files of the same name are present on both ; user 0 and the logged-in user number, the open, search ; for next, and read sequential functions will match directory ; entries on a first-come first-served basis. ; this problem can be avoided by adhering to the ; following rules: ;**1). keep on user 0 only commonly used programs ; (files.com) ;**2). set these files to $sys to reduce directory ; clutter ;**3). set these files to $r/o to avoid accidental ; erasure ;**4). never place on any other user number programs ; of the same name ;**5). have turn-key applications cold-boot directly ; into the assigned user number. the user number ; is the high nibble od address 4 ; org epatch1 ;this patch is in bios patch2: mov a,b ora a jnz patch2a ldax d cpi 0e5h jz patch2a mov a,m ora a jz bbdos+77ch patch2a: mov a,b cpi 0dh jmp bbdos+761h endif epatch2 equ $ ; this patch causes the CCP of a CP/M 2.x system to look ; on drive A when you are logged into a drive other than ; A and call for a .COM file that does not exist on that ; drive. giving an explicit drive reference overrides this ; feature, so that you can always force the file to be loaded ; from a specified drive. ; if pat3 org cpmb+6dbh jz patch3 ;replaces "jz cpmb+76bh" ; org cpmb+7f2h ;replaces an used area of NOP's patch3: lxi h,cpmb+7f0h ;get drv from current command ora m ;accum was 0 on entry, so this ;fetches drv jnz cpmb+76bh ;command has explicit drv...errror inr m ;force explicit reference to drv A lxi d,cpmb+7d6h ;we need set up when we jmp cpmb+6cdh ;re-enter CCP endif org epatch2 ; ; boot ; this section is executed whenever reset and run ; after the coldstart loader reads in ; the cp/m system. ; boot: MVI A,0CEH ;INIT I/O OUT 0E1H OUT 0E3H MVI A,27H OUT 0E1H OUT 0E3H in cdata in cdata+2 lxi sp,80h ;set stack pointer. lxi h,smsg ;print signon message. call pmsg xra a ;clear scratch area. sta cdisk sta iobyte ;do it! mvi c,endz-startz ;get length of zero area. lxi h,startz ;get scratch address. bootl: mov m,a ;put zero in memory. inx h ;increment pointer. dcr c ;decrement counter. jnz bootl ;loop till done. mvi m,llimit ;set line limit inx h ;point to latch byte mvi m,0 ;set latch code = 0. lda cdisk ;get disk number to gocpm: ; ; set jumps into cp/m in lower memory. ; if intrp ;if interrupts allowed, ei ;enable them here. endif mov c,a ;pass to ccp in c. mvi a,0c3h ;put jmp to wboot sta 0 ;adr at zero. lxi h,wboote shld 1 sta 5 lxi h,bdos ;put jump to bdos shld 6 ;at adr 5,6,7. lxi h,80h ;set default dma adr. shld dmaadd jmp cpmb ;jump to ccp. ; rderr: lxi h,btmsg ;get address of "boot error". call pmsg ;print it. call conin ;read a char from console. pop psw ;get old dsk back jmp wboot1 ;do 10 more trys ; ; warm-boot: read all of cp/m back in ; except bios, then jump to ccp. ; wboot: lxi sp,80h ;set stack pointer. lda diskno wboot1: push psw if intrp ;if interrupts allowed, ei ;allow them here. endif mvi c,0 ;select disk zero. call seldsk call home ;move to track zero. jnz rderr ;if error, print message. mvi d,nsects ;get # sectors for cp/m read. lxi b,2 ;track (b) = 0, sector (c) = 2. lxi h,cpmb ;get starting address. if intrp ;if intrrupts allowed, di ;disable them here. endif rdblk: push b ;save b&c. mov c,b ;go to track in b. call settrk pop b ;restore b&c. jnz rderr ;if error, print message. rblk1: shld dmaadd ;set starting address. call setsec ;read starting at sector in c. call read jnz rderr ;if error, print message. dcr d ;decrement sector count. jz aldone ;all done when d=0. inr c ;increment sector number. mov a,c ;if sector no. cpi spt+1 ;if not sectors/track + 1 jc rblk1 ;keep reading on this track. mvi c,1 ;otherwise, reset sector = 1. inr b ;increment track no. jmp rdblk ;and read next track. aldone: pop psw ;get saved dsk no mov b,a lda cdisk ani 0f0h ora b jmp gocpm ; ; ; check console input status. ; const: in cstat ;get status ani ckbr ;look at flag bit stat$ret: mvi a,0 ;set a=0, for return rz cma ;if ready (pressed) a=ff ret ; ; read a character from console. ; conin: call const ;get console status ora a jz conin ;not ready yet in cdata ;get data byte mask: ani 7fh ;make most sig. bit = 0. ret ; ; write a character to the console device. ; conot: call conota ;output the char if colcnt push h lxi h,lcount ;get char. counter endif mov a,c ;get char. cpi formfd ;is it form feed if not colcnt rnz endif if colcnt jnz isitcr endif push b push h mvi c,delay and 0ffh ; ; this is the delay routine. it will loop here for the ; number of milliseconds specified in register . ; delays: mvi b,80 ;force delay for 1 millisec <3mhz> del2: nop ;instructions to fill in time dad h ;same dad h ;same dcr b ;at 1 millisecond yet ?? jnz del2 ;no, keep on looping dcr c ;end of request interval yet ?? jnz delays ;no, bake until done pop h pop b if not colcnt ret endif if colcnt jmp slimit isitcr: cpi 13 ;is it /cr/ jz slimit cpi 10 ;is it /lf/ jz lfeed cpi 8 ;is it /bs/ jnz getcnt inr m lfeed: inr m getcnt: dcr m jnz conota-2 mvi c,13 ;echo a /cr/ call conota mvi c,10 ;and a /lf/ call conota slimit: mvi m,llimit ;reset counter pop h ret endif conota: in cstat ;read console status ani cptr jz conota ;loop until ready mov a,c ;get character out cdata ;print it ret ; ; write a character on listing device ; ltbsy: in lstat ;read lister status ani lrbit ;look at ready bit jmp stat$ret list: bsytst: call ltbsy ;check for busy ora a ;set flags jz bsytst ;still busy ;lst:=lpt: mov a,c ;get the char. out ldata ;print it. ret ;return from list. ; ; read paper tape ; reader: mvi a,1ah ;cp/m eof returned for them. ; ; punch paper tape. ; punch: ret ;don't service them, just return. ; ; disk i/o for the western digital 1771 ; controller chip ; ; version of 3-29-80 ; hlab set 8 ;8 for hd ld at beg of seek. stprat set 2 ;rate 1=6ms, 2=10ms, 3=20ms. ; disk equ 0e8h ;disk base address. dcom equ disk ;disk command port. dstat equ disk ;disk status port. track equ disk+1 ;disk track port. sectp equ disk+2 ;disk sector port. ddata equ disk+3 ;disk data port. wait equ disk+4 ;disk wait port. dcont equ disk+4 ;disk control port. rtcnt equ 10 ;retry count. ; ; ; select dsk number according to register c. ; seldsk: if not onedsk lxi h,0 ;error return code mov a,c ;get new dsk no. cpi ndisks rnc ;error dskno > ndisks endif if onedsk mov a,c push a ;save disk number. lxi h,mntmsg ;get adr of mount mess. call pmsg ;print "mount". pop a ;get dsk number. sta diskno ;update old with new. adi 'A' ;add ascii for 'a'. mov c,a ;put into c. call conot ;print it. call conin ;read a character endif if not onedsk selmor: lxi h,diskno ;get adr of old dsk no. mov e,m ;put old dsk no. in d&e. mvi d,0 lxi h,trtab ;get addr. of track table. push h dad d ;add dsk no. to address. in track ;read 1771 track reg. mov m,a ;put into table. mov e,c ;new disk no. in d$e. pop h ;get addr. of track table dad d ;add dsk no. to addr. mov a,m ;get new track no. out track ;put into 1771 track reg. mov a,c ;update old dsk no. sta diskno lxi h,select$table mvi b,0 dad b mov a,m sta latch ;save new latch code. endif getdph: lhld diskno dad h ;*2 dad h ;*4 dad h ;*8 dad h ;*16 lxi d,dpbase dad d ; = dpbase(diskno*16) ret ;return from seldsk. select$table: db 0 or(drva and 1);drv db 6 or(drvb and 1);drv db 10 or(drvc and 1);drv db 18 or(drvd and 1);drv ; ; move dsk to track zero. ; home: mvi c,0 ;seek to track zero. ; ; set track no. to whatever is in register c. ; also perform move to correct track (seek). ; settrk: mov a,c ;get new track no. rrc ;shift right once push h push psw ;save revised trk # lhld latch ;get new & old latch mov a,l ;put new latch in a ani 1 ;is it a double sided drv? mov a,l jnz dsided ;yes call newlatch pop psw mov a,c pop h sta trk ;update old with new. ; ; move head to the track in register a. ; seek: push b ;save b&c. mov b,a ;save destination track. mvi a,rtcnt ;get retry count. sretry: sta sercnt ;store in error counter. in track ;read present track no. mov c,a ;save in c. mov a,c ;delay. cmp b ;same as new track no.? mov a,b ;restore a from b. jnz nothr ;jump if not there. there: pop b ;restore b&c. ret ;return from seek. nothr: ; ; this routine is to allow time for the drive ; tunnel erase to terminate before moving the ; head. the delay is approx. 700 micro-sec. @ ; 4 mhz cpu time, and double this for 2 mhz cpu's. ; push psw ;save accum and flags mvi a,0d0h ;delay count = 208 busy1: dcr a ;decrease count. jnz busy1 ;loop till done pop psw ;restore accum and flags. out ddata ;track to data register. busy: in dstat ;read dsk status. rrc ;look a bit 0. jc busy ;wait till not busy. mvi a,14h+stprat+hlab ;get step rate, do out dcom ;seek with verify. in wait ;wait for intrq. in dstat ;read status. ani 91h ;look at bits. jz there ;ok, if zero. lda sercnt ;get err cnt. dcr a ;decrement cnt. jnz sretry ;retry seek. pop b ;restore b&c lxi h,skmsg ;print "seek". jmp ermsg ;do common messages. dsided: pop psw push psw mov a,l jc setlat ;if trk # odd, side #2 ani 1eh ;clear latch bit for side 1 setlat: call newlatch pop psw ani 7fh ;clear msb mov c,a jmp seek-4 newlatch: out dcont ;output to the latch sta clatch ;set current latch code xra h ;compare old with new ani 1eh ;ignore side bit mvi a,0ffh ;if old not = new, jnz setfl ; set flag = ff cma ;if old = new, flag = 0 setfl: sta hlsf ;set flag zero if same ret ; ; set dsk sector no. ; setsec: mov a,c ;get sector no. sta sect ;put at sect # addr. ret ;return from setsec. ; ; set dsk dma address. ; setdma: mov h,b ;move b&c to h&l. mov l,c shld dmaadd ;put dma addr address. ret ;return from setdma. ; ; translate the sector given by using the ; translate table given by ; sectran: xchg ; = trans dad b ; = trans(sector) mov l,m ; = trans(sector) mvi h,0 ; = trans(sector) ret ;with value in ; ; read the sector at sect, from the present track. ; use starting address at dmaadd. ; read: mvi a,rtcnt ;get retry count. rretry: call rw$com ;store in error ctr. adi 88h ;add code for read sect. reade: out dcom ;send command to 1771. rloop: in wait ;wait for drq or intrq. ora a ;set flags jp rddone ;done if intrq. in ddata ;get byte from dsk. mov m,a ;put byte into memory. inx h ;increment memory pointer. jmp rloop ;keep reading. rddone: in dstat ;read dsk status. if intrp ;if interrupts allowed, ei ;allow again here. endif ani 9dh ;look at error bits. rz ;return if none. call erchk ;check for seek error. lda ercnt ;get error count dcr a ;decrement count. jnz rretry ;try to read again. lxi h,rdmsg ;print "read ". ermsg: call pmsg ;print origin message. ermsg1: lxi h,errmsg ;print "error." call pmsg mvi a,1 ;set for perm err msg ora a ret ; ; erchk - check for record not found error. ; erchk: ani 10h ;if record not found, rz ;return, no errors ; check for seek to correct track, ; and change if necessary. chksk: mvi a,0c4h ;send command to 1771 out dcom ;to read address. in wait ;wait for drq or intrq. in ddata ;read the track address. mov b,a ;save in reg b. chks2: in wait ;wait for intrq. ora a ;set flags. jp chks3 ;done with read adr op. in ddata ;read another byte. jmp chks2 ;do it again. chks3: in dstat ;read dsk status. ora a ;set flags. jz chks4 ;read adr ok if 0. call home ;otherwise, home first. jmp chks5 chks4: mov a,b ;update track reg. out track chks5: lda trk ;get required track no. jmp seek ;move head to it. ; ; write the sector at sect, on the present track. ; use starting address at dmaadd. ; write: mvi a,rtcnt ;get retry count. wretry: call rw$com ;store in error counter. adi 0a8h ;add code for write. write2: out dcom wloop: in wait ;wait for ready. ora a ;set flags. jp wdone ;hop when done. mov a,m ;get byte from mem. out ddata ;write onto dsk. inx h ;increment mem ptr. jmp wloop ;keep writing. wdone: in dstat ;read dsk status. if intrp ;if interrupts allowed, ei ;enable again here. endif ani 0fdh ;look at these bits. rz ;return if no err. call erchk ;check/correct seek err. lda ercnt ;get error count. dcr a ;decrement count. jnz wretry ;try to write again. lxi h,wtmsg ;print "write ". jmp ermsg ; ; read / write common subroutine ; rw$com: sta ercnt ;store in error counter lhld dmaadd ;get starting addr. mvi a,0d0h ;cause interrupt. out dcom xthl ;some delay xthl if intrp ;if interrupts allowed, di ;disable them here. endif lda sect ;get sector no. out sectp ;set sector into 1771. ; ; hdld - get head-load bit if required. ; hdld: lda hlsf ;get head-load flag. ora a ;is a = 0? jz hdld1 ;hop if so. cma ;set a = 0. sta hlsf ;set flag = 0 if not. ; ; if changing to a new drive, perform a seek to ; the same track to allow the head to unload. ; in track ;get present track out ddata ;and tell 1771 about it. mvi a,14h+stprat+hlab ;get the step rate. out dcom ;send it to floppy controller. in wait ;wait for intrq. hdldy: mvi a,4 ;set bit to load head. ret ;return from hdld. hdld1: in dstat ;read 1771 status. ani 20h ;look at hl bit. jz hdldy ;load if not loaded. xra a ;otherwise, a = 0. ret ;return from hdld. ; ; print the message at h&l until a zero. ; pmsg: mov a,m ;get a char. ora a ;if it's zero, rz ;return mov c,a ;otherwise, call conot ;print it. inx h ;increment h&l, jmp pmsg ;and get another. ; ; cbios messages ; btmsg: db 'B' errmsg: db ' Err',0 rdmsg: db 0dh,0ah,'Rd',0 wtmsg: db 0dh,0ah,'Wrt',0 skmsg: db 0dh,0ah,'Sk',0 if onedsk mntmsg: db 0dh,0ah,'Mount ',0 endif ;************************************************ ;*note: because there are only six (6) sectors * ;* available for bios on the second system * ;* track (1) , the last address before this* ;* point should be no greater than bios * ;* starting address + 037f (hex). * ;* this will normally be xd7f (hex). * ;************************************************ ; bios scratch area. ; org $ trk: ds 1 ;current track no. sect: ds 1 ;current sector no. dmaadd: ds 2 ;disk transfer address ; ; the next several bytes, between startz and ; endz, are set to zero at cold boot time. ; org $ startz: ;start of zeroed area. diskno: ds 2 ;disk no. (to cp/m). ; special flags. hlsf: ds 1 ;head-load select flag. ; ; trtab - disk track table - present position of ; heads for up to ndisks drvs ; trtab: ds 4 ;up to 4 drvs endz: ;end of zeroed area. ; org $ lcount: ds 1 ;character counter latch: ds 1 ;new code for latch clatch: ds 1 ;current code in latch. ercnt: ds 1 ;error count for retries sercnt: ds 1 ;seek retry counter ; ; scratch ram area for bdos use ; begdat equ $ ;beginning of data area dirbf: ds 128 ;scratch directory area if not drva all00: ds 31 ;allocation vector 0 chk00: ds 16 ;check vector 0 endif if not drvb all01: ds 31 ;allocation vector 1 chk01: ds 16 ;check vector 1 endif if not drvc all02: ds 31 ;allocation vector 2 chk02: ds 16 ;check vector 2 endif if not drvd all03: ds 31 ;allocation vector 3 chk03: ds 16 ;check vector 3 endif if drva all00: ds 32 ;allocation vector 0 chk00: ds 32 ;check vector 0 endif if drvb all01: ds 32 ;allocation vector 1 chk01: ds 32 ;check vector 1 endif if drvc all02: ds 32 ;allocation vector 2 chk02: ds 32 ;check vector 2 endif if drvd all03: ds 32 ;allocation vector 3 chk03: ds 32 ;check vector 3 endif ; enddat equ $ ;end of data area datsiz equ $-begdat ;size of data area end