Files
Digital-Research-Source-Code/MPM OPERATING SYSTEMS/MPM-86/MPM-86 2.0 SOURCES/01/FILE1.BDO
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

904 lines
25 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;*****************************************************************
;*****************************************************************
;** **
;** b a s i c d i s k o p e r a t i n g s y s t e m **
;** **
;*****************************************************************
;*****************************************************************
;
;
; error message handlers
;
pererror:
;report permanent error
MOV BX,OFFSET PERMSG
MOV CH,1
JMPS GOERR
roderror:
;report read/only disk error
MOV BX,OFFSET RODMSG
MOV CH,2
JMPS GOERR
roferror:
;report read/only file error
MOV BX,OFFSET ROFMSG
MOV CH,3
JMPS GOERR
selerror:
;report select error
MOV BX,OFFSET SELMSG
MOV CH,4
goerr:
MOV CL,0FFH
MOV ARET,CX ;SET ARET
CMP ERROR_MODE,0FFH ;IF ERROR_MODE = 0FFH THEN
JE RTN_PHY_ERRS ;RETURN PHYSICAL ERROR TO USER
JMPS REPORT_ERR ;REPORT ERROR TO USER
RTN_PHY_ERRS:
MOV BX,OFFSET RETURN_FFFF ;IF RETURN_FFFF THEN ARET = 0FFFFH
TEST B[BX],TRUE
JNZ $+5
JMP GOBACK
MOV B[BX],FALSE
MOV ARET,0FFFFH
JMP GOBACK
REPORT_ERR:
PUSH BX ;SAVE ERROR MESSAGE OFFSET
CALL XCRLF ;PRINT CR,LF
MOV AL,SELDSK
ADD AL,'A'
MOV DSKERR,AL ;SET D: FIELD
MOV CX,OFFSET DSKMSG
CALL XPRINT ;PRINT "Bdos Err On D:"
POP CX
CALL XPRINT ;PRINT ERROR MESSAGE
MOV AL,FX ;CONVERT FUNCTION TO CHARACTER
MOV CH,30H
MOV BX,OFFSET PRFX1
CMP AL,100
JC RPT_ERR1
MOV B[BX],31H
INC BX
SUB AL,100
RPT_ERR1:
SUB AL,10
JC RPT_ERR2
INC CH
JMPS RPT_ERR1
RPT_ERR2:
MOV [BX],CH
INC BX
ADD AL,3AH
MOV [BX],AL
INC BX
MOV B[BX],20H
MOV BX,OFFSET PR_FCB ;0 = MESSAGE DELIMITER
MOV B[BX],0
TEST RESEL,TRUE ;WAS RESELECT CALLED?
JZ RPT_ERR3 ;NO - DON'T PRINT FCB
MOV B[BX],20H ;REMOVE DELIMITER
MOV DX,INFO
INC DX
MOV BX,OFFSET PR_FCB1
MOV CL,8
CALL MOVE ;MOVE FILE NAME TO MESSAGE
MOV BX,DI
MOV DX,SI
MOV B[BX],'.' ;MOVE '.' TO MESSAGE
INC BX
MOV CL,3 ;MOVE FILE TYPE TO MESSAGE
CALL MOVE
RPT_ERR3:
CALL XCRLF ;ADVANCE TO NEW LINE
MOV CX,OFFSET PR_FX
CALL XPRINT ;PRINT "Bdos Function : ### "
; + "File: FFFFFFFF.TTT"
CALL XCRLF
CMP ERROR_MODE,0FEH ;IS ERROR MODE PRINT &
; RETURN ERRORS?
JE RPT_ERR4 ;YES
MOV SI,RLR
OR PFLAG[SI],PF_CTLC ;SET PROCESS ^C FLAG
RPT_ERR4:
JMP RTN_PHY_ERRS
;
; local subroutines for bios interface
;
;
;
move:
;move data length of length cl from source dx to
;destination given by bx
push cx
mov ch,0
mov si,dx
mov di,bx
rep movs al,al
pop cx
ret
;
;
selectdisk:
;select the disk drive given by curdsk, and fill
;the base addresses curtrka - alloca, then fill
;the values of the disk parameter block
;
MOV CURDSK,0FFH
MOV CL,SELDSK ;current disk# to cl
;lsb of dl = 0 if not yet
;logged in
call seldskf ;bx filled by call
;bx = 0000 if error,
;otherwise disk headers
cmp bx,0
jz ret4 ;rz
mov dx,[bx]
add bx,2
mov cdrmaxa,bx
add bx,2
MOV DRVLBLA,BX
ADD BX,4
;dx still contains .tran
xchg bx,dx
mov tranv,bx ;.tran vector
mov bx,offset buffa ;dx= source for move, bx=dest
mov cl,addlist
call move ;addlist filled
;now fill the disk
;parameter block
mov dx,dpbaddr
mov bx,offset sectpt ;bx is destination
mov cl,dpblist
call move ;data filled
;set single/double map mode
mov al,byte ptr maxall+1 ;largest allocation number
MOV SINGLE,TRUE ;assume a=00
or al,al
jz retselect
;high order of maxall not
;zero, use double dm
MOV SINGLE,FALSE
retselect:
MOV AL,SELDSK
MOV CURDSK,AL
INC AL
ret4: ret ;select disk function ok
;
home:
;move to home position, then offset to start of dir
call homef
;first directory pos. selected
MOV DBLK,0
ret
;
PASS_ARECORD:
MOV DX,ARECORD
MOV CH,BYTE PTR ARECORD+2
RET
;
rdbuff:
;read buffer and check if ok
CALL PASS_ARECORD
call readf ;current drive, track,....
jmps diocomp ;check for i/o errors
;
wrbufflg:
mov cl,wflag
wrbuff:
;write buffer and check condition
;write type (wrtype) is in register cl
;wrtype = 0 => normal write operation
;wrtype = 1 => directory write operation
;wrtype = 2 => start of new block
CALL PASS_ARECORD
OR CL,REM_DRV
call writef ;current drive, track, ...
diocomp: ;check for disk errors
or al,al
jz ret4 ;rz
CMP AL,2
JZ $+5
jmp pererror
JMP RODERROR
;
seekdir:
;seek the record containing the current dir entry
MOV DX,0FFFFH! XOR AH,AH ; MASK = FFFF
MOV BX,DBLK! CMP BX,0! JZ SEEKDIR1
INC DX! MOV DL,BLKMSK
MOV CL,BLKSHF! MOV AL,BH
; AH+BX = SHL(DBLK,BLKSHF)
SHL BX,CL! SHL AX,CL
SEEKDIR1:
MOV SI,DCNT
MOV CL,DSKSHF! SHR SI,CL
; ARECORD = SHL(DBLK,BLKSHF) + SHR(DCNT,DSKSHF) & MASK
AND DX,SI
ADD BX,DX! ADC AH,0
MOV ARECORD,BX! MOV BYTE PTR ARECORD+2,AH
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
PUSH DX! MOV CX,AX
ADD CX,OFFSETV
CALL SETTRKF ;SET BIOS/XIOS TRACK
POP CX! MOV DX,TRANV
CALL SECTRAN ;SET BIOS/XIOS SECTOR
MOV CX,BX
JMP SETSECF ;RET
;
; utility functions for file access
;
dmposition:
;compute disk map position for vrecord to bx
mov bx,offset blkshf
mov cl,[bx] ;shift count to cl
mov al,vrecord ;current virtual record to a
shr al,cl
;a = shr(vrecord,blkshf) = vrecord/2**(sect/block)
mov ch,al ;save it for later addition
mov cl,7
sub cl,[bx]
mov al,extval ;extent value ani extmsk
;
;blkshf = 3,4,5,6,7
;cl=4,3,2,1,0
;shift is 4,3,2,1,0
shl al,cl
;arrive here with a = 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
;
GET_DMA: ;BX = .FCB(DSKMAP)
MOV BX,INFO
ADD BX,DSKMAP
RET
;
getdm:
;return disk map value from position given by cx
CALL GET_DMA
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]
mov bh,0
ret ;with bx=00bb
getdmd:
add bx,cx ;bx=.fcb(dm+1*2)
;return double precision value
mov bx,[bx]
ret
;
index:
;compute disk block number from current fcb
call dmposition ;0...15 in register al
mov cl,al
mov ch,0
call getdm ;value to bx
mov arecord,bx
ret
;
alloct:
;called following index to see if block allocated
mov bx,arecord
or bx,bx
ret
;
atran:
;compute actual record address, assuming index called
mov cl,blkshf ;shift count to reg al
mov bx,arecord
XOR AH,AH
MOV AL,BH
shl bx,cl
SHL AX,CL
mov ablock,bx ;save shifted block #
mov al,vrecord
and al,blkmsk ;masked value in al
or bl,al
mov arecord,bx ;arecord=bx or
;(vrecord and blkmsk)
MOV BYTE PTR ARECORD+2,AH
ret
;
GET_ATTS:
;GET INTERFACE ATTRIBUTES (F5' - F8') FROM FCB
;RETURN ATTRIBUTES LEFT SHIFTED IN AL
;ZERO INTERFACE ATTRIBUTE BITS IN FCB
MOV BX,INFO! ADD BX,5
MOV CL,4! MOV DX,01111111B
GET_ATTS_LOOP:
MOV AL,[BX]! MOV CH,AL
RCL AL,1! ADC DH,DH
AND CH,DL! MOV [BX],CH
INC BX! DEC CL! JNZ GET_ATTS_LOOP
MOV AL,DH! MOV CL,4! SHL AL,CL! RET
;
GET_S1: ;GET CURRENT S1 FIELD TO AL
CALL GETEXTA! INC BX! MOV AL,[BX]! RET
;
GET_RRA:
;GET CURRENT RAN REC FIELD ADDRES TO BX
MOV BX,INFO! ADD BX,RANREC! RET
;
GET_RCNTA:
;GET RECCNT ADDRES TO BX
MOV BX,INFO! ADD BX,RECCNT! RET
;
getexta:
;get current extent field address to al
mov bx,info
add bx,extnum ;bx=.fcb(extnum)
; mov al,[bx] ;*************** removed 7/14
ret
;
gtfcba:
;compute reccnt and nxtrec addresses for get/setfcb
mov dx,reccnt
add dx,info ;dx=.fcb(reccnt)
mov bx,(nxtrec-reccnt)
add bx,dx ;bx=.fcb(nxtrec)
ret
;
getfcb:
;set variables from currently addressed fcb
call gtfcba ;addresses in dx, bx
mov al,[bx]
mov vrecord,al ;vrecord=fcb(nxtrec)
xchg bx,dx
mov al,[bx]
mov rcount,al ;rcount=fcb(reccnt)
call getexta ;bx=.fcb(extnum)
mov al,extmsk ;extent mask to a
and al,[bx] ;fcb(extnum) and extmsk
mov extval,al
ret
;
setfcb:
;place values back into current fcb
call gtfcba ;addresses to dx, bx
mov al,seqio
cmp al,02
jnz setfc1
xor al,al ;check ranfill
setfc1:
;=1 if sequential i/o
add al,vrecord
mov [bx],al ;fcb(nxtrec)=vrecord+seqio
xchg bx,dx
mov al,rcount
mov [bx],al ;fcb(reccnt)=rcount
ret
;
;
cmpecs:
;compute checksum for current directory buffer
mov cx,recsiz ;size of directory buffer
mov bx,buffa ;current directory buffer
xor al,al ;clear checksum value
cmpec0:
add al,[bx]
inc bx
loop cmpec0
ret ;with checksum in a
;
CHKSUM_FCB: ;COMPUTE CHECKSUM FOR FCB
;ADD 1ST 12 BYTES OF FCB + CURDSK +
; HIGH_EXT + XFCB_READONLY + BBH
SUB AL,AL
if MPM
MOV BX,OFFSET PDCNT
MOV CX,4
endif
if CPM
MOV BX,OFFSET HIGH_EXT
MOV CX,3
endif
CALL CMPEC0
ADD AL,0BBH ;ADD BIAS
MOV BX,INFO ;ADD 1ST 12 BYTES OF FCB
MOV CX,12
CALL CMPEC0
INC BX ;SKIP EXTENT
ADD AL,[BX] ;ADD S1
ADD BX,3 ;SKIP MODNUM & RECCNT
MOV CX,16 ;CHECKSUM DISK MAP
CALL CMPEC0
OR AL,AL ;ZERO FLAG SET IF CHEKSUM VALID
RET
;
SET_CHKSUM_FCB:
CALL CHKSUM_FCB ;COMPUTE FCB CHECKSUM
JZ RET45 ;RETURN IF VALID
MOV AH,AL ;SAVE CURRENT CHECKSUM VALUE
CALL GETS1 ;GET S1 BYTE
SUB AH,AL ;SUBTRACT FROM CHECKSUM VALUE
NEG AH ;NEGATE RESULT
MOV [BX],AH ;RESTORE S1
RET45: RET
;
RESET_CHECKSUM_FCB:
MOV COMP_FCB_CKS,0
CALL CHKSUM_FCB ;COMPUTE FCB CHECKSUM
JNZ RET45 ;RETURN IF INVALID
CALL GETS1 ;INVALIDATE S1
INC B[BX]
RET
;
CHEK_FCB:
CMP HIGH_EXT,01100000B ;DOES HIGH_EXT = 60H
JNE CHKSUM_FCB ;NO
MOV BX,INFO ;YES - SET FCB(0) TO ZERO
MOV B[BX],0
JMP CHKSUM_FCB
;
CHECK_FCB:
if MPM
MOV CHECK_FCB_RET,FALSE
CHECK_FCB1:
endif
CALL CHEK_FCB ;COMPUTE FCB CHECKSUM
JZ RET45 ;VALID IF ZERO
if MPM
AND AL,0FH ;IS MOD(CHKSUM,16) = 0 ?
JNZ CHECK_FCB3 ;NO - INVALID CHECKSUM
CMP PD_CNT,0 ;IS PDCNT = 0 ?
JZ CHECK_FCB3 ;YES - INVALID CHECKSUM
MOV BYTE PTR SDCNT+1,0FFH
MOV DONT_CLOSE,TRUE
CALL CLOSE1 ;ATTEMPT PARTIAL CLOSE
MOV BX,OFFSET LRET
INC B[BX]
JZ CHECK_FCB3 ;PARTIAL CLOSE FAILED
MOV B[BX],0 ;ZERO LRET
CALL PACK_SDCNT ;LOOK FOR FILE IN LOCK LIST
MOV CH,5
CALL SEARCH_OLIST
JNZ CHECK_FCB3 ;NOT FOUND - INVALID CHECKSUM
RET ;FOUND - CHECKSUM OK
CHECK_FCB3:
endif
POP BX ;DISCARD RETURN ADDRESS
if MPM
CHECK_FCB4:
TEST CHECK_FCB_RET,TRUE
JNZ RET45
endif
MOV AL,10 ;10 = CHECKSUM ERROR
JMP STA_RET
setcdisk:
;set a "1" value in SELDSK position of cx
MOV AL,SELDSK
SET_CDISK1: ;SET A "1" VALUE IN AL POSITION OF CX
push cx ;save input parameter
MOV CL,AL
mov bx,1 ;number to shift
shl bx,cl ;bx = mask to integrate
pop cx ;original mask
or bx,cx ;bx = mask or rol(1,curdsk)
ret
;
nowrite:
;return true if dir checksum difference occurred
MOV DX,RODSK
TEST_VECTOR:
MOV CL,SELDSK
TEST_VECTOR1:
SHR DX,CL
AND DX,1
ret ;non zero if nowrite
;
setro:
;set current disk to read only
mov cx,rodsk
call setcdisk ;sets bit to 1
mov rodsk,bx
;high water mark in directory
;goes to max
mov dx,dirmax
inc dx
mov bx,cdrmaxa ;bx = .cdrmax
mov [bx],dx ;cdrmax = dirmax
ret
;
ckrodir:
;check current directory element for read/only status
call getdptra ;address of element
;
ckrofile:
;check current buff(dptr) or fcb(0) for r/o status
CALL RO_TEST
jae ret5 ;rnc
jmp roferror
RO_TEST:
ADD BX,ROFILE ;offset to r/o bit
mov al,[bx]
rcl al,1
RET
checkwrite:
;check for write protected disk
call nowrite
jz ret5 ;rz
jmp roderror
;
getdptra:
;compute the address of a directory element at
;positon dptr in the buffer
mov bx,buffa
mov al,dptr
addh:
;bx = bx + al
mov ah,0
add bx,ax
ret5: ret
;
;
getmodnum:
;compute the address of the module number
;bring module number to accumulator
;(high order bit is fwf (file write flag)
mov bx,info
add bx,modnum
mov al,[bx]
ret ;al=fcb(modnum)
;
clrmodnum:
;clear the module number field for user open/make
call getmodnum
mov b[bx],0 ;fcb(modnum)=0
ret
;
CLR_EXT:
CALL GETEXTA
AND B[BX],1FH
RET
;
setfwf:
call getmodnum ;bx=.fcb(modnum),
;al=fcb(modnum)
;set fwf(file write flag) to 1
or al,fwfmsk
mov [bx],al ;fcb(modnum)=fcb(modnum) + 80h
;also returns non zero
;in accumulator
ret
;
;
compcdr:
;return cy if cdrmax > dcnt
mov dx,dcnt ;dx = directory counter
mov bx,cdrmaxa ;bx=.cdrmax
cmp dx,[bx]
;condition dcnt - cdrmax
;produces cy if cdrmax>dcnt
ret6: ret
;
setcdr:
;if not (cdrmax > dcnt) then cdrmax = dcnt+1
call compcdr
jb ret6 ;return if cdrmax > dcnt
;otherwise, bx = .cdrmax+1,
;dx = dcnt
inc dx
mov [bx],dx
ret
;
subdh:
;compute bx = dx - bx
push dx
sub dx,bx
mov bx,dx
pop dx
ret
;
newchecksum:
;drop through to compute new checksum
mov cl,true
checksum:
;compute current checksum record and update the
;directory element if cl=true, or check for = if not
;drec < chksiz?
MOV DX,ARECORD
mov bx,chksiz
AND BH,7FH ;REMOVE PERMANENT DRIVE BIT
call subdh ;dx-bx
jae ret6 ;skip checksum if past
;checksum vector size
;drec < chksiz, so continue
push cx ;save init flag
call cmpecs ;check sum value to al
MOV BX,ARECORD ;value of ARECORD
add bx,checka ;bx=.check(ARECORD)
pop cx ;recall true or false to cl
inc cl ;true produces zero flag
jz initcs
if MPM
INC CL ;0FEH PRODUCES ZERO FLAG
JZ TEST_DIR_CS
endif
;not initializing, compare
cmp al,[bx] ;compute$cs=check(ARECORD)?
jz ret7 ;no message if ok
;checksum error, are we beyond
;the end of the disk?
call compcdr
jae ret7 ;no message if so
if MPM
CALL NOWRITE ;FLUSH FILES IF DRIVE IS NOT
JNZ RET7 ;READ/ONLY
CALL FLUSH_FILE0
endif
jmp setro ;read/only disk set
if MPM
TEST_DIR_CS:
CMP AL,[BX] ;COMPUTE_CS=CHECK(ARECORD)
JZ RET7
JMP FLUSH_FILES
endif
initcs:
;initializing the checksum
mov [bx],al
ret7: ret
;
;
wrdir:
;write the current directory entry, set checksum
CALL CHECK_WRITE ;VERIFY DISK IS READ/WRITE
call newchecksum ;initialize entry
call setdir ;directory dma
mov cl,1 ;indicates a write directory
call wrbuff ;write the buffer
jmp setdata ;to data dma address
;ret
;
rddirbuf:
;read a directory entry into the directory buffer
call setdir ;directory dma
call rdbuff ;directory record loaded
; jmp setdata
; ret
;
setdata:
;set data dma address
mov cx,dmabase
call setdmbf ;set disk i/o base
mov bx,offset dmaad
jmps setdma ;to complete the call
;
setdir:
;set directory dma address
mov cx,ds
call setdmbf ;set bios disk i/o base
mov bx,offset buffa ;jmp setdma to complete call
;
setdma:
;bx=.dma address to set (i.e., buffa or dmaad)
mov cx,[bx] ;parameter ready
jmp setdmf
;
;
dirtouser:
;copy the directory entry to the user buffer
;after call to search or searchn by user code
mov dx,buffa ;source is directory buffer
mov bx,dmaad ;destination is user dma addr.
mov cl,recsiz ;copy entire record
push es ;move to user segment
mov es,parametersegment
call move
pop es
ret
MAKE_FCB_INV:
;FLAG FCB AS INVALID
CALL SETFWF ;RESET FCB WRITE FLAG
INC BX
INC BX
MOV W[BX],0FFFFH
RET
CHK_INV_FCB:
;CHECK FOR INVALID FCB
CALL GETDMA
JMPS TEST_FFFF
TST_INV_FCB:
;TEST FOR INVALID FCB
CALL CHK_INV_FCB
JNZ RET8
POP BX
MOV AL,9
JMP STA_RET
endofdir:
;return zero flag if at end of director, non zero
;if not at end (end of dir if dcnt = 0ffffh)
mov bx,offset dcnt
TEST_FFFF:
CMP W[BX],0FFFFH
; mov al,[bx] ;may be 0ffh
; inc bx
; cmp al,[bx] ;low(dcnt) = high(dcnt)?
; jnz ret8 ;return non zero if different
; ;high and low the same,= 0ffh?
; inc al ;0ffh becomes 00 if so
ret8: ret
;
setenddir:
;set dcnt to the end of the directory
MOV DCNT,ENDDIR
ret
;
rddir:
;read next directory entry, with cl=true if initializing
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)
call subdh ;dx-bx
jae rddir0
;yes, set dcnt to end
;of directory
jmps setenddir
; ret
rddir0:
;not at end of directory, seek next element
;cl=initialization flag
mov al,ldcnt
and al,dskmsk ;low(dcnt) and dskmsk
push cx
mov cl,fcbshf ;to multiply by fcb size
shl al,cl
pop cx
;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
push cx ;save initialization flag cl
call seekdir ;seek proper record
call rddirbuf ;read the directory record
pop cx ;recall initialization flag
jmp checksum ;checksum the directory elt
;ret
;
;
getallocbit:
;given allocation vector
;position on cx, return byte
;containing cx shifted so that the least significant
;bit is in the low order accumulator position. bx is
;the address of the byte for
;possible replacement in
;memory upon return, and dh contains the number of shifts
;required to place the returned value back into position
;
mov dx,cx
and cl,111b
inc cl
mov ch,cl
;ch and cl both contain the
;number of bit positions to
;shift
mov cl,3
shr dx,cl ;shift bit address right 3 for byte address
;dx shr 3 to dx
mov bx,alloca ;base addr. of alloc. vector
add bx,dx
;byte to a, hl =
mov al,[bx] ;.alloc(cx shr 3)
;now move the bit to the
;low order position of al
mov cl,ch
rol al,cl
mov dx,cx
ret71: ret
;
;
setallocbit:
;cx is the bit position of alloc to set or reset. the
;value of the bit is in register dl.
push dx
call getallocbit ;shifted val al,count in dl
and al,11111110b ;mask low bit to zero
;(may be set)
pop cx
or al,cl ;low bit of cl masked into al
; jmp rotr
; ret
rotr:
;byte value from alloc is in register al, with shift count
;in register ch (to place bit back into position), and
;target alloc position in registers bx, rotate and replace
;
push cx
mov cl,dh
ror al,cl
mov [bx],al
pop cx
ret
;
;************* end bdos filesystem part 1 **************
end