title 'CP/M V3.0 Loader' ; Copyright (C) 1982 ; Digital Research ; Box 579, Pacific Grove ; California, 93950 ; Revised: ; 01 Nov 82 by Bruce Skidmore base equ $ abase equ base-0100h cr equ 0dh lf equ 0ah fcb equ abase+005ch ;default FCB address buff equ abase+0080h ;default buffer address ; ; System Equates ; resetsys equ 13 ;reset disk system printbuf equ 09 ;print string open$func equ 15 ;open function read$func equ 20 ;read sequential setdma$func equ 26 ;set dma address ; ; Loader Equates ; comtop equ abase+80h comlen equ abase+81h bnktop equ abase+82h bnklen equ abase+83h osentry equ abase+84h cseg lxi sp,stackbot call bootf ;first call is to Cold Boot mvi c,resetsys ;Initialize the System call bdos mvi c,printbuf ;print the sign on message lxi d,signon call bdos mvi c,open$func ;open the CPM3.SYS file lxi d,cpmfcb call bdos cpi 0ffh lxi d,openerr jz error lxi d,buff call setdma$proc call read$proc ;read the load record lxi h,buff lxi d,mem$top mvi c,6 cloop: mov a,m stax d inx d inx h dcr c jnz cloop call read$proc ;read display info mvi c,printbuf ;print the info lxi d,buff call bdos ; ; Main System Load ; ; ; Load Common Portion of System ; lda res$len mov h,a lda mem$top call load ; ; Load Banked Portion of System ; lda bank$len ora a jz execute mov h,a lda bank$top call load ; ; Execute System ; execute: lxi h,fcb+1 mov a,m cpi '$' jnz execute$sys inx h mov a,m cpi 'B' cz break execute$sys: lxi sp,osentry$adr ret ; ; Load Routine ; ; Input: A = Page Address of load top ; H = Length in pages of module to read ; load: ora a ;clear carry mov d,a mvi e,0 mov a,h ral mov h,a ;h = length in records of module loop: xchg lxi b,-128 dad b ;decrement dma address by 128 xchg push d push h call setdma$proc call read$proc pop h pop d dcr h jnz loop ret ; ; Set DMA Routine ; setdma$proc: mvi c,setdma$func call bdos ret ; ; Read Routine ; read$proc: mvi c,read$func ;Read the load record lxi d,cpmfcb ;into address 80h call bdos ora a lxi d,readerr rz ; ; Error Routine ; error: mvi c,printbuf ;print error message call bdos di hlt break: db 0ffh ret cpmfcb: db 0,'CPM3 SYS',0,0,0,0,0,0 dw 0,0,0,0,0,0,0,0,0 openerr: db cr,lf db 'CPMLDR error: failed to open CPM3.SYS' db cr,lf,'$' readerr: db cr,lf db 'CPMLDR error: failed to read CPM3.SYS' db cr,lf,'$' signon: db cr db lf,lf,lf,lf,lf,lf,lf,lf,lf,lf,lf,lf db lf,lf,lf,lf,lf,lf,lf,lf,lf,lf,lf,lf db 'CP/M V3.0 Loader',cr,lf db 'Copyright (C) 1982, Digital Research' db cr,lf,'$' db '021182',0,0,0,0 stackbot: mem$top: ds 1 res$len: ds 1 bank$top: ds 1 bank$len: ds 1 osentry$adr: ds 2 ; title 'CP/M 3.0 LDRBDOS Interface, Version 3.1 Nov, 1982' ;***************************************************************** ;***************************************************************** ;** ** ;** B a s i c D i s k O p e r a t i n g S y s t e m ** ;** ** ;** I n t e r f a c e M o d u l e ** ;** ** ;***************************************************************** ;***************************************************************** ; ; Copyright (c) 1978, 1979, 1980, 1981, 1982 ; Digital Research ; Box 579, Pacific Grove ; California ; ; Nov 1982 ; ; ; equates for non graphic characters ; rubout equ 7fh ; char delete tab equ 09h ; tab char cr equ 0dh ; carriage return lf equ 0ah ; line feed ctlh equ 08h ; backspace ; serial: db 0,0,0,0,0,0 ; ; Enter here from the user's program with function number in c, ; and information address in d,e ; bdos: bdose: ; Arrive here from user programs xchg! shld info! xchg ; info=de, de=info mov a,c! cpi 14! jc bdose2 sta fx ; Save disk function # xra a! sta dircnt lda seldsk! sta olddsk ; Save seldsk bdose2: mov a,e! sta linfo ; linfo = low(info) - don't equ lxi h,0! shld aret ; Return value defaults to 0000 shld resel ; resel = 0 ; Save user's stack pointer, set to local stack dad sp! shld entsp ; entsp = stackptr lxi sp,lstack ; local stack setup lxi h,goback ; Return here after all functions push h ; jmp goback equivalent to ret mov a,c! cpi nfuncs! jnc high$fxs ; Skip if invalid # mov c,e ; possible output character to c lxi h,functab! jmp bdos$jmp ; look for functions 100 -> high$fxs: sbi 100! jc lret$eq$ff ; Skip if function < 100 bdos$jmp: mov e,a! mvi d,0 ; de=func, hl=.ciotab dad d! dad d! mov e,m! inx h! mov d,m ; de=functab(func) lhld info ; info in de for later xchg xchg! pchl ; dispatched ; dispatch table for functions functab: dw func$ret, func1, func2, func3 dw func$ret, func$ret, func6, func$ret dw func$ret, func9, func10, func11 diskf equ ($-functab)/2 ; disk funcs dw func12,func13,func14,func15 dw func16,func17,func18,func19 dw func20,func21,func22,func23 dw func24,func25,func26,func27 dw func28,func29,func30,func31 dw func32,func33,func34,func35 dw func36,func37,func38,func39 dw func40,func42,func43 dw func44,func45,func46,func47 dw func48,func49,func50 nfuncs equ ($-functab)/2 entsp: ds 2 ; entry stack pointer ; 40 level stack dw 0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h dw 0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h dw 0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h dw 0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h dw 0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h lstack: page title 'CP/M 3.0 LDRBDOS Interface, Version 3.1 July, 1982' ;***************************************************************** ;***************************************************************** ;** ** ;** B a s i c D i s k O p e r a t i n g S y s t e m ** ;** ** ;** C o n s o l e P o r t i o n ** ;** ** ;***************************************************************** ;***************************************************************** ; ; July, 1982 ; ; ; console handlers conout: ;compute character position/write console char from C ;compcol = true if computing column position lda compcol! ora a! jnz compout ;write the character, then compute the column ;write console character from C push b ;recall/save character call conoutf ;externally, to console pop b ;recall the character compout: mov a,c ;recall the character ;and compute column position lxi h,column ;A = char, HL = .column cpi rubout! rz ;no column change if nulls inr m ;column = column + 1 cpi ' '! rnc ;return if graphic ;not graphic, reset column position dcr m ;column = column - 1 mov a,m! ora a! rz ;return if at zero ;not at zero, may be backspace or eol mov a,c ;character back to A cpi ctlh! jnz notbacksp ;backspace character dcr m ;column = column - 1 ret notbacksp: ;not a backspace character, eol? cpi lf! rnz ;return if not ;end of line, column = 0 mvi m,0 ;column = 0 ret ; ; tabout: ;expand tabs to console mov a,c! cpi tab! jnz conout ;direct to conout if not ;tab encountered, move to next tab pos tab0: mvi c,' '! call conout ;another blank lda column! ani 111b ;column mod 8 = 0 ? jnz tab0 ;back for another if not ret ; print: ;print message until M(BC) = '$' LXI H,OUTDELIM ldax b! CMP M! rz ;stop on $ ;more to print inx b! push b! mov c,a ;char to C call tabout ;another character printed pop b! jmp print ; ; func2: equ tabout ;write console character with tab expansion ; func9: ;write line until $ encountered xchg ;was lhld info mov c,l! mov b,h ;BC=string address jmp print ;out to console ; sta$ret: ;store the A register to aret sta aret func$ret: ret ;jmp goback (pop stack for non cp/m functions) ; setlret1: ;set lret = 1 mvi a,1! jmp sta$ret ; func1: equ func$ret ; func3: equ func$ret ; func6: equ func$ret ; func10: equ func$ret func11: equ func$ret ; ; data areas ; compcol:db 0 ;true if computing column position ; end of BDOS Console module ;********************************************************************** ;***************************************************************** ; ; Error Messages md equ 24h err$msg: db cr,lf,'BDOS ERR: ',md err$select: db 'Select',md err$phys: db 'Perm.',md ;***************************************************************** ;***************************************************************** ; ; common values shared between bdosi and bdos aret: ds 2 ; address value to return lret equ aret ; low(aret) ;***************************************************************** ;***************************************************************** ;** ** ;** b a s i c d i s k o p e r a t i n g s y s t e m ** ;** ** ;***************************************************************** ;***************************************************************** ; literal constants true equ 0ffh ; constant true false equ 000h ; constant false enddir equ 0ffffh ; end of directory byte equ 1 ; number of bytes for "byte" type word equ 2 ; number of bytes for "word" type ; fixed addresses in low memory tbuff equ 0080h ; default buffer location ; error message handlers sel$error: ; report select error lxi b,err$msg call print lxi b,err$select jmp goerr1 goerr: lxi b,err$msg call print lxi b,err$phys goerr1: call print di ! hlt bde$e$bde$m$hl: mov a,e! sub l! mov e,a mov a,d! sbb h! mov d,a rnc! dcr b! ret bde$e$bde$p$hl: mov a,e! add l! mov e,a mov a,d! adc h! mov d,a rnc! inr b! ret shl3bv: inr c shl3bv1: dcr c! rz dad h! adc a! jmp shl3bv1 compare: ldax d! cmp m! rnz inx h! inx d! dcr c! rz jmp compare ; ; local subroutines for bios interface ; move: ; Move data length of length c from source de to ; destination given by hl inr c ; in case it is zero move0: dcr c! rz ; more to move ldax d! mov m,a ; one byte moved inx d! inx h ; to next byte jmp move0 selectdisk: ; Select the disk drive given by register D, and fill ; the base addresses curtrka - alloca, then fill ; the values of the disk parameter block mov c,d ; current disk# to c ; lsb of e = 0 if not yet logged - in call seldskf ; hl filled by call ; hl = 0000 if error, otherwise disk headers mov a,h! ora l! rz ; Return with C flag reset if select error ; Disk header block address in hl mov e,m! inx h! mov d,m! inx h ; de=.tran inx h ! inx h shld curtrka! inx h! inx h ; hl=.currec shld curreca! inx h! inx h ; hl=.buffa inx h! inx h inx h! inx h ; de still contains .tran xchg! shld tranv ; .tran vector lxi h,dpbaddr ; de= source for move, hl=dest mvi c,addlist! call move ; addlist filled ; Now fill the disk parameter block lhld dpbaddr! xchg ; de is source lxi h,sectpt ; hl is destination mvi c,dpblist! call move ; data filled ; Now set single/double map mode lhld maxall ; largest allocation number mov a,h ; 00 indicates < 255 lxi h,single! mvi m,true ; Assume a=00 ora a! jz retselect ; high order of maxall not zero, use double dm mvi m,false retselect: ; C flag set indicates successful select stc ret home: ; Move to home position, then offset to start of dir call homef xra a ; constant zero to accumulator lhld curtrka! mov m,a! inx h! mov m,a ; curtrk=0000 lhld curreca! mov m,a! inx h! mov m,a ; currec=0000 inx h! mov m,a ; currec high byte=00 ret pass$arecord: lxi h,arecord mov e,m! inx h! mov d,m! inx h! mov b,m ret rdbuff: ; Read buffer and check condition call pass$arecord call readf ; current drive, track, sector, dma diocomp: ; Check for disk errors ora a! rz mov c,a cpi 3! jc goerr mvi c,1! jmp goerr seekdir: ; Seek the record containing the current dir entry lhld dcnt ; directory counter to hl mvi c,dskshf! call hlrotr ; value to hl mvi b,0! xchg lxi h,arecord mov m,e! inx h! mov m,d! inx h! mov m,b ret seek: ; Seek the track given by arecord (actual record) lhld curtrka! mov c,m! inx h! mov b,m ; bc = curtrk push b ; s0 = curtrk lhld curreca! mov e,m! inx h! mov d,m inx h! mov b,m ; bde = currec lhld arecord! lda arecord+2! mov c,a ; chl = arecord seek0: mov a,l! sub e! mov a,h! sbb d! mov a,c! sbb b push h ; Save low(arecord) jnc seek1 ; if arecord >= currec then go to seek1 lhld sectpt! call bde$e$bde$m$hl ; currec = currec - sectpt pop h! xthl! dcx h! xthl ; curtrk = curtrk - 1 jmp seek0 seek1: lhld sectpt! call bde$e$bde$p$hl ; currec = currec + sectpt pop h ; Restore low(arecord) mov a,l! sub e! mov a,h! sbb d! mov a,c! sbb b jc seek2 ; if arecord < currec then go to seek2 xthl! inx h! xthl ; curtrk = curtrk + 1 push h ; save low (arecord) jmp seek1 seek2: xthl! push h ; hl,s0 = curtrk, s1 = low(arecord) lhld sectpt! call bde$e$bde$m$hl ; currec = currec - sectpt pop h! push d! push b! push h ; hl,s0 = curtrk, ; s1 = high(arecord,currec), s2 = low(currec), ; s3 = low(arecord) xchg! lhld offset! dad d mov b,h! mov c,l! shld track call settrkf ; call bios settrk routine ; Store curtrk pop d! lhld curtrka! mov m,e! inx h! mov m,d ; Store currec pop b! pop d! lhld curreca! mov m,e! inx h! mov m,d inx h! mov m,b ; currec = bde pop b ; bc = low(arecord), de = low(currec) mov a,c! sub e! mov l,a ; hl = bc - de mov a,b! sbb d! mov h,a call shr$physhf mov b,h! mov c,l lhld tranv! xchg ; bc=sector#, de=.tran call sectran ; hl = tran(sector) mov c,l! mov b,h ; bc = tran(sector) shld sector call setsecf ; sector selected lhld curdma! mov c,l! mov b,h! jmp setdmaf shr$physhf: lda physhf! mov c,a! jmp hlrotr ; file control block (fcb) constants empty equ 0e5h ; empty directory entry recsiz equ 128 ; record size fcblen equ 32 ; file control block size dirrec equ recsiz/fcblen ; directory fcbs / record dskshf equ 2 ; log2(dirrec) dskmsk equ dirrec-1 fcbshf equ 5 ; log2(fcblen) extnum equ 12 ; extent number field maxext equ 31 ; largest extent number ubytes equ 13 ; unfilled bytes field namlen equ 15 ; name length reccnt equ 15 ; record count field dskmap equ 16 ; disk map field nxtrec equ fcblen ; utility functions for file access dm$position: ; Compute disk map position for vrecord to hl lxi h,blkshf! mov c,m ; shift count to c lda vrecord ; current virtual record to a dmpos0: ora a! rar! dcr c! jnz dmpos0 ; a = shr(vrecord,blkshf) = vrecord/2**(sect/block) mov b,a ; Save it for later addition mvi a,8! sub m ; 8-blkshf to accumulator mov c,a ; extent shift count in register c lda extval ; extent value ani extmsk dmpos1: ; blkshf = 3,4,5,6,7, c=5,4,3,2,1 ; shift is 4,3,2,1,0 dcr c! jz dmpos2 ora a! ral! jmp dmpos1 dmpos2: ; Arrive here with a = shl(ext and extmsk,7-blkshf) add b ; Add the previous shr(vrecord,blkshf) value ; a is one of the following values, depending upon alloc ; bks blkshf ; 1k 3 v/8 + extval * 16 ; 2k 4 v/16+ extval * 8 ; 4k 5 v/32+ extval * 4 ; 8k 6 v/64+ extval * 2 ; 16k 7 v/128+extval * 1 ret ; with dm$position in a getdma: lhld info! lxi d,dskmap! dad d! ret getdm: ; Return disk map value from position given by bc call getdma dad b ; Index by a single byte value lda single ; single byte/map entry? ora a! jz getdmd ; Get disk map single byte mov l,m! mov h,b! ret ; with hl=00bb getdmd: dad b ; hl=.fcb(dm+i*2) ; double precision value returned mov a,m! inx h! mov h,m! mov l,a! ret index: ; Compute disk block number from current fcb call dm$position ; 0...15 in register a sta dminx mov c,a! mvi b,0! call getdm ; value to hl shld arecord! mov a,l! ora h! ret atran: ; Compute actual record address, assuming index called ; arecord = shl(arecord,blkshf) lda blkshf! mov c,a lhld arecord! xra a! call shl3bv shld arecord! sta arecord+2 shld arecord1 ; Save low(arecord) ; arecord = arecord or (vrecord and blkmsk) lda blkmsk! mov c,a! lda vrecord! ana c mov b,a ; Save vrecord & blkmsk in reg b & blk$off sta blk$off lxi h,arecord! ora m! mov m,a! ret getexta: ; Get current extent field address to hl lhld info! lxi d,extnum! dad d ; hl=.fcb(extnum) ret getrcnta: ; Get reccnt address to hl lhld info! lxi d,reccnt! dad d! ret getfcba: ; Compute reccnt and nxtrec addresses for get/setfcb call getrcnta! xchg ; de=.fcb(reccnt) lxi h,(nxtrec-reccnt)! dad d ; hl=.fcb(nxtrec) ret getfcb: ; Set variables from currently addressed fcb call getfcba ; addresses in de, hl mov a,m! sta vrecord ; vrecord=fcb(nxtrec) xchg! mov a,m! sta rcount ; rcount=fcb(reccnt) call getexta ; hl=.fcb(extnum) lda extmsk ; extent mask to a ana m ; fcb(extnum) and extmsk sta extval ret setfcb: ; Place values back into current fcb call getfcba ; addresses to de, hl mvi c,1 lda vrecord! add c! mov m,a ; fcb(nxtrec)=vrecord+seqio xchg! lda rcount! mov m,a ; fcb(reccnt)=rcount ret hlrotr: ; hl rotate right by amount c inr c ; in case zero hlrotr0: dcr c! rz ; return when zero mov a,h! ora a! rar! mov h,a ; high byte mov a,l! rar! mov l,a ; low byte jmp hlrotr0 hlrotl: ; Rotate the mask in hl by amount in c inr c ; may be zero hlrotl0: dcr c! rz ; return if zero dad h! jmp hlrotl0 set$cdisk: ; Set a "1" value in curdsk position of bc lda seldsk push b ; Save input parameter mov c,a ; Ready parameter for shift lxi h,1 ; number to shift call hlrotl ; hl = mask to integrate pop b ; original mask mov a,c! ora l! mov l,a mov a,b! ora h! mov h,a ; hl = mask or rol(1,curdsk) ret test$vector: lda seldsk mov c,a! call hlrotr mov a,l! ani 1b! ret ; non zero if curdsk bit on getdptra: ; Compute the address of a directory element at ; positon dptr in the buffer lhld buffa! lda dptr ; hl = hl + a add l! mov l,a! rnc ; overflow to h inr h! ret clr$ext: ; fcb ext = fcb ext & 1fh call getexta! mov a,m! ani 0001$1111b! mov m,a! ret subdh: ; Compute hl = de - hl mov a,e! sub l! mov l,a! mov a,d! sbb h! mov h,a ret get$buffa: push d! lxi d,10! dad d mov e,m! inx h! mov d,m xchg! pop d! ret rddir: ; Read a directory entry into the directory buffer call seek$dir lda phymsk! ora a! jz rddir1 mvi a,3 call deblock$dir! jmp setdata rddir1: call setdir ; directory dma shld buffa! call seek call rdbuff ; directory record loaded setdata: ; Set data dma address lhld dmaad! jmp setdma ; to complete the call setdir: ; Set directory dma address lhld dirbcba call get$buffa setdma: ; hl=.dma address to set (i.e., buffa or dmaad) shld curdma! ret end$of$dir: ; Return zero flag if at end of directory, non zero ; if not at end (end of dir if dcnt = 0ffffh) lxi h,dcnt mov a,m ; may be 0ffh inx h! cmp m ; low(dcnt) = high(dcnt)? rnz ; non zero returned if different ; high and low the same, = 0ffh? inr a ; 0ffh becomes 00 if so ret set$end$dir: ; Set dcnt to the end of the directory lxi h,enddir! shld dcnt! ret read$dir: ; Read next directory entry, with c=true if initializing lhld dirmax! xchg ; in preparation for subtract lhld dcnt! inx h! shld dcnt ; dcnt=dcnt+1 ; while(dirmax >= dcnt) call subdh ; de-hl jc set$end$dir ; not at end of directory, seek next element ; initialization flag is in c lda dcnt! ani dskmsk ; low(dcnt) and dskmsk mvi b,fcbshf ; to multiply by fcb size read$dir1: add a! dcr b! jnz read$dir1 ; a = (low(dcnt) and dskmsk) shl fcbshf sta dptr ; ready for next dir operation ora a! rnz ; Return if not a new record push b ; Save initialization flag c call rd$dir ; Read the directory record pop b ; Recall initialization flag ret compext: ; Compare extent# in a with that in c, return nonzero ; if they do not match push b ; Save c's original value push psw! lda extmsk! cma! mov b,a ; b has negated form of extent mask mov a,c! ana b! mov c,a ; low bits removed from c pop psw! ana b ; low bits removed from a sub c! ani maxext ; Set flags pop b ; Restore original values ret get$dir$ext: ; Compute directory extent from fcb ; Scan fcb disk map backwards call getfcba ; hl = .fcb(vrecord) mvi c,16! mov b,c! inr c! push b ; b=dskmap pos (rel to 0) get$de0: pop b dcr c xra a ; Compare to zero get$de1: dcx h! dcr b ; Decr dskmap position cmp m! jnz get$de2 ; fcb(dskmap(b)) ~= 0 dcr c! jnz get$de1 ; c = 0 -> all blocks = 0 in fcb disk map get$de2: mov a,c! sta dminx lda single! ora a! mov a,b jnz get$de3 rar ; not single, divide blk idx by 2 get$de3: push b! push h ; Save dskmap position & count mov l,a! mvi h,0 ; hl = non-zero blk idx ; Compute ext offset from last non-zero ; block index by shifting blk idx right ; 7 - blkshf lda blkshf! mov d,a! mvi a,7! sub d mov c,a! call hlrotr! mov b,l ; b = ext offset lda extmsk! cmp b! pop h! jc get$de0 ; Verify computed extent offset <= extmsk call getexta! mov c,m cma! ani maxext! ana c! ora b ; dir ext = (fcb ext & (~ extmsk) & maxext) | ext offset pop b ; Restore stack ret ; a = directory extent search: ; Search for directory element of length c at info lhld info! shld searcha ; searcha = info mov a,c! sta searchl ; searchl = c call set$end$dir ; dcnt = enddir call home ; to start at the beginning searchn: ; Search for the next directory element, assuming ; a previous call on search which sets searcha and ; searchl mvi c,false! call read$dir ; Read next dir element call end$of$dir! jz lret$eq$ff ; not end of directory, scan for match lhld searcha! xchg ; de=beginning of user fcb call getdptra ; hl = buffa+dptr lda searchl! mov c,a ; length of search to c mvi b,0 ; b counts up, c counts down mov a,m! cpi empty! jz searchn searchloop: mov a,c! ora a! jz endsearch ; Scan next character if not ubytes mov a,b! cpi ubytes! jz searchok ; not the ubytes field, extent field? cpi extnum ; may be extent field jz searchext ; Skip to search extent ldax d sub m! ani 7fh ; Mask-out flags/extent modulus jnz searchn ; Skip if not matched jmp searchok ; matched character searchext: ldax d ; Attempt an extent # match push b ; Save counters mov c,m ; directory character to c call compext ; Compare user/dir char pop b ; Recall counters ora a ; Set flag jnz searchn ; Skip if no match searchok: ; current character matches inx d! inx h! inr b! dcr c jmp searchloop endsearch: ; entire name matches, return dir position xra a sta lret ; lret = 0 ; successful search - ; return with zero flag reset mov b,a! inr b ret lret$eq$ff: ; unsuccessful search - ; return with zero flag set ; lret,low(aret) = 0ffh mvi a,255 ! mov b,a ! inr b ! jmp sta$ret open: ; Search for the directory entry, copy to fcb mvi c,namlen! call search rz ; Return with lret=255 if end ; not end of directory, copy fcb information open$copy: call getexta ! mov a,m ! push a ; save extent to check for extent ; folding - move moves entire dir FCB call getdptra! xchg ; hl = .buff(dptr) lhld info ; hl=.fcb(0) mvi c,nxtrec ; length of move operation call move ; from .buff(dptr) to .fcb(0) ; Note that entire fcb is copied, including indicators call get$dir$ext! mov c,a pop a ! mov m,a ; restore extent ; hl = .user extent#, c = dir extent# ; above move set fcb(reccnt) to dir(reccnt) ; if fcb ext < dir ext then fcb(reccnt) = fcb(reccnt) | 128 ; if fcb ext = dir ext then fcb(reccnt) = fcb(reccnt) ; if fcb ext > dir ext then fcb(reccnt) = 0 set$rc: ; hl=.fcb(ext), c=dirext mvi b,0 xchg! lxi h,(reccnt-extnum)! dad d ldax d! sub c! jz set$rc2 mov a,b! jnc set$rc1 mvi a,128! mov b,m set$rc1: mov m,a! mov a,b! sta actual$rc! ret set$rc2: sta actual$rc mov a,m! ora a! rnz ; ret if rc ~= 0 lda dminx! ora a! rz ; ret if no blks in fcb lda fx! cpi 15! rz ; ret if fx = 15 mvi m,128 ; rc = 128 ret restore$rc: ; hl = .fcb(extnum) ; if actual$rc ~= 0 then rcount = actual$rc push h lda actual$rc! ora a! jz restore$rc1 lxi d,(reccnt-extnum)! dad d mov m,a! xra a! sta actual$rc restore$rc1: pop h! ret open$reel: ; Close the current extent, and open the next one ; if possible. call getexta mov a,m! mov c,a inr c! call compext jz open$reel3 mvi a,maxext! ana c! mov m,a ; Incr extent field mvi c,namlen! call search ; Next extent found? ; not end of file, open call open$copy open$reel2: call getfcb ; Set parameters xra a! sta vrecord! jmp sta$ret ; lret = 0 open$reel3: inr m ; fcb(ex) = fcb(ex) + 1 call get$dir$ext! mov c,a ; Is new extent beyond dir$ext? cmp m! jnc open$reel4 ; no dcr m ; fcb(ex) = fcb(ex) - 1 jmp set$lret1 open$reel4: call restore$rc call set$rc! jmp open$reel2 seqdiskread: ; Sequential disk read operation ; Read the next record from the current fcb call getfcb ; sets parameters for the read lda vrecord! lxi h,rcount! cmp m ; vrecord-rcount ; Skip if rcount > vrecord jc recordok ; not enough records in the extent ; record count must be 128 to continue cpi 128 ; vrecord = 128? jnz setlret1 ; Skip if vrecord<>128 call open$reel ; Go to next extent if so ; Check for open ok lda lret! ora a! jnz setlret1 ; Stop at eof recordok: ; Arrive with fcb addressing a record to read call index ; Z flag set if arecord = 0 jz setlret1 ; Reading unwritten data ; Record has been allocated call atran ; arecord now a disk address lda phymsk! ora a ; if not 128 byte sectors jnz read$deblock ; go to deblock call setdata ; Set curdma = dmaad call seek ; Set up for read call rdbuff ; Read into (curdma) jmp setfcb ; Update FCB curselect: lda seldsk! inr a! jz sel$error dcr a! lxi h,curdsk! cmp m! rz ; Skip if seldsk = curdsk, fall into select select: ; Select disk info for subsequent input or output ops mov m,a ; curdsk = seldsk mov d,a ; Save seldsk in register D for selectdisk call lhld dlog! call test$vector ; test$vector does not modify DE mov e,a! push d ; Send to seldsk, save for test below call selectdisk! pop h ; Recall dlog vector jnc sel$error ; returns with C flag set if select ok ; Is the disk logged in? dcr l ; reg l = 1 if so rz ; yes - drive previously logged in lhld dlog! mov c,l! mov b,h ; call ready call set$cdisk! shld dlog ; dlog=set$cdisk(dlog) ret set$seldsk: lda linfo! sta seldsk! ret reselectx: xra a! sta high$ext! jmp reselect1 reselect: ; Check current fcb to see if reselection necessary mvi a,80h! mov b,a! dcr a! mov c,a ; b = 80h, c = 7fh lhld info! lxi d,7! xchg! dad d mov a,m! ana b ; fcb(7) = fcb(7) & 7fh mov a,m! ana c! mov m,a ; high$ext = 80h & fcb(8) inx h! mov a,m! ana b! sta high$ext ; fcb(8) = fcb(8) & 7fh mov a,m! ana c! mov m,a ; fcb(ext) = fcb(ext) & 1fh call clr$ext ; if fcb(rc) & 80h ; then fcb(rc) = 80h, actual$rc = fcb(rc) & 7fh ; else actual$rc = 0 call getrcnta! mov a,m! ana b! jz reselect1 mov a,m! ana c! mov m,b reselect1: sta actual$rc lxi h,0 shld fcbdsk ; fcbdsk = 0 mvi a,true! sta resel ; Mark possible reselect lhld info! mov a,m ; drive select code ani 1$1111b ; non zero is auto drive select dcr a ; Drive code normalized to 0..30, or 255 sta linfo ; Save drive code cpi 0ffh! jz noselect ; auto select function, seldsk saved above mov a,m! sta fcbdsk ; Save drive code call set$seldsk noselect: call curselect mvi a,0 ! lhld info ! mov m,a ret ; ; individual function handlers ; func12 equ func$ret func13: ; Reset disk system - initialize to disk 0 lxi h,0! shld dlog xra a! sta seldsk dcr a! sta curdsk lxi h,tbuff! shld dmaad ; dmaad = tbuff jmp setdata ; to data dma address func14: ; Select disk info call set$seldsk ; seldsk = linfo jmp curselect func15: ; Open file call reselectx call open! call openx ; returns if unsuccessful, a = 0 ret openx: call end$of$dir! rz call getfcba! mov a,m! inr a! jnz openxa dcx d! dcx d! ldax d! mov m,a openxa: ; open successful pop h ; Discard return address mvi c,0100$0000b ret func16 equ func$ret func17 equ func$ret func18 equ func$ret func19 equ func$ret func20: ; Read a file call reselect jmp seqdiskread func21 equ func$ret func22 equ func$ret func23 equ func$ret func24 equ func$ret func25: lda seldsk ! jmp sta$ret func26: xchg ! shld dmaad jmp setdata func27 equ func$ret func28: equ func$ret func29 equ func$ret func30 equ func$ret func31 equ func$ret func32 equ func$ret func33 equ func$ret func34 equ func$ret func35 equ func$ret func36 equ func$ret func37 equ func$ret func38 equ func$ret func39 equ func$ret func40 equ func$ret func42 equ func$ret func43 equ func$ret func44 equ func$ret func45 equ func$ret func46 equ func$ret func47 equ func$ret func48 equ func$ret func49 equ func$ret func50 equ func$ret func100 equ func$ret func101 equ func$ret func102 equ func$ret func103 equ func$ret func104 equ func$ret func105 equ func$ret func106 equ func$ret func107 equ func$ret func108 equ func$ret func109 equ func$ret goback: ; Arrive here at end of processing to return to user lda fx! cpi 15! jc retmon lda olddsk! sta seldsk ; Restore seldsk lda resel! ora a! jz retmon lhld info! mvi m,0 ; fcb(0)=0 lda fcbdsk! ora a! jz goback1 ; Restore fcb(0) mov m,a ; fcb(0)=fcbdsk goback1: ; fcb(8) = fcb(8) | high$ext inx h! lda high$ext! ora m! mov m,a ; fcb(rc) = fcb(rc) | actual$rc call getrcnta! lda actual$rc! ora m! mov m,a ; return from the disk monitor retmon: lhld entsp! sphl lhld aret! mov a,l! mov b,h ret ; ; data areas ; dlog: dw 0 ; logged-in disks curdma ds word ; current dma address buffa: ds word ; pointer to directory dma address ; ; curtrka - alloca are set upon disk select ; (data must be adjacent, do not insert variables) ; (address of translate vector, not used) cdrmaxa:ds word ; pointer to cur dir max value (2 bytes) curtrka:ds word ; current track address (2) curreca:ds word ; current record address (3) drvlbla:ds word ; current drive label byte address (1) lsn$add:ds word ; login sequence # address (1) ; +1 -> bios media change flag (1) dpbaddr:ds word ; current disk parameter block address checka: ds word ; current checksum vector address alloca: ds word ; current allocation vector address dirbcba:ds word ; dir bcb list head dtabcba:ds word ; data bcb list head hash$tbla: ds word ds byte addlist equ $-dpbaddr ; address list size ; ; buffer control block format ; ; bcb format : drv(1) || rec(3) || pend(1) || sequence(1) || ; 0 1 4 5 ; ; track(2) || sector(2) || buffer$add(2) || ; 6 8 10 ; ; link(2) ; 12 ; ; sectpt - offset obtained from disk parm block at dpbaddr ; (data must be adjacent, do not insert variables) sectpt: ds word ; sectors per track blkshf: ds byte ; block shift factor blkmsk: ds byte ; block mask extmsk: ds byte ; extent mask maxall: ds word ; maximum allocation number dirmax: ds word ; largest directory number dirblk: ds word ; reserved allocation bits for directory chksiz: ds word ; size of checksum vector offset: ds word ; offset tracks at beginning physhf: ds byte ; physical record shift phymsk: ds byte ; physical record mask dpblist equ $-sectpt ; size of area ; ; local variables ; blk$off: ds byte ; record offset within block dir$cnt: ds byte ; direct i/o count tranv: ds word ; address of translate vector linfo: ds byte ; low(info) dminx: ds byte ; local for diskwrite actual$rc: ds byte ; directory ext record count single: ds byte ; set true if single byte allocation map olddsk: ds byte ; disk on entry to bdos rcount: ds byte ; record count in current fcb extval: ds byte ; extent number and extmsk vrecord:ds byte ; current virtual record curdsk: adrive: db 0ffh ; current disk arecord:ds word ; current actual record ds byte arecord1: ds word ; current actual block# * blkmsk ;******** following variable order critical ***************** high$ext: ds byte ; fcb high ext bits ;xfcb$read$only: ds byte ; local variables for directory access dptr: ds byte ; directory pointer 0,1,2,3 ; ; local variables initialized by bdos at entry ; fcbdsk: ds byte ; disk named in fcb phy$off: ds byte curbcba: ds word track: ds word sector: ds word read$deblock: mvi a,1! call deblock$dta jmp setfcb column db 0 outdelim: db '$' dmaad: dw 0080h seldsk: db 0 info: dw 0 resel: db 0 fx: db 0 dcnt: dw 0 searcha: dw 0 searchl: db 0 ; ************************** ; Blocking/Deblocking Module ; ************************** deblock$dir: lhld dirbcba jmp deblock deblock$dta: lhld dtabcba deblock: ; BDOS Blocking/Deblocking routine ; a = 1 -> read command ; a = 2 -> write command ; a = 3 -> locate command ; a = 4 -> flush command ; a = 5 -> directory update push a ; Save z flag and deblock fx ; phy$off = low(arecord) & phymsk ; low(arecord) = low(arecord) & ~phymsk call deblock8 lda arecord! mov e,a! ana b! sta phy$off mov a,e! ana c! sta arecord shld curbcba! call getbuffa! shld curdma call deblock9 ; Is command flush? pop a! push a! cpi 4 jnc deblock1 ; yes ; Is referenced physical record ;already in buffer? call compare! jz deblock45 ; yes xra a deblock1: call deblock10 ; Read physical record buffer mvi a,2! call deblock$io call deblock9 ; phypfx = adrive || arecord call move! mvi m,0 ; zero pending flag deblock45: ; recadd = phybuffa + phy$off*80h lda phy$off! inr a! lxi d,80h! lxi h,0ff80h deblock5: dad d! dcr a! jnz deblock5 xchg! lhld curdma! dad d ; If deblock command = locate ; then buffa = recadd; return pop a! cpi 3! jnz deblock6 shld buffa! ret deblock6: xchg! lhld dmaad! lxi b,80h ; If deblock command = read jmp move$tpa ; then move to dma deblock8: lda phymsk! mov b,a! cma! mov c,a! ret deblock9: lhld curbcba! lxi d,adrive! mvi c,4! ret deblock10: lxi d,4 deblock11: lhld curbcba! dad d! ret deblock$io: ; a = 0 -> seek only ; a = 1 -> write ; a = 2 -> read push a! call seek pop a! dcr a cp rdbuff ; Move track & sector to bcb call deblock10! inx h! inx h lxi d,track! mvi c,4! jmp move org base+((($-base)+255) and 0ff00h)-1 db 0 ; Bios equates bios$pg equ $ bootf equ bios$pg+00 ; 00. cold boot conoutf equ bios$pg+12 ; 04. console output function homef equ bios$pg+24 ; 08. disk home function seldskf equ bios$pg+27 ; 09. select disk function settrkf equ bios$pg+30 ; 10. set track function setsecf equ bios$pg+33 ; 11. set sector function setdmaf equ bios$pg+36 ; 12. set dma function sectran equ bios$pg+48 ; 16. sector translate movef equ bios$pg+75 ; 25. memory move function readf equ bios$pg+39 ; 13. read disk function move$out equ movef move$tpa equ movef end