; mp/m basic input/output operating system (bios) ; ; ; true equ 0ffffh ;define value of true. false equ not true ;define value of false. ; msize equ 48 ;memory size in kbytes. ndisks equ 1 ;number of disk drives vers equ 22 ;cp/m version number bvers equ 18 ;bios version number onedsk equ not((((ndisks-1)+3)and 4)shr 2) if onedsk disk4 equ false disk3 equ false endif if not onedsk disk4 equ (ndisks and 0000$0100b) shr 2 disk3 equ ndisks and 0000$0001b endif dubsid equ false ;true for double sided drives. intrp equ false ;set true for intrrupts allowed. COLCNT EQU TRUE ;SET TRUE FOR NETRONICS TERMINAL cpl equ 32 ;characters/line llimit equ cpl ;characters/line ; 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. ; plci0 equ 3 plco0 equ 2 pldsk equ 1 pllpt equ 0 poll equ 131 ; 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 cbase equ 12032 ;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 nmbcns equ 1 org 000h pdisp equ $-3 xdos equ pdisp-3 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 coldstart ;from cold start loader. wboote: jmp wboot ;from warm boot. jmp constd ;check console kb status. jmp conind ;read console char. jmp conotd ;write console char. jmp list ;write listing char. jmp rtnempty ;write punch char. jmp rtnempty ;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 pollpt ;return list status. jmp sectran jmp selmemory jmp polldevice jmp startclock jmp stopclock jmp exitregion jmp maxconsole jmp systeminit db 0 jmp idle coldstart: warmstart: mvi c,0 jmp xdos ; ; data byte for number of disk drives ; nodsks: db ndisks ; ; ; fixed data tables for tw0-drive standard ; ibm-compatible 8" disks ; ; disk parameter header for disk 00 dpbase: dw trans,0000h dw 0000h,0000h dw dirbf,dpblk dw chk00,all00 ; disk parameter header for disk 01 dw trans,0000h dw 0000h,0000h dw dirbf,dpblk dw chk01,all01 if disk3 or disk4 ; disk parameter header for disk 02 dw trans,0000h dw 0000h,0000h dw dirbf,dpblk dw chk02,all02 endif if disk4 ; disk parameter header for disk 03 dw trans,0000h dw 0000h,0000h dw dirbf,dpblk dw chk03,all03 endif ; ; ; sector translate vector ; trans: 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 ; dpblk: ;disk parameter block, common to all disks dw spt ;sectors per track if not dubsid db 3 ;block shift factor 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 endif if dubsid 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 endif dw off ;track offset ; ; end of fixed tables ; ; ; signon message ; smsg: db 12,10,13 db 'Explorer-85 ' db 10,13 db'Multi processor' db 10,13 db'Time-Sharing' db 10,13 db'Computer System' if dubsid endif db 10,13 db 10,13 db 10,13 db 0 ; ; boot ; this section is executed whenever reset and run ; after the coldstart loader reads in ; the cp/m system. ; STAT51 EQU 0E1H cntl51 equ 0E1H rxd51 equ 0E0H mode51 equ 0ceH cmd51 equ 27H boot: mvi a,mode51 out cntl51 OUT CNTL51+2 mvi a,cmd51 out cntl51 OUT CNTL51+2 in rxd51 IN RXD51+2 boot1: if intrp ;if interrupts allowed, ei ;enable them here. endif xra a ;clear scratch area. sta cdisk 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. lxi h,smsg ;print signon message. call pmsg gocpm: ; ; set jumps into cp/m in lower memory. ; 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 lda cdisk ;get disk number to mov c,a ;pass to ccp in c. jmp bootr ;jump to ccp. ; rderr: lxi h,btmsg ;get address of "boot error". call pmsg ;print it. call conin ;read a char from console. ; ; warm-boot: read all of cp/m back in ; except bios, then jump to ccp. ; wboot: lxi sp,80h ;set stack pointer. if intrp ;if interrupts allowed, ei ;allow them here. endif lda diskno ;save disk no. sta temp mvi c,0 ;select disk zero. call seldsk call home ;move to track zero. jnz rderr ;if error, print message. jmp aldon 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 aldon ;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. aldon: lda temp ;restore disk no. if intrp ;if interrupts allowed, ei ;allow them again here. endif sta diskno jmp xdos ;go back to cp/m. constd: call ptbljmp dw pt0st conind: call ptbljmp dw pt0in conotd: call ptbljmp dw pt0out ptbljmp: mov a,d cpi nmbcns jc tbljmp pop psw rtnempty: xra a ret tbljmp: add a pop h mov e,a mvi d,0 dad d mov e,m inx h mov d,m xchg pchl polci0: pt0st: jmp const pt0in: mvi c,poll mvi e,plci0 call xdos jmp conin pt0out: in cstat ani cptr jnz co0rdy push b call pt0wait pop b co0rdy: jmp conot pt0wait: mvi c,poll mvi e,plco0 jmp xdos ret polco0: in cstat ani cptr rz mvi a,0ffh ret list: call ltbsy ora a jnz lptrdy push b mvi c,poll mvi e,pllpt call xdos pop b lptrdy: mov a,c out ldata ret pollpt: jmp ltbsy nmbdev equ 4 polldevice: mov a,c cpi nmbdev jc devok mvi a,nmbdev devok: call tbljmp dw pollpt dw poldsk dw polco0 dw polci0 dw rtnempty selmemory: ret startclock: mvi a,0ffh sta tickn ret stopclock: xra a sta tickn ret exitregion: lda preemp ora a rnz ei ret maxconsole: mvi a,nmbcns ret systeminit: call boot1 bootr: ei push psw push b push d push h mvi a,0c3h sta 0020h lxi h,intlhnd shld 0021h mvi a,9fh out 0efh pop psw pop b pop d pop h ret idle: ei hlt ret flagset equ 133 dsptch equ 142 intlhnd: push psw mvi a,9fh out 0efh lda slice dcr a sta slice jz t16ms pop psw ei ret t16ms: mvi a,16 sta slice pop psw shld svdhl pop h shld svdret push psw lxi h,0 dad sp shld svdsp lxi sp,intstk+48 push d push b mvi a,0ffh sta preemp lda tickn ora a jz notickn mvi c,flagset mvi e,1 call xdos notickn: lxi h,cnt64 dcr m jnz notlsec mvi m,57h mvi c,flagset mvi e,2 call xdos notlsec: xra a sta preemp pop b pop d lhld svdsp sphl pop psw lhld svdret push h lhld svdhl jmp pdisp ; ; slice: db 16 cnt64: db 64 intstk: ds 48 svdhl: dw 0 svdsp: dw 0 svdret: dw 0 tickn: db 0 preemp: db 0 ; 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: IF COLCNT push h call conota ;output the char lxi h,lcount ;get char. counter mov a,c ;get char. CPI 12 ;IS IT FORM FEED JNZ ISITCR ;JP TO ISITCR PUSH B PUSH H MVI B,2 LXI H,0 WAIT1: DCR L JNZ WAIT1 DCR H JNZ WAIT1 DCR B JNZ WAIT1 POP H POP B 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 conxit mvi c,13 ;echo a 'cr' call conota mvi c,10 ;and a 'lf' call conota slimit: mvi m,llimit ;reset counter conxit: 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 ; ; read paper tape ; reader: mvi a,1ah ;cp/m eof returned for them. ; ; punch paper tape. ; punch: ret ;don't service them, just return. ; ; ; ; lisk 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: lxi h,0000h ;error return code di nop ;call check nop nop nop nop nop ;call rdfacil nop nop nop nop lda nodsks ;get no. of dsks if not onedsk dcr a endif cmp c rc ;err ret if diskno > nodsks new$old: mov a,c ;get new dsk no. lxi h,diskno ;get adr of old dsk no. cmp m ;new = old? jz getdph ;yes, return = dph if onedsk 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: 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) nop ;call rlfacil nop nop nop nop nop ret ;return from seldsk. SELECT$TABLE: DB 0 DB 6 DB 10 DB 18 ; rdfacil: push psw push b push d push h mvi a,000h lxi h,store mov m,a pop psw pop b pop d pop h di ret store: db 0ffh poldsk: lxi h,store mov a,m ret rlfacil: push psw push h mvi a,0ffh lxi h,store mov m,a pop psw pop h ret check: push psw push b push d push h mvi c,poll mvi e,pldsk call xdos pop psw pop b pop d pop h di ret ; 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: if not dubsid ;if not double-sided dsk push h ;save h&l. lhld latch ;get new & old latch. mov a,l ;get new latch. out dcont ;select drv. now. sta clatch ;remember current latch. cmp h ;is it same as old? mvi a,0ffh ;if not, set flag = ff jnz sflag cma ;if new = old, flag = 0. sflag: sta hlsf ;set head-load/select flag. pop h ;restore h&l. endif mov a,c ;get new track no. if dubsid ;if double-sided dsk rrc ;shift right once push psw ;save revised trk # push h lhld latch ;get new & old latch mov a,l ;put new latch in a jc side2 ;if trk # odd, side #2 ani 1eh ;clear latch bit for side 1 jmp setlat ;go ahead and set latch side2: ori 1 ;set latch bit for side 2 setlat: 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 pop h pop psw ani 7fh ;clear msb mov c,a ;set c = trk # endif 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. ; ; 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$retry ;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 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 ;se| 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$retry ;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 retry subroutine ; rw$retry: 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 'Boot ' errmsg: db 'Error.',0 rdmsg: db 0dh,0ah,'Read ',0 wtmsg: db 0dh,0ah,'Write ',0 skmsg: db 0dh,0ah,'Seek ',0 if onedsk mntmsg: db 0dh,0ah,'Mount ',0 endif ; 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 ndisks 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 ;err/r count for retries sercnt: ds 1 ;seek retry counter temp: ds 1 ;temporary storage ; ; scratch ram area for bdos use ; begdat equ $ ;beginning of data area dirbf: ds 128 ;scratch directory area if not dubsid all00: ds 31 ;allocation vector 0 chk00: ds 16 ;check vector 0 all01: ds 31 ;allocation vector 1 chk01: ds 16 ;check vector 1 endif if disk3 or disk4 and not dubsid all02: ds 31 ;allocation vector 2 chk02: ds 16 ;check vector 2 endif if disk4 and not dubsid all03: ds 31 ;allocation vector 3 chk03: ds 16 ;check vector 3 endif if dubsid all00: ds 32 ;allocation vector 0 chk00: ds 32 ;check vector 0 all01: ds 32 ;allocation vector 1 chk01: ds 32 ;check vector 1 endif if disk3 or disk4 and dubsid all02: ds 32 ;allocation vector 2 chk02: ds 32 ;check vector 2 endif if disk4 and dubsid all03: ds 32 ;allocation vector 3 chk03: ds 32 ;check vector 3 endif ; enddat equ $ ;end of data area db 0 datsiz equ $-begdat ;size of data area db 0 end