Category : Files from Magazines
Archive   : MSJV2-5.ZIP
Filename : MDM_DRV.ALL

 
Output of file : MDM_DRV.ALL contained in archive : MSJV2-5.ZIP
Microsoft Systems Journal
Volume 2; Issue 5; November, 1987

Code Listings For:

MDM_DRV
pp. 51-65

Author(s): Ross M. Greenberg
Title: A Strategy for Building And Debugging Your FIrst MS-DOS
Device Driver



;; Code listings for MDM-DRV.ASM and DEBUG.ASM


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; MDM_DRV.ASM An XMODEM Device Driver
;;
;; Copyright 1987, Ross M. Greenberg
;;
;; This program is a device driver which enables a simply DOS command
;; such as:
;;
;; C>COPY MDM FILENAME.EXT
;;
;; to automatically use the popular XMODEM/Checksum protocol for
;; transferring files through the serial port.
;;
;; The current version of the code expects there to be a Hayes
;; compatible modem atached to the serial port.
;;
;; To compile and use this program, type the following commands (you
;; must have MASM4.0):
;;
;;
;; C> MASM MDM_DRV;
;; C> LINK MDM_DRV;
;; C> EXE2BIN MDM_DRV.EXE MDM_DRV.SYS
;;
;; Remember that you must include a line of the form:
;;
;; DEVICE=C:\devices\MDM_DRV.SYS
;;
;; in your CONFIG.SYS file on the disk you usually boot from.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;DEBUG equ 0 ; remove the semi colon
; to get debug messages
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; GLOBAL DEFINITIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

MAX_CMD equ 16 ; total number of commands in this driver

TIMER_ADD equ (01ch * 4h)

NULL equ 0
TRUE equ 1
FALSE equ 0

FULLBLKSIZE equ (128 + 3 + 1) ; Total size of an XMODEM/Checksum
; block. SOH + BLK + ~BLK + DATA + CHK
BLKSIZE equ (128) ; Number of data bytes in an XMODEM Blk
NAK_MAX equ 5 ; Total number of NAKs before an abort

SOH equ 01h ; the first character of an XMODEM block
EOT equ 04h ; End of transmission in XMODEM
ACK equ 06h ; Last block received alright
NAK equ 015h ; Last block was not received properly
ABORT equ 018h ; Abort (CANcel) this transfer

CR equ 0dh
LF equ 0ah
ESCAPE equ 01bh

LEFT_BRACKET equ '[' ; characters output by the STAT
RIGHT_BRACKET equ ']' ; macro.
DOT equ '.'
STAR equ '*'
QUESTION equ '?'
EXCLAIM equ '!'
DUP_BLK equ 'D'


ONE_SECOND equ 18
TEN_SECONDS equ (ONE_SECOND * 10)
ONE_MINUTE equ (ONE_SECOND * 60)
FOREVER equ -1 ; or close enough!

ERROR equ 08000h ; general error bit
BUSY equ 0200h ; Device Busy Bit
DONE equ 0100h ; command finished

UNK_COMMAND equ 03h
WRITE_ERROR equ 0ah
READ_FAULT equ 0bh
GEN_FAILURE equ 0ch
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; MACRO DEFINITIONS
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; STAT
;;
;; This macro simply outputs the character represtned by .
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
STAT macro arg
push ax
mov al, arg
call o_char
pop ax

endm


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PUSHALL
;; POPALL
;;
;; PUSHALL_AX
;; POPALL_AX
;;
;; Simple macros for saving all the registers, then another to pop them
;; back.
;;
;; The "_AX" macors do not save or restore the AX Register
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PUSHALL macro
pushf
push ax
push bx
push cx
push dx

push si
push di

push bp

push ds
push es
endm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
POPALL macro
pop es
pop ds

pop bp

pop di
pop si

pop dx
pop cx
pop bx
pop ax
popf
endm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PUSHALL_AX macro
pushf
push bx
push cx
push dx

push si
push di

push bp

push ds
push es
endm

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
POPALL_AX macro
pop es
pop ds

pop bp

pop di
pop si

pop dx
pop cx
pop bx
popf
endm

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

code segment public 'CODE'

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; The driver itself is one large procedure, called 'driver'
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
driver proc far

assume cs:code, ds:code, es:code ; each segment should
; point to the code segment

org 0 ; drivers *must* start at
; ofset of zero
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DEVICE DRIVER HEADER
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
header1 dd -1 ; This *must* be set to -1. Filled in by DOS
dw 0e800h ; character device,
; IOCTL supported,
; Output till busy,
; open/close/rm supported

dw strat ; point to the strategy routine
dw ints ; point to the interrupt routine
db 'MDM ' ; The name of the device. Left justified,
; space filled to eight characters


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; THE REQUEST HEADER
;; The address of the Request Header is passed to the device driver
;; when the strategy routine is called. This address should be saved
;; and then made available to the interrupt routine.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
request struc
rlength db 0 ; 0 - length of pertinent data in header
unit db 0 ; 1 - unit number (not used in MDM_DRV)
command db 0 ; 2 - the actual command
status dw 0 ; 3 - return status
reserve db 8 dup (0) ; 5 - Reserved for DOS
media db 0 ; 13 - Media desciptor. (Not used in MDM_DRV)
address dd 0 ; 14 - Doubleword pointer for I/O
count dw 0 ; 18 - unsigned int count for character I/O
sector dw 0 ; 20 - starting sector. (Not used in MDM_DRV)
request ends
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The dispatch table points to each of the routines the device
;; driver needs. This table is used by the interrupt routine,
;; and the particular routine the Command Byte in the Request Header
;; is used as index into the table.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
dispatch:
dw init ; 0x00 - init the driver. Called on install
dw media_chk ; 0x01 - media check (Not used in MDM_DRV)
dw bld_bpb ; 0x02 - Build the BPB (Not used by MDM_DRV)
dw rd_ioctl ; 0x03 - Read IOCTL
dw read ; 0x04 - Read 'count' characters
dw nd_read ; 0x05 - Non-destructive character read
dw inp_stat ; 0x06 - Input Status
dw inp_flush ; 0x07 - Input Flush
dw write ; 0x08 - Write 'count' characters to device
dw write_vfy ; 0x09 - Write Verify
dw out_stat ; 0x0A - Output Status
dw out_flush ; 0x0B - Fluch Output Buffers
dw wrt_ioctl ; 0x0C - Write IOCTL
dw dev_open ; 0x0D - Device Open (DOS 3.x only)
dw dev_close ; 0x0E - Device Close (DOS 3.x only)
dw rem_media ; 0x0F - Removable Media Routine (DOS 3.x only)
dw out_busy ; 0x10 - Output Until Busy (DOS 3.x only)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; STRATEGY ROUTINE
;;
;; This rouinte merely stored the address of the Request header found
;; on entry in the ES:BX register pair.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
strat proc far

mov word ptr cs:[rh_ptr], bx
mov word ptr cs:[rh_ptr + 2], es
ret

strat endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GLOBAL VARIABLES
;;
;; These variables are used for a variety of purposes. Additionally
;; the 'com_port' variable is the first value returned or written to
;; for IOCTL READ and IOCTL WRITE calls, allowing application programs
;; to change the communications port, baud rate and other information
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

com_port dw 0 ; the comm port we are communicating
; with. COM1 = 0, COM2 = 1, etc.
init_data db 10000011b ; The initialization byte. See the
; BIOS Technical Reference Manual for
; details.
block_num dw 0 ; current block being transferred
abort_xfer dw 0 ; whether the current transfer is to
; aborted. (TRUE = YES)
nak_cnt dw 0 ; How many Nak's have been sent or
; received

inrequest dw 0 ; the number of bytes not yet processed
in_block dw 0 ; has a block been received?
was_dialed dw 0 ; if set, stat was offline at start
eof_flag dw 0 ; set if eof seen in read

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

new_stack dw 128 dup (0) ; 128 words for the new stack
new_stack_end dw 0

old_stack dw 0 ; store old stack offset
dw 0 ; store old stack segment

timer dw 0 ; the timer count
old_timer dw 0 ; store old timer offset
dw 0 ; store old timer segment

rh_ptr dd 0 ; this is where the Request Header
; address is stored

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The I/O buffer, which looks like an XMODEM block
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

_soh db 0 ; This will be an SOH
_blk1 db 0 ; the block number
_blk2 db 0 ; and its complement
_buf db 128 dup (0) ; the actual data
_chksum db 0 ; the sum of all data bytes

_outptr dw _buf ; points to next character to be output
_outcnt dw 0 ; how many characters to be output

_inptr dw _soh ; points to next input position
_incnt dw 0 ; the number of chars in the buffer

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; A variety of messages
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

offline db CR, LF, 'You are currently off line. Enter number to dial (or t
o answer): $'
dialing db CR, LF, 'Dialing...standby....$'
no_con db CR, LF, 'Could not connect to remote.....', CR, LF, LF, '$'
con db CR, LF, 'Connected to remote... to continue', CR, LF, LF, '$'
xfer db CR, LF, 'Starting Transfer Request...', CR, LF, LF, '$'
await db CR, LF, 'Waiting for Carrier....', CR, LF, '$'
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
kb_len dw 0 ; number of chars in num_buf
answerstring db 'ATS0=1', CR, NULL ; Output for Autoanswer
dialstring db 'ATDT', NULL ; Output for AutoDial
numbuf db 20 dup (0) ; and the number to dial
return db CR, NULL
pluses db '+++', NULL ; used in hanging up
hangup db 'ATH', CR, NULL ; the hang-up-the-modem string
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
IFDEF DEBUG ; commented out above
include debug.asm ; debug procedures
ELSE
DO_PRINT macro arg ; else empty macros
endm
DO_ERR_PRINT macro arg
endm
ENDIF
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INTERRUPT ROUTINE
;;
;; The main interrupt routine sets up a local stack, pushes all
;; registers onto the new stack, gets the Request Header, determines
;; if the Command Byte is a legitimate one and returns an error
;; condition if it isn't.
;;
;; Otherwise the Command Byte is used as an index into the dispatch
;; table, and a near call is generated to the approriate routine.
;; Since DOS doesn't casre about the Done Bit being set in the event
;; of an error, this bit gets OR'ed in upon return of the new status
;; in the AX register. The status is then saved in the Request Header,
;; all registers are popped, and the original stack is restored.
;;
;; Finally, a far return to the caller (which should be DOS)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ints proc far

cli
mov cs:[old_stack], sp
mov cs:[old_stack + 2], ss
mov sp, cs
mov ss, sp
mov sp, offset cs:new_stack_end
sti

PUSHALL

push cs
pop ds

les di, cs:[rh_ptr]
mov bl, es:[di.command]
xor bh, bh
cmp bx, MAX_CMD
jle ints1

mov ax, ERROR + UNK_COMMAND
jmp ints2

ints1:
shl bx, 1

call word ptr dispatch[bx]

les di, cs:[rh_ptr] ; reset just in case...

ints2:
or ax, DONE ; Set the done flag
mov es:[di.status], ax ; Save status in the structure


POPALL

cli
mov ss, cs:[old_stack + 2]
mov sp, cs:[old_stack]
sti

ret

ints endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; READ ROUTINE
;;
;; This routine first saves the original charactrer count request, then
;; zeros out the original call value, and loads up the "to" buffer
;; address.
;;
;; If it as already processing a block, meaning that the last read
;; request did not exhaust the buffer totally, then it skips down
;; and starts loading characters one at a time (a reps would work just
;; as well!), until either the read request is exhausted or the buffer
;; is exhausted.
;;
;; When the block is exhausted, an ACK is sent to the remote which
;; will then start to send another block.
;;
;; The in_block variable is turned off, and a jump to the top of
;; thr routine is made, where data is collected a character at a time
;; until another full block is reached, verified and the entire routine
;; starts again.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
read proc near
DO_PRINT msg_read
cmp cs:[eof_flag], TRUE
jz r_back

mov ax, es:[di.count]
mov cs:[inrequest], ax ; save the request count
mov es:[di.count], FALSE ; and zero the rh count
lds bx, es:[di.address] ; ds:bx points to data

top_read:
cmp cs:[in_block], TRUE ; are we in a block?
jnz lp_it
jmp read_loop ; yes

lp_it:
mov cs:[_incnt], FALSE
mov si, offset cs:[_soh] ; set pointer to block begin
mov cs:[timer], TEN_SECONDS ; set up timer
mov cs:[nak_cnt], FALSE ; set to no NAKS
cmp cs:[block_num], 1 ; first time?
jnz rd_blk ; no
call send_nak ; send the first NAK
STAT LEFT_BRACKET ; show a '['

rd_blk:
call get_char ; any characters?
jc rd_blk2 ; no

cmp cs:[_incnt], FALSE ; first character?
jnz nxt_char ; no

cmp al, SOH ; first character an SOH?
jz nxt_char ; yes. store it

cmp al, ABORT ; Abort xfer?
jz abort_it ; yep. Abort it!

cmp al, EOT ; are we done?
jnz rd_blk ; nope, so throw char away

call send_ack ; process EOF
STAT RIGHT_BRACKET ; show a ']'
STAT CR ; and a clean line
STAT LF
r_back:
mov es:[di.count], FALSE ; and zero the rh count
mov cs:[eof_flag], TRUE
xor ax, ax
ret

nxt_char:
mov cs:[si], al ; store it
inc cs:[_incnt] ; bump the character count
inc si ; and the pointer

rd_blk2:
cmp cs:[_incnt], FULLBLKSIZE ; enough bytes to check?
jz chkblk

cmp cs:[timer], FALSE ; out of time?
jnz rd_blk ; nope. try again
STAT DOT ; show a dot
jmp bad_blk2
bad_blk:
STAT QUESTION ; show a question mark
bad_blk2:
call send_nak ; yep. send a NAK
cmp word ptr cs:[abort], TRUE ; abort?
jnz rd_blk3 ; no, so reset timer, try again

abort_it:
call send_abort ; send an abort
call send_abort ; twice
STAT EXCLAIM ; finish with a bang!
mov ax, 800ch ; mark an error
ret

rd_blk3:
mov cs:[timer], TEN_SECONDS ; reset the timer
jmp rd_blk ; and try again

chkblk:
mov ax, cs:[block_num]
push ax

dec al ; temporary
cmp al, cs:[_blk1] ; duplicate block?
jnz real_blk ; no
STAT DUP_BLK ; yes
mov cs:[_incnt], FALSE
jmp ack_blk

real_blk:
pop ax ; back up to correct block

cmp cs:[_blk1], al ; is the block count alright?
jnz bad_blk ; no
not al
cmp cs:[_blk2], al ; block complement?
jnz bad_blk ; no

mov cx, BLKSIZE
mov si, offset _buf
call do_chksum
cmp cs:[_chksum], al
jnz bad_blk ; checksum bad

mov cs:[in_block], TRUE ; looks good!
mov cs:[_inptr], offset cs:_buf ; set up pointer
sub cs:[_incnt], FULLBLKSIZE - BLKSIZE

read_loop:
mov si, cs:[_inptr] ; reset the pointer
rd_loop:
mov al, cs:[si] ; get the character
mov ds:[bx], al ; and stuff it
inc bx ; up each pointer
inc si
inc cs:[_inptr]

inc word ptr es:[di.count] ; inc the counter
dec cs:[_incnt] ; drop the characters left
jnz stf_nxt_char ; still more!

inc cs:[block_num]
STAT STAR ; send a '*'
ack_blk:
call send_ack ; send the ack
mov cs:[in_block], FALSE ; out of characters
dec cs:[inrequest]
jz read_back
jmp top_read

stf_nxt_char:
dec cs:[inrequest] ; anymore?
jnz rd_loop

read_back:
xor ax,ax ; no more request, no errors
ret
read endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; WRITE ROUTINE
;;
;; This routine starts loading characters one at a time (again a reps
;; could be used) into a local buffer. When the total number of
;; characters collected (some of which may be left over from last
;; time through) reaches 128, then the send_block routine is
;; called.
;;
;; The return from the send_block routine only happens if there
;; was a successful ACK, or too many NAKs have been sent, which
;; causes the abort flag to be set.
;;
;; Stay in this loop until there are no more characters to store and
;; send, as per the original character count from the Request Header
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
write proc near
PUSHALL_AX

DO_PRINT msg_write

mov cx, es:[di.count] ; how many bytes?
xor dx, dx ; zero our character count
mov es:[di.count], dx ; zero out reciept area

lds bx, es:[di.address] ; ds:bx points to data

mov si, cs:[_outptr] ; where to stick each char
wr_lp:
mov al, ds:[bx] ; get the character
mov cs:[si], al ; and save it in buffer
inc bx ; move pointers
inc cs:[_outcnt] ; total character count
inc es:[di.count] ; and current character count

cmp cs:[_outcnt], BLKSIZE ; time to send a block?
jnz wr_ok ; not yet
call send_block ; send the block
cmp cs:[abort_xfer], FALSE ; should we abort?
jz blk_ok ; no, block sent okay

call send_abort ; yes,
call send_abort ; send it twice!

mov es:[di.count], FALSE ; mark no characters as sent
mov ax, ERROR + WRITE_ERROR ; mark as a write error
ret

blk_ok:
mov cs:[_outptr], offset _buf ; reset the pointer,
mov si, offset _buf ; the register,
mov cs:[_outcnt], FALSE ; and the number of chars

wr_ok:
inc si ; point to next char position
loop wr_lp

mov cs:[_outptr], si ; now save it
xor ax, ax ; no errors
POPALL_AX
ret
write endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; WRITE WITH VERIFY ROUTINE
;;
;; This routine simply jumps to the "regular" write routine.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
write_vfy proc near
DO_PRINT msg_write_vfy
jmp write
write_vfy endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DEVICE OPEN ROUTINE
;;
;; After setting counts to their initial, startup condition, this
;; routine steals the timer tick, then initializes the comm port
;; to the parameters stored in the init_data variable, then gets
;; the status of the comm port.
;;
;; It eats all characters available on the comm port, then calls
;; the dial_rout routine which will attempt to put the attached device
;; on-line if it isn't already.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
dev_open proc near
PUSHALL_AX
DO_PRINT msg_dev_open

mov cs:[_inptr], offset cs:_soh
mov cs:[_incnt], FALSE

mov cs:[_outptr], offset cs:_buf
mov cs:[_outcnt], FALSE

mov cs:[in_block], FALSE
mov cs:[block_num], 1

mov dx, cs:[com_port] ; init the comm port
mov al, cs:[init_data]
mov ah, 0
int 14h

eat_loop:
mov dx, ds:[com_port]
mov ah, 3 ; get status
int 14h

test ah, 1 ; any data ready?
jz continue ; no
test al, 20h ; DSR must be set, or next int14
jz continue ; will (basically) never return

mov ah, 2
int 14h ; eat the character and
jmp eat_loop ; try again

continue:
call dial_rout ; if not on line, make on line

xor ax, ax
POPALL_AX
ret
dev_open endp


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DEVICE CLOSE ROUTINE
;;
;; A check is made to determine if there is a partial block to be
;; sent (127/128 chance!), and if so, the block is sent, and an EOT
;; is sent to indicate end of transmission.
;;
;; If this was a "dialed" call (either Autoanswer or AutoDial), then
;; the Hayes hangup sequence is generated (1 second pause, output three
;; plus signs, 1 second pause, then a ATH followed by a ).
;;
;; In any case, the timer tick (originally set in the dev_open routine)
;; is reset and then control is returned to the caller
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
dev_close proc near
DO_PRINT msg_dev_close

mov cs:[eof_flag], FALSE

cmp cs:[_outcnt], FALSE ; any characters left?
jz no_send ; no

call send_block ; yes. send the remainder
STAT RIGHT_BRACKET
STAT CR
STAT LF
mov cs:[_outptr], offset _buf ; reset the pointer,
mov si, offset _buf ; the register,
mov cs:[_outcnt], FALSE ; and the number of chars
inc cs:[block_num] ; just in case....

no_send:
cmp cs:[block_num], 1 ; was at least one block sent?
jz no_hang ; no

mov al, EOT ; send the EOT
mov ah, 1
mov dx, cs:[com_port]
int 14h

cmp cs:[was_dialed], TRUE ; were we the dialer?
jnz no_hang

call do_hang ; send the +++ATH

no_hang:
mov cs:[in_block], FALSE
mov cs:[was_dialed], FALSE

xor ax, ax
ret
dev_close endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; NON-DESTRUCTIVE READ ROUTINE
;;
;; This routine returns the next character in the input buffer if
;; there is one.
;;
;; In the event of an empty buffer, it returns a '?' and an
;; error condition
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
nd_read proc near
DO_PRINT msg_nd_read
PUSHALL_AX

mov es:[di.count], 1 ; we always return some count
lds bx, es:[di.address] ; ds:bx points to data buffer
cmp cs:[_incnt], FALSE ; any characters?
jnz get_nd_char ; yes!

mov ax, ERROR + READ_FAULT ; mark an error condition
mov byte ptr es:[bx], '?'
jmp nd_return

get_nd_char:
xor ax, ax ; no error
mov si, cs:[_inptr]
mov al, cs:[si] ; get the next character
mov es:[bx], al

nd_return:
POPALL_AX
ret
nd_read endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INPUT STATUS ROUTINE
;;
;; This routine simply examines the in_block variable to determine
;; if there are any outstanding characters (from a good block) and
;; returns the BUSY bit set if there are.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
inp_stat proc near
DO_PRINT msg_inp_stat
xor ax,ax ; start off empty
cmp cs:[in_block], FALSE ; are we in a block
jz no_chars ; no

or ax, BUSY ; yes. mark it
no_chars:
ret
inp_stat endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FLUSH INPUT BUFFER ROUTINE
;;
;; This routine simply resets pointers and counts to indicate an
;; empty input buffer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
inp_flush proc near
DO_PRINT msg_inp_flush

mov cs:[_inptr], offset cs:_soh
mov cs:[_incnt], FALSE

xor ax, ax
ret
inp_flush endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; OUTPUT STATUS ROUTINE
;;
;; This is, basically, a do nothing routine, since the write routine
;; never exits with a busy status. If it did, then the status would
;; be examined and the BUSY bit set if required. Here, it is left
;; unset.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
out_stat proc near
DO_PRINT msg_out_stat

xor ax, ax
ret
out_stat endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FLUSH OUTPUT BUFFERS ROUTINE
;;
;; This routine would make things very difficult to sort out if
;; it were to actually flush the output buffers. As such, it is
;; a "do-nothing" routine.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
out_flush proc near
DO_PRINT msg_out_flush

xor ax, ax
ret
out_flush endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; READ I/O CONTROL ROUTINE
;;
;; This routine simply copies 'count' bytes into the address specified
;; as the transfer address, starting at the com_port variable.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
rd_ioctl proc near
DO_PRINT msg_rd_ioctl
ret
rd_ioctl endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Write I/O CONTROL ROUTINE
;;
;; This routine simply copies 'count' bytes from the address specified
;; as the transfer address, starting at the com_port variable.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
wrt_ioctl proc near
DO_PRINT msg_wrt_ioctl

ret
wrt_ioctl endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;








;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; These routines are not used by this driver
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
media_chk proc near
DO_ERR_PRINT msg_media_chk
xor ax,ax
ret
media_chk endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

bld_bpb proc near
DO_ERR_PRINT msg_bld_bpb
xor ax,ax
ret
bld_bpb endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

rem_media proc near
DO_ERR_PRINT msg_rem_media
xor ax,ax
ret
rem_media endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

out_busy proc near
DO_ERR_PRINT msg_out_busy
xor ax,ax
ret
out_busy endp






;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GENERALIZED ROUTINES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TIMER TICK: this routine simply decrements a counter if the counter
;; is non-zero
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
my_tick proc far
cmp cs:[timer], 0 ; and ticks to decrement?
jz no_dec ; no
dec cs:[timer] ; yes, so do it
no_dec:
jmp dword ptr cs:[old_timer] ; call the original tick
my_tick endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DIAL_ROUT
;;
;; Called from the device open routine, this routine will simply return
;; if Carrier is detected.
;;
;; Otherwise a message is displayed, and the user is allowed to
;; either input a number, or to only enter a return. The modem then
;; is output a dial string or an answer string and the status is checked
;; until the modem is online (or until a timeout occurs).
;;
;; Once on-line, any data on the comm port is displayed, and any data
;; at the keyboard is displayed and sent. When an escape is discovered,
;; the routine returns back to the calling program.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
dial_rout proc near
test al, 080h ; DCD on?
jz off_line
ret

off_line:
mov dx, offset cs:offline ; output the message
call out_line

mov si, offset cs:numbuf
mov cx, 19 ; maximum length
call get_num

cmp cs:[kb_len], 0 ; only a return?
jnz dial_it

mov dx, offset cs:await
call out_line

mov si, offset cs:answerstring
call out_string
mov cs:[timer], FOREVER
jmp offline_lp ; now wait for carrier

dial_it:
mov dx, offset cs:dialing
call out_line

mov si, offset cs:dialstring
call out_string

mov si, offset cs:numbuf
call out_string

mov si, offset cs:return
call out_string
mov cs:[timer], ONE_MINUTE

mov cs:[was_dialed], TRUE ; mark as a dialed call for ATH later

offline_lp:
mov ah, 3 ; are we on-line?
mov dx, cs:[com_port]
int 14h

test al, 080h ; DCD on?
jnz made_con ; yes

cmp cs:[timer], 0 ; out of time?
jnz offline_lp ; nope

abort_call:
mov cs:[was_dialed], FALSE
mov dx, offset cs:no_con
call out_line

abort_out:
xor ax,ax
mov es:[di.count], ax ; set count to zero

mov ax, ERROR + GEN_FAILURE ; mark as an error
POPALL_AX
ret

made_con:
mov dx, offset cs:con
call out_line

term_em_lp:
call get_char
jc get_term_char

cmp al, ESCAPE
jz term_exit ; escape. Get out!

call o_char

get_term_char:
mov ah, 1h
int 16h ; any characters?
jz term_em_lp ; nope
mov ah, 0 ; yep. get the character
int 16h

mov ah, 1
mov dx, ds:[com_port]
int 14h

cmp al, ESCAPE
jz term_exit ; escape. Get out!

call o_char

jmp get_term_char

term_exit:
mov dx, offset cs:xfer ; status message
call out_line

eat_c:
call get_char
jnc eat_c

ret
dial_rout endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SEND_BLOCK ROUTINE
;;
;; This is the generalized XMODEM Send-A-Block Routine.
;;
;; If this is the first time since dev_open that this routine is
;; called, then the block count will be set to 1. Wait for the
;; initial NAK from the remote before sending, or wait for a time
;; out.
;;
;; Send the block, then wait for the ACK or NAK response. If an ACK,
;; return immediately. If a NAK, then try again, until the error
;; count gets too high. If too high, set the abort flag and return.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
send_block proc near
PUSHALL
push cs
pop ds

cmp cs:[block_num], 1 ; is this the first block?
jnz not_first ; no
STAT LEFT_BRACKET

first_nak:
call get_ack ; get the first ack
cmp al, NAK ; was it a NAK?
jnz first_nak ; nope

not_first:
mov word ptr ds:[_soh], SOH
mov ax, cs:[block_num]
mov ds:[_blk1], al
not al
mov ds:[_blk2], al

mov cx, BLKSIZE
mov si, offset _buf
call do_chksum
mov cs:[_chksum], al

send_top:
mov cx, FULLBLKSIZE
mov si, offset _soh

send_lp:
mov ah, 1
mov al, ds:[si]
mov dx, cs:[com_port]
int 14h

inc si
loop send_lp

call get_ack
jnc send_ok

STAT QUESTION
inc cs:[nak_cnt] ; error. inc the counter
cmp cs:[nak_cnt], NAK_MAX ; too many blocks?
jle send_top ; no. try again
mov cs:[abort_xfer], TRUE ; mark for abort
STAT EXCLAIM
jmp send_back ; return

send_ok:
STAT STAR
inc cs:[block_num] ; block sent okay, inc the counter
mov cs:[nak_cnt], FALSE ; reset the nak counter

send_back:
POPALL
ret
send_block endp


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GET_ACK ROUTINE
;;
;; This routine will return with the carry flag set if an NAK was
;; received or if it times out.
;;
;; Carry clear if the character received is an ACK
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
get_ack proc near

not_ack:
mov cs:[timer], TEN_SECONDS

not_yet:
mov ah, 3
mov dx, cs:[com_port]
int 14h

test ah, 1 ; data ready?
jnz data_ready ; yes

cmp cs:[timer], 0 ; out of time?
jnz not_yet ; no
STAT DOT
jmp set_c ; yes. set carry and return

data_ready:
mov ah, 2
mov dx, cs:[com_port]
int 14h

test ah, 087h ; any errors?
jnz ack_back ; yes

cmp al, ACK ; is it an ACK?
jz ack_back ; yes
cmp al, NAK ; a NAK?
jz set_c ; set the carry flag for return
cmp al, ABORT ; a cancel?
jnz not_ack ; try again
mov cs:[abort_xfer], TRUE ; set for an abort
set_c:
stc
ret

ack_back:
clc
ret

get_ack endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Simply send an abort out the serial port
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
send_abort proc near

mov ah, 1 ; abort by sending an abort
mov al, ABORT ; character
int 14h ; yes, abort

ret
send_abort endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DO_HANG
;;
;; Hang up the modem with a second of silence, +++, a second of silence
;; then an ATH, followed by a return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
do_hang proc near
mov cs:[timer], ONE_SECOND ; one second of silence

sil_lp1:
cmp cs:[timer], FALSE ; expired?
jnz sil_lp1

mov si, offset cs:pluses ; get the modems attention
call out_string

mov cs:[timer], ONE_SECOND ; one second of silence

sil_lp2:
cmp cs:[timer], FALSE ; expired?
jnz sil_lp2

mov si, offset cs:hangup ; now hangup
call out_string
do_hang endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DO_CHKSUM ROUTINE
;;
;; Starting at CS:[SI], add the next CX characters into AX
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
do_chksum proc near

mov ax, FALSE ; zero out the count
chk_lp:
add al, cs:[si] ; add the individual byte
inc si
loop chk_lp ; for each byte

ret

do_chksum endp


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Simply send the NAK, increment the count of NAKs, and set the
;; abort flag if required.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
send_nak proc near

push ax
push dx

mov ah, 1 ; send the NAK
mov al, NAK
mov dx, cs:[com_port]
int 14h

mov cs:[_incnt], FALSE
mov si, offset cs:[_soh] ; set pointer to block begin
mov cs:[timer], TEN_SECONDS ; set up timer

inc cs:[nak_cnt]
cmp cs:[nak_cnt], NAK_MAX ; too many NAKs?
jnz nak_ret ; no
mov word ptr cs:[abort], FALSE ; yes, so abort
nak_ret:
pop dx
pop ax
ret

send_nak endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Simply send the ACK
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
send_ack proc near

push ax
push dx

mov ah, 1 ; send the ACK
mov al, ACK
mov dx, cs:[com_port]
int 14h

pop dx
pop ax
ret
send_ack endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; If there is a character at the console, get the character into al,
;; and clear the carry bit. Otherwise, set the carry bit.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
get_char proc near
mov ah, 3 ; is there a character?
mov dx, cs:[com_port]
int 14h
test ah, 1 ; data ready?
jnz get_ch2 ; yes
stc
ret

get_ch2:
mov ah, 2 ; get the character

mov dx, cs:[com_port]
int 14h
clc
ret
get_char endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set the character count to zero, get upto CX number of characters
;; and store them into a buffer, one at a time, starting at cs:[si].
;;
;; Return when CX characters have been entered, or a is hit.
;; kb_len will contain how many characters were entered
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
get_num proc near
mov cs:[kb_len], FALSE
get_kbc:
mov ah, 0 ; get a character from kb
int 16h

call o_char
cmp al, CR ; if CR, return
jnz store
ret

store:
mov cs:[si], al ; store it
inc si ; bump pointer
xor ax, ax
mov cs:[si], al ; zero out from last time
inc cs:[kb_len]
loop get_kbc
ret
get_num endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Output the characters through the comm port, starting at cs:[si]
;; until a NULL is hit.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
out_string proc near

out_lp:
mov al, cs:[si] ; load the character
cmp al, NULL ; at end?
jnz out_it ; no
ret ; yes

out_it:
mov dx, cs:[com_port] ; output the character
mov ah, 1
int 14h
inc si
jmp out_lp

out_string endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; O_CHAR
;;
;; Using the BIOS Teletype Calls, this routine merely dumps the character
;; in al onto page 0 at the current cursor position
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
o_char proc near
push bx
xor bx,bx
mov ah, 0eh
int 10h
pop bx
ret
o_char endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; OUT_LINE
;;
;; This routine will output (via the device driver safe BIOS calls)
;; the string pointed to by DS:DX, which is terminated with either
;; a '$' or a NULL byte. It used the O_CHAR routine for each character.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
out_line proc near
push ax
push si
mov si, dx
out_line_lp:
mov al, cs:[si]
cmp al, '$'
jz out_line_exit
cmp al, NULL
jz out_line_exit
call o_char
inc si
jmp out_line_lp
out_line_exit:
pop si
pop ax
ret
out_line endp


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INITIALIZE DRIVER ROUTINE
;;
;; This routine determines if the correct DOS version is running, exiting
;; with an error message if it is not.
;;
;; The comm port is initialized, a greeting message is displayed and
;; the data following the equal sign in the CONFIG.SYS file is then
;; displayed.
;;
;; Finally, the only real requirement of the initialization routine
;; is accomplished: it sets the "break address" bnack in the request
;; header
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
init proc near
DO_PRINT msg_init

mov ah, 030h ; get the DOS version
int 21h

cmp ah, 3 ; version 3.x?
jge okay_dos ; yes. At least.

mov dx, offset cs:wrong_dos ; output the message
call out_line

endless_loop:
cli
jmp endless_loop ; very ugly, but effective!

okay_dos:

mov dx, offset cs:greetings ; output the message
call out_line

call set_timer

push ds
mov ds, es:[di.count + 2] ; get the segment of CONFIG.SYS line
mov si, es:[di.count] ; get the offset of CONFIG.SYS line
call output_chars
pop ds

mov dx, offset cs:end_greetings ; output the message
call out_line

mov word ptr es:[di.address], offset cs:init
mov word ptr es:[di.address + 2], cs
xor ax,ax
ret
init endp

wrong_dos db '????You must run this device driver under DOS 3.0 or higher',
db CR, LF, ' System Halted! $'
greetings db CR, LF, LF, LF, 'MDM_DRV being installed...', CR, LF
db 'CONFIG.SYS Line is: $'
end_greetings db CR, LF, LF, LF, '$'



output_chars proc near
push ax
output_loop:
mov al, ds:[si] ; get the character
cmp al, LF ; is it NULL?
jnz outit ; no
pop ax
ret

outit:
call o_char ; output character
inc si
jmp output_loop
output_chars endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Save the current timer tick address at old_timer, and point the
;; tick to my_tick in this code seg.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
set_timer proc near
cli
push ax
push es
xor ax, ax
mov es, ax

mov ax, es:[TIMER_ADD]
mov cs:[old_timer], ax
mov ax, es:[TIMER_ADD + 2]
mov cs:[old_timer + 2], ax

mov ax, offset cs:my_tick
mov es:[TIMER_ADD], ax
mov ax, cs
mov es:[TIMER_ADD + 2], ax

pop es
pop ax
sti
ret
set_timer endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

driver endp
code ends
end









Subject: 3K source on debug.asm

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DO_PRINT macro arg
push dx
push ax
mov dx, offset cs:arg
call out_line
call out_rh
pop ax
pop dx
endm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DO_ERR_PRINT macro arg
DO_PRINT arg
DO_PRINT msg_char_err
endm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
form_num proc near

push ax
push bx
push cx
push dx

mov bx, dx
mov dx, cx
num_lp1:
mov cx, 4
rol ax,cl
mov cx, ax
and cx, 0fh
add cx, '0'
cmp cx, '9'
jbe is_decimal
add cx, 'A'-'9'-1
is_decimal:
mov [bx], cl
inc bx
dec dx
jnz num_lp1

pop dx
pop cx
pop bx
pop ax
ret

form_num endp

out_rh proc near

push ax
push cx
push dx

mov ax, es
mov dx, offset rh_0
mov cx, 4
call form_num

mov ax, di
mov dx, offset rh_00
mov cx, 4
call form_num

mov ah, es:[di.rlength]
xor al, al
mov dx, offset rh_1_val
mov cx, 2
call form_num

mov ah, es:[di.unit]
xor al, al
mov dx, offset rh_2_val
mov cx, 2
call form_num

mov ah, es:[di.command]
xor al, al
mov dx, offset rh_3_val
mov cx, 2
call form_num

mov ax, es:[di.status]
mov dx, offset rh_4_val
mov cx, 4
call form_num

mov ah, es:[di.media]
xor al, al
mov dx, offset rh_5_val
mov cx, 2
call form_num

mov ax, word ptr es:[di.address + 2]
mov dx, offset rh_6_val
mov cx, 4
call form_num

mov ax, word ptr es:[di.address]
mov dx, offset rh_7_val
mov cx, 4
call form_num

mov ax, es:[di.count]
mov dx, offset rh_8_val
mov cx, 4
call form_num

mov ax, es:[di.sector]
mov dx, offset rh_9_val
mov cx, 4
call form_num

mov dx, offset rh_1
call out_line

pop dx
pop cx
pop ax

ret

out_rh endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

rh_1 db 'RH:'
rh_0 db 'XXXX'
db ':'
rh_00 db 'XXXX '
db 'RH_LEN:'
rh_1_val db 'XX '
db 'UNIT:'
rh_2_val db 'XX '
db 'Command:'
rh_3_val db 'XX '
db 'Status:'
rh_4_val db 'XXXX ', CR, LF
db 'Media:'

rh_5_val db 'XX '
db 'Address:'
rh_6_val db 'XXXX'
db ':'
rh_7_val db 'XXXX '
db 'Count:'
rh_8_val db 'XXXX '
db 'Sector:'
rh_9_val db 'XXXX', CR, LF, '$'
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

adr_fmt db 'XXXX:XXXX', CR, LF, '$'

msg_init db 'In init ',CR,LF,'$'
msg_media_chk db '??Media Check $'
msg_bld_bpb db '??Bld_Bpb $'
msg_rd_ioctl db 'In rd_ioctl ',CR,LF,'$'
msg_read db 'In read ',CR,LF,'$'
msg_nd_read db 'In nd_read ',CR,LF,'$'
msg_inp_stat db 'In inp_stat ',CR,LF,'$'
msg_inp_flush db 'In inp_flush',CR,LF,'$'
msg_write db 'In write ',CR,LF,'$'
msg_write_vfy db 'In write_vfy',CR,LF,'$'
msg_out_stat db 'In out_stat ',CR,LF,'$'
msg_out_flush db 'In out_flush',CR,LF,'$'
msg_wrt_ioctl db 'In wrt_ioctl',CR,LF,'$'
msg_dev_open db 'In dev_open ',CR,LF,'$'
msg_dev_close db 'In dev_close',CR,LF,'$'
msg_rem_media db '??Rem_Media $'
msg_out_busy db '??Out_Busy $'
msg_char_err db ' called??', CR, LF, LF, '$'
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;














Subject: 1.5K of source SET_MDM.ASM


code segment
assume cs:code
org 100h


start:
jmp install ; install out vectors and

old_dos dw 0
dw 0

; then go TSR
my_int21 proc far
pushf
cmp ah, 03dh ; handle open?
jnz normal_dos ; no

push si
mov si, dx
cmp [si], 'DM' ; cmp against "MDM",0
jnz not_me ; remembering that each pair of bytes
cmp word ptr [si + 2], 004dh; is reversed!
jnz not_me
pop si

; with the flags already on the stack,
call dword ptr cs:[old_dos] ; do a long call to the old DOS

jc normal_ret ; an error, so merely return

pushf ; save DOS return status
push ax ; save the registers
push bx
push dx

mov bx, ax ; the handle just returned
mov ax, 4400h ; get IOCTL
int 21h

xor dh, dh ; zero out the top of the word
or dl, 20h ; set the raw bit
mov ax, 4401h ; set IOCTL
int 21h

pop dx
pop bx
pop ax
popf

normal_ret:
ret 2 ; return with the flag word DOS left


not_me:
pop si
normal_dos:
popf
jmp dword ptr cs:[old_dos] ; and call the original DOS
my_int21 endp

my_size equ (($-code)/16 + 1) ; waste a 'graph!

install:
mov ax, 3521h ; get the original DOS int vector
int 21h

mov cs:[old_dos], bx
mov cs:[old_dos + 2], es

push cs
pop ds
mov dx, offset cs:my_int21
mov ax, 2521h
int 21h

mov dx, my_size
mov ax, 3100h ; go TSR
int 21h

int 20h ; belts *and* suspenders! 🙂

code ends
end start



======================[ End Listing for MDM-DRV.ASM ]====================



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; DEBUG AND MACRO DEFINITIONS
;;
;; These macros and routines are included if DEBUG is defined in the
;; main body of the program.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DO_PRINT
;;
;; Outputs the '$' terminated string , then prints the contents
;; of the Request Header, field by field
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DO_PRINT macro arg
push dx
push ax
mov dx, offset cs:arg
mov ah, 09h
int 21h
call out_rh
pop ax
pop dx
endm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DO_ERR_PRINT
;;
;; Cause arg to be output, then output the common error suffix message
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DO_ERR_PRINT macro arg
DO_PRINT arg
DO_PRINT msg_char_err
endm

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; OUTNUM ROUTINE
;;
;; Output the number in AX (or AH) in hex into the buffer starting at
;; DX, and for CX characters.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
outnum proc near

push ax
push bx
push cx
push dx

mov bx, dx
mov dx, cx
num_lp1:
mov cx, 4
rol ax,cl
mov cx, ax
and cx, 0fh
add cx, '0'
cmp cx, '9'
jbe is_decimal
add cx, 'A'-'9'-1
is_decimal:
mov [bx], cl
inc bx
dec dx
jnz num_lp1

pop dx
pop cx
pop bx
pop ax
ret

outnum endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; OUTPUT REQUEST HEADER ROUTINE
;;
;; Output the Request Header, a field at a time.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
out_rh proc near

push ax
push cx
push dx

mov ax, es
mov dx, offset rh_0
mov cx, 4
call outnum

mov ax, di
mov dx, offset rh_00
mov cx, 4
call outnum

mov ah, es:[di.rlength]
xor al, al
mov dx, offset rh_1_val
mov cx, 2
call outnum

mov ah, es:[di.unit]
xor al, al
mov dx, offset rh_2_val
mov cx, 2
call outnum

mov ah, es:[di.command]
xor al, al
mov dx, offset rh_3_val
mov cx, 2
call outnum

mov ax, es:[di.status]
mov dx, offset rh_4_val
mov cx, 4
call outnum

mov ah, es:[di.media]
xor al, al
mov dx, offset rh_5_val
mov cx, 2
call outnum

mov ax, word ptr es:[di.address + 2]
mov dx, offset rh_6_val
mov cx, 4
call outnum

mov ax, word ptr es:[di.address]
mov dx, offset rh_7_val
mov cx, 4
call outnum

mov ax, es:[di.count]
mov dx, offset rh_8_val
mov cx, 4
call outnum

mov ax, es:[di.sector]
mov dx, offset rh_9_val
mov cx, 4
call outnum

mov dx, offset rh_1
mov ah, 9
int 21h

pop dx
pop cx
pop ax

ret

out_rh endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The Layout of the Request Header output.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
rh_1 db 'RH:',
rh_0 db 'XXXX'
db ':'
rh_00 db 'XXXX '
db 'RH_LEN:'
rh_1_val db 'XX '
db 'UNIT:'
rh_2_val db 'XX '
db 'Command:'
rh_3_val db 'XX '
db 'Status:'
rh_4_val db 'XXXX ', CR, LF
db 'Media:'
rh_5_val db 'XX '
db 'Address:'
rh_6_val db 'XXXX'
db ':'
rh_7_val db 'XXXX '
db 'Count:'
rh_8_val db 'XXXX '
db 'Sector:'
rh_9_val db 'XXXX', CR, LF, '$'
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


msg_init db 'In init ',CR,LF,'$'
msg_media_chk db '??Media Check $'
msg_bld_bpb db '??Bld_Bpb $'
msg_rd_ioctl db 'In rd_ioctl ',CR,LF,'$'
msg_read db 'In read ',CR,LF,'$'
msg_nd_read db 'In nd_read ',CR,LF,'$'
msg_inp_stat db 'In inp_stat ',CR,LF,'$'
msg_inp_flush db 'In inp_flush',CR,LF,'$'
msg_write db 'In write ',CR,LF,'$'
msg_write_vfy db 'In write_vfy',CR,LF,'$'
msg_out_stat db 'In out_stat ',CR,LF,'$'
msg_out_flush db 'In out_flush',CR,LF,'$'
msg_wrt_ioctl db 'In wrt_ioctl',CR,LF,'$'
msg_dev_open db 'In dev_open ',CR,LF,'$'
msg_dev_close db 'In dev_close',CR,LF,'$'
msg_rem_media db '??Rem_Media $'
msg_out_busy db '??Out_Busy $'
msg_char_err db ' called??', CR, LF, LF, '$'


  3 Responses to “Category : Files from Magazines
Archive   : MSJV2-5.ZIP
Filename : MDM_DRV.ALL

  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/