📄 transmit.asm
字号:
; TRANSMIT.ASM
;
; This program is the transmitter portion of the programs that transmit files
; across a Laplink compatible parallel cable.
;
; This program assumes that the user want to use LPT1: for transmission.
; Adjust the equates, or read the port from the command line if this
; is inappropriate.
.286
.xlist
include stdlib.a
includelib stdlib.lib
.list
dseg segment para public 'data'
TimeOutConst equ 4000 ;About 1 min on 66Mhz 486.
PrtrBase equ 10 ;Offset to LPT1: adrs.
MyPortAdrs word ? ;Holds printer port address.
FileHandle word ? ;Handle for output file.
FileBuffer byte 512 dup (?) ;Buffer for incoming data.
FileSize dword ? ;Size of incoming file.
FileNamePtr dword ? ;Holds ptr to filename
dseg ends
cseg segment para public 'code'
assume cs:cseg, ds:dseg
; TestAbort- Check to see if the user has pressed ctrl-C and wants to
; abort this program. This routine calls BIOS to see if the
; user has pressed a key. If so, it calls DOS to read the
; key (function AH=8, read a key w/o echo and with ctrl-C
; checking).
TestAbort proc near
push ax
push cx
push dx
mov ah, 1
int 16h ;See if keypress.
je NoKeyPress ;Return if no keypress.
mov ah, 8 ;Read char, chk for ctrl-C.
int 21h ;DOS aborts if ctrl-C.
NoKeyPress: pop dx
pop cx
pop ax
ret
TestAbort endp
; SendByte- Transmit the byte in AL to the receiving site four bits
; at a time.
SendByte proc near
push cx
push dx
mov ah, al ;Save byte to xmit.
mov dx, MyPortAdrs ;Base address of LPT1: port.
; First, just to be sure, write a zero to bit #4. This reads as a one
; in the busy bit of the receiver.
mov al, 0
out dx, al ;Data not ready yet.
; Wait until the receiver is not busy. The receiver will write a zero
; to bit #4 of its data register while it is busy. This comes out as a
; one in our busy bit (bit 7 of the status register). This loop waits
; until the receiver tells us its ready to receive data by writing a
; one to bit #4 (which we read as a zero). Note that we check for a
; ctrl-C every so often in the event the user wants to abort the
; transmission.
inc dx ;Point at status register.
W4NBLp: mov cx, 10000
Wait4NotBusy: in al, dx ;Read status register value.
test al, 80h ;Bit 7 = 1 if busy.
loopne Wait4NotBusy ;Repeat while busy, 10000 times.
je ItsNotbusy ;Leave loop if not busy.
call TestAbort ;Check for Ctrl-C.
jmp W4NBLp
; Okay, put the data on the data lines:
ItsNotBusy: dec dx ;Point at data register.
mov al, ah ;Get a copy of the data.
and al, 0Fh ;Strip out H.O. nibble
out dx, al ;"Prime" data lines, data not avail.
jmp $+2 ;Short delay to allow the data time
jmp $+2 ; to stablize on the data lines.
or al, 10h ;Turn data available on.
out dx, al ;Send data w/data available strobe.
; Wait for the acknowledge from the receiving site. Every now and then
; check for a ctrl-C so the user can abort the transmission program from
; within this loop.
inc dx ;Point at status register.
W4ALp: mov cx, 10000 ;Times to loop between ctrl-C checks.
Wait4Ack: in al, dx ;Read status port.
test al, 80h ;Ack = 1 when rcvr acknowledges.
loope Wait4Ack ;Repeat 10000 times or until ack.
jne GotAck ;Branch if we got an ack.
call TestAbort ;Every 10000 calls, check for a
jmp W4ALp ; ctrl-C from the user.
; Send the data not available signal to the receiver:
GotAck: dec dx ;Point at data register.
mov al, 0 ;Write a zero to bit 4, this appears
out dx, al ; as a one in the rcvr's busy bit.
; Okay, on to the H.O. nibble:
inc dx ;Point at status register.
W4NB2: mov cx, 10000 ;10000 calls between ctrl-C checks.
Wait4NotBusy2: in al, dx ;Read status register.
test al, 80h ;Bit 7 = 1 if busy.
loopne Wait4NotBusy2 ;Loop 10000 times while busy.
je NotBusy2 ;H.O. bit clear (not busy)?
call TestAbort ;Check for ctrl-C.
jmp W4NB2
; Okay, put the data on the data lines:
NotBusy2: dec dx ;Point at data register.
mov al, ah ;Retrieve data to get H.O. nibble.
shr al, 4 ;Move H.O. nibble to L.O. nibble.
out dx, al ;"Prime" data lines.
or al, 10h ;Data + data available strobe.
out dx, al ;Send data w/data available strobe.
; Wait for the acknowledge from the receiving site:
inc dx ;Point at status register.
W4A2Lp: mov cx, 10000
Wait4Ack2: in al, dx ;Read status port.
test al, 80h ;Ack = 1
loope Wait4Ack2 ;While while no acknowledge
jne GotAck2 ;H.O. bit = 1 (ack)?
call TestAbort ;Check for ctrl-C
jmp W4A2Lp
; Send the data not available signal to the receiver:
GotAck2: dec dx ;Point at data register.
mov al, 0 ;Output a zero to bit #4 (that
out dx, al ; becomes busy=1 at rcvr).
mov al, ah ;Restore original data in AL.
pop dx
pop cx
ret
SendByte endp
; Synchronization routines:
;
; Send0s- Transmits a zero to the receiver site and then waits to
; see if it gets a set of ones back. Returns carry set if
; this works, returns carry clear if we do not get a set of
; ones back in a reasonable amount of time.
Send0s proc near
push cx
push dx
mov dx, MyPortAdrs
mov al, 0 ;Write the initial zero
out dx, al ; value to our output port.
xor cx, cx ;Checks for ones 10000 times.
Wait41s: inc dx ;Point at status port.
in al, dx ;Read status port.
dec dx ;Point back at data port.
and al, 78h ;Mask input bits.
cmp al, 78h ;All ones yet?
loopne Wait41s
je Got1s ;Branch if success.
clc ;Return failure.
pop dx
pop cx
ret
Got1s: stc ;Return success.
pop dx
pop cx
ret
Send0s endp
; Send1s- Transmits all ones to the receiver site and then waits to
; see if it gets a set of zeros back. Returns carry set if
; this works, returns carry clear if we do not get a set of
; zeros back in a reasonable amount of time.
Send1s proc near
push cx
push dx
mov dx, MyPortAdrs ;LPT1: base address.
mov al, 0Fh ;Write the "all ones"
out dx, al ; value to our output port.
mov cx, 0
Wait40s: inc dx ;Point at input port.
in al, dx ;Read the status port.
dec dx ;Point back at data port.
and al, 78h ;Mask input bits.
loopne Wait40s ;Loop until we get zero back.
je Got0s ;All zeros? If so, branch.
clc ;Return failure.
pop dx
pop cx
ret
Got0s: stc ;Return success.
pop dx
pop cx
ret
Send1s endp
; Synchronize- This procedure slowly writes all zeros and all ones to its
; output port and checks the input status port to see if the
; receiver site has synchronized. When the receiver site
; is synchronized, it will write the value 05h to its output
; port. So when this site sees the value 05h on its input
; port, both sites are synchronized. Returns with the
; carry flag set if this operation is successful, clear if
; unsuccessful.
Synchronize proc near
print
byte "Synchronizing with receiver program"
byte cr,lf,0
mov dx, MyPortAdrs
mov cx, TimeOutConst ;Time out delay.
SyncLoop: call Send0s ;Send zero bits, wait for
jc Got1s ; ones (carry set=got ones).
; If we didn't get what we wanted, write some ones at this point and see
; if we're out of phase with the receiving site.
Retry0: call Send1s ;Send ones, wait for zeros.
jc SyncLoop ;Carry set = got zeros.
; Well, we didn't get any response yet, see if the user has pressed ctrl-C
; to abort this program.
DoRetry: call TestAbort
; Okay, the receiving site has yet to respond. Go back and try this again.
loop SyncLoop
; If we've timed out, print an error message and return with the carry
; flag clear (to denote a timeout error).
print
byte "Transmit: Timeout error waiting for receiver"
byte cr,lf,0
clc
ret
; Okay, we wrote some zeros and we got some ones. Let's write some ones
; and see if we get some zeros. If not, retry the loop.
Got1s:
call Send1s ;Send one bits, wait for
jnc DoRetry ; zeros (carry set=got zeros).
; Well, we seem to be synchronized. Just to be sure, let's play this out
; one more time.
call Send0s ;Send zeros, wait for ones.
jnc Retry0
call Send1s ;Send ones, wait for zeros.
jnc DoRetry
; We're syncronized. Let's send out the 05h value to the receiving
; site to let it know everything is cool:
mov al, 05h ;Send signal to receiver to
out dx, al ; tell it we're sync'd.
xor cx, cx ;Long delay to give the rcvr
FinalDelay: loop FinalDelay ; time to prepare.
print
byte "Synchronized with receiving site"
byte cr,lf,0
stc
ret
Synchronize endp
; File I/O routines:
;
; GetFileInfo- Opens the user specified file and passes along the file
; name and file size to the receiving site. Returns the
; carry flag set if this operation is successful, clear if
; unsuccessful.
GetFileInfo proc near
; Get the filename from the DOS command line:
mov ax, 1
argv
mov word ptr FileNamePtr, di
mov word ptr FileNamePtr+2, es
printf
byte "Opening %^s\n",0
dword FileNamePtr
; Open the file:
push ds
mov ax, 3D00h ;Open for reading.
lds dx, FileNamePtr
int 21h
pop ds
jc BadFile
mov FileHandle, ax
; Compute the size of the file (do this by seeking to the last position
; in the file and using the return position as the file length):
mov bx, ax ;Need handle in BX.
mov ax, 4202h ;Seek to end of file.
xor cx, cx ;Seek to position zero
xor dx, dx ; from the end of file.
int 21h
jc BadFile
mov word ptr FileSize, ax ;Save file length
mov word ptr FileSize+2, dx
; Need to rewind file back to the beginning (seek to position zero):
mov bx, FileHandle ;Need handle in BX.
mov ax, 4200h ;Seek to beginning of file.
xor cx, cx ;Seek to position zero
xor dx, dx
int 21h
jc BadFile
; Okay, transmit the good stuff over to the receiving site:
mov al, byte ptr FileSize ;Send the file
call SendByte ; size over.
mov al, byte ptr FileSize+1
call SendByte
mov al, byte ptr FileSize+2
call SendByte
mov al, byte ptr FileSize+3
call SendByte
les bx, FileNamePtr ;Send the characters
SendName: mov al, es:[bx] ; in the filename to
call SendByte ; the receiver until
inc bx ; we hit a zero byte.
cmp al, 0
jne SendName
stc ;Return success.
ret
BadFile: print
byte "Error transmitting file information:",0
puti
putcr
clc
ret
GetFileInfo endp
; GetFileData- This procedure reads the data from the file and transmits
; it to the receiver a byte at a time.
GetFileData proc near
mov ah, 3Fh ;DOS read opcode.
mov cx, 512 ;Read 512 bytes at a time.
mov bx, FileHandle ;File to read from.
lea dx, FileBuffer ;Buffer to hold data.
int 21h ;Read the data
jc GFDError ;Quit if error reading data.
mov cx, ax ;Save # of bytes actually read.
jcxz GFDDone ; quit if at EOF.
lea bx, FileBuffer ;Send the bytes in the file
XmitLoop: mov al, [bx] ; buffer over to the rcvr
call SendByte ; one at a time.
inc bx
loop XmitLoop
jmp GetFileData ;Read rest of file.
GFDError: print
byte "DOS error #",0
puti
print
byte " while reading file",cr,lf,0
GFDDone: ret
GetFileData endp
; Okay, here's the main program that controls everything.
Main proc
mov ax, dseg
mov ds, ax
meminit
; First, get the address of LPT1: from the BIOS variables area.
mov ax, 40h
mov es, ax
mov ax, es:[PrtrBase]
mov MyPortAdrs, ax
; See if we have a filename parameter:
argc
cmp cx, 1
je GotName
print
byte "Usage: transmit <filename>",cr,lf,0
jmp Quit
GotName: call Synchronize ;Wait for the transmitter program.
jnc Quit
call GetFileInfo ;Get file name and size.
jnc Quit
call GetFileData ;Get the file's data.
Quit: ExitPgm ;DOS macro to quit program.
Main endp
cseg ends
sseg segment para stack 'stack'
stk byte 1024 dup ("stack ")
sseg ends
zzzzzzseg segment para public 'zzzzzz'
LastBytes byte 16 dup (?)
zzzzzzseg ends
end Main
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -