title 'Non-Resident Portion of Banked BDOS' ;*************************************************************** ;*************************************************************** ;** ** ;** B a s i c D i s k O p e r a t i n g S y s t e m ** ;** ** ;** N o n - R e s i d e n t P o r t i o n ** ;** B a n k e d B D O S ** ;** ** ;*************************************************************** ;*************************************************************** ;/* ; Copyright (C) 1978,1979,1980 ; Digital Research ; P.O. Box 579 ; Pacific Grove, CA 93950 ; ; Revised: ; 17 Jan 80 by Thomas Rolander ;*/ on equ 0ffffh off equ 00000h ; ctlc equ 03h ;control-c cr equ 0dh ;carriage return lf equ 0ah ;line feed ; ; BDOS call equates dskinit equ 13 dskslct equ 14 dsksetDMA equ 26 setusercode equ 32 ; ; Process Descriptor Offsets pname equ 6 console equ 14 memseg equ 15 thread equ 18 disksetDMA equ 20 diskselect equ 22 diskparams equ 23 drvact equ 28 ; ; System Data Page Offsets userstackflag equ 3 brkvctr equ 48 usrstkregion equ 80 ; ; ; MP/M Data Page Offsets rlros equ 5 thrdrt equ 17 msegtbl equ 150 ; ;************************************************** ;* * ;************************************************** ; org 0000h bnkbdose: jmp bnkbdos bnkbdosinit: pop h! push h! inx h! inx h! inx h lxi d,extjmptbl! mvi c,51! xchg jmp move extjmptbl: reboot: jmp $-$ rlr: jmp $-$ rlradr: jmp $-$ dsplynm: jmp $-$ xprint: jmp $-$ xcrlf: jmp $-$ conoutx: jmp $-$ getmemseg: jmp $-$ coninf: jmp $-$ ;console input function homef: jmp $-$ ;disk home function seldskf: jmp $-$ ;select disk function settrkf: jmp $-$ ;set track function setsecf: jmp $-$ ;set sector function setdmaf: jmp $-$ ;set DMA function readf: jmp $-$ ;read disk function writef: jmp $-$ ;write disk function sectran: jmp $-$ ;sector translate function ; ;************************************************ ;* * ;************************************************ ; bnkbdos: push d! push b ;perform the necessary BDOS parameter initialization ;disk set DMA call rlr! lxi b,disksetDMA! dad b mov e,m! inx h! mov d,m! push h! xchg! shld dmaad mov b,h! mov c,l! call setdmaf ;disk select pop h! inx h! push h mov a,m! rrc! rrc! rrc! rrc! ani 0fh! mov e,a mvi c,dskslct! lda curdsk! cmp e! cnz bdose ;set user code pop h! push h mov a,m! ani 0fh! mov e,a mvi c,setusercode! lda usrcode! cmp e! cnz bdose ;copy local disk params pop h! inx h push h! lxi d,dcnt! call copy3bytes ;perform requested BDOS function pop h! pop b! pop d! push h call bdose ;save results pop d! push h ; save results in HL ;copy disk params lxi h,dcnt! call copy3bytes ;return to non-banked bdos, will restore calling bank ; and release disk mutual exclusion message pop h! ret bdose: xchg! shld info! xchg ;save parameter mov a,e! sta linfo ;linfo = low(info) - don't equ lxi h,0! shld aret ;return value defaults to 0000 xra a! sta fcbdsk! sta resel ;fcbdsk,resel=false lxi h,goback! push h ;fake return address ;compute disk function code address mov a,c! mov c,e! mov b,d lxi h,diskf-24! mov e,a! mvi d,0 ;DE=func, HL=.diskf-24 dad d! dad d! mov e,m! inx h! mov d,m ;DE=functab(func) xchg! pchl ;dispatched ; ; dispatch table for disk functions diskf: 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 nfuncs equ ($-diskf)/2 ; ; cmnret: ret ; ; error subroutines per$error: ;report permanent error lxi h,permsg! jmp report$err ;to report the error ; sel$error: ;report select error lxi h,selmsg! jmp report$err ;wait console before boot ; rod$error: ;report write to read/only disk lxi h,rodmsg! jmp report$err ;wait console ; rof$error: ;report read/only file lxi h,rofmsg ;drop through to wait for console ; report$err: ;report error to console, message address in HL push h ;stack msg address ;set D=console # call rlr! lxi d,console! dad d! mov d,m call xcrlf ;new line lda curdsk! adi 'A'! sta dskerr ;current disk name lxi b,dskmsg! call xprint ;the error message pop b! call xprint ;error mssage tail jmp reboot ;terminate process ;ret ; ; error messages dskmsg: db 'Bdos Err On ' dskerr: db ' : $' ;filled in by errflg permsg: db 'Bad Sector$' selmsg: db 'Select$' rofmsg: db 'File ' rodmsg: db 'R/O$' ; ; ;**************************************************************** ;**************************************************************** ; ; common values shared between bdosi and bdos usrcode:db 0 ;current user number curdsk: db 0 ;current disk number info: ds 2 ;information address 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 ** ;** ** ;**************************************************************** ;**************************************************************** ; dvers equ 22h ;version 2.2 ; module addresses ; ; 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 ; ; local subroutines for bios interface ; copy3bytes: ;copy 3 bytes, HL=source, DE=destination (move used) mvi c,3! xchg ;call move ;ret 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 curdsk, and fill ;the base addresses curtrka - alloca, then fill ;the values of the disk parameter block ;lsb of e = 0 if not logged in lda curdsk! mov c,a ;current disk# to c call seldskf ;HL filled by call ;HL = 0000 if error, otherwise disk headers mov a,h! ora l! rz ;return with 0000 in HL and z flag ;disk header block address in hl mov e,m! inx h! mov d,m! inx h ;DE=.tran shld cdrmaxa! inx h! inx h ;.cdrmax shld curtrka! inx h! inx h ;HL=.currec shld curreca! inx h! inx h ;HL=.buffa ;DE still contains .tran xchg! shld tranv ;.tran vector lxi h,buffa ;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: mvi a,true! ora a! ret ;select disk function ok ; home: ;move to home position, then offset to start of dir call homef ;move to track 00, sector 00 reference lxi h,offset! mov c,m! inx h! mov b,m! call settrkf ;first directory position selected 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 ;curtrk, currec both set to 0000 ret ; rdbuff: ;read buffer and check condition call readf ;current drive, track, sector, dma ora a! jnz per$error ret ; wrbuff: ;write buffer and check condition ;write type (wrtype) is in register C ;wrtype = 0 => normal write operation ;wrtype = 1 => directory write operation ;wrtype = 2 => start of new block call writef ;current drive, track, sector, dma ora a! jnz per$error ret ; seekdir: ;seek the record containing the current dir entry lhld dcnt ;directory counter to HL mvi c,dskshf! call hlrotr ;value to HL shld arecord! shld drec ;ready for seek ;call seek ;ret ; seek: ;seek the track given by arecord (actual record) ;local equates for registers arech equ b! arecl equ c ;arecord = BC crech equ d! crecl equ e ;currec = DE ctrkh equ h! ctrkl equ l ;curtrk = HL tcrech equ h! tcrecl equ l ;tcurrec = HL ;load the registers from memory lxi h,arecord! mov arecl,m! inx h! mov arech,m lhld curreca ! mov crecl,m! inx h! mov crech,m lhld curtrka ! mov a,m! inx h! mov ctrkh,m! mov ctrkl,a ;loop while arecord < currec seek0: mov a,arecl! sub crecl! mov a,arech! sbb crech jnc seek1 ;skip if arecord >= currec ;currec = currec - sectpt push ctrkh! lhld sectpt mov a,crecl! sub l! mov crecl,a mov a,crech! sbb h! mov crech,a pop ctrkh ;curtrk = curtrk - 1 dcx ctrkh jmp seek0 ;for another try seek1: ;look while arecord >= (t:=currec + sectpt) push ctrkh lhld sectpt! dad crech ;HL = currec+sectpt jc seek2 mov a,arecl! sub tcrecl! mov a,arech! sbb tcrech jc seek2 ;skip if t > arecord ;currec = t xchg ;curtrk = curtrk + 1 pop ctrkh! inx ctrkh jmp seek1 ;for another try seek2: pop ctrkh ;arrive here with updated values in each register push arech! push crech! push ctrkh ;to stack for later ;stack contains (lowest) BC=arecord, DE=currec, HL=curtrk xchg! lhld offset! dad d ;HL = curtrk+offset mov b,h! mov c,l! call settrkf ;track set up ;note that BC - curtrk is difference to move in bios pop d ;recall curtrk lhld curtrka! mov m,e! inx h! mov m,d ;curtrk updated ;now compute sector as arecord-currec pop crech ;recall currec lhld curreca! mov m,crecl! inx h! mov m,crech pop arech ;BC=arecord, DE=currec mov a,arecl! sub crecl! mov arecl,a mov a,arech! sbb crech! mov arech,a lhld tranv! xchg ;BC=sector#, DE=.tran call sectran ;HL = tran(sector) mov c,l! mov b,h ;BC = tran(sector) jmp setsecf ;sector selected ;ret ; ; file control block (fcb) constants empty equ 0e5h ;empty directory entry lstrec equ 127 ;last record# in extent recsiz equ 128 ;record size fcblen equ 32 ;file control block size dirrec equ recsiz/fcblen ;directory elts / 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 modnum equ 14 ;data module number maxmod equ 15 ;largest module number fwfmsk equ 80h ;file write flag is high order modnum namlen equ 15 ;name length reccnt equ 15 ;record count field dskmap equ 16 ;disk map field lstfcb equ fcblen-1 nxtrec equ fcblen ranrec equ nxtrec+1;random record field (2 bytes) ; ; reserved file indicators rofile equ 9 ;high order of first type char invis equ 10 ;invisible file in dir command ; equ 11 ;reserved ; ; 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 ; getdm: ;return disk map value from position given by BC lhld info ;base address of file control block lxi d,dskmap! dad d ;HL =.diskmap 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! mvi h,0! ret ;with HL=00bb getdmd: dad b ;HL=.fcb(dm+i*2) ;double precision value returned mov e,m! inx h! mov d,m! xchg! ret ; index: ;compute disk block number from current fcb call dm$position ;0...15 in register A mov c,a! mvi b,0! call getdm ;value to HL shld arecord! ret ; allocated: ;called following index to see if block allocated lhld arecord! mov a,l! ora h! ret ; atran: ;compute actual record address, assuming index called lda blkshf ;shift count to reg A lhld arecord atran0: dad h! dcr a! jnz atran0 ;shl(arecord,blkshf) shld arecord1 ;save shifted block # lda blkmsk! mov c,a ;mask value to C lda vrecord! ana c ;masked value in A ora l! mov l,a ;to HL shld arecord ;arecord=HL or (vrecord and blkmsk) ret ; getexta: ;get current extent field address to A lhld info! lxi d,extnum! dad d ;HL=.fcb(extnum) ret ; getfcba: ;compute reccnt and nxtrec addresses for get/setfcb lhld info! lxi d,reccnt! dad d! 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 lda seqio cpi 2! jnz setfcb1! xra a ;check ranfill setfcb1: mov c,a ;=1 if sequential i/o 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 ; compute$cs: ;compute checksum for current directory buffer mvi c,recsiz ;size of directory buffer lhld buffa ;current directory buffer xra a ;clear checksum value computecs0: add m! inx h! dcr c ;cs=cs+buff(recsiz-C) jnz computecs0 ret ;with checksum in A ; 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 push b ;save input parameter lda curdsk! 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 ; nowrite: ;return true if dir checksum difference occurred lhld rodsk! lda curdsk! mov c,a! call hlrotr mov a,l! ani 1b! ret ;non zero if nowrite ; set$ro: ;set current disk to read only lxi h,rodsk! mov c,m! inx h! mov b,m call set$cdisk ;sets bit to 1 shld rodsk ;high water mark in directory goes to max lhld dirmax! inx h! xchg ;DE = directory max lhld cdrmaxa ;HL = .cdrmax mov m,e! inx h! mov m,d ;cdrmax = dirmax ret ; check$rodir: ;check current directory element for read/only status call getdptra ;address of element ;jmp check$rofile ;share code ; check$rofile: ;check current buff(dptr) or fcb(0) for r/o status lxi d,rofile! dad d ;offset to ro bit mov a,m! ral! rnc ;return if not set jmp rof$error ;exit to read only disk message ; check$write: ;check for write protected disk call nowrite! rz ;ok to write if not rodsk jmp rod$error ;read only disk error ; getdptra: ;compute the address of a directory element at ;positon dptr in the buffer lhld buffa! lda dptr ;jmp addh ; addh: ;HL = HL + A add l! mov l,a! rnc ;overflow to H inr h! ret ; getmodnum: ;compute the address of the module number ;bring module number to accumulator ;(high order bit is fwf (file write flag) lhld info! lxi d,modnum! dad d ;HL=.fcb(modnum) mov a,m! ret ;A=fcb(modnum) ; clrmodnum: ;clear the module number field for user open/make call getmodnum! mvi m,0 ;fcb(modnum)=0 ret ; setfwf: call getmodnum ;HL=.fcb(modnum), A=fcb(modnum) ;set fwf (file write flag) to "1" ori fwfmsk! mov m,a ;fcb(modnum)=fcb(modnum) or 80h ;also returns non zero in accumulator ret ; setlret1: ;set lret = 1 mvi a,1! sta lret! ret ; compcdr: ;return cy if cdrmax > dcnt lhld dcnt! xchg ;DE = directory counter lhld cdrmaxa ;HL=.cdrmax mov a,e! sub m ;low(dcnt) - low(cdrmax) inx h ;HL = .cdrmax+1 mov a,d! sbb m ;hig(dcnt) - hig(cdrmax) ;condition dcnt - cdrmax produces cy if cdrmax>dcnt ret ; setcdr: ;if not (cdrmax > dcnt) then cdrmax = dcnt+1 call compcdr rc ;return if cdrmax > dcnt ;otherwise, HL = .cdrmax+1, DE = dcnt inx d! mov m,d! dcx h! mov m,e ret ; subdh: ;compute HL = DE - HL mov a,e! sub l! mov l,a! mov a,d! sbb h! mov h,a ret ; newchecksum: mvi c,true ;drop through to compute new checksum checksum: ;compute current checksum record and update the ;directory element if C=true, or check for = if not ;drec < chksiz? lhld drec! xchg! lhld chksiz! call subdh ;DE-HL rnc ;skip checksum if past checksum vector size ;drec < chksiz, so continue push b ;save init flag call compute$cs ;check sum value to A lhld checka ;address of check sum vector xchg lhld drec ;value of drec dad d ;HL = .check(drec) pop b ;recall true=0ffh or false=00 to C inr c ;0ffh produces zero flag jz initial$cs ;not initializing, compare cmp m ;compute$cs=check(drec)? rz ;no message if ok ;checksum error, are we beyond ;the end of the disk? call compcdr rnc ;no message if so call set$ro ;read/only disk set ret initial$cs: ;initializing the checksum mov m,a! ret ; wrdir: ;write the current directory entry, set checksum call newchecksum ;initialize entry call setdir ;directory dma mvi c,1 ;indicates a write directory operation call wrbuff ;write the buffer ;call setdata ;to data dma address ;ret ; setdata: ;set data dma address lxi h,dmaad ;jmp setdma ;to complete the call ; setdma: ;HL=.dma address to set (i.e., buffa or dmaad) mov c,m! inx h! mov b,m ;parameter ready jmp setdmaf ; setdir: ;set directory dma address lxi h,buffa! jmp setdma ;to complete the call ; rd$dir: ;read a directory entry into the directory buffer call setdir ;directory dma call rdbuff ;directory record loaded jmp setdata ;to data dma address ;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 ;continue while dirmax >= dcnt (dirmax-dcnt no cy) call subdh ;DE-HL jnc read$dir0 ;yes, set dcnt to end of directory call set$end$dir ret read$dir0: ;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 seek$dir ;seek proper record call rd$dir ;read the directory record pop b ;recall initialization flag jmp checksum ;checksum the directory elt ;ret ; getallocbit: ;given allocation vector position BC, return with byte ;containing BC shifted so that the least significant ;bit is in the low order accumulator position. HL is ;the address of the byte for possible replacement in ;memory upon return, and D contains the number of shifts ;required to place the returned value back into position mov a,c! ani 111b! inr a! mov e,a! mov d,a ;d and e both contain the number of bit positions to shift mov a,c! rrc! rrc! rrc! ani 11111b! mov c,a ;C shr 3 to C mov a,b! add a! add a! add a! add a! add a ;B shl 5 ora c! mov c,a ;bbbccccc to C mov a,b! rrc! rrc! rrc! ani 11111b! mov b,a ;BC shr 3 to BC lhld alloca ;base address of allocation vector dad b! mov a,m ;byte to A, hl = .alloc(BC shr 3) ;now move the bit to the low order position of A rotl: rlc! dcr e! jnz rotl! ret ; ; setallocbit: ;BC is the bit position of ALLOC to set or reset. The ;value of the bit is in register E. push d! call getallocbit ;shifted val A, count in D ani 1111$1110b ;mask low bit to zero (may be set) pop b! ora c ;low bit of C is masked into A ;call rotr ;to rotate back into proper position ;ret ; rotr: ;byte value from ALLOC is in register A, with shift count ;in register C (to place bit back into position), and ;target ALLOC position in registers HL, rotate and replace rrc! dcr d! jnz rotr ;back into position mov m,a ;back to ALLOC ret ; scandm: ;scan the disk map addressed by dptr for non-zero ;entries, the allocation vector entry corresponding ;to a non-zero entry is set to the value of C (0,1) call getdptra ;HL = buffa + dptr ;HL addresses the beginning of the directory entry lxi d,dskmap! dad d ;hl now addresses the disk map push b ;save the 0/1 bit to set mvi c,fcblen-dskmap+1 ;size of single byte disk map + 1 scandm0: ;loop once for each disk map entry pop d ;recall bit parity dcr c! rz ;all done scanning? ;no, get next entry for scan push d ;replace bit parity lda single! ora a! jz scandm1 ;single byte scan operation push b ;save counter push h ;save map address mov c,m! mvi b,0 ;BC=block# jmp scandm2 scandm1: ;double byte scan operation dcr c ;count for double byte push b ;save counter mov c,m! inx h! mov b,m ;BC=block# push h ;save map address scandm2: ;arrive here with BC=block#, E=0/1 mov a,c! ora b ;skip if = 0000 jz scandm3 ;check valid block # lhld maxall! mov a,l! sub c mov a,h! sbb b ;maxall - block# cnc set$alloc$bit scandm3: ;bit set to 0/1 pop h! inx h ;to next bit position pop b ;recall counter jmp scandm0 ;for another item ; initialize: ;initialize the current disk ;lret = false ;set to true if $ file exists ;compute the length of the allocation vector - 2 lhld maxall! mvi c,3 ;perform maxall/8 ;number of bytes in alloc vector is (maxall/8)+1 call hlrotr! inx h ;HL = maxall/8+1 mov b,h! mov c,l ;count down BC til zero lhld alloca ;base of allocation vector ;fill the allocation vector with zeros initial0: mvi m,0! inx h ;alloc(i)=0 dcx b ;count length down mov a,b! ora c! jnz initial0 ;set the reserved space for the directory lhld dirblk! xchg lhld alloca ;HL=.alloc() mov m,e! inx h! mov m,d ;sets reserved directory blks ;allocation vector initialized, home disk call home ;cdrmax = 3 (scans at least one record) lhld cdrmaxa! mvi m,3! inx h! mvi m,0 ;cdrmax = 0000 call set$end$dir ;dcnt = enddir ;read directory entries and check for allocated storage initial2: mvi c,true! call read$dir call end$of$dir! rz ;return if end of directory ;not end of directory, valid entry? call getdptra ;HL = buffa + dptr mvi a,empty! cmp m jz initial2 ;go get another item ;not empty, user code the same? lda usrcode cmp m! jnz pdollar ;same user code, check for '$' submit inx h! mov a,m ;first character sui '$' ;dollar file? jnz pdollar ;dollar file found, mark in lret dcr a! sta lret ;lret = 255 pdollar: ;now scan the disk map for allocated blocks mvi c,1 ;set to allocated call scandm call setcdr ;set cdrmax to dcnt jmp initial2 ;for another entry ; copy$dirloc: ;copy directory location to lret following ;delete, rename, ... ops lda dirloc! sta lret 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 ; search: ;search for directory element of length C at info mvi a,0ffh! sta dirloc ;changed if actually found lxi h,searchl! mov m,c ;searchl = C lhld info! shld searcha ;searcha = info call set$end$dir ;dcnt = enddir call home ;to start at the beginning ;call searchn ;start the search operation ; 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 search$fin ;skip to end if so ;not end of directory, scan for match lhld searcha! xchg ;DE=beginning of user fcb ldax d ;first character cpi empty ;keep scanning if empty jz searchnext ;not empty, may be end of logical directory push d ;save search address call compcdr ;past logical end? pop d ;recall address jnc search$fin ;artificial stop searchnext: call getdptra ;HL = buffa+dptr lda searchl! mov c,a ;length of search to c mvi b,0 ;b counts up, c counts down searchloop: mov a,c! ora a! jz endsearch ldax d! cpi '?'! jz searchok ;? matches all ;scan next character if not ubytes mov a,b! ora a! jnz searchon ;now see if search is for empty dir entry ldax d! cpi empty! jz searchfirst mov a,m! ora a! jz searchok searchfirst: mov a,b searchon: cpi ubytes! jz searchok ;not the ubytes field, extent field? cpi extnum ;may be extent field ldax d ;fcb character jz searchext ;skip to search extent sub m! ani 7fh ;mask-out flags/extent modulus jnz searchn ;skip if not matched jmp searchok ;matched character searchext: ;A has fcb character ;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 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 lda dcnt! ani dskmsk! sta lret ;lret = low(dcnt) and 11b lxi h,dirloc! mov a,m! ral! rnc ;dirloc=0ffh? ;yes, change it to 0 to mark as found xra a! mov m,a ;dirloc=0 ret search$fin: ;end of directory, or empty name call set$end$dir ;may be artifical end mvi a,255! sta lret! ret ; delete: ;delete the currently addressed file call check$write ;write protected? mvi c,extnum! call search ;search through file type delete0: ;loop while directory matches call end$of$dir! rz ;stop if end ;set each non zero disk map entry to 0 ;in the allocation vector ;may be r/o file call check$rodir ;ro disk error if found call getdptra ;HL=.buff(dptr) ;make certain that the user code matches ; (all searches match for user code 0) lda usrcode! cmp m jnz delete1 ;ignore delete if no match mvi m,empty mvi c,0! call scandm ;alloc elts set to 0 call wrdir ;write the directory delete1: call searchn ;to next element jmp delete0 ;for another record ; get$block: ;given allocation vector position BC, find the zero bit ;closest to this position by searching left and right. ;if found, set the bit to one and return the bit position ;in hl. if not found (i.e., we pass 0 on the left, or ;maxall on the right), return 0000 in hl mov d,b! mov e,c ;copy of starting position to de lefttst: mov a,c! ora b! jz righttst ;skip if left=0000 lefttst0: ;left not at position zero, bit zero? dcx b! push d! push b ;left,right pushed call getallocbit rar! jnc retblock ;return block number if zero ;bit is one, so try the right pop b! pop d ;left, right restored righttst: lhld maxall ;value of maximum allocation# mov a,e! sub l! mov a,d! sbb h ;right=maxall? jnc retblock0 ;return block 0000 if so inx d! push b! push d ;left, right pushed mov b,d! mov c,e ;ready right for call call getallocbit rar! jnc retblock ;return block number if zero pop d! pop b ;restore left and right pointers jmp lefttst ;for another attempt retblock: ral! inr a ;bit back into position and set to 1 ;d contains the number of shifts required to reposition call rotr ;move bit back to position and store pop h! pop d ;HL returned value, DE discarded ret retblock0: ;try to the left mov a,c! ora b! jnz lefttst0 ;cannot find an available bit, return 0000 lxi h,0000h! ret ; copy$fcb: ;copy the entire file control block mvi c,0! mvi e,fcblen ;start at 0, to fcblen-1 ;call copy$dir ; copy$dir: ;copy fcb information starting at C for E bytes ;into the currently addressed directory entry push d ;save length for later mvi b,0 ;double index to BC lhld info ;HL = source for data dad b! xchg ;DE=.fcb(C), source for copy call getdptra ;HL=.buff(dptr), destination pop b ;DE=source, HL=dest, C=length call move ;data moved seek$copy: ;enter from close to seek and copy current element call seek$dir ;to the directory element jmp wrdir ;write the directory element ;ret ; rename: ;rename the file described by the first half of ;the currently addressed file control block. the ;new name is contained in the last half of the ;currently addressed file conrol block. the file ;name and type are changed, but the reel number ;is ignored. the user number is identical call check$write ;may be write protected ;search up to the extent field mvi c,extnum! call search ;copy position 0 lhld info! mov a,m ;HL=.fcb(0), A=fcb(0) lxi d,dskmap! dad d;HL=.fcb(dskmap) mov m,a ;fcb(dskmap)=fcb(0) ;assume the same disk drive for new named file rename0: call end$of$dir! rz ;stop at end of dir ;not end of directory, rename next element call check$rodir ;may be read-only file ;make certain that the user code matches ; (search always matches user code 0) call getdptra lda usrcode! cmp m jnz rename1 ;do not rename if no match mvi c,dskmap! mvi e,extnum! call copy$dir ;element renamed, move to next rename1: call searchn jmp rename0 ; indicators: ;set file indicators for current fcb mvi c,extnum! call search ;through file type indic0: call end$of$dir! rz ;stop at end of dir ;not end of directory, continue to change mvi c,0! mvi e,extnum ;copy name call copy$dir call searchn jmp indic0 ; open: ;search for the directory entry, copy to fcb mvi c,namlen! call search call end$of$dir! rz ;return with lret=255 if end ;set drive active bit in PD call setdrvact ;not end of directory, copy fcb information open$copy: ;(referenced below to copy fcb info) call getexta! mov a,m! push psw! push h ;save extent# call getdptra! xchg ;DE = .buff(dptr) lhld info ;HL=.fcb(0) mvi c,nxtrec ;length of move operation push d ;save .buff(dptr) call move ;from .buff(dptr) to .fcb(0) ;note that entire fcb is copied, including indicators call setfwf ;sets file write flag pop d! lxi h,extnum! dad d ;HL=.buff(dptr+extnum) mov c,m ;C = directory extent number lxi h,reccnt! dad d ;HL=.buff(dptr+reccnt) mov b,m ;B holds directory record count pop h! pop psw! mov m,a ;restore extent number ;HL = .user extent#, B = dir rec cnt, C = dir extent# ;if user ext < dir ext then user := 128 records ;if user ext = dir ext then user := dir records ;if user ext > dir ext then user := 0 records mov a,c! cmp m! mov a,b ;ready dir reccnt jz open$rcnt ;if same, user gets dir reccnt mvi a,0! jc open$rcnt ;user is larger mvi a,128 ;directory is larger open$rcnt: ;A has record count to fill lhld info! lxi d,reccnt! dad d! mov m,a ret ; mergezero: ;HL = .fcb1(i), DE = .fcb2(i), ;if fcb1(i) = 0 then fcb1(i) := fcb2(i) mov a,m! inx h! ora m! dcx h! rnz ;return if = 0000 ldax d! mov m,a! inx d! inx h ;low byte copied ldax d! mov m,a! dcx d! dcx h ;back to input form ret ; close: ;locate the directory element and re-write it lxi h,0! shld aret! shld dcnt call nowrite! rnz ;skip close if r/o disk ;check file write flag - 0 indicates written call getmodnum ;fcb(modnum) in A ani fwfmsk! rnz ;return if bit remains set mvi c,namlen! call search ;locate file call end$of$dir! rz ;return if not found ;merge the disk map at info with that at buff(dptr) lxi b,dskmap! call getdptra dad b! xchg ;DE is .buff(dptr+16) ;set t3' on, indicating file update lxi h,-5! dad d! mov a,m! ori 80h! mov m,a lhld info! dad b ;DE=.buff(dptr+16), HL=.fcb(16) mvi c,(fcblen-dskmap) ;length of single byte dm merge0: lda single! ora a! jz merged ;skip to double ;this is a single byte map ;if fcb(i) = 0 then fcb(i) = buff(i) ;if buff(i) = 0 then buff(i) = fcb(i) ;if fcb(i) <> buff(i) then error mov a,m! ora a! ldax d! jnz fcbnzero ;fcb(i) = 0 mov m,a ;fcb(i) = buff(i) fcbnzero: ora a! jnz buffnzero ;buff(i) = 0 mov a,m! stax d ;buff(i)=fcb(i) buffnzero: cmp m! jnz mergerr ;fcb(i) = buff(i)? jmp dmset ;if merge ok merged: ;this is a double byte merge operation call mergezero ;buff = fcb if buff 0000 xchg! call mergezero! xchg ;fcb = buff if fcb 0000 ;they should be identical at this point ldax d! cmp m! jnz mergerr ;low same? inx d! inx h ;to high byte ldax d! cmp m! jnz mergerr ;high same? ;merge operation ok for this pair dcr c ;extra count for double byte dmset: inx d! inx h ;to next byte position dcr c! jnz merge0 ;for more ;end of disk map merge, check record count ;DE = .buff(dptr)+32, HL = .fcb(32) lxi b,-(fcblen-extnum)! dad b! xchg! dad b ;DE = .fcb(extnum), HL = .buff(dptr+extnum) ldax d ;current user extent number ;if fcb(ext) >= buff(fcb) then ;buff(ext) := fcb(ext), buff(rec) := fcb(rec) cmp m! jc endmerge ;fcb extent number >= dir extent number mov m,a ;buff(ext) = fcb(ext) ;update directory record count field lxi b,(reccnt-extnum)! dad b! xchg! dad b ;DE=.buff(reccnt), HL=.fcb(reccnt) mov a,m! stax d ;buff(reccnt)=fcb(reccnt) endmerge: mvi a,true! sta fcb$copied ;mark as copied call seek$copy ;ok to "wrdir" here - 1.4 compat ret mergerr: ;elements did not merge correctly lxi h,lret! dcr m ;=255 non zero flag set ret ; make: ;create a new file by creating a directory entry ;then opening the file call check$write ;may be write protected ;check for existing file ; mvi c,namlen ; call search ; inr a ; rz ;return with error condition 255 if found lhld info! push h ;save fcb address, look for e5 lxi h,efcb! shld info ;info = .empty mvi c,1! call search ;length 1 match on empty entry call end$of$dir ;zero flag set if no space pop h ;recall info address shld info ;in case we return here rz ;return with error condition 255 if not found xchg ;DE = info address ;clear the remainder of the fcb lxi h,namlen! dad d ;HL=.fcb(namlen) mvi c,fcblen-namlen ;number of bytes to fill xra a ;clear accumulator to 00 for fill make0: mov m,a! inx h! dcr c! jnz make0 lxi h,ubytes! dad d ;HL = .fcb(ubytes) mov m,a ;fcb(ubytes) = 0 call setcdr ;may have extended the directory ;now copy entry to the directory call copy$fcb ;and set the file write flag to "1" call setfwf setdrvact: lxi h,0001h! lda linfo! mov c,a! call hlrotl jmp intrnlaccdrv ;ret ; open$reel: ;close the current extent, and open the next one ;if possible. RMF is true if in read mode xra a! sta fcb$copied ;set true if actually copied call close ;close current extent ;lret remains at enddir if we cannot open the next ext call end$of$dir! rz ;return if end ;increment extent number lhld info! lxi b,extnum! dad b ;HL=.fcb(extnum) mov a,m! inr a! ani maxext! mov m,a ;fcb(extnum)=++1 jz open$mod ;move to next module if zero ;may be in the same extent group mov b,a! lda extmsk! ana b ;if result is zero, then not in the same group lxi h,fcb$copied ;true if the fcb was copied to directory ana m ;produces a 00 in accumulator if not written jz open$reel0 ;go to next physical extent ;result is non zero, so we must be in same logical ext jmp open$reel1 ;to copy fcb information open$mod: ;extent number overflow, go to next module lxi b,(modnum-extnum)! dad b ;HL=.fcb(modnum) inr m ;fcb(modnum)=++1 ;module number incremented, check for overflow mov a,m! ani maxmod ;mask high order bits jz open$r$err ;cannot overflow to zero ;otherwise, ok to continue with new module open$reel0: mvi c,namlen! call search ;next extent found? call end$of$dir! jnz open$reel1 ;end of file encountered lda rmf! inr a ;0ffh becomes 00 if read jz open$r$err ;sets lret = 1 ;try to extend the current file call make ;cannot be end of directory call end$of$dir jz open$r$err ;with lret = 1 jmp open$reel2 open$reel1: ;not end of file, open call open$copy open$reel2: call getfcb ;set parameters xra a! sta lret ;lret = 0 ret ;with lret = 0 open$r$err: ;cannot move to next extent of this file call setlret1 ;lret = 1 jmp setfwf ;ensure that it will not be closed ;ret ; seqdiskread: ;sequential disk read operation mvi a,1! sta seqio ;drop through to diskread ; diskread: ;(may enter from seqdiskread) mvi a,true! sta rmf ;read mode flag = true (open$reel) ;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 diskeof ;skip if vrecord<>128 call open$reel ;go to next extent if so xra a! sta vrecord ;vrecord=00 ;now check for open ok lda lret! ora a! jnz diskeof ;stop at eof recordok: ;arrive with fcb addressing a record to read call index ;error 2 if reading unwritten data ;(returns 1 to be compatible with 1.4) call allocated ;arecord=0000? jz diskeof ;record has been allocated, read it call atran ;arecord now a disk address call seek ;to proper track,sector call rdbuff ;to dma address call setfcb ;replace parameters ret diskeof: jmp setlret1 ;lret = 1 ;ret ; seqdiskwrite: ;sequential disk write mvi a,1! sta seqio ;drop through to diskwrite ; diskwrite: ;(may enter here from seqdiskwrite above) mvi a,false! sta rmf ;read mode flag ;write record to currently selected file call check$write ;in case write protected lhld info ;HL = .fcb(0) call check$rofile ;may be a read-only file call getfcb ;to set local parameters lda vrecord! cpi lstrec+1 ;vrecord-128 ;skip if vrecord > lstrec jc diskwr0 ;vrecord = 128, cannot open next extent call setlret1! ret ;lret=1 diskwr0: ;can write the next record, so continue call index call allocated mvi c,0 ;marked as normal write operation for wrbuff jnz diskwr1 ;not allocated ;the argument to getblock is the starting ;position for the disk search, and should be ;the last allocated block for this file, or ;the value 0 if no space has been allocated call dm$position sta dminx ;save for later lxi b,0000h ;may use block zero ora a! jz nopblock ;skip if no previous block ;previous block exists at A mov c,a! dcx b ;previous block # in BC call getdm ;previous block # to HL mov b,h! mov c,l ;BC=prev block# nopblock: ;BC = 0000, or previous block # call get$block ;block # to HL ;arrive here with block# or zero mov a,l! ora h! jnz blockok ;cannot find a block to allocate mvi a,2! sta lret! ret ;lret=2 blockok: ;allocated block number is in HL shld arecord xchg ;block number to DE lhld info! lxi b,dskmap! dad b ;HL=.fcb(dskmap) lda single! ora a ;set flags for single byte dm lda dminx ;recall dm index jz allocwd ;skip if allocating word ;allocating a byte value call addh! mov m,e ;single byte alloc jmp diskwru ;to continue allocwd: ;allocate a word value mov c,a! mvi b,0 ;double(dminx) dad b! dad b ;HL=.fcb(dminx*2) mov m,e! inx h! mov m,d ;double wd diskwru: ;disk write to previously unallocated block mvi c,2 ;marked as unallocated write diskwr1: ;continue the write operation of no allocation error ;C = 0 if normal write, 2 if to prev unalloc block lda lret! ora a! rnz ;stop if non zero returned value push b ;save write flag call atran ;arecord set lda seqio dcr a dcr a ;is this random read with block fill jnz diskwr11 pop b push b mov a,c dcr a dcr a ;is this a newly allocated block jnz diskwr11 ;first write of random block with fill ; push h ;hl contains arecord after atran ;note than atran has been modified to store the ;arecord for first record in block in arecord1 ; lhld buffa ;buffa is zeroed to use as fill records mov d,a ;zero to d for counter fill0: mov m,a inx h inr d jp fill0 call setdir lhld arecord1 ;first record of block mvi c,02 ;set allocate flag fill1: shld arecord ;change arecord for zero fill push b call seek ;to proper file position pop b call wrbuff ;write the zero fill record lhld arecord ;recover last actual record mvi c,0 ;change allocate flag lda blkmsk mov b,a ana l cmp b ;check for last record to fill inx h jnz fill1 pop h ;restore arecord shld arecord call setdata ;restore the DMA address diskwr11: call seek ;to proper file position pop b! push b ;restore/save write flag (C=2 if new block) call wrbuff ;written to disk pop b ;C = 2 if a new block was allocated, 0 if not ;increment record count if rcount<=vrecord lda vrecord! lxi h,rcount! cmp m ;vrecord-rcount jc diskwr2 ;rcount <= vrecord mov m,a! inr m ;rcount = vrecord+1 mvi c,2 ;mark as record count incremented diskwr2: ;A has vrecord, C=2 if new block or new record# ;*** the next three instructions are replaced by NOP's ;dcr c ;dcr c ;jnz noupdate db 0,0,0,0,0 push psw ;save vrecord value call getmodnum ;HL=.fcb(modnum), A=fcb(modnum) ;reset the file write flag to mark as written fcb ani (not fwfmsk) and 0ffh ;bit reset mov m,a ;fcb(modnum) = fcb(modnum) and 7fh pop psw ;restore vrecord noupdate: ;check for end of extent, if found attempt to open ;next extent in preparation for next write cpi lstrec ;vrecord=lstrec? jnz diskwr3 ;skip if not ;may be random access write, if so we are done lda seqio! cpi 1! jnz diskwr3 ;skip next extent open op ;update current fcb before going to next extent call setfcb call open$reel ;rmf=false ;vrecord remains at lstrec causing eof if ;no more directory space is available lxi h,lret! mov a,m! ora a! jnz nospace ;space available, set vrecord=255 dcr a! sta vrecord ;goes to 00 next time nospace: mvi m,0 ;lret = 00 for returned value diskwr3: jmp setfcb ;replace parameters ;ret ; rseek: ;random access seek operation, C=0ffh if read mode ;fcb is assumed to address an active file control block ;(modnum has been set to 1100$0000b if previous bad seek) xra a! sta seqio ;marked as random access operation rseek1: push b ;save r/w flag lhld info! xchg ;DE will hold base of fcb lxi h,ranrec! dad d ;HL=.fcb(ranrec) mov a,m! ani 7fh! push psw ;record number mov a,m! ral ;cy=lsb of extent# inx h! mov a,m! ral! ani 11111b ;A=ext# mov c,a ;C holds extent number, record stacked mov a,m! rar! rar! rar! rar! ani 1111b ;mod# mov b,a ;B holds module#, C holds ext# pop psw ;recall sought record # ;check to insure that high byte of ran rec = 00 inx h! mov l,m ;l=high byte (must be 00) inr l! dcr l! mvi l,6 ;zero flag, l=6 ;produce error 6, seek past physical eod jnz seekerr ;otherwise, high byte = 0, A = sought record lxi h,nxtrec! dad d ;HL = .fcb(nxtrec) mov m,a ;sought rec# stored away ;arrive here with B=mod#, C=ext#, DE=.fcb, rec stored ;the r/w flag is still stacked. compare fcb values lxi h,extnum! dad d! mov a,c ;A=seek ext# sub m! jnz ranclose ;tests for = extents ;extents match, check mod# lxi h,modnum! dad d! mov a,b ;B=seek mod# ;could be overflow at eof, producing module# ;of 90H or 10H, so compare all but fwf sub m! ani 7fh! jz seekok ;same? ranclose: push b! push d ;save seek mod#,ext#, .fcb call close ;current extent closed pop d! pop b ;recall parameters and fill mvi l,3 ;cannot close error #3 lda lret! inr a! jz badseek lxi h,extnum! dad d! mov m,c ;fcb(extnum)=ext# lxi h,modnum! dad d! mov m,b ;fcb(modnum)=mod# call open ;is the file present? lda lret! inr a! jnz seekok ;open successful? ;cannot open the file, read mode? pop b ;r/w flag to c (=0ffh if read) push b ;everyone expects this item stacked mvi l,4 ;seek to unwritten extent #4 inr c ;becomes 00 if read operation jz badseek ;skip to error if read operation ;write operation, make new extent call make mvi l,5 ;cannot create new extent #5 lda lret! inr a! jz badseek ;no dir space ;file make operation successful seekok: pop b ;discard r/w flag xra a! sta lret! ret ;with zero set badseek: ;fcb no longer contains a valid fcb, mark ;with 1100$000b in modnum field so that it ;appears as overflow with file write flag set push h ;save error flag call getmodnum ;HL = .modnum mvi m,1100$0000b pop h ;and drop through seekerr: pop b ;discard r/w flag mov a,l! sta lret ;lret=#, nonzero ;setfwf returns non-zero accumulator for err jmp setfwf ;flag set, so subsequent close ok ;ret ; randiskread: ;random disk read operation mvi c,true ;marked as read operation call rseek cz diskread ;if seek successful ret ; randiskwrite: ;random disk write operation mvi c,false ;marked as write operation call rseek cz diskwrite ;if seek successful ret ; compute$rr: ;compute random record position for getfilesize/setrandom xchg! dad d ;DE=.buf(dptr) or .fcb(0), HL = .f(nxtrec/reccnt) mov c,m! mvi b,0 ;BC = 0000 0000 ?rrr rrrr lxi h,extnum! dad d! mov a,m! rrc! ani 80h ;A=e000 0000 add c! mov c,a! mvi a,0! adc b! mov b,a ;BC = 0000 000? errrr rrrr mov a,m! rrc! ani 0fh! add b! mov b,a ;BC = 000? eeee errrr rrrr lxi h,modnum! dad d! mov a,m ;A=XXX? mmmm add a! add a! add a! add a ;cy=? A=mmmm 0000 push psw! add b! mov b,a ;cy=?, BC = mmmm eeee errr rrrr push psw ;possible second carry pop h ;cy = lsb of L mov a,l ;cy = lsb of A pop h ;cy = lsb of L ora l ;cy/cy = lsb of A ani 1 ;A = 0000 000? possible carry-out ret ; getfilesize: ;compute logical file size for current fcb mvi c,extnum call search ;zero the receiving ranrec field lhld info! lxi d,ranrec! dad d! push h ;save position mov m,d! inx h! mov m,d! inx h! mov m,d;=00 00 00 getsize: call end$of$dir jz setsize ;current fcb addressed by dptr call getdptra! lxi d,reccnt ;ready for compute size call compute$rr ;A=0000 000? BC = mmmm eeee errr rrrr ;compare with memory, larger? pop h! push h ;recall, replace .fcb(ranrec) mov e,a ;save cy mov a,c! sub m! inx h ;ls byte mov a,b! sbb m! inx h ;middle byte mov a,e! sbb m ;carry if .fcb(ranrec) > directory jc getnextsize ;for another try ;fcb is less or equal, fill from directory mov m,e! dcx h! mov m,b! dcx h! mov m,c getnextsize: call searchn jmp getsize setsize: pop h ;discard .fcb(ranrec) ret ; setrandom: ;set random record from the current file control block lhld info! lxi d,nxtrec ;ready params for computesize call compute$rr ;DE=info, A=cy, BC=mmmm eeee errr rrrr lxi h,ranrec! dad d ;HL = .fcb(ranrec) mov m,c! inx h! mov m,b! inx h! mov m,a ;to ranrec ret ; curselect: lda linfo! lxi h,curdsk mov m,a ;curdsk=info ;call select ;ret ; select: ;select disk info for subsequent input or output ops lhld dlog! lda curdsk! mov c,a! call hlrotr push h! xchg ;save it for test below, send to seldsk call selectdisk! pop h ;recall dlog vector cz sel$error ;returns true if select ok ;is the disk logged in? mov a,l! rar! rc ;return if bit is set ;disk not logged in, set bit and initialize lhld dlog! mov c,l! mov b,h ;call ready call set$cdisk! shld dlog ;dlog=set$cdisk(dlog) jmp initialize ; reselect: ;check current fcb to see if reselection necessary 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..15, or 255 sta linfo ;save drive code cpi 16! jc autoselect lda curdsk! sta linfo autoselect: ;auto select function, save curdsk lda curdsk! sta olddsk ;olddsk=curdsk mov a,m! sta fcbdsk ;save drive code ani 1110$0000b! mov m,a ;preserve hi bits call curselect ;set user code lda usrcode ;0...15 lhld info! ora m! mov m,a ret ; diskreset: call func39 ;free all drives to be reset from current PD lhld info intrnldiskreset: xchg! call rlradr! lxi b,thrdrt-rlros! dad b ;DE = drive reset vector, HL = threadroot dskrst0: mov c,m! inx h! mov b,m ;BC = next PD addr mov a,c! ora b! rz ;end of thread list lxi h,drvact! dad b ;HL = PD.drvact mov a,m! ana e! inx h jnz dskrst1 mov a,m! ana d jnz dskrst2 lxi h,thread! dad b jmp dskrst0 dskrst1: mvi l,0ffh jmp dskrst3 dskrst2: mvi l,7 dskrst3: inr l! rar! jnc dskrst3 mvi a,'A'! add l ;A = active drive (in ASCII) push b! push psw call rlr! lxi d,console! dad d! mov d,m ;D = console # lxi b,deniedmsg! call xprint pop psw! mov c,a! call conoutx mvi c,':'! call conoutx lxi b,cnsmsg! call xprint pop h! push h! lxi b,console! dad b mov a,m! adi '0'! mov c,a! call conoutx lxi b,progmsg! call xprint pop h! call dsplynm pop h ;remove return addr from diskreset lxi h,0ffffh! shld aret ;flag the error ret ; deniedmsg: db cr,lf,'Disk reset denied, Drive ','$' cnsmsg: db ' Console ','$' progmsg: db ' Program ','$' ; ; individual function handlers func12: ;return version number lxi h,0100h+dvers! shld aret ;lret = dvers (high = 01) ret ; ; func13: lxi h,0ffffh! shld info call diskreset ;reset disk system - initialize to disk 0 lxi h,0! shld rodsk! shld dlog xra a! sta curdsk ;note that usrcode remains unchanged call getmemseg ;A = mem seg tbl index call rlradr! lxi b,msegtbl-rlros! dad b add a! add a! mov e,a! mvi d,0! dad d mov h,m! mvi l,80h jmp intrnlsetDMA ;ret ; func14: ;select disk lda info! ani 0fh! push psw call curselect ;put disk selected into process descriptor call rlr! lxi b,diskselect! dad b mov a,m! ani 0fh! rrc! rrc! rrc! rrc mov b,a! pop psw! ora b! rrc! rrc! rrc! rrc mov m,a! ret ; func15: ;open file call clrmodnum ;clear the module number call reselect jmp open ; func16: ;close file call reselect jmp close ; func17: ;search for first occurrence of a file mvi c,0 ;length assuming '?' true lhld info! mov a,m! cpi '?' ;no reselect if ? jz qselect ;skip reselect if so ;normal search ;check extent call getexta! mov a,m! cpi '?' cnz clrmodnum ;module number zeroed call reselect mvi c,namlen qselect: jmp search ; func18: ;search for next occurrence of a file name lhld info! shld searcha call reselect! jmp searchn ; func19: ;delete a file call reselect call delete jmp copy$dirloc ; func20: ;read a file call reselect jmp seqdiskread ; func21: ;write a file call reselect jmp seqdiskwrite ; func22: ;make a file call clrmodnum call reselect jmp make ; func23: ;rename a file call reselect call rename jmp copy$dirloc ; func24: ;return the login vector lhld dlog! shld aret ret ; func25: ;return selected disk number lda curdsk! sta lret ret ; func26: ;save DMA address in process descriptor lhld info intrnlsetDMA: xchg call rlr! lxi b,disksetDMA! dad b mov m,e! inx h! mov m,d ;set the subsequent dma address to info xchg! shld dmaad ;dmaad = info jmp setdata ;to data dma address ; func27: ;return the login vector address lhld alloca! shld aret ret ; func28: ;write protect current disk ;first check for open files on disk lda curdsk! mov c,a! lxi h,0001h call hlrotl! call intrnldiskreset jmp set$ro ; func29: ;return r/o bit vector lhld rodsk! shld aret ret ; func30: ;set file indicators call reselect call indicators jmp copy$dirloc ;lret=dirloc ; func31: ;return address of disk parameter block lhld dpbaddr! shld aret ret ; func32: ;set user code lda linfo! cpi 0ffh! jnz setusrcode ;interrogate user code instead lda usrcode! sta lret ;lret=usrcode ret setusrcode: ani 0fh! sta usrcode ;save user code in process descriptor call rlr! lxi b,diskselect! dad b lda usrcode! mov b,a mov a,m! ani 0f0h! ora b! mov m,a ret ; func33: ;random disk read operation call reselect jmp randiskread ;to perform the disk read ; func34: ;random disk write operation call reselect jmp randiskwrite ;to perform the disk write ; func35: ;return file size (0-65536) call reselect jmp getfilesize ; func36 equ setrandom ;set random record ;jmp setrandom ; func37: ;drive reset call diskreset lhld info! mov a,l! cma! mov e,a mov a,h! cma lhld dlog! ana h! mov d,a mov a,l! ana e! mov e,a lhld rodsk! xchg! shld dlog mov a,l! ana e! mov l,a mov a,h! ana d! mov h,a shld rodsk! ret ; func38: ;access drive lhld info intrnlaccdrv: xchg call rlr! lxi b,drvact! dad b mov a,m! ora e! mov m,a! inx h mov a,m! ora d! mov m,a ret ; func39: ;free drive lhld info! mov a,h! cma! mov d,a mov a,l! cma! mov e,a call rlr! lxi b,drvact! dad b mov a,m! ana e! mov m,a! inx h mov a,m! ana d! mov m,a ret ; func40: ;random disk write with zero fill of unallocated blocks call reselect! mvi a,2! sta seqio mvi c,false! call rseek1 jz diskwrite ret ; goback: ;arrive here at end of processing to return to user lda resel! ora a! jz retmon ;reselection may have taken place lhld info! mvi m,0 ;fcb(0)=0 lda fcbdsk! ora a! jz retmon ;restore disk number mov m,a ;fcb(0)=fcbdsk lda olddsk! sta linfo! call curselect ; ; return from the disk monitor retmon: lhld aret! mov a,l! mov b,h ;BA = HL = aret ret ; ; data areas ; ; initialized data efcb: db empty ;0e5=available dir entry rodsk: dw 0 ;read only disk vector dlog: dw 0 ;logged-in disks dmaad: dw 0080h ;initial 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 curtrka:ds word ;current track address curreca:ds word ;current record address buffa: ds word ;pointer to directory dma address dpbaddr:ds word ;current disk parameter block address checka: ds word ;current checksum vector address alloca: ds word ;current allocation vector address addlist equ $-buffa ;address list size ; ; 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 dpblist equ $-sectpt ;size of area ; ; local variables tranv: ds word ;address of translate vector fcb$copied: ds byte ;set true if copy$fcb called rmf: ds byte ;read mode flag for open$reel dirloc: ds byte ;directory flag in rename, etc. seqio: ds byte ;1 if sequential i/o linfo: ds byte ;low(info) dminx: ds byte ;local for diskwrite tinfo: ds word ;temp for info in "make" single: ds byte ;set true if single byte allocation map resel: ds byte ;reselection flag olddsk: ds byte ;disk on entry to bdos fcbdsk: ds byte ;disk named in fcb rcount: ds byte ;record count in current fcb extval: ds byte ;extent number and extmsk vrecord:ds word ;current virtual record arecord:ds word ;current actual record arecord1: ds word ;current actual block# * blkmsk srchflg:db 0 ;search flag ; ; local variables for directory access dptr: ds byte ;directory pointer 0,1,2,3 drec: ds word ;directory record 0,1,...,dirmax/4 ; ; structure copied into disk params of process descriptor dcnt: ds word ;directory counter 0,1,...,dirmax searchl:ds byte ;search length searcha:ds word ;search address ; ; db 0 ;force out last byte of hex file end