Category : Assembly Language Source Code
Archive   : NORTASM.ZIP
Filename : DISK_IO.ASM

 
Output of file : DISK_IO.ASM contained in archive : NORTASM.ZIP
.MODEL SMALL

;-----------------------------------------------------------------------;
; This file contains the procedures to handle disk I/O, such as reading ;
; and writing a disk sector. It also contains procedures that handle ;
; reading and writing of sectors within a file. ;
; ;
; One word of caution. As with the version in the book, Dskpatch ;
; assumes that sectors are 512 bytes. If your machine uses longer ;
; sectors, you may want to modify Dskpatch so that it handles longer ;
; sectors. In any case, the procedures that read and write file ;
; "sectors" are not dependent on the actual sector size. ;
; ;
; Here is a list of procedure in this file: ;
; ;
; INIT_DISK Initializes variables, etc. ;
; TRAP_DISK_ERRORS Handles disk errors for file functions ;
; WRITE_IO_ERROR Writes or clears the error message ;
; WRITE_ERROR_MESSAGE Writes an error message on screen ;
; CLEAR_ERROR_MESSAGE Clears the last error message ;
; READ_SECTOR Read logical or file sector ;
; READ_LOGICAL_SECTOR Read a logical disk sector ;
; WRITE_SECTOR Write logical or file sector ;
; WRITE_LOGICAL_SECTOR Writes a logical sector to the disk ;
; PREVIOUS_SECTOR Read the previous sector from disk/file ;
; NEXT_SECTOR Read the next sector from disk/file ;
; CHANGE_DISK_DRIVE Change to a different disk drive ;
; READ_SECTOR_NUMBER Change to a sector, by number ;
; OPEN_NEW_FILE Open a file for reading and writing ;
; READ_FILE_NAME Read the name of the file to open ;
; OPEN_FILE Actually open the file ;
; QUIT_FILE_MODE Closes the open file ;
; READ_OFFSET_NUMBER Ask for the sector number within a file ;
; READ_FILE_SECTOR Actually read a sector from the file ;
; SET_FILE_POINTER Move to the start of a sector in file ;
; WRITE_FILE_SECTOR Writes a file's sector back to the disk ;
;-----------------------------------------------------------------------;

.DATA

EXTRN SECTOR:BYTE
EXTRN DISK_DRIVE_NO:BYTE
EXTRN CURRENT_SECTOR_NO:WORD
EXTRN ERROR_MESSAGE_LINE_NO:BYTE

ERROR_CODE_TABLE LABEL WORD
DW OFFSET ERROR_0
DW OFFSET ERROR_1
DW OFFSET ERROR_2
DW OFFSET ERROR_3
DW OFFSET ERROR_4
DW OFFSET ERROR_5
DW OFFSET ERROR_6
DW OFFSET ERROR_7
DW OFFSET ERROR_8
DW OFFSET ERROR_9
DW OFFSET ERROR_A
DW OFFSET ERROR_B
DW OFFSET ERROR_C
DW OFFSET ERROR_D ;Used by File I/O procedure
DW OFFSET ERROR_E ;Used by File I/O procedure
DW OFFSET ERROR_F ;Used by READ_FILE_SECTOR
DW OFFSET ERROR_10 ;Used by FILE_WRITE_SECTOR

ERROR_0 DB 'Write attempt on write-protected diskette',0
ERROR_1 DB 'Unknown unit',0
ERROR_2 DB 'Drive not ready',0
ERROR_3 DB 'Unknown command',0
ERROR_4 DB 'Data error (CRC)',0
ERROR_5 DB 'Bad request structure length',0
ERROR_6 DB 'Seek error',0
ERROR_7 DB 'Unknown media type',0
ERROR_8 DB 'Sector not found',0
ERROR_9 DB 'Printer out of paper',0
ERROR_A DB 'Write fault',0
ERROR_B DB 'Read fault',0
ERROR_C DB 'General failure',0
ERROR_D DB 'Error reading file',0 ;Used by File I/O procedures
ERROR_E DB 'File not found',0 ;Used by File I/O procedures
ERROR_F DB 'Read past end of file',0 ;Used by READ_FILE_S...
ERROR_10 DB 'No room on disk',0 ;Used by WRITE_FILE_SECTOR

READING_MESSAGE DB ' Reading...',0
WRITING_MESSAGE DB ' Writing...',0
PRESS_KEY DB 'Press any key to continue... ',0

BYTES_READ DW ? ;Bytes read by READ_FILE_SECTOR


.CODE

PUBLIC DISK_ERROR_FLAG, DISK_ERROR_CODE
DISK_ERROR_FLAG DB 0 ;Used to keep track of errors
DISK_ERROR_CODE DB 0 ;Error code from disk operation


PUBLIC INIT_DISK
;-----------------------------------------------------------------------;
; This procedure initializes several things that Disk Patch needs to ;
; function properly: ;
; ;
; 1. It sets DISK_DRIVE_NO to the number of the current drive, ;
; rather than simply drive A:. This is useful if you start ;
; Dskpatch from a hard disk with no floppy disk in the drive. ;
; ;
; 2. It tells DOS to call TRAP_DISK_ERRORS whenever there is a ;
; disk error and we're reading a file. We use this so that ;
; we can report disk errors. ;
; ;
; Writes: DISK_DRIVE_NO ;
;-----------------------------------------------------------------------;
INIT_DISK PROC
MOV AH,19h ;Ask DOS for the disk drive number
INT 21h
MOV DISK_DRIVE_NO,AL ;Save this drive number

MOV AL,24h ;Set INT 24H (ERROR) vector
MOV AH,25h ;Set vector function
LEA DX,TRAP_DISK_ERRORS
INT 21h

RET
INIT_DISK ENDP


PUBLIC TRAP_DISK_ERRORS
;-----------------------------------------------------------------------;
; DOS calls this procedure in case of INT 21h errors using an INT 24h ;
; call. ;
; ;
; This procedure saves the error code in DISK_ERROR_CODE, then returns ;
; to DOS with AL set to 0. This tells DOS to "ignore" the error. ;
; Dskpatch checks the error flag for errors after making INT 21h calls. ;
; ;
; Writes: DISK_ERROR_FLAG, DISK_ERROR_CODE ;
;-----------------------------------------------------------------------;
TRAP_DISK_ERRORS PROC
PUSH AX
MOV AX,DI ;Get error code into AL
MOV CS:DISK_ERROR_FLAG,1 ;Set the error flag
MOV CS:DISK_ERROR_CODE,AL ;Save this disk error code
POP AX
MOV AL,0 ;Tell DOS to ignore this error
IRET ;Let DOS return to Dskpatch
TRAP_DISK_ERRORS ENDP



PUBLIC WRITE_IO_ERROR
;-----------------------------------------------------------------------;
; This procedure writes an error message to the error message line. ;
; ;
; Uses: WRITE_ERROR_MESSAGE, CLEAR_ERROR_MESSAGE ;
; Reads: DISK_ERROR_FLAG, DISK_ERROR_CODE, ERROR_CODE_TABLE ;
;-----------------------------------------------------------------------;
WRITE_IO_ERROR PROC
PUSH BX
PUSH DX

CMP DISK_ERROR_FLAG,1 ;Was there an error?
JE WRITE_MESSAGE ;Yes, then write the message
CALL CLEAR_ERROR_MESSAGE ;No, then clear the message
JMP DONE_WRITE_IO_ERROR ;We're all done here
WRITE_MESSAGE:
MOV BL,DISK_ERROR_CODE ;Get the error code
XOR BH,BH ;Convert to a word
SHL BX,1 ;Multiply by 2 for word look-up
MOV DX,ERROR_CODE_TABLE[BX] ;Get address of error message
CALL WRITE_ERROR_MESSAGE ;Display the error message

DONE_WRITE_IO_ERROR:
POP DX
POP BX
RET
WRITE_IO_ERROR ENDP


PUBLIC WRITE_ERROR_MESSAGE
EXTRN GOTO_XY:PROC
EXTRN WRITE_STRING:PROC
EXTRN CLEAR_TO_END_OF_LINE:PROC
EXTRN WRITE_ATTRIBUTE_N_TIMES:PROC
;-----------------------------------------------------------------------;
; This procedure writes an error message in the message area near the ;
; bottom of the screen. ;
; ;
; DS:DX Address of the error message ;
; ;
; Uses: GOTO_XY, WRITE_STRING, CLEAR_TO_END_OF_LINE ;
; WRITE_ATTRIBUTE_N_TIMES ;
; Reads: ERROR_MESSAGE_LINE_NO ;
;-----------------------------------------------------------------------;
WRITE_ERROR_MESSAGE PROC
PUSH CX
PUSH DX
MOV CX,DX ;Put the string address into CX
MOV DH,ERROR_MESSAGE_LINE_NO ;Get the line number
MOV DL,30 ;Indent message 30 spaces
CALL GOTO_XY ;Move cursor to start of the message
XCHG CX,DX ;Put the string address back into DX
CALL WRITE_STRING ;Write the string here
CALL CLEAR_TO_END_OF_LINE ;Then clear the rest of the line
XCHG CX,DX ;Put the coordinates back into DX
CALL GOTO_XY ;Move to the start of the message
MOV DL,0Fh ;High-intensity attribute
MOV CX,49 ;Set the entire line to bright
CALL WRITE_ATTRIBUTE_N_TIMES
POP DX
POP CX
RET
WRITE_ERROR_MESSAGE ENDP


PUBLIC CLEAR_ERROR_MESSAGE
EXTRN GOTO_XY:PROC
EXTRN CLEAR_TO_END_OF_LINE:PROC
;-----------------------------------------------------------------------;
; This procedure clears the error message which appears after a disk ;
; error. ;
; ;
; Uses: GOTO_XY, CLEAR_TO_END_OF_LINE ;
; Reads: ERROR_MESSAGE_LINE_NO ;
;-----------------------------------------------------------------------;
CLEAR_ERROR_MESSAGE PROC
PUSH DX
MOV DH,ERROR_MESSAGE_LINE_NO ;Get the line number for the error
XOR DL,DL ;Start of the message line
CALL GOTO_XY ;Move to start of error message line
CALL CLEAR_TO_END_OF_LINE ;Clear the entire line
POP DX
RET
CLEAR_ERROR_MESSAGE ENDP


PUBLIC READ_SECTOR
.DATA
EXTRN DISK_DRIVE_NO:BYTE
EXTRN CURRENT_SECTOR_NO:WORD
EXTRN FILE_FLAG:BYTE
.CODE
;-----------------------------------------------------------------------;
; This procedure reads one sector (512 bytes) into SECTOR. ;
; ;
; Uses: CLEAR_SECTOR, READ_LOGICAL_SECTOR, READ_FILE_SECTOR ;
; WRITE_IO_ERROR ;
; Reads: DISK_ERROR_FLAG, FILE_FLAG ;
; Writes: DISK_ERROR_FLAG ;
;-----------------------------------------------------------------------;
READ_SECTOR PROC
MOV DISK_ERROR_FLAG,0 ;Clear the disk error flag before read
CALL CLEAR_SECTOR ;Clear the sector before we start

CMP FILE_FLAG,1 ;Are we in file mode?
JE FILE_READ ;Yes, then read sector from file
CALL READ_LOGICAL_SECTOR ;No, read a logical disk sector
JMP JUST_READ_SECTOR ;Now check for errors
FILE_READ:
CALL READ_FILE_SECTOR ;Read a sector from the file

JUST_READ_SECTOR:
CMP DISK_ERROR_FLAG,1 ;Was there a disk error?
JNE DONE_READ_SECTOR ;No, then we're all done
CALL CLEAR_SECTOR ;Yes, then clear SECTOR
DONE_READ_SECTOR:
CALL WRITE_IO_ERROR ;Write or clear error message
RET
READ_SECTOR ENDP


PUBLIC CLEAR_SECTOR
.DATA
EXTRN SECTOR:BYTE
.CODE
;-----------------------------------------------------------------------;
; This procedure sets SECTOR to all 0s. Call this procedure before ;
; you try a read a sector so that the sector will be all 0s if there ;
; is an error. ;
; ;
; Writes: SECTOR ;
;-----------------------------------------------------------------------;
CLEAR_SECTOR PROC
PUSH AX ;Clear SECTOR to all 0s
PUSH CX
PUSH DI
LEA DI,SECTOR ;Point to the sector buffer
MOV CX,512 ;Number of bytes in one sector
CLD ;Set direction for increment
MOV AL,0 ;Store zeros in SECTOR
REP STOSB ;Clear SECTOR
POP DI
POP CX
POP AX
RET
CLEAR_SECTOR ENDP


PUBLIC READ_LOGICAL_SECTOR
.DATA
EXTRN DISK_DRIVE_NO:BYTE
EXTRN CURRENT_SECTOR_NO:WORD
EXTRN SECTOR:BYTE

LONG_SECTOR_NO DD 0 ;A 4-byte sector number
SECTORS_TO_READ DW 1 ;Number of sectors to read
BUFFER_OFFSET DW 0 ;Where to save the segment
BUFFER_SEGMENT DW 0

.CODE
;-----------------------------------------------------------------------;
; This procedure reads a logical sector from the disk, using the DOS ;
; function 25h. ;
; ;
; Reads: DISK_DRIVE_NO, CURRENT_SECTOR_NO ;
; Writes: SECTOR, DISK_ERROR_FLAG, DISK_ERROR_CODE ;
;-----------------------------------------------------------------------;
READ_LOGICAL_SECTOR PROC
PUSH AX
PUSH BX
PUSH CX
PUSH DX
MOV AL,DISK_DRIVE_NO ;Drive number
MOV CX,1 ;Read only 1 sector
MOV DX,CURRENT_SECTOR_NO ;Logical sector number
LEA BX,SECTOR ;Where to store this sector
INT 25h ;Read the sector
JNC READ_LOGICAL_NO_ERROR ;There wasn't an error, continue

;---------------------------------------------------------------;
; If we're using DOS 4.0 (or COMPAQ DOS 3.31) with a hard disk ;
; partition larger than 32MB, we'll get the error 0207 in AX. ;
; In this case we need to set up the register differently. ;
;---------------------------------------------------------------;
CMP AX,207h ;Could this be an extended partition?
JNE READ_LOGICAL_ERROR ;No, report the error

POPF ;Pop old flags off the stack
MOV AL,DISK_DRIVE_NO ;Set up for DOS 4.0 extended read
MOV CX,-1 ;Tell DOS we're making special read
MOV BX,CURRENT_SECTOR_NO ;Which sector to read
MOV Word Ptr LONG_SECTOR_NO,BX
LEA BX,SECTOR ;Where to save the sector
MOV BUFFER_OFFSET,BX
MOV BUFFER_SEGMENT,DS ;Which segment buffer is in
LEA BX,LONG_SECTOR_NO ;DS:BX points to extended info
INT 25h
JNC READ_LOGICAL_NO_ERROR ;There wasn't an error

READ_LOGICAL_ERROR:
MOV DISK_ERROR_FLAG,1 ;Set the error flag
MOV DISK_ERROR_CODE,AL ;And save the error code
READ_LOGICAL_NO_ERROR:
POPF ;Discard flags put on stack by INT 25h
POP DX
POP CX
POP BX
POP AX
RET
READ_LOGICAL_SECTOR ENDP


PUBLIC WRITE_SECTOR
EXTRN WRITE_STRING:PROC, WRITE_PROMPT_LINE:PROC
.DATA
EXTRN FILE_FLAG:BYTE
.CODE
;-----------------------------------------------------------------------;
; This procedure writes a sector back to the disk. Note, if the last ;
; read resulted in an error (in which case the error flag will still ;
; be set) this procedure does nothing. ;
; ;
; Uses: WRITE_IO_ERROR, WRITE_STRING, WRITE_EDITOR_PROMPT ;
; WRITE_LOGICAL_SECTOR, WRITE_FILE_SECTOR ;
; Reads: FILE_FLAG, WRITING_MESSAGE, DISK_ERROR_FLAG ;
;-----------------------------------------------------------------------;
WRITE_SECTOR PROC
PUSH AX
CMP DISK_ERROR_FLAG,0 ;Was there a disk error?
JNE DONT_WRITE_SECTOR ;Yes, then don't write a sector

LEA DX,WRITING_MESSAGE ;Provide feedback while we write
CALL WRITE_STRING

CMP FILE_FLAG,1 ;Are we in file mode?
JE FILE_WRITE ;Yes, then write sector to file
CALL WRITE_LOGICAL_SECTOR ;No, write a logical disk sector
JMP JUST_WROTE_SECTOR ;Now check for errors
FILE_WRITE:
CALL WRITE_FILE_SECTOR ;Write a sector to the file

JUST_WROTE_SECTOR:
CMP DISK_ERROR_FLAG,1 ;Was there a disk error?
JE WRITE_SECTOR_ERROR ;Yes, then report it

DONE_WRITE_SECTOR:
CALL WRITE_IO_ERROR ;Write or clear error message
CALL WRITE_EDITOR_PROMPT
DONT_WRITE_SECTOR:
POP AX
RET

WRITE_SECTOR_ERROR:
CALL WRITE_IO_ERROR ;Display the error message
LEA DX,PRESS_KEY ;Display "Press any key..." message
CALL WRITE_PROMPT_LINE ;Display this prompt
CALL READ_KEY ;And read a character
MOV DISK_ERROR_FLAG,0 ;Clear the error flag
JMP DONE_WRITE_SECTOR ;We're all done
WRITE_SECTOR ENDP


PUBLIC WRITE_LOGICAL_SECTOR
EXTRN WRITE_EDITOR_PROMPT:PROC
.DATA
EXTRN SECTOR:BYTE
EXTRN DISK_DRIVE_NO:BYTE
EXTRN CURRENT_SECTOR_NO:WORD
.CODE
;-----------------------------------------------------------------------;
; This procedure writes a logical sector to the disk drive. Not, if ;
; the last read resulted in an error, this procedure won't write a ;
; sector to the disk. ;
; ;
; Reads: DISK_DRIVE_NO, CURRENT_SECTOR_NO ;
; Writes: SECTOR, DISK_ERROR_FLAG, DISK_ERROR_CODE ;
;-----------------------------------------------------------------------;
WRITE_LOGICAL_SECTOR PROC
PUSH AX
PUSH BX
PUSH CX
PUSH DX
MOV AL,DISK_DRIVE_NO ;Drive number
MOV CX,1 ;Write 1 sector
MOV DX,CURRENT_SECTOR_NO ;Logical sector
LEA BX,SECTOR ;Get address of our sector
INT 26h ;Write the sector to disk
JNC WRITE_LOGICAL_NO_ERROR ;There wasn't an error, continue

;---------------------------------------------------------------;
; If we're using DOS 4.0 (or COMPAQ DOS 3.31) with a hard disk ;
; partition larger than 32MB, we'll get the error 0207 in AX. ;
; In this case we need to set up the registers differently. ;
;---------------------------------------------------------------;
CMP AX,207h ;Could this be an extended partition?
JNE WRITE_LOGICAL_ERROR ;No, report the error

POPF ;Pop old flags off the stack
MOV AL,DISK_DRIVE_NO ;Set up for DOS 4.0 extended read
MOV CX,-1 ;Tell DOS we're making special read
MOV BX,CURRENT_SECTOR_NO ;Which sector to read
MOV Word Ptr LONG_SECTOR_NO,BX
LEA BX,SECTOR ;Where to save the sector
MOV BUFFER_OFFSET,BX
MOV BUFFER_SEGMENT,DS ;Which segment buffer is in
LEA BX,LONG_SECTOR_NO ;DS:BX points to extended info
INT 26h
JNC WRITE_LOGICAL_NO_ERROR ;There wasn't an error

WRITE_LOGICAL_ERROR:
MOV DISK_ERROR_FLAG,1 ;Set the error flag
MOV DISK_ERROR_CODE,AL ;Save the error code
WRITE_LOGICAL_NO_ERROR:
POPF ;Discard the flag information
POP DX
POP CX
POP BX
POP AX
RET
WRITE_LOGICAL_SECTOR ENDP


PUBLIC PREVIOUS_SECTOR
EXTRN INIT_SEC_DISP:PROC
EXTRN WRITE_STRING:PROC
EXTRN WRITE_EDITOR_PROMPT:PROC
.DATA
EXTRN CURRENT_SECTOR_NO:WORD
.CODE
;-----------------------------------------------------------------------;
; This procedure reads the previous sector, if possible ;
; ;
; Uses: WRITE_STRING, WRITE_EDITOR_PROMPT, INIT_SEC_DISP ;
; Reads: CURRENT_SECTOR_NO, READING_MESSAGE ;
; Writes: CURRENT_SECTOR_NO ;
;-----------------------------------------------------------------------;
PREVIOUS_SECTOR PROC
PUSH AX
PUSH DX
MOV AX,CURRENT_SECTOR_NO ;Get current sector number
OR AX,AX ;Is sector number zero?
JZ DONT_DECREMENT_SECTOR ;Yes, then we're all done
DEC AX ;No, then subtract one
MOV CURRENT_SECTOR_NO,AX ;Save new sector number
LEA DX,READING_MESSAGE ;Provide feedback while we're reading
CALL WRITE_STRING
CALL INIT_SEC_DISP ;Display the new screen
CALL WRITE_EDITOR_PROMPT ;Display the editor prompt
DONT_DECREMENT_SECTOR:
POP DX
POP AX
RET
PREVIOUS_SECTOR ENDP


PUBLIC NEXT_SECTOR
EXTRN INIT_SEC_DISP:PROC
EXTRN WRITE_STRING:PROC
EXTRN WRITE_EDITOR_PROMPT:PROC
.DATA
EXTRN CURRENT_SECTOR_NO:WORD
.CODE
;-----------------------------------------------------------------------;
; Read the next sector. ;
; ;
; Uses: WRITE_EDITOR_PROMPT, INIT_SEC_DISP, WRITE_STRING ;
; Reads: CURRENT_SECTOR_NO, READING_MESSAGE ;
; Writes: CURRENT_SECTOR_NO ;
;-----------------------------------------------------------------------;
NEXT_SECTOR PROC
PUSH AX
PUSH DX
MOV AX,CURRENT_SECTOR_NO ;Get the current sector number
INC AX ;Move to next sector
MOV CURRENT_SECTOR_NO,AX ;Save this new number
LEA DX,READING_MESSAGE ;Provide feedback while we read
CALL WRITE_STRING
CALL INIT_SEC_DISP ;Display the new screen
CALL WRITE_EDITOR_PROMPT
POP DX
POP AX
RET
NEXT_SECTOR ENDP


PUBLIC CHANGE_DISK_DRIVE
EXTRN READ_BYTE:PROC
EXTRN WRITE_STRING:PROC
EXTRN CHAR_TO_UPPER:PROC
EXTRN WRITE_PROMPT_LINE:PROC
EXTRN WRITE_EDITOR_PROMPT:PROC
EXTRN INIT_SEC_DISP:PROC
.DATA
EXTRN DISK_DRIVE_NO:BYTE, DISK_PROMPT:BYTE
EXTRN FILE_FLAG:BYTE, CURRENT_SECTOR_NO:WORD
.CODE
;-----------------------------------------------------------------------;
; This procedure prompts for a new disk drive letter, like A, B, .... ;
; ;
; Uses: WRITE_PROMPT_LINE, WRITE_EDITOR_PROMPT, INIT_SEC_DISP ;
; WRITE_STRING, CHAR_TO_UPPER, READ_BYTE, QUIT_FILE_MODE ;
; Reads: DISK_PROMPT, FILE_FLAG, READING_MESSAGE ;
; Writes: DISK_DRIVE_NO, CURRENT_SECTOR_NO ;
;-----------------------------------------------------------------------;
CHANGE_DISK_DRIVE PROC
PUSH AX
PUSH DX

READ_DRIVE_LOOP:
LEA DX,DISK_PROMPT ;Display prompt for drive letter
CALL WRITE_PROMPT_LINE
CALL READ_BYTE ;Get new disk-drive number
CMP AH,0 ;Did it read one character?
JNZ DONT_CHANGE_DRIVE ;No, don't change the drive number
CMP AL,25 ;Yes, was it hex number <=25 decimal
JBE CHANGE_DRIVE ;Yes, change drive number

CMP AL,'0' ;No, is it a single digit number
JB READ_DRIVE_LOOP ;Yes, convert to drive number
CMP AL,'9' ;Is it a digit?
JBE IS_SINGLE_DIGIT ;Yes, convert to drive number

CALL CHAR_TO_UPPER ;Convert letter to upper-case
CMP AL,'A' ;Is this a legal drive letter?
JB READ_DRIVE_LOOP ;No, try again
CMP AL,'Z' ;Is this a legal drive letter?
JA READ_DRIVE_LOOP ;No, then try again

IS_DRIVE_LETTER: ;Yes, then convert to drive number
SUB AL,'A' ;Convert Upper letter to drive number
JMP CHANGE_DRIVE ;Change the drive number

IS_SINGLE_DIGIT:
SUB AL,'0' ;Convert to drive number

CHANGE_DRIVE:
CALL QUIT_FILE_MODE ;Turn off file mode, if on
MOV CURRENT_SECTOR_NO,0 ;And view sector 0 of new drive
CHANGE_DRIVE_NOT_FILE:
MOV DISK_DRIVE_NO,AL ;Save the new drive number
LEA DX,READING_MESSAGE ;Provide some feedback while reading
CALL WRITE_STRING ;Display this string
CALL INIT_SEC_DISP ;And display the new sector
DONT_CHANGE_DRIVE:
CALL WRITE_EDITOR_PROMPT ;Display the Edit prompt
POP DX
POP AX
RET
CHANGE_DISK_DRIVE ENDP


PUBLIC READ_SECTOR_NUMBER
EXTRN WRITE_PROMPT_LINE:PROC, READ_DECIMAL:PROC
EXTRN WRITE_STRING:PROC, INIT_SEC_DISP:PROC
EXTRN WRITE_EDITOR_PROMPT:PROC
.DATA
EXTRN SECTOR_PROMPT:BYTE
EXTRN CURRENT_SECTOR_NO:WORD, FILE_FLAG:BYTE
.CODE
;-----------------------------------------------------------------------;
; This procedure prompts for the number of a new sector to read. ;
; ;
; Uses: WRITE_PROMPT_LINE, READ_DECIMAL, WRITE_STRING ;
; INIT_SEC_DISP, QUIT_FILE_MODE, WRITE_EDITOR_PROMPT ;
; Reads: SECTOR_PROMPT, READING_MESSAGE ;
; Writes: CURRENT_SECTOR_NO ;
;-----------------------------------------------------------------------;
READ_SECTOR_NUMBER PROC
PUSH AX
PUSH DX
LEA DX,SECTOR_PROMPT ;Prompt for the sector number
CALL WRITE_PROMPT_LINE
CALL READ_DECIMAL ;Read the new sector number into AX
JC DONT_CHANGE_SECTOR ;Error, so exit without changing
CALL QUIT_FILE_MODE ;Turn off file mode, if on
MOV CURRENT_SECTOR_NO,AX ;Save the new sector number
LEA DX,READING_MESSAGE ;Provide feedback while we read
CALL WRITE_STRING
CALL INIT_SEC_DISP ;Display the new sector
DONT_CHANGE_SECTOR:
CALL WRITE_EDITOR_PROMPT ;Display the Edit prompt
POP DX
POP AX
RET
READ_SECTOR_NUMBER ENDP


PUBLIC OPEN_NEW_FILE
EXTRN WRITE_STRING:PROC, INIT_SEC_DISP:PROC
EXTRN WRITE_PROMPT_LINE:PROC, READ_KEY:PROC
EXTRN WRITE_EDITOR_PROMPT:PROC
.DATA
EXTRN CURRENT_SECTOR_NO:WORD, FILE_FLAG:BYTE
.CODE
;-----------------------------------------------------------------------;
; This procedure reads a file name, opens that file, and displays the ;
; first sector. ;
; ;
; If the open failed, puts you back to editing whatever you were ;
; editing before. ;
; ;
; NOTE: This procedure needs a lot of work. If you were in file mode ;
; when you ask to open a new file, this procedure closes the old ;
; file, then tries to open the new file. If there was an error ;
; reading the new file, or you just hit , this procedure ;
; doesn't return to the file you were previously editing. ;
; instead, it returns to sector mode. See if you can change ;
; this procedure so that it returns to editing the last file you ;
; were on if you were in file mode. ;
; ;
; Uses: READ_FILE_NAME, OPEN_FILE, WRITE_STRING ;
; INIT_SEC_DISP, WRITE_IO_ERROR, WRITE_PROMPT_LINE ;
; READ_KEY, WRITE_EDITOR_PROMPT, QUIT_FILE_MODE ;
; Reads: READING_MESSAGE, DISK_ERROR_FLAG, PRESS_KEY ;
; Writes: CURRENT_SECTOR_NO, FILE_FLAG, DISK_ERROR_FLAG ;
;-----------------------------------------------------------------------;
OPEN_NEW_FILE PROC
PUSH AX
PUSH DX
CALL QUIT_FILE_MODE ;Turn off old file mode, if on

CALL READ_FILE_NAME ;First, read in the file name
OR AX,AX ;Did we read any characters?
JLE DONE_OPEN_NEW_FILE ;No, then return without change

LEA DX,READING_MESSAGE ;Yes, Provide feedback while we read
CALL WRITE_STRING
CALL OPEN_FILE ;Try to open this file
CMP DISK_ERROR_FLAG,1 ;Was there an I/O error?
JE OPEN_NEW_FILE_ERROR ;Yes, then report it.

SET_FILE_MODE:
MOV FILE_FLAG,1 ;No, signal now in file mode
MOV CURRENT_SECTOR_NO,0 ;Start at beginning of file

DONE_OPEN_NEW_FILE:
CALL INIT_SEC_DISP ;Display this new sector
CALL WRITE_IO_ERROR ;Display or clear error message
CALL WRITE_EDITOR_PROMPT ;Display the Edit prompt
POP DX
POP AX
RET

;-----------------------------------------------;
; Here we report the error, then pause until ;
; you press a key before returning to the ;
; previous mode and sector that you were ;
; viewing. ;
;-----------------------------------------------;
OPEN_NEW_FILE_ERROR:
CALL WRITE_IO_ERROR ;Display the error message
LEA DX,PRESS_KEY ;Display prompt message
CALL WRITE_PROMPT_LINE ;Display this prompt
CALL READ_KEY ;Read one character
JMP DONE_OPEN_NEW_FILE ;We're all done now
OPEN_NEW_FILE ENDP


PUBLIC READ_FILE_NAME
EXTRN WRITE_PROMPT_LINE:PROC, READ_STRING:PROC
.DATA
EXTRN FILE_NAME_PROMPT:BYTE
EXTRN FILE_NAME_STRING:BYTE
EXTRN LENGTH_READ:BYTE
EXTRN FILE_NAME:BYTE
.CODE
;-----------------------------------------------------------------------;
; This procedure prompts for a file name, then reads in the file name. ;
; We've written this as a separate procedure only to make the code more ;
; readable. ;
; ;
; Returns: AX Number of characters read. ;
; -1 if you pressed a special key ;
; ;
; Uses: WRITE_PROMPT_LINE, READ_STRING ;
; Reads: FILE_NAME_PROMPT, FILE_NAME_STRING, LENGTH_READ ;
; Writes: FILE_NAME ;
;-----------------------------------------------------------------------;
READ_FILE_NAME PROC
PUSH BX
PUSH DX
LEA DX,FILE_NAME_PROMPT ;Display the "file name:"
CALL WRITE_PROMPT_LINE ; prompt
LEA DX,FILE_NAME_STRING ;Read in the file name
READ_NAME_AGAIN:
CALL READ_STRING
MOV BL,LENGTH_READ ;Get number of
OR BL,BL ;Was this a function key?
JL DONE_READ_FILE_NAME ;Yes, then ignore it

XOR BH,BH ;Clear the upper byte
MOV FILE_NAME[BX],0 ;Convert string into ASCIIZ
DONE_READ_FILE_NAME:
MOV AL,BL ;Return the length of the string
CBW ;Extend sign into the upper byte
POP DX
POP BX
RET
READ_FILE_NAME ENDP


PUBLIC OPEN_FILE
.DATA
EXTRN FILE_HANDLE:WORD
.CODE
;-----------------------------------------------------------------------;
; This procedure is used by OPEN_NEW_FILE to open a file for reading ;
; and writing. ;
; ;
; Reads: FILE_NAME ;
; Writes: DISK_ERROR_FLAG, DISK_ERROR_CODE, FILE_HANDLE ;
;-----------------------------------------------------------------------;
OPEN_FILE PROC
PUSH AX
PUSH DX
MOV DISK_ERROR_FLAG,0 ;Clear the error condition
MOV AX,3D02h ;Open file for read/write
LEA DX,FILE_NAME ;Get address of the file name
INT 21h
JC FILE_OPEN_ERROR ;There was an error, report it
MOV FILE_HANDLE,AX ;Save this file handle
DONE_OPEN_FILE:
POP DX
POP AX
RET

FILE_OPEN_ERROR:
CMP AX,2 ;Is this a file-not-found message?
JNE CHECK_ACCESS
MOV AX,0Eh ;Error code for file-not-found
JMP DONE_FILE_ERROR
CHECK_ACCESS:
MOV AX,0Dh ;General Error reading file message
DONE_FILE_ERROR:
MOV DISK_ERROR_FLAG,1 ;Set the error flag
MOV DISK_ERROR_CODE,AL ;Save the new error code
JMP DONE_OPEN_FILE ;We're all done, return.
OPEN_FILE ENDP


PUBLIC QUIT_FILE_MODE
.DATA
EXTRN FILE_HANDLE:WORD
EXTRN CURRENT_SECTOR_NO:WORD
.CODE
;-----------------------------------------------------------------------;
; This procedure checks to see if we're in file mode, and if so, it ;
; closes the file, then turns off file mode. ;
; ;
; Reads: FILE_HANDLE ;
; Writes: FILE_FLAG, CURRENT_SECTOR_NO, DISK_ERROR_FLAG ;
;-----------------------------------------------------------------------;
QUIT_FILE_MODE PROC
PUSH AX
PUSH BX
CMP FILE_FLAG,1 ;Are we in file mode?
JNE DONE_QUIT_FILE ;No, then we're all done
MOV AH,3Eh ;Ask to close this file
MOV BX,FILE_HANDLE ;Get the file handle
INT 21h ;Close this file
MOV FILE_FLAG,0 ;Turn off file mode
MOV CURRENT_SECTOR_NO,0 ;Move to start of disk
MOV DISK_ERROR_FLAG,0 ;Ignore any error conditions
DONE_QUIT_FILE:
POP BX
POP AX
RET
QUIT_FILE_MODE ENDP



PUBLIC READ_OFFSET_NUMBER
.DATA
EXTRN OFFSET_PROMPT:BYTE
EXTRN FILE_FLAG:BYTE, CURRENT_SECTOR_NO:WORD
.CODE
EXTRN WRITE_PROMPT_LINE:PROC, READ_DECIMAL:PROC
EXTRN WRITE_STRING:PROC, INIT_SEC_DISP:PROC
EXTRN WRITE_EDITOR_PROMPT:PROC
;-----------------------------------------------------------------------;
; This procedure reads in the sector offset within a file, then ;
; displays the new sector. ;
; ;
; Uses: WRITE_PROMPT_LINE, READ_DECIMAL, WRITE_STRING ;
; INIT_SEC_DISP, WRITE_EDITOR_PROMPT ;
; Reads: FILE_FLAG, OFFSET_PROMPT, READING_MESSAGE ;
; Writes: CURRENT_SECTOR_NO ;
;-----------------------------------------------------------------------;
READ_OFFSET_NUMBER PROC
PUSH AX
PUSH DX
CMP FILE_FLAG,1 ;In file mode?
JNE READ_OFFSET_NOT_FILE_MODE ;No, then just ignore request
LEA DX,OFFSET_PROMPT ;Yes, display the offset prompt
CALL WRITE_PROMPT_LINE
CALL READ_DECIMAL ;Read in the decimal number
JC DONT_CHANGE_OFFSET ;Error, so exit without changing
LEA DX,READING_MESSAGE ;Provide feedback while we read
CALL WRITE_STRING
MOV CURRENT_SECTOR_NO,AX ;Save the new number
CALL INIT_SEC_DISP ;Draw the new display
DONT_CHANGE_OFFSET:
READ_OFFSET_NOT_FILE_MODE:
CALL WRITE_EDITOR_PROMPT ;Display the Edit prompt
POP DX
POP AX
RET
READ_OFFSET_NUMBER ENDP


PUBLIC READ_FILE_SECTOR
.DATA
EXTRN FILE_HANDLE:WORD
EXTRN SECTOR:BYTE
.CODE
;-----------------------------------------------------------------------;
; This procedure reads a random sector from a file. It also saves a ;
; count of the number of bytes actually read so that the write function ;
; won't increase the size of a file. ;
; ;
; Uses: SET_FILE_POINTER ;
; Reads: FILE_HANDLE, DISK_ERROR_FLAG ;
; Writes: SECTOR, DISK_ERROR_FLAG, DISK_ERROR_CODE, BYTES_READ ;
;-----------------------------------------------------------------------;
READ_FILE_SECTOR PROC
PUSH AX
PUSH BX
PUSH CX
PUSH DX
CALL SET_FILE_POINTER ;Set to start of current sector
CMP DISK_ERROR_FLAG,1 ;Was there an error?
JE DONE_READ_FILE_SECTOR ;Yes, then don't try to read sector

MOV AH,3Fh ;Call for a read on this file
MOV BX,FILE_HANDLE ;Get the file handle
MOV CX,512 ;Read one sector of 512 bytes
LEA DX,SECTOR ;Address of our sector buffer
INT 21h ;Let DOS read this sector
MOV BYTES_READ,AX ;Save number of bytes we read

OR AX,AX ;Did we read anything from file?
JZ READ_PAST_END ;No, we read past end of file, signal

DONE_READ_FILE_SECTOR:
POP DX
POP CX
POP BX
POP AX
RET

READ_PAST_END:
MOV DISK_ERROR_FLAG,1 ;Signal that there was an error
MOV DISK_ERROR_CODE,0Fh ;Read-past-end error code
JMP DONE_READ_FILE_SECTOR
READ_FILE_SECTOR ENDP


PUBLIC SET_FILE_POINTER
.DATA
EXTRN CURRENT_SECTOR_NO:WORD
EXTRN FILE_HANDLE:WORD
.CODE
;-----------------------------------------------------------------------;
; This procedure sets the file pointer to the sector number. ;
; ;
; Reads: CURRENT_SECTOR_NO, FILE_HANDLE ;
;-----------------------------------------------------------------------;
SET_FILE_POINTER PROC
PUSH AX
PUSH BX
PUSH CX
PUSH DX
MOV AX,CURRENT_SECTOR_NO ;Get sector number for sector to read
MOV BX,512 ;Size of the sector
MUL BX ;DX:AX = Offset from start of file
MOV CX,AX ;DX:CX = Offset in bytes
XCHG CX,DX ;CX:DX now contains the offset

MOV AX,4200h ;Move to this position in the file
MOV BX,FILE_HANDLE ;Get the file handle
INT 21h ;Move the file pointer

POP DX
POP CX
POP BX
POP AX
RET
SET_FILE_POINTER ENDP


PUBLIC WRITE_FILE_SECTOR
.DATA
EXTRN FILE_HANDLE:WORD
.CODE
;-----------------------------------------------------------------------;
; This procedure writes a single sector back to the file that you were ;
; viewing. Note, this can change the length of the file if you write ;
; the last sector, and the file was not a multiple of 512 bytes. In ;
; such cases, this procedure will increase the file length to a ;
; multiple of 512 bytes. ;
; ;
; Uses: SET_FILE_POINTER
; Reads: DISK_ERROR_FLAG, FILE_HANDLE, BYTES_READ, SECTOR
;-----------------------------------------------------------------------;
WRITE_FILE_SECTOR PROC
PUSH AX
PUSH BX
PUSH CX
PUSH DX

CALL SET_FILE_POINTER ;Set to start of current sector
CMP DISK_ERROR_FLAG,1 ;Was there an error?
JE DONE_WRITE_FILE_SECTOR ;Yes, then don't try to write sector

MOV AH,40h ;Call for a write to this file
MOV BX,FILE_HANDLE ;Get the file handle
MOV CX,BYTES_READ ;Don't write more than we read
LEA DX,SECTOR ;Address of our sector buffer
INT 21h ;Let DOS write this sector
CMP DISK_ERROR_FLAG,1 ;Was there a disk error
JE DONE_WRITE_FILE_SECTOR ;Yes, then return

MOV AH,45h ;Duplicate the file handle
INT 21h ;AX == the new file handle
MOV BX,AX ;Put new file handle into AX
MOV AH,3Eh ;Close duplicate handle to update
INT 21h ;The changes we just made

DONE_WRITE_FILE_SECTOR:
POP DX
POP CX
POP BX
POP AX
RET
WRITE_FILE_SECTOR ENDP


END


  3 Responses to “Category : Assembly Language Source Code
Archive   : NORTASM.ZIP
Filename : DISK_IO.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/