| UMBC CMSC 211 |
If the file has a fix-length format, that every record is the same size, then it is very easy to locate the proper position. It is simply:
Variable length records are more difficult and require a more advanced technique to get to the correct location, such as indexing or hashing. Again, though, once you know where to go, it is only one step to get there.
For our purposes, that one step is a macro, _LSeek. (All four general registers are used, so I recommend that you use sLSeek instead).
;; ;; _LSeek Handle, Handle, SeekType, LoDistance[, HiDistance] ;; SeekType is the actual numerical value of ;; the seek type, 0, 1, 2, or one of the corresponding ;; symbolic values FromStart, FromCur, or FromEnd ;; Seek distance is a double-word integer ;; HiDistanceLoDistance. ;; If disktance is single precision,positive or ;; negative, HiDistancecan be omitted ;; If success, returns Carry Flag clear and new ;; current position is in DX/AX ;;
| SeekType | distance | locates to |
|---|---|---|
| FromStart | 0 | the beginning of the file (a 'rewind') |
| FromStart | n | absolute byte n in the file |
| FromCur | n > 0 | n bytes forward from the current location |
| FromCur | -n, n > 0 | n bytes backward from the current location |
| FromEnd | 0 | the end of the file and (AX, DX becomes the file length) |
| FromEnd | -n, n < 0 | n bytes back from the end of the file |
| FromCur | 0 | File position (unchanged) to AX, DX |
If we look at the underlying DOS command be have:
| AH | 42H | ||||||
| AL |
| ||||||
| BX | file handle of opened disk file | ||||||
| CX | most significant part of offset | ||||||
| DX | least significant part of offset |
;; LOG.ASM--Program looks for a file LOGFILE in current directory
;; and if it doesn't exist, creates one. It then appends to
;; this file current date and time (form mm/dd/yy hh:mm:ss), then
;; on the following line, indented four spaces, writes the
;; rest of the command that invoked it.
;;
;; LOG makes use of a procedure TwoDigs defined here which takes
;; number from 0 to 99 in al and converts it to two consecutive
;; ASCII digits at si. Si is then updated to point to the
;; character after the second digit, + 1.
;;
;; Program text from "Assembly Language for the IBM PC Family" by
;; William B. Jones, (c) Copyright 1992, 1997, 2001, Scott/Jones Inc.
;;
INCLUDE PCMAC.INC
.MODEL SMALL
.586
.STACK 100h
IFDEF ??version
PSP SEGMENT AT 0 USE16
ELSE
PSP SEGMENT AT 0
ENDIF
ORG 128
CmdLen DB ?
Command DB 127 DUP (?)
PSP ENDS
.DATA
FileName DB 'LOGFILE', 0
AccessMsg DB 'Cannot access LOGFILE$'
NoCreate DB 'Cannot create LOGFILE$'
WErrMsg DB 'LOGFILE write error$'
Stamp DB 'mm/dd/yy hh:mm:ss'
CRLF DB 13, 10 ; Used both for Stamp and by itself
STAMPLEN EQU $ - Stamp ; Number of bytes to write for Stamp
INDENT EQU 4
LogIndent DB INDENT DUP (' ')
Do2 MACRO whence
mov al, &whence&
call TwoDigs
ENDM
.CODE
EXTRN CCheck : NEAR, WCheck : NEAR
Logger PROC
_Begin
ASSUME es:PSP
_Open FileName, Write ; Get Log File
jnc GotLogFile
_Creat FileName ; File doesn't exist (or is read-only)
call CCheck ; Try to make it
GotLogFile:
mov di, ax ; Save handle in di
_LSeek di, FromEnd, 0 ; Seek to end of file
call CCheck
; Write date and time stamp
_GetDate
mov si, OFFSET Stamp
Do2 dh ; Month
Do2 dl ; Day
mov ax, cx ; Reduce year to 2 digits
mov bl, 100 ; avoiding Y2K-type problems
div bl
Do2 ah ; Year % 100
_GetTime
Do2 ch ; Hour
Do2 cl ; Minute
Do2 dh ; Second
_Write di, Stamp, STAMPLEN
call WCheck
; Write log message
_Write di, LogIndent, INDENT
call WCheck
push ds ; must have ds --> PSP for next _Write
mov al, CmdLen
sub ah, ah ; Convert CmdLen to word
_Write di, Command, ax, es
pop ds
call WCheck
_Write di, CRLF, 2
call WCheck
_Close di
_Exit 0
Logger ENDP
TwoDigs PROC
aam ; Standard trick to convert # <= 99 to
add ax, '00' ; two ASCII digits; store at [si]
xchg ah, al
mov [si], ax
add si, 3 ; skip si over digits and punctuation
ret
TwoDigs ENDP
END Logger