title 'CCP/M-86 Bdos Loader' ;***************************************************************** ;***************************************************************** ;** ** ;** 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) 1983 ; Digital Research ; Box 579, Pacific Grove ; California ; ; January 1983 ; ;***************************************************** ; bdosoffset equ 0000h ; offset of BDOS-86 ldroffset equ 0906h ; offset of CLDCCPM biosoffset equ 0900h ; offset of BIOS-86 ; ;******************* BDOS symbols: ******************* ; ; true equ 0ffffh false equ not true ; ; Special 8086 symbols: ; b equ byte ptr 0 w equ word ptr 0 ; ;***************************************************** ; ; BIOS Function numbers ; ;io_const equ 0 ;console status function ;io_conin equ 1 ;console input function ;io_conout equ 2 ;console output function ;io_listst equ 3 ;list status function ;io_list equ 4 ;list output function ;io_auxin equ 5 ;aux input function ;io_auxout equ 6 ;aux output function ;io_ equ 7 ;io_ equ 8 io_seldsk equ 9 ;select disk function io_read equ 10 ;read disk function ;io_write equ 11 ;write disk function ;io_flush equ 12 ;flush buffers function ;io_ equ 13 ; ; literal constants ; enddir EQU 0ffffh ;end of directory ; ; file control block (fcb) constants ; fcblen EQU 32 ;fcb length ;empty EQU 0e5h ;empty directory entry recsiz EQU 128 ;record size dirrec EQU recsiz/fcblen ;directory elts / record dskshf EQU 2 ;log2(dirrec) dskmsk EQU dirrec-1 fcbshf EQU 5 ;log2(fcblen) maxext EQU 31 ;largest extent number maxmod EQU 63 ;largest module number namlen EQU 15 ;name length ;lstfcb EQU fcblen-1 ;drv EQU 0 ;drive field ;f1 EQU 1 ;file name byte 1 to 8 ;f2 EQU 2 ;f3 EQU 3 ;f4 EQU 4 ;f5 EQU 5 ;f6 EQU 6 f7 EQU 7 ;f8 EQU 8 ; ; reserved file indicators ; ;rofile EQU 9 ;t1' -> read/only file ;sysfil EQU 10 ;t2' -> system file ;ARCHIV EQU 11 ;t3' -> FILE HAS BEEN ARCHIVED extnum EQU 12 ;extent number field chksum EQU 13 ;unfilled bytes field modnum EQU 14 ;data module number reccnt EQU 15 ;record count field dskmap EQU 16 ;disk map field nxtrec EQU fcblen ;ranrec EQU nxtrec+1 ;random record field (3 bytes) ; ; ; interrupt control indicators ; interruptbit equ 200h ; bit 9 of flag word ; ; ;**************** end of BDOS symbols **************** ;***************** BDOS entry module ***************** ; ; Perform branching, error handling and special ; 8086 handling. ; ; cseg org ldroffset ldr_entry: ; org bdosoffset os_init: jmp user_init os_entry: jmp user_entry sysdat dw 0 ;system data segment ;========= user_init: ;========= mov ax,cs mov ds,ax mov es,ax mov ss,ax mov sp,offset bdosstack mov bioscs,cs callf dword ptr iosentry mov biosco,bios_offset+3 ;set io call to entry xor ax,ax push ds! mov ds,ax mov word ptr .0380h,offset os_entry mov .0382h,cs pop ds jmp ldr_entry ; ;----------------------------------------------------- ;========== user_entry: ;========== ; User Entry Point - enter here from a INT 224 ; ; REGISTER USAGE ; ENTRY EXIT ; ----- ---- ; CL - Function Code AX - Copy of BX ; DX - Param BX - Return ; DS - Seg Addr CX - Error Code ; ES - Segment Return ; ; DS,SI,DI,BP preserved through call ; ; contents of users stack ; Flags ; CS ; IP <- u_stack_ss,_sp ; DS = Sysdat Segment ; u_wrkseg = user's DS ; u_retseg = user's ES cld ! mov ax,ds ;AX = user's DS push cs ! pop ds mov u_retseg,es ;save user's ES mov u_wrkseg,ax ;wipe out earlier work seg mov bx,sp ;enable interrupt if it was on test ss:word ptr 4[bx],interruptbit jz bdose1 sti bdose1: push ds! pop es push si! push di! push bp call func ;chk netwrk, returns BX, ES, CX pop bp! pop di! pop si ;setup user's environment and return mov es,u_retseg ;restore user's ES mov ds,u_wrkseg ;DS = user's entry DS mov ax,bx ;parameter return BX = AX iret ;back to user ... func: ;---- cmp cl,14! jne fn1 mov si,offset bdofunc+0 jmp bdo_entry fn1: cmp cl,15! jne fn2 mov si,offset bdofunc+3 jmp bdo_entry fn2: cmp cl,20! jne fn3 mov si,offset bdofunc+6 jmp bdo_entry fn3: cmp cl,26! je func26 cmp cl,32! je func32 cmp cl,44! je func44 cmp cl,51! je func51 badfunc: mov bx,0ffffh ;return ffff for illegal function ret ; func26: ;set the subsequent dma address to info ;====== mov dmaad,dx ;dmaad = info ret func32: ;set user code ;====== mov al,dl cmp al,0ffh jnz setusrcode mov bl,p_user ;interrogate user code instead ret setusrcode: and al,0fh mov p_user,al ret ;jmp goback func44: ;set multi-sector count ;====== mov al,dl xor bx,bx or al,al jz return_not_ok cmp al,129 jnb return_not_ok mov u_mult_cnt,al ret return_not_ok: dec bx ;BX = 0ffffh ret func51: ;set segment base for disk I/O ;====== mov dmabase,dx ret ; ;*************** end BDOS entry module *************** ;**************** BDOS Disk Functions **************** ;***************************************************** ;* ;* bdos function table ;* ;***************************************************** ; structure of entry in functab btab_addr equ word ptr 0 btab_flag equ byte ptr (btab_addr + word) bf_getmx equ 001h ;get mxdisk queue bf_cshell equ 002h ;conditional shell function ; bdos function table bdofunc equ offset $ dw func14 ! db 0001b ; fxi14 equ 01h ;select disk dw func15 ! db 0001b ; fxi15 equ 02h ;open file dw func20 ! db 0011b ; fxi20 equ 07h ;read sequential ;======== bdoentry: ; bdos module entry point ;======== ; entry: SI = offset of bdos functab entry ; DX = argument ; DS = ES = system data area ; exit: BX = return code mov ax,word ptr p_dsk mov word ptr seldsk,ax ;set default disk and user code mov cx,zerolength ! xor ax,ax ;zero local variables mov di,offset fcbdsk rep stosb mov info,dx ;info=dx cmp mult_cnt,1 ! je noshell ;if mult_cnt <> 1 and test cs:btabflag[si],bf_cshell ; func uses mult_cnt then jnz shell ; use bdos multi sector shell noshell: call call_bdos retmon: mov bx,aret ret ; shell: ;----- ; entry: SI = offset of functab entry mov shell_si,si mov ax,dmaad! mov shell_dma,ax mov ah,cs:btabflag[si] call parsave33 mov shell_flag,true mov al,mult_cnt mult_io1: MOV MULT_NUM,AL push ax mov si,shell_si mov dx,info call call_bdos mov bl,byte ptr aret or bl,bl jz no_shell_err mov bh,mult_cnt pop ax sub bh,al ;BH = # of successfull records jmps shell03 no_shell_err: add dmaad,80h pop ax! dec al jnz mult_io1 xor bx,bx shell03: mov aret,bx mov ax,shell_dma! mov dmaad,ax mov shell_flag,false call parunsave jmp retmon ; call_bdos: ;--------- ; entry: DX = argument ; SI = offset of bdos functab entry call setdata ;ready to go to the function mov savesp,sp call cs:btab_addr[si] bdos_return: cmp resel,0 je retmon5 mov al,fcbdsk mov info_fcb,al retmon5: cmp parcopfl,true jne retmon6 call parunsave ;copy local vars to uda retmon6: ret ; parsave33: ;copy 33 byte length FCB ;--------- mov cl,33 ;jmps parsave ; parsave: ;copy FCB from user segment to bdos segment ;------- ; entry: CL = length of FCB to save test shell_flag,true jnz parret mov parcopfl,true mov parlg,cl xor ch,ch mov si,info mov di,offset info_fcb push ds mov ds,parametersegment rep movsb pop ds parret: ret ; parunsave: ;copy local FCB to user segment ;--------- test shell_flag,true jnz parret mov cl,parlg xor ch,ch mov si,offset info_fcb mov di,info push es mov es,parametersegment rep movsb pop es ret setlret1: mov al,1 staret: mov lret,al ret ;these functions added for mpm interface ;***************************************************** ;* ;* bdos - xios interface ;* ;***************************************************** ;====== ======================== xiosif: ; xios interface routine ;====== ======================== ; entry: AL = function number ; CX = argument 1 ; DX = argument 2 ; exit: AX = BX = output push es callf dword ptr iosentry cld! pop es ret ;======== ================================ rwxiosif: ; disk read/write xios interface ;======== ================================ ; entry: AL = function number ; exit: AX = BX = output mov dx,arecord mov ch,byte ptr arecord+2 mov bl,curdsk mov bh,1 xchg bh,mult_sec ;BH = multi sector count ; stack on entry to the xios push bx ; +C | DRV | MCNT | push track ; +C | TRACK | push sector ; +8 | SECTOR | push curdmabase ; +6 | DMA_SEG | push curdma ; +4 | DMA_OFF | ; +2 | RET_SEG | ; SP+0 | RET_OFF | callf dword ptr iosentry add sp,10 ;remove parameters from stack cld! push ds! pop es ret ;***************************************************************** ;***************************************************************** ;** ** ;** b a s i c d i s k o p e r a t i n g s y s t e m ** ;** ** ;***************************************************************** ;***************************************************************** ;***************** bdos file system ****************** ; error message handlers pererror: ;report permanent error ;-------- mov ch,1 jmps goerr selerror: ;report select error ;-------- mov curdsk,0ffh mov ch,4 goerr: mov cl,0ffh mov aret,cx ;set aret goback: mov sp,save_sp jmp bdos_return ; local subroutines for bios interface ;----------------------------------------------------- move: ;block move data ;---- ; entry: CL = length of data to move ; DX = source offset ; BX = destination offset xor ch,ch mov si,dx mov di,bx rep movsb ret selectdisk: ;---------- ; select the disk drive given in AL, and fill ; the base addresses curtrka - alloca, then fill ; the values of the disk parameter block ; entry: AL = disk to select ; exit: C flag set if no error mov cl,al ;current disk# to cl ;lsb of dl = 0 if not yet mov al,io_seldsk ;logged in call xiosif ;bx filled by call ;bx = 0000 if error, or bx,bx ;otherwise disk headers jz ret4 ;rz - carry flag reset add bx,8 mov si,bx MOV di,OFFSET DPBADDR ;dx= source for move, bx=dest mov cx,addlist ;addlist filled rep movsb ;now fill the disk mov si,dpbaddr ;parameter block mov di,offset sectpt ;bx is destination mov cx,dpblist rep movsb ;data filled mov cl,physhf ;convert # sectors per track shl sectpt,cl ; from physical to logical ;set single/double map mode mov al,byte ptr maxall+1 ;largest allocation number or al,al jz retselect ;if high order of maxall not mov al,1 ;zero then use double disk map retselect: dec al mov single,al ;true if single disk map mode stc ;select disk function ok ret4: ret rdbuff: ;read buffer and check if ok ;------ mov al,io_read call rwxiosif ;current drive, track,.... diocomp: ;check for disk errors ;------- ; entry: AL = xios return code or al,al! jz ret4 ;rz jmp pererror ; no seek: ;seek the track given by arecord (actual record) ;---- mov ax,arecord ;compute track/sector xor dx,dx mov dl,byte ptr arecord+2 div sectpt ;dx=sector, ax=track add ax,offsetv MOV TRACK,ax ;save bios/xios track MOV CL,PHYSHF SHR dx,CL ;PHY SECTOR = SHR(LOG SECTOR,PHYSHF) MOV SECTOR,dx ;save bios/xios sector ret ; utility functions for file access dmposition: ;compute disk map position for vrecord ;---------- ; exit: AL = disk map position of vrecord mov cl,blkshf ;shift count to CL mov ch,vrecord ;current virtual record to a shr ch,cl ;CH = shr(vrecord,blkshf) ; = vrecord/2**(sect/block) neg cl add cl,7 ;CL = 7 - blkshf mov al,extval ;extent value and extmsk ;blkshf = 3,4,5,6,7 ;CL = 4,3,2,1,0 ;shift is 4,3,2,1,0 shl al,cl ;AL = shl(ext and extmsk,7-blkshf) add al,ch ;add the previous ;shr(vrecord,blkshf) value ;AL 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 cx ;----- ; entry: CX = index into disk map ; exit: BX = disk map value at position CX mov bx,offset info_fcb+dskmap add bx,cx ;index by asingle byte value cmp single,0 ;single byte/map entry? jz getdmd ;get disk map single byte mov bl,[bx] xor bh,bh ret ;with bx=00bb getdmd: add bx,cx ;bx=.fcb(dm+1*2) mov bx,[bx] ;return double precision value ret index: ;compute disk block number from current fcb ;----- ; exit: BX = disk map value for vrecord in current fcb ; Z flag set according to value in BX call dmposition ;0...15 in register al MOV DMINX,AL mov cl,al xor ch,ch call getdm ;value to bx mov arecord,bx or bx,bx ret atran: ;compute actual record address, assuming index called ;----- mov cl,blkshf ;shift count to reg al mov ax,arecord xor bh,bh mov bl,ah shl ax,cl shl bx,cl xchg ax,bx mov al,vrecord and al,blkmsk ;masked value in al mov blkoff,al or bl,al mov arecord,bx ;arecord=bx or ;(vrecord and blkmsk) mov byte ptr arecord+2,ah ret getfcb: ;set local variables from currently addressed fcb ;------ mov al,info_fcb+nxtrec mov vrecord,al ;vrecord=fcb(nxtrec) cmp info_fcb+reccnt,0 jne getfcb0 call get_dir_ext mov cl,al call set_rc getfcb0: mov al,info_fcb+reccnt cmp al,81h! jb getfcb1 mov al,80h getfcb1: mov rcount,al ;rcount=fcb(reccnt) mov al,extmsk ;extent mask to a and al,info_fcb+extnum ;fcb(extnum) and extmsk mov extval,al ret setfcb: ;place local values back into current fcb ;------ mov al,1 add al,vrecord mov info_fcb+nxtrec,al ;fcb(nxtrec)=vrecord+seqio cmp info_fcb+reccnt,80h jnb ret41 ;dont reset if fcb(rc) > 7fh mov al,rcount mov info_fcb+reccnt,al ;fcb(reccnt)=rcount ret41: ret getdptra: ;-------- ; compute the address of a directory element at ; positon dptr in the buffer ; exit: BX = buffa + dptr mov bl,dptr xor bh,bh add bx,buffa ;bx = buffa + dptr ret rddir: ;read the current directory record ;----- MOV ax,DCNT ;seek the record containing MOV CL,DSKSHF! SHR ax,CL ; the current dir entry MOV ARECORD,ax ;ARECORD = SHR(DCNT,DSKSHF) MOV BYTE PTR ARECORD+2,0 MOV AH,3 ;LOCATE COMMAND CALL DEBLOCK_DIR ;JMPS SETDATA setdata: ;set data dma address ;------- MOV ax,DMABASE MOV CUR_DMABASE,ax MOV ax,DMAAD MOV CURDMA,ax RET endofdir: ;check if end of directory (dcnt = 0ffffh) ;-------- ; exit: Z flag set if at end of directory mov bx,offset dcnt test_ffff: ;--------- cmp w[bx],0ffffh ret setenddir: ;set dcnt to the end of directory (dcnt = 0ffffh) ;--------- mov dcnt,enddir ret read_dir: ;read next directory entry ;-------- mov dx,dirmax ;in preparation for subtract mov bx,dcnt inc bx mov dcnt,bx ;dcnt=dcnt+1 ;continue while dirmax >= dcnt ;(dirmax-dcnt no cy) sub dx,bx jb setenddir ;yes, set dcnt to end ;of directory ; not at end of directory, seek next element ;cl=initialization flag mov al,ldcnt and al,dskmsk ;low(dcnt) and dskmsk mov cl,fcbshf ;to multiply by fcb size shl al,cl ;a = (low(dcnt) and dskmsk) ;shl fcbshf mov dptr,al ;ready for next dir operation or al,al jnz ret71 ;return if not a new record call rddir ;read the directory record ret71: ret compext: ;compare extent# in al with that in cl ;------- ; entry: AL,CL = extent numbers to compare ; exit: Z flag set if extent numbers match ; BX,CX,DX = preserved push cx ;save cx's original value mov ch,extmsk not ch ;ch has negated form of ;extent mask and cl,ch ;low bits removed from cl and al,ch ;low bits removed from al sub al,cl and al,maxext ;set flags pop cx ;restore cx ret get_dir_ext: ;----------- ; compute directory extent from fcb ; scan fcb disk map backwards ; upon return dminx = 0 if no blocks are in fcb ; exit: AL = directory extent number ; BX = .fcb(extnum) mov bx,offset info_fcb+nxtrec ;BX = .fcb(vrecord) mov dx,1001h ;DH = disk map position (rel to 1) ;DL = no blocks switch get_de1: dec dh dec bx ;decrement disk map ptr cmp b[bx],0 ;is disk map byte non-zero ? jne get_de2 ; yes or dh,dh ; no - continue scan jnz get_de1 dec dl ;DL = 0 if no blocks found get_de2: mov dminx,dl ;dminx = 0 if no blocks in fcb cmp single,true ;are disk block indexes single byte ? mov al,dh ;al = block offset in disk map jz get_de3 ;yes shr al,1 ;divide block offset by 2 ; al = last non-zero block index in fcb (rel to 0) ; compute ext offset from last non-zero block index by ; shifting block index right 7-blkshf get_de3: mov cl,7 sub cl,blkshf shr al,cl ;al = ext offset mov ah,extmsk ;if ext offset > extmsk then cmp ah,al ; continue scan jb get_de1 ; dir ext = (fcb ext & (~extmsk) & maxext) | ext offset mov bx,offset info_fcb+extnum ;bx = .fcb(ext) mov cl,[bx] ;cl = fcb extent value not ah ;ah = ~extmsk and ah,maxext ;ah = ah & maxext and ah,cl ;ah = ah & fcb extent or al,ah ;al = dir ext ret searchi: ;search initialization ;------- mov bx,offset info_fcb mov srcha,bx ;searcha = .info_fcb mov srchl,cl ;searchl = cl ret search_name: ;search matching name ;----------- mov cl,namlen ;jmps search search: ;search for directory element at .info_fcb ;------ ; entry: CL = search length call searchi call setenddir ;dcnt = enddir ;to start at the beginning ;(drop through to searchn) ; searchn: ;------- ; search for the next directory element, assuming ; a previous call on search which sets searcha and searchhl ; exit: Z flag = 0 for successful searches call read_dir ;read next dir element call endofdir jz srchfin ;skip to end if so ;not end of directory, ;scan for match mov dx,srcha ;dx=beginning of user fcb call getdptra ;bx = buffa+dptr mov cl,srchl ;length of search to cl xor ch,ch ;ch counts up, cl counts down mov al,[bx] ;is fcb an xfcb ? and al,11101111b cmp al,[bx] je srch_loop ;no jmps search_n srchfin: ;end of directory, or empty name lret_eq_ff: mov al,255 mov ch,al inc ch ;zero flag set on unsuccessful jmp staret ;searches srchloop: or cl,cl jz endsearch mov si,dx lods al ;fcb character and al,07fh ;scan next character if not ;chksum byte cmp ch,chksum jz srchok ;not the ubytes field, ;extent field cmp ch,extnum ;may be extent field jz srchext ;skip to search extent cmp ch,modnum ;is field module # ? jnz $+4 ;no and al,3fh ;yes - mask off high order bits sub al,[bx] and al,7fh ;mask-out flags/extent modulus jnz searchn_jmp jmps srchok ;matched character srchext: ;AL = fcb character ;attempt an extent # match push cx ;save counters mov cl,[bx] ;directory character to c call compext ;compare user/dir char pop cx ;recall counters jnz searchn_jmp ;skip if no match srchok: ;current character matches inc dx! inc bx inc ch! dec cl jmps srchloop searchn_jmp: jmp searchn endsearch: ;entire name matches, return dir position xor al,al mov lret,al mov ch,al inc ch ;zero flag reset on successful ret ;searches check_wild: ;check for ? in file name or type ;---------- mov bx,offset info_fcb call chk_wild jnz ret10 mov al,9 ;extended error 9 jmp set_aret chk_wild: ;check fcb for ? marks ;-------- ; entry: BX = .fcb(0) ; exit: Z flag = 1 if ? mark found mov cx,3f0bh ;ch = 3f, cl = 11 chk_wild1: inc bx ;advance fcb ptr mov al,ch sub al,[bx] and al,ch jz ret10 ;rtn with z flag set if ? fnd dec cl jnz chk_wild1 or al,al ;rtn with z flag reset - no ?s ret10: ret open: ;search for the directory entry, copy to fcb ;---- call search_name jz ret10 ;return with lret=255 if end ;not end of directory,copy fcb opencopy: ;(referenced below to copy fcb push bx ;bx = .fcb(modnum) dec bx! dec bx mov ah,[bx] ;ah = extnum push ax ;save extnum & modnum call getdptra mov dx,bx ;dx = .buff(dptr) mov bx,offset info_fcb ;bx=.fcb(0) mov cl,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 cl,al ;cl = dir_ext pop ax! pop bx mov [bx],al ;restore modnum dec bx! dec bx mov [bx],ah ;restore extnum number ; bx = .user extent#, cl = 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 set_rc: ;------ ; entry: BX = .user extent # ; CL = dir extent # xor ch,ch mov si,offset info_fcb+reccnt mov al,[bx] ;al = current extent sub al,cl ;compare fcb ext to dir ext jz set_rc2 ;fcb ext = dir ext ; actual_rc = 0, fcb(rc) = fcb(rc) mov al,ch jae set_rc1 ;fcb ext > dir ext ; actual_rc = 0, fcb(rc) = 0 mov al,128 ;fcb ext < dir ext or al,[si] ; fcb(rc) = 128 | fcb(rc) set_rc1: mov [si],al ret11: ret set_rc2: cmp b[si],al ;is fcb(rc) = 0? jnz ret11 ;no xor al,al ;AL = 0 mov b[si],al ;required by func 99 cmp dminx,al ;do blocks exist in fcb? jz ret11 ;no mov b[si],80h ;fcb(rc) = 80h ret restore_rc: ;---------- ; if actual_rc ~= 0 then fcb(rc) = actual_rc ; entry: BX = .fcb(extnum) mov al,info_fcb+reccnt cmp al,81h jb restore_rc1 and al,7fh mov info_fcb+reccnt,al restore_rc1: ret openreel: ;-------- ; close the current extent, and open the next one ; if possible. rmf is true if in read mode. ; lret is set to 0 if successful mov al,info_fcb+modnum mov save_mod,al ;save current module # mov bx,offset info_fcb+extnum mov al,[bx] mov cl,al inc cl ;increment ext # call comp_ext ;did we cross a dir fcb boundary ? jnz $+5 jmp openr3 ;no mov al,maxext and al,cl mov [bx],al ;update fcb extent field jnz openr0 ;branch if in same module ; extent number overflow, go to next module add bx,(modnum-extnum) ;bx=.fcb(modnum) inc b[bx] ;fcb(modnum)=++1 ;module number incremented, ;check for overflow mov al,[bx] and al,maxmod ;mask high order bits jz openerr ;cannot overflow to 0 ;otherwise, ok to continue ;with new module openr0: call search_name ;next extent found? jz openerr ;end of file encountered call opencopy openr2: call getfcb ;set parameters xor al,al mov vrecord,al jmp staret ;lret = 0 ;ret openerr: ;------- mov bx,offset info_fcb+extnum mov al,save_mod mov 2[bx],al mov al,[bx] dec al and al,1fh mov [bx],al ;cannot move to next extent ;of this file jmp setlret1 ;lret = 1 ;ret openr3: mov [bx],cl ;increment extent field call get_dir_ext mov cl,al cmp al,[bx] ;is dir ext < fcb(ext)? jae openr4 ;no dec b[bx] ;decrement extent jmp set_lret1 ;yes - reading unwritten data openr4: call restore_rc call set_rc jmps openr2 diskread: ;(may enter from seqdiskread) ;-------- ;read the next record from ;the current fcb call getfcb ;sets parameters for the read mov al,vrecord cmp al,rcount ;vrecord-rcount ;skip if rcount > vrecord jb recordok ;not enough records in extent ;record count must be 128 ;to continue cmp al,128 ;vrecord = 128? jnz diskeof ;skip if vrecord<>128 call openreel ;go to next extent if so ;now check for open ok cmp lret,0 ;stop at eof jnz diskeof recordok: ;fcb addresses a record to read call index ;error 2 if reading ;unwritten data ;(returns 1 to be compatible ;with 1.4) ;arecord=0000? jz diskeof call atran ;arecord now a disk address CALL CHECK_NPRS JC RECORDOK2 JNZ $+5 JMP READ_DEBLOCK CALL SETDATA call seek ;to proper track,sector call rdbuff ;to dma address RECORDOK2: jmp setfcb ;replace parameter diskeof: jmp setlret1 ;lret = 1 ;ret CHECK_NPRS: ;---------- ; DIR_CNT CONTAINS THE NUMBER OF 128 BYTE RECORDS ; TO TRANSFER DIRECTLY. THIS ROUTINE SET DIR_CNT ; WHEN INITIATING A SEQUENCE OF DIRECT PHYSICAL I/O ; OPERATIONS. DIR_CNT IS DECREMENTED EACH TIME CHECK_NPRS ; IS CALLED DURING SUCH A SEQUENCE. ; exit: ~C FLG & ~Z FLG - DIRECT PHYSICAL I/O OPERATION ; ~C FLG & Z FLG - INDIRECT(DEBLOCK) I/O OPERATION ; C FLG - NO PHYSICAL I/O OPERATION MOV CH,blk_off ;CH = VRECORD & BLKMSK MOV AL,DIR_CNT CMP AL,2 ;IS DIR_CNT > 1? JC CHECK_NPR1 ;NO DEC AL ;DIR_CNT = DIR_CNT - 1 MOV DIR_CNT,AL ;we are in mid-multi sector i/o STC ret ;RETURN WITH C FLAG SET CHECK_NPR1: MOV AL,PHYMSK ;CL = PHYMSK MOV CL,AL AND AL,CH ;AL = VRECORD & PHYMSK ;ARE WE IN MID-PHYSICAL RECORD? JZ CHECK_NPR11 ;NO CHECK_NPR1X: OR CL,CL ;IS PHYMSK = 0? JZ CHECK_NPR1Y ;YES XOR AL,AL ;RETURN WITH Z FLAG SET & C FLAG RESET RET CHECK_NPR1Y: OR AL,1 ;RESET C & Z FLAGS RET CHECK_NPR11: MOV DH,CL not DH ;DH = ~ PHYMSK MOV AL,MULTNUM CMP AL,2 ;IS MULT_NUM < 2? JC CHECK_NPR1X ;YES MOV BX,OFFSET VRECORD MOV AH,[BX] ;AH = VRECORD ADD AL,ah CMP AL,80H JC CHECK_NPR2 MOV AL,80H CHECK_NPR2: ;AL = MIN(VRECORD + MULT_NUM,80H) = X PUSH CX ;SAVE VRECORD & BLKMSK, PHYMSK MOV B[BX],7FH ;VRECORD = 7F PUSH BX! PUSH AX MOV BL,AL ;BL = X MOV AL,BLKMSK MOV DL,AL INC DL ;DL = BLKMSK + 1 not AL AND AH,AL ;AH = VRECORD & ~BLKMSK MOV AL,RCOUNT AND AL,DH ;AL = RCOUNT & ~PHYMSK CMP AL,BL ;IS AL < X? JC CHECK_NPR23 ;YES MOV AL,BL ;AL = MIN(VRECORD + MULT_NUM,80H) = X CHECK_NPR23: SUB AL,AH ;AL = AL - VRECORD & ~BLKMSK CMP AL,DL ;IS AL < BLKMSK + 1? JC CHECK_NPR9 ;YES PUSH AX ;AL = MAX # OF RECORDS CALL DMPOSITION ;COMPUTE MAXIMUM DISK POSITION MOV CH,AL ;CH = MAX DISK POS MOV AL,DMINX ;AL = CURRENT DISK POS CMP AL,CH ;IS CURR POS = MAX POS? MOV DL,AL JZ CHECK_NPR5 ;YES MOV CL,AL PUSH CX ;CL = CURR POS, CH = MAX POS MOV CH,0 CALL GET_DM ;BX = BLOCK NUMBER (CURR POS) CHECK_NPR4: PUSH BX INC CX ;CURR POS = CURR POS + 1 CALL GET_DM ;GET NEXT BLOCK(CURR POS) POP DX INC DX CMP BX,DX ;DOES CURR BLK = LAST BLK + 1? JZ CHECK_NPR4 ;YES DEC CL ;CL = INDEX OF LAST SEQ BLK POP DX MOV AL,DH ;AL = MAX POS CMP AL,CL ;IS AL < LAST SEQ BLK POS JC CHECK_NPR5 ;YES MOV AL,CL CHECK_NPR5: ;AL = LAST BLK POS SUB AL,DL ;AL = AL - STARTING POS MOV CH,AL INC CH ;CH = # OF CONSECUTIVE BLOCKS MOV AL,BLKMSK INC AL ;AL = BLKMSK + 1 MUL CH ;AL = # OF CONSECUTIVE LOGICAL RECS POP CX ;CL = MAXIMUM # OF RECORDS XCHG AL,CL CMP AL,CL ;IS MAX < # OF CONS. RECS? JC CHECK_NPR9 ;YES MOV AL,CL CHECK_NPR9: ;AL = # OF CONSECUTIVE RECS POP CX POP BX MOV [BX],CH ;RESTORE VRECORD POP CX MOV DH,MULT_NUM SUB AL,CH ;AL = AL - VRECORD & blkmsk CMP AL,DH ;IS AL < MULT_NUM JC CHECK_NPR10 ;YES MOV AL,DH CHECK_NPR10: ;AL = # OF CONSECUTIVE RECS not CL AND AL,CL ;IF DIR_CNT = 0 THEN JZ RET13B ;RETURN WITH Z FLAG SET, C FLAG RESET MOV DIR_CNT,AL ;DIR_CNT = AL & ~PHYMSK MOV CL,PHYSHF SHR AL,CL ;AL = # OF CONSECUTIVE PHYSICAL RECS mov mult_sec,al ;save multisector count for r/w OR AL,1 ;RETURN WITH C AND Z FLAGS RESET RET13B: RET tmp_select: ;---------- ; entry: DL = drive to select (0-f) mov seldsk,dl curselect: ;--------- mov al,seldsk cmp al,curdsk jne select ;don't select if seldsk = curdsk inc al! jz select0 ret SELECT: ;select disk in info_fcb for subsequent input or output ops ;------ cmp al,16 ! jb disk_select select0: jmp selerror disk_select: ;----------- ; entry: AL = disk to select mov adrive,al mov curdsk,al xor dx,dx ;dl = 1 if drive logged in call selectdisk jnc select0 ;carry set if select ok ret parsave33r: ;---------- call parsave33 ;jmps reselect reselect: ;-------- mov cl,07fh mov bx,offset info_fcb+f7 and [bx],cl ;fcb(7) = fcb(7) & 7fh and 1[bx],cl and byte ptr extnum-f7[bx],1fh ;fcb(ext) = fcb(ext) & 1fh ;check current fcb to see if reselection necessary mov resel,true ;mark possible reselect mov al,info_fcb ;drive select code mov fcbdsk,al ;save drive and al,00011111b ;non zero is auto drive select dec al ;drive code normalized to ;0...30, or 255 cmp al,0ffh jz noselect ;auto select function, mov seldsk,al ;save seldsk noselect: call curselect ;set user code mov al,usrcode ;0...31 mov info_fcb,al ret compare: ;compare strings ;------- ; entry: CL = length of strings ; BX,DX = offset of strings ; exit: Z flag set if strings match mov ch,0 mov si,bx mov di,dx repe cmps al,al ret ; individual function handlers ;----------------------------------------------------- func14: ;select disk info ;====== call tmp_select mov al,seldsk mov p_dsk,al ret func15: ;open file ;====== call parsave33r ;copy fcb from user seg. mov info_fcb+modnum,0 ;fcb(modnum)=0 call check_wild ;check for ?s in fcb call open ;attempt to open fcb call openx ;returns if unsuccessful xor al,al ret30: ret ;no - open failed openx: ;----- call end_of_dir ;was open successful jz ret30 ;no mov bx,offset info_fcb+nxtrec cmp b[bx],0ffh jne openxa mov al,info_fcb+chksum mov [bx],al openxa: pop bx ;discard return address MOV CL,40H ret func20: ;read a file ;====== call parsave33r jmp disk_read ;jmp goback set_aret: ;-------- mov cl,al ;save error index mov byte ptr aret+1,al ;aret+1 = extended errors call lret_eq_ff jmp goback ;return physical & extended errors ; BLOCKING/DEBLOCKING BUFFER CONTROL BLOCK (BCB) FORMAT ; +-------+-------+-------+-------+-------+-------+ ; 00h | DRV | RECORD | PEND | SEQ | ; +-------+-------+-------+-------+-------+-------+ ; 06h | TRACK | SECTOR | BUFFER_ADDR | ; +-------+-------+-------+-------+-------+-------+ ; 0Ch | LINK | PROCESS_OFF | ; +-------+-------+-------+-------+ READ_DEBLOCK: ;------------ mov ah,1 ;AH = 1 CALL DEBLOCK_DTA JMP SET_FCB DEBLOCK_DIR: ;----------- MOV CUR_DMABASE,DS ;BUFFERS IN SYSTEM DATA AREA MOV BX,DIRBCBA jmps DEBLOCK ;PREVIOUS LOCATE CALL DEBLOCK_DTA: ;----------- MOV BX,DTABCBA MOV CUR_DMABASE,0 ;get segment from BCB DEBLOCK: ;BDOS BLOCKING/DEBLOCKING ROUTINE ;------- ; entry: Z flag reset -> get new BCB address ; AH = 1 -> READ COMMAND ; = 2 -> WRITE COMMAND ; = 3 -> LOCATE COMMAND ; = 4 -> FLUSH COMMAND ; = 5 -> DIRECTORY UPDATE MOV DEBLOCK_FX,AH ;SAVE DEBLOCK FX mov cl,phymsk ;CL = PHYMSK MOV AL,BYTE PTR ARECORD AND AL,cl MOV PHY_OFF,AL ;PHY_OFF = LOW(ARECORD) & PHYMSK not cl ;CL = ~PHYMSK and BYTE PTR ARECORD,cl ;LOW(ARECORD) = LOW(ARECORD) & ~PHYMSK mov bx,[bx] ;GET BCB ADDRESS MOV CURBCBA,BX ;BX = 1st curbcb MOV ax,10[BX] ;dma address field - offset/segment cmp cur_dmabase,0 ;if cur_dmabase <> 0, deblocking is jne deblock0 ; for dir buf and addr is offset mov cur_dmabase,ax ;else deblocking data buffer and xor ax,ax ; addr is segment, offset = 0 deblock0: MOV CURDMA,ax ;save current buffer address CALL DEBLOCK9 ;BX=CURBCBA, DX=.ADRIVE, CL=4 cmp b[bx],0ffh! je deblock2 CALL COMPARE ;DOES BCB(0-3) = ADRIVE || ARECORD? JZ DEBLOCK45 ;YES DEBLOCK2: mov bx,curbcba mov b[bx],0ffh ;discard in case of error MOV AL,2 CALL DEBLOCK_IO ;READ PHYSICAL RECORD CALL DEBLOCK9 ;BX=CURBCBA, DX=.ADRIVE, CL=4 CALL MOVE MOV B[DI],0 ;CURBCBA->BCB(4) = 0 DEBLOCK45: xor al,al MOV ah,PHY_OFF shr ax,1 ;AX = phy_off * 080h MOV SI,CURDMA ADD SI,AX ;SI = CURDMA + PHY_OFF*80H MOV al,DEBLOCK_FX CMP al,3 ;IS DEBLOCK_FX = LOCATE? JNZ DEBLOCK6 ;NO MOV BUFFA,SI ;YES RET DEBLOCK6: MOV CX,40H ;transfer 40h words MOV DI,DMAAD mov ax,dmabase mov dx,cur_dmabase push ds! push es mov ds,dx ;setup source segment mov es,ax ;setup destination segment rep movsw ;transfer data pop es! pop ds RET DEBLOCK9: ;SETUP FOR MOVE OR COMPARE MOV BX,CURBCBA MOV DX,OFFSET ADRIVE MOV CL,4 RET DEBLOCK_IO: ;---------- ; entry: AL = 0 -> SEEK ONLY ; = 1 -> WRITE ; = 2 -> READ PUSH AX CALL SEEK POP AX DEC AL JS deblk_io2 ; JNZ deblk_io1 ; mov cl,1 ; JMP WRBUFF ;deblk_io1: CALL RDBUFF deblk_io2: mov si,offset track mov di,curbcba add di,6 ;MOVE TRACK & SECTOR mov cx,2 ;TO BCB rep movsw ret endcode equ offset $ ;*************** end bdos file system **************** ;****************** BDOS data area ******************* ; ; dseg ; Variables in data segment: org endcode iosentry rw 0 biosco dw biosoffset ;offset and bioscs dw 0 ;segment for callf to BIOS ; ;***************************************************** ; ;***************************************************** ;* ;* bdos file system data area ;* ;***************************************************** ; variables in data segment: ; ;the following variables are set to zero upon entry to file system fcbdsk db 0 ;disk named in fcb parcopfl db 0 ;true if parameter block copied aret dw 0 ;adr value to return lret equ byte ptr aret ;low(aret) resel db 0 ;reselection flag DIR_CNT DB 0 ;DIRECT I/O COUNT MULT_NUM DB 0 ;MULTI-SECTOR COUNT used in bdos zerolength equ (offset $)-(offset fcbdsk) mult_sec db 1 ;multi sector count passed to xios BLK_OFF DB 0 ;RECORD OFFSET WITHIN BLOCK ; DEBLOCK_FX DB 0 ;DEBLOCK FUNCTION # PHY_OFF DB 0 ;RECORD OFFSET WITHIN PHYSICAL RECORD CURBCBA DW 0 ;CURRENT BCB OFFSET TRACK DW 0 ;BCB RECORD'S TRACK SECTOR DW 0 ;BCB RECORD'S SECTOR ; seldsk,usrcode are initialized as a pair p_dsk db 0 ;selected disk num p_user db 0 ;curr user num ;info dw 0 ;info adr srcha dw 0 ;search adr ;the following variable order is critical ;variables copied from uda for mp/m x ;variables included in fcb checksum for mp/m and cp/m x ;variables used to access system lock list for mp/m x ;dmaad dw 0 ;dma offset 1 ;dmabase dw 0 ;dma base 2 ;srchl db 0 ;search len 4 ;dcnt dw 0 ;directory counter 7 ;dblk dw 0 ;directory block 8 ?? - not used - ?? u_mult_cnt rb 0 mult_cnt db 1 ;bdos multi-sector cnt 10 ;df_password rb 8 ;process default pw 11 ;high_ext db 0 ;fcb high extent bits 2 ;xfcb_read_only db 0 ;xfcb read only flag 3 curdsk db 0ffh ;current disk 4 1 org ((offset $) + 1) and 0fffeh ; curtrka - alloca are set upon disk select ; (data must be adjacent) ;cdrmaxa dw 0 ;ptr to cur dir max val ;drvlbla dw 0 ;drive label data byte addr buffa dw 0 ;ptr to dir dma addr dpbaddr dw 0 ;curr disk param block addr checka dw 0 ;curr checksum vector addr alloca dw 0 ;curr alloc vector addr DIRBCBA DW 0 ;DIRECTORY BUFFER CONTROL BLOCK ADDR DTABCBA dw 0 ;DATA BUFFER CONTROL BLOCK ADDR addlist equ 10 ;"$-buffa" = addr list size ; sectpt - offset obtained from disk parm block at dpbaddr ; (data must be adjacent) sectpt dw 0 ;sectors per track blkshf db 0 ;block shift factor blkmsk db 0 ;block mask extmsk db 0 ;extent mask maxall dw 0 ;max alloc num dirmax dw 0 ;max dir num dirblk dw 0 ;reserved alloc bits for dir chksiz dw 0 ;size of checksum vector offsetv dw 0 ;offset tracks at beginning PHYSHF DB 0 ;PHYSICAL RECORD SHIFT FACTOR PHYMSK DB 0 ;PHYSICAL RECORD MASK endlist rs 0 ;end of list dpblist equ (offset endlist)-(offset sectpt) ;size ; local variables save_mod db 0 ;open_reel module save field dminx db 0 ;local for diskwrite single db 0 ;set true if single byte ;alloc map rcount db 0 ;record count in curr fcb extval db 0 ;extent num and extmsk vrecord db 0 ;curr virtual record ADRIVE DB 0 ;CURRENT DISK - must preceed arecord arecord dw 0 ;curr actual record db 0 ;curr actual record high byte CUR_DMABASE DW 0 CUR_DMA DW 0 ; local variables for directory access dptr db 0 ;directory pointer 0,1,2,3 ; shell variables shell_si dw 0 ;bdos command offset shell_dma dw 0 ;dmaad save area shell_flag db 0 ;parsave shell flag ; special 8086 variables: u_retseg dw 0 ;user return segment parlg db 0 ;len of parameter block ; bdos stack switch variables and stack ; used for all bdos disk functions org ((offset $) + 1) and 0fffeh ; 69 word bdos stack dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch dw 0cccch,0cccch,0cccch bdosstack rw 0 save_sp rw 1 ; local buffer area: info_fcb rb 0;40 ;local user FCB db 'COPYRIGHT(C)1983,' db 'DIGITAL RESEARCH(01/26/83)' db 'XXXX-0000-654321' ; info dw 0 ;information address ; u_wrkseg rw 0 parametersegment dw 0 ;user parameter segment ; ; dmaad dw 0 ;user DMA address dmabase dw 0 ;user DMA base seldsk db 0 ;current user disk usrcode db 0 ;current user number dcnt dw 0 ;directory index ldcnt equ byte ptr dcnt ;low(dcnt) srchl db 0 ;search length ; ; db 0 ;end of this data area ;***************** end BDOS data area **************** ;******************** end of BDOS ******************** end