home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
CPM
/
MODEMS
/
MODEM
/
TINYTERM.LBR
/
TINYTERM.MQC
/
TINYTERM.MAC
Wrap
Text File
|
2000-06-30
|
14KB
|
428 lines
;=====================================================
; tinyterm - a tiny terminal program with XMODEM
; file download facility.
; tinyterm v1.0 (Z-80 version) 3rd June, 1985
; Written by Michael Brandon.
; The idea behind tinyterm is that it is a terminal
; program you can use to download a better terminal
; program (e.g. MODEM7A or YAM). Tinyterm is small
; enough to type in by hand (minus the comments of
; course !) or to download the source code using PIP.
;=====================================================
.z80 ; use Zilog mnemonics
org 100h ; normal cp/m origin
;==========================================================
; You will almost certainly have to change the following -
;
; mod_data - the SIO or UART data port through which the
; modem is accessed
; mod_status - the port from which the status of the SIO
; or UART can be read
;==========================================================
mod_data equ 40h ; modem data port
mod_status equ 41h ; modem status port
;==============================================================
; You may have to change the following -
;
; rx_mask - when ANDed with the SIO or UART status, extracts
; the bit (or bits) which tell whether or not the
; SIO or UART has a received a character
; rx_ready - the value of 'rx_mask AND status' when a
; character has been received
; tx_mask - when ANDed with the SIO or UART status, extracts
; the bit (or bits) which tell whether or not the
; SIO or UART is ready to transmit a character
; tx_ready - the value of 'tx_mask AND status' when the SIO
; or UART is ready to transmit a character
; * note * : the values for the above four constants as they
; stand are correct for a Zilog Z-80 SIO
; cpu_speed - the speed of the processor in kHz. Not very
; critical.
;==============================================================
rx_mask equ 00000001b ; "Rx ready" modem status mask
rx_ready equ 00000001b ; value when char is received
tx_mask equ 00000100b ; "Tx ready" modem status mask
tx_ready equ 00000100b ; value when ready to transmit
cpu_speed equ 4000d ; cpu speed in kHz
;===================================================
; You might like to change the following -
;
; exit_char - when this character is typed, the
; program exits to cp/m
; rx_char - when this character is typed, a file
; is downloaded, using XMODEM protocols
;===================================================
exit_char equ 03h ; <ctrl> C
rx_char equ 12h ; <ctrl> R
;=================================
; Don't, what ever you do, change
; the following constants !!!
;=================================
warm_boot equ 0000h ; cp/m warm boot address
bdos equ 0005h ; entry into cp/m's bdos
fcb equ 005ch ; & (file control block 1)
def_dma equ 0080h ; default DMA address
rx_delay equ cpu_speed / 4 ; delay count for a one
; second receive timeout
soh equ 01h ; ASCII <soh> char
eot equ 04h ; ASCII <eot> char
ack equ 06h ; ASCII <ack> char
lf equ 0ah ; ASCII <lf> char
cr equ 0dh ; ASCII <cr> char
nak equ 15h ; ASCII <nak> char
;=============================================================
; You shouldn't have to modify any code beyond this point,
; other than adding code to initialise your SIO or UART
; between main1 and main2.
; The only things which will stand modification are -
; (i) test_rx - see if a character has been received
; (ii) test_tx - see if we can transmit a character
; (iii) tx_byte - transmit a character
; (iv) rx_t1 - receive a character with a 1 second timeout
; (v) the line marked (***) after main4
; but modify them only if necessary.
;=============================================================
main:
ld a,(def_dma) ; get length of command line tail
or a ; has user just typed "tinyterm" ?
jr nz,main1 ; skip if not
call print
defm 'Usage: tinyterm file',cr,lf,0
jp warm_boot
main1:
;===============================================================
; Put the code to initialise your SIO or UART chip in here. The
; communications port should be set for 300 baud, 8 data bits,
; 1 stop bit and no parity. You may not need to do any
; initialisation at all ...
;===============================================================
main2:
ld c,0bh ; cp/m "get console status" request
call bdos ; has a character been typed ?
or a ; test return value (00 or ff)
jr z,main4 ; skip if no character
ld c,06h ; cp/m "direct console I/O" request
ld e,0ffh ; console input indicator
call bdos ; get the character
cp exit_char ; exit to the operating system ?
jp z,warm_boot ; do a warm boot if so
cp rx_char ; receive a file ?
jr nz,main3 ; skip if not
call rx_file ; receive the file, XMODEM style
jr main4 ; (don't send the rx_char character)
main3:
call tx_byte ; send the byte out the modem port
main4:
call test_rx ; received a character from the modem ?
jr nz,main2 ; keep looping if not
in a,(mod_data) ; get the character (***)
call put_char ; print the character
jr main2 ; and keep looping ...
;--------------------------------------------
; put_char - print a character on the screen
; Input : char in a
; Clobbers : A, B, C, D, E, H, L, flags
;--------------------------------------------
put_char:
ld c,02h ; cp/m "console output" request
ld e,a ; char to print in e
jp bdos ; call bdos & return
;---------------------------------------------
; print - print a null-terminated sequence
; of characters "in line" i.e.
; call print
; defm 'string of chars'
; defb 0
; Clobbers : A, D, E, H, L, flags
;---------------------------------------------
print:
pop hl ; get return address (start of string)
push bc ; save
prin1:
ld a,(hl) ; get char
inc hl
or a ; end of string ? (0 byte terminator)
jr z,prin2 ; skip if so
push hl ; save ^
call put_char ; print the character
pop hl ; retrieve ^
jr prin1 ; keep looping ...
prin2:
pop bc ; retrieve
jp (hl) ; return to the code after the string
;-----------------------------------------------
; test_rx - test the modem status port to see
; if a character is ready to be read
; Output : Z if modem has a character
; NZ if not
; Clobbers : A
;-----------------------------------------------
test_rx:
in a,(mod_status) ; get the modem status
and rx_mask ; mask out all but the bits we want
cp rx_ready ; modem got a char for us to read ?
ret ; return (Z) if ready, (NZ) if not
;-----------------------------------------------
; test_tx - test the modem status port to see
; if it is ready to send a character
; Output : Z if the modem is ready
; NZ if not
; Clobbers : A
;-----------------------------------------------
test_tx:
in a,(mod_status) ; get the modem status
and tx_mask ; mask out all but the bits we want
cp tx_ready ; modem ready to send a char ?
ret ; return (Z) if ready, (NZ) if not
;----------------------------------------------------
; tx_byte - transmit a byte when the modem is ready
; Input : A - byte to transmit
;----------------------------------------------------
tx_byte:
push af ; save byte
tx_b1:
call test_tx ; modem ready to transmit ?
jr nz,tx_b1 ; loop until ready
pop af ; get byte back
out (mod_data),a ; send it out the data port
ret
;------------------------------------------------
; rx_t1 - wait for a character to be received;
; time out after one second
; Output : Z if a character was received
; NZ if we timed out
; A - character received if no time out
;------------------------------------------------
rx_t1:
push hl ; save
ld hl,rx_delay ; delay counter for 1 second timeout
rx_t1a:
call test_rx ; is there a char ready for us ?
jr z,rx_t1c ; jump if so
xor a ; A := 0
rx_t1b:
dec a
jr nz,rx_t1b ; wait around a while ...
dec hl
ld a,h
or l ; timed out ?
jr nz,rx_t1a ; loop if not
inc a ; set flags to NZ
pop hl
ret ; return (NZ) - timed out
rx_t1c:
in a,(mod_data) ; get the byte from the data port
pop hl
ret ; return (Z) - got a char
;------------------------------------------------
; rx_t10 - wait for a character to be received;
; time out after ten seconds
; Output : Z if a character was received
; NZ if we timed out
; A - character received if no time out
;------------------------------------------------
rx_t10:
push hl ; save
ld hl,rx_delay*10 ; counter for 10 second timeout
jr rx_t1a ; carry on as for 1 second timeout
;---------------------------------------------------------
; tx_ack - wait until the line is clear (i.e. we time out
; while receiving chars), and then send an <ack>
;---------------------------------------------------------
tx_ack:
push af ; save
tx_a1:
call rx_t1 ; get a char, with 1 second timeout
jr z,tx_a1 ; if no timeout, keep gobbling chars
ld a,ack ; <ack> char
call tx_byte ; send it
pop af
ret
;---------------------------------------------------------
; tx_nak - wait until the line is clear (i.e. we time out
; while receiving chars), and then send a <nak>
;---------------------------------------------------------
tx_nak:
push af ; save
tx_n1:
call rx_t1 ; get a char, with 1 second timeout
jr z,tx_n1 ; if no timeout, keep gobbling chars
ld a,nak ; <nak> char
call tx_byte ; send it
pop af
ret
;--------------------------------------------------
; get_block - get a block of data from the modem,
; consisting of -
; (i) block number
; (ii) complement of the block number
; (iii) 128 data bytes (1 sector), and
; (iv) checksum
; Output : Z if all went o.k.
; A - block number of the block
; - 128 bytes at *(def_dma)
; NZ if an error occurred
; - timed out
; - block no. <> ~ (~ block no.)
; - checksum wrong
;--------------------------------------------------
get_block:
push bc
push de
push hl ; save
call rx_t1 ; get the block no.
jr nz,get_2 ; return (NZ) if we timed out
ld c,a ; save the block no.
call rx_t1 ; get ~ block no.
jr nz,get_2 ; return (NZ) if we timed out
cpl ; ~ ~ block no. (we hope)
cp c ; is the block no. right ?
jr nz,get_2 ; return (NZ) if block no. wrong
ld hl,def_dma ; put data in the default DMA buffer
ld b,128 ; 128 bytes / block
ld d,0 ; initial checksum
get_1:
call rx_t1 ; get a data byte
jr nz,get_2 ; return (NZ) if we timed out
ld (hl),a ; save the byte
inc hl
add a,d ; generate a new checksum
ld d,a ; save it
djnz get_1 ; do the whole block
call rx_t1 ; get checksum
jr nz,get_2 ; return (NZ) if we timed out
cp d ; checksums the same ?
ld a,c ; return the block no. in A
get_2:
pop hl
pop de
pop bc
ret
;----------------------------------------------------------------
; rx_file - download a file using the XMODEM file transfer
; protocol. Any existing file with the name specified
; on the command line will be overwritten (as will
; any file previously downloaded in this invocation
; of the program).
; Clobbers : A, B, C, D, E, H, L, flags
;----------------------------------------------------------------
rx_file:
ld c,13h ; cp/m "delete file" request
ld de,fcb ; & (file control block)
call bdos ; delete the file (if it exists !)
ld c,16h ; cp/m "make file" request
ld de,fcb ; & (file control block)
xor a ; a := 0
ld (fcb+12),a ; set things up ...
ld (fcb+14),a ; ... in the file control block
ld (fcb+32),a ; (don't know what it does !)
call bdos ; create the file, and open it
inc a ; error if a = 0ffh after the bdos call
jr nz,rx_f1 ; skip if all's well
call print ; print the error message
defm 'can''t create file',cr,lf,0
ret
rx_f1:
ld c,0 ; initial "previous" block no.
call tx_nak ; send an initital <nak>
rx_f2:
ld b,10 ; retry block errors 10 times
rx_f3:
call rx_t10 ; look for a <soh> or <eot>
jr z,rx_f5 ; skip if we got something
call print
defm 'timeout',cr,lf,0
rx_f4:
call tx_nak ; send a <nak> to signal error
djnz rx_f3 ; retry 10 times
call print
defm 'aborted',cr,lf,0
ld c,13h ; cp/m "delete file" request
ld de,fcb ; & (file control block)
call bdos ; delete the file
ret
rx_f5:
cp eot ; <eot> (end of transmission) ?
jr nz,rx_f6 ; skip if not
call print
defm 'file transfer completed',cr,lf,0
call tx_ack ; acknowledge it
ld c,10h ; cp/m "close file" request
ld de,fcb ; & (file control block)
call bdos ; close the file
inc a ; error if a = 0ffh after bdos call
ret nz ; return if o.k.
call print
defm 'can''t close file',cr,lf,0
ret
rx_f6:
cp soh ; <soh> (start of header) ?
jr z,rx_f7 ; skip if so
call print
defm 'no <soh> or <eot>',cr,lf,0
jp rx_f4
rx_f7:
call get_block ; get the block
jr z,rx_f8 ; skip if no errors
call print
defm 'error in block',cr,lf,0
jp rx_f4
rx_f8:
cp c ; resent the old block ?
jr nz,rx_f9 ; skip if not
call tx_ack ; acknowledge it
call print
defm '(repeated block)',cr,lf,0
jp rx_f2
rx_f9:
inc c ; next block no.
cp c ; block no.s match ?
jr z,rx_f10 ; skip if so
call print
defm 'error in block number',cr,lf,0
dec c ; go back a block
jp rx_f4
rx_f10:
call print
defm 'block received',cr,lf,0
push bc ; save the block number
ld c,15h ; cp/m "write sequential" request
ld de,fcb ; & (file control block)
call bdos ; write sector to disc
pop bc ; retrieve the block number
inc a ; error if a = 0ffh after bdos call
jr nz,rx_f11 ; skip if write went o.k.
call print
defm 'disc limit reached',cr,lf,0
ld c,13h ; cp/m "delete file" request
ld de,fcb ; & (file control block)
call bdos ; delete the file (if it exists !)
ret
rx_f11:
call tx_ack ; acknowledge the block
jp rx_f2 ; loop for next block
end main