You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

844 lines
18 KiB

;-----------------------------------------------------------------------------
;
; Overlay for ZMP (Z-Modem Program)
;
; Name ZMO-WBW.Z80
;
; Dated April 14, 2025
;
; Written by -
; Phil Summers, Wayne Warthen
;
; ROMWBW version using HBIOS and https://github.com/mecparts/zmp
; version of zmodem
;
; - All modem/serial i/o is through RomWBW HBIOS.
;
; - Data bits, stop bits, and parity are determined by HBIOS.
; The ZMP settings will have no effect. Use RomWBW MODE
; command to set serial port config before starting ZMP.
;
; - Timing delay calculations based on HBIOS reported CPU speed.
;
; - The pcfg equate determines whether the overlay will
; implement port initialization. Normally, pcfg will be
; set to false because ZMP does not allow configuring
; many HBIOS speeds (notably the 115200 baud hardwired
; into many RCBus systems). In this case, you must
; configure the modem port using the RomWBW
; MODE command as desired before starting ZMP.
;
; - The modem port is assigned to an HBIOS character unit. By
; default, the modem is assigned to HBIOS character unit 1.
; An alternate HBIOS character unit may be specified as a
; parameter on the command line as a single number. For
; example, the following will assign HBIOS port 3 as the
; ZMP modem port:
;
; ZMP 3
;
; - The original version of ZMP from Ron Murray allows you to
; select from 2 logical modem ports. This overlay ignores
; any attempt to select ports from within ZMP. The desired
; modem port should be assigned using the command line
; parameter as described above.
;
; - Teraterm users may need to change the ZmodemWinSize value
; to 1024 in the teraterm.ini configuration file.
;
;-----------------------------------------------------------------------------
;
;
; System-dependent code overlay for ZMODEM
;
;
;
; Insert your own code as necessary in this file. Code contained herein
; has been written in Z80 code for use with M80 or SLR. Assemble as follows:
;
; SLR ZMO-xx01/h
; MLOAD ZMP.COM=ZMODEM.COM,ZMO-xx01.HEX
; or
; M80 =ZMO-xx01.Z80
; RELHEX ZMO-xx01
; MLOAD ZMP.COM=ZMODEM.COM,ZMO-xx01.HEX
;
;
; (Don't use L80 without changing the source for assembly as a
; cseg file.)
;
;-----------------------------------------------------------------------------
;
;
; Notes on modifying this file:
;
; C requires that functions do not change either index register (IX or IY).
; If your overlay requires either of these to be changed, ensure they are
; restored to the original values on return.
; Since collecting parameters from C functions can be tricky, only change
; the parts marked 'Insert your own code here'. Do NOT modify the jump
; table at the start. Do NOT modify the entry/exit sections of each
; function. Do NOT pass 'GO'. Do NOT collect $200.
; Apart from defining modem functions, this file also defines terminal
; characteristics. Examples provided are for ADM-3A (with a few of my own
; additions). Modify to suit your own terminal. An inline print routine
; is provided for printing strings in the usual way: usage is
;
; call print
; db 'required string',0
;
;-----------------------------------------------------------------------------
;
;
; Don't forget to set your clock speed at the clkspd variable.
;
;
; If you find your overlay exceeds the maximum size (currently 0400h),
; you will have to contact me for another version. If too many people need
; to do it, we haven't allowed enough room.
;
; Ron Murray 15/8/88
;
;
;
;---------------------------------------------------------------------------
false equ 0
true equ not false
;------------------------------------------------------------------------------
; User-set variables:
debug equ false ; to allow debugging of overlay with Z8E etc.
clkspd equ 8 ; Processor clock speed in MHz
pcfg equ false ; Allow ZMP to configure port
;
;Set the following two equates to the drive and user area which will contain
; ZMP's .OVR files, .CFG file, .FON file and .HLP file. Set both to zero
; (null) to locate them on the drive from which ZMP was invoked.
overdrive equ 0 ; Drive to find overlay files on ('A'-'P')
overuser equ 0 ; User area to find files
;------------------------------------------------------------------------------
; NOT user-set variables
userdef equ 0145h ; origin of this overlay
; This address should not change with
; subsequent revisions.
mspeed equ 03ch ; location of current baud rate.
ovsize equ 0400h ; max size of this overlay
fcb equ 05ch ; primary command line CP/M fcb
.z80 ; use z80 code
aseg ; absolute
if debug
org 100h ; so you can debug it with cebug, zsid, etc
else
org userdef
endif
esc equ 1bh
ctrlq equ 11h
cr equ 0dh
lf equ 0ah
bdos equ 5
codebgn equ $
;Jump table for the overlay: do NOT change this
jump_tab:
jp scrnpr ; screen print
jp mrd ; modem read with timeout
jp mchin ; get a character from modem
jp mchout ; send a character to the modem
jp mordy ; test for tx buffer empty
jp mirdy ; test for character received
jp sndbrk ; send break
jp cursadd ; cursor addressing
jp cls ; clear screen
jp invon ; inverse video on
jp invoff ; inverse video off
jp hide ; hide cursor
jp show ; show cursor
jp savecu ; save cursor position
jp rescu ; restore cursor position
jp mint ; service modem interrupt
jp invec ; initialise interrupt vectors
jp dinvec ; de-initialise interrupt vectors
jp mdmerr ; test uart flags for error
jp dtron ; turn DTR on
jp dtroff ; turn DTR OFF
jp init ; initialise uart
jp wait ; wait seconds
jp mswait ; wait milliseconds
jp userin ; user-defined entry routine
jp userout ; user-defined exit routine
jp getvars ; get system variables
jp setport ; set port (0 or 1)
; Spare jumps for compatibility with future versions
jp spare ; spare for later use
jp spare ; spare for later use
jp spare ; spare for later use
jp spare ; spare for later use
jp spare ; spare for later use
jp spare ; spare for later use
; Local storage
hbunit db 1 ; Active HBIOS unit for modem I/O
cpumhz db clkspd ; CPU clock speed in MHz
;
; Main code starts here
;
;Screen print function
scrnpr:
; <== Insert your own code here
call print
db 'Screen print not supported.',cr,lf,0
; <== End of your own code
spare:
ret
; User-defined entry routine: leave empty if not needed
userin:
push bc
push de
push hl
call print
db cr,lf,'ZMP Overlay for RomWBW HBIOS v1.0',cr,lf,0
; Scan and parse default FCB to initialize HBIOS unit if
; specified.
ld a,(fcb + 1) ; get parm from fcb
cp ' ' ; anything there?
jr z,userin1 ; if empty, done
sub '0' ; ASCII to binary
jr c,parmerr ; less than 0, parm error
cp 10 ; >= 10?
jr nc,parmerr ; greater than 9, parm error
ld (hbunit),a ; save it
userin1:
call showcom ; show com port info
; check that unit is actually available in HBIOS
ld bc,0f800h ; get HBIOS char unit count
rst 8 ; do it
ld a,(hbunit) ; get active modem unit
cp e ; compare to units available
jr nc,porterr ; unit too high, port error
if pcfg
; Force port initialization by setting an invalid
; baud rate (mspeed).
; speed to an arbitrary value of 8 (9600 baud).
ld a,-1
ld (mspeed),a
else
; We don't support port configuration. Here we set the modem
; speed to an arbitrary value of 8 (9600 baud).
ld a,8
ld (mspeed),a
endif
ld bc,0f8f0h ; HBIOS func get CPU info
rst 08 ; do it, l = CPU speed in MHz
ld a,l ; to accum
ld (cpumhz),a ; and save it for wait loop
ld hl,2
call waithls
pop hl
pop de
pop bc
ret
parmerr:
; Handle a bad parameter
call print ; print error message
db cr,lf,'ZMP parameter error!',cr,lf,0
rst 0 ; bail out to OS
porterr:
; Handle a bad port
call print ; print error message
db cr,lf,'Invalid HBIOS unit for ZMP Modem port!',cr,lf,0
rst 0 ; bail out to OS
showcom:
; Display the HBIOS unit to be used for the ZMP Modem
call print
db cr,lf,'ZMP Modem on HBIOS Unit #',0
ld a,(hbunit)
add a,'0'
call cout
call print
db cr,lf,0
; User-defined exit routine: leave empty if not needed
userout:
ret
;Get a character from the modem: return in HL
mchin:
push bc
; <== Insert your own code here
ld a,(hbunit)
ld c,a
ld b,00h
rst 08
ld a,e
; <== End of your own code
ld l,a ; put in HL
ld h,0
or a ; set/clear Z
pop bc
ret
;Send a character to the modem
mchout:
ld hl,2 ; get the character
add hl,sp
ld a,(hl)
; <== Insert your own code here
push bc
ld e,a
ld a,(hbunit)
ld c,a
ld b,01h
rst 08
pop bc
; <== End of your own code
ret ; done
;Test for output ready: return TRUE (1) in HL if ok
mordy:
; <== Insert your own code here
push bc
ld a,(hbunit)
ld c,a
ld b,03h
rst 08
ld h,0
ld l,a
pop bc
; <== End of your own code
ld a,l ; set/clear Z
or a
ret
;Test for character at modem: return TRUE (1) in HL if so
mirdy:
; <== Insert your own code here
push bc
ld a,(hbunit)
ld c,a
ld b,02h
rst 08
ld h,0
ld l,a
pop bc
; <== End of your own code
ld a,l ; set/clear Z
or a
ret
;Send a break to the modem: leave empty if your system can't do it
sndbrk:
; <== Insert your own code here
ld hl,300 ; wait 300 mS
call waithlms
; <== End of your own code
ret
;
;Test UART flags for error: return TRUE (1) in HL if error.
mdmerr:
; <== Insert your own code here
xor a ; not implemented
; <== End of your own code
ld a,l ; set/clear Z
or a
ret
;Turn DTR ON
dtron:
; <== Insert your own code here
; <== End of your own code
ret
;Turn DTR OFF
dtroff:
; <== Insert your own code here
; <== End of your own code
ret
;Initialise the uart
init:
ld hl,2 ; get parameters
add hl,sp
ex de,hl
call getparm ; in HL
ld (brate),hl ; baud rate
call getparm
ld (parity),hl ; parity
call getparm
ld (data),hl ; data bits (BINARY 7 or 8)
call getparm
ld (stop),hl ; stop bits (BINARY 1 or 2)
; <== Insert your own code here
; using values below
; don't forget to load mspeed with the
; current brate value if the new rate is
; valid. See table of values below.
push bc
; If pcfg is true, attempt to initialize the active
; HBIOS unit. If false, initialization if bypassed with
; the assumption that the RomWBW MODE command was used
; to initialize the port priot to running ZMP.
if pcfg
call print
db 'Initializing device: ',0
call diport
ld a,(hbunit) ; get device type
ld c,a
ld b,06h
rst 08
or a ; check if valid
jr nz,initerr
ld a,(brate) ; get baud rate to set
ld c,a
ld b,0
ld hl,baudtbl
add hl,bc
ld a,(hl) ; convert to encoded hbios
cp a,-1
jr z,initerr
push af
ld a,(hbunit) ; get line characteristics
ld c,a
ld b,05h
rst 08
ld a,d ; mask out exisitng
and 11100000b ; replace with rate
ld d,a
pop af
or d
ld d,a
ld b,04h ; set new
ld a,(hbunit) ; speed
ld c,a
rst 08
or a
jr nz,initerr
ld a,(brate) ; load mspeed with the current brate value if
ld (mspeed),a ; the new rate is valid. See table of values below.
call print
db lf,lf,'Initization completed, device: ',0
call diport
jr init_z
initerr:call print
db lf,lf,'Initization failed, device: ',0
call diport
jr init_z
diport: ld a,(hbunit) ; Display port
diport1:add a,'0'
call cout
call print
db cr,lf,0
ld hl,2
call waithls
ret
else
;call print
;db cr,lf,'Modem port initialization...',0
;ld hl,2
;call waithls
ld b,05h ; HBIOS port reset function
ld a,(hbunit) ; get active modem port
ld c,a ; and put in accum
rst 8 ; reset port
or a ; check result
jr nz,init1 ; if error, handle it
ld a,8 ; dummy value for speed
ld (mspeed),a ; save it
jr init_z ; done
init1:
; Handle error return from initialization
call print
db cr,lf,'Modem port initialization failed!',cr,lf,0
ld hl,2
call waithls
jr init_z
endif
init_z:
pop bc
; <== End of your own code
ret
;--------------------------------------------------------------------------
stop: dw 1 ; stop bits
parity: dw 'N' ; parity
data: dw 8 ; data bits
brate: dw 7 ; baud rate:
;--------------------------------------------------------------------------
;
;Values of brate for each baud rate
;
; hb encode baud rate brate
;
baudtbl:
db -1 ; 110 0 not supported
db 2 ; 300 1
db 17 ; 450 2
db 3 ; 600 3
db -1 ; 710 4 not supported
db 4 ; 1200 5
db 5 ; 2400 6
db 6 ; 4800 7
db 7 ; 9600 8
db 8 ; 19200 9
db 9 ; 38400 10
db 24 ; 57600 11
db 10 ; 76800 12
;
; Set the port. ZMP supplies either 0 or 1 as a parameter. You're on your
; own here -- your system is bound to be different from any other! You may
; implement a software switch on all the modem-dependent routines, or perhaps
; you can have one or two centralised routines for accessing the UARTs and
; modify the code from this routine to select one or the other. (Who said
; there was anything wrong with self-modifying code?). If you have only one
; UART port, or if you don't want to go through all the hassles, just have
; this routine returning with no changes made. Note that ZMP calls this
; routine with both values for the port on initialisation.
;
; Only originl ZMP calls setport. MECPARTS variant does not.
;
; We ignore this call. Since we are using a command line parameter
; to specify the desired modem port, it makes no sense.
;
setport:
ld hl,2 ; get port number
add hl,sp
ex de,hl
call getparm ; in HL (values are 0 and 1)
; <== Insert your own code here
; <== End of your own code
ret
;
;****************************************************************************
;Video terminal sequences: these are for VT-100: Modify as you wish
;Cursor addressing:
cursadd:
ld hl,2 ; get parameters
add hl,sp
ex de,hl
call getparm ; in HL
inc hl
ld (row),hl ; row
call getparm
inc hl
ld (col),hl ; column
;
push bc
call print
db esc,'[',0
ld a,(row) ; row first
call cursconv
ld a,';'
call cout
ld a,(col) ; same for column
call cursconv
ld a,'H'
call cout
pop bc
ret
;
cursconv:
ld b,a
xor a
ca1: add a,1
daa
djnz ca1
ld (num),a
and 0f0h
jr z,ca2
srl a
srl a
srl a
srl a
or '0'
call cout
ca2: ld a,(num)
and 0fh
or '0'
call cout
ret
;
row: ds 2 ; row
col: ds 2 ; column
num: ds 1
;
;Clear screen:
cls:
call print
db esc,"[H",esc,"[2J",0
ret
;
;Inverse video on:
invon:
call print
db esc,"[7m",0
ret
;
;Inverse video off:
invoff:
call print
db esc,"[m",0
ret
;
;Turn off cursor:
hide:
call print
db esc,'[?25l',0
ret
;
;Turn on cursor:
show:
call print
db esc,'[?25h',0
ret
;
;Save cursor position:
savecu:
call print
db esc,'[7',0
ret
;
;Restore cursor position:
rescu:
call print
db esc,'[8',0
ret
;****************************************************************************
;Service modem interrupt:
mint:
ret ; handled in HBIOS
;Initialise interrupt vectors:
invec:
ret ; ditto
;De-initialise interrupt vectors:
dinvec:
ret ; ditto
;****************** End of user-defined code ********************************
; Do not change anything below here.
;Modem character test for 100 ms
mrd:
push bc ; save bc
ld bc,100 ; set limit
mrd1:
call mirdy ; char at modem?
jr nz,mrd2 ; yes, exit
ld hl,1 ; else wait 1ms
call waithlms
dec bc ; loop till done
ld a,b
or c
jr nz,mrd1
ld hl,0 ; none there, result=0
xor a
mrd2:
pop bc
ret
; Inline print routine: destroys A and HL
print:
ex (sp),hl ; get address of string
ploop:
ld a,(hl) ; get next
inc hl ; bump pointer
or a ; done if zero
jr z,pdone
call cout ; else print
jr ploop ; and loop
pdone:
ex (sp),hl ; restore return address
ret ; and quit
;
;Output a character in A to the console
;
cout:
push bc ; save regs
push de
push hl
ld e,a ; character to E
ld c,2
call bdos ; print it
pop hl
pop de
pop bc
ret
;Wait seconds
wait:
ld hl,2
add hl,sp
ex de,hl ; get delay size
call getparm
; fall thru to..
;Wait seconds in HL
waithls:
push hl ; save loop control
ld hl,1000 ; 1000ms = 1 second
call waithlms
pop hl ; restore loop control
dec hl
ld a,h
or l
jr nz,waithls
ret
;Wait milliseconds
mswait:
ld hl,2
add hl,sp
ex de,hl ; get delay size
call getparm
; fall thru to..
;Wait milliseconds in HL
waithlms:
; burn 1000us (1ms)
ld a,(cpumhz)
ld e,a
hlms1:
; burn 1000 t-states
; 50 * 20 = 1000 t-states
ld d,50
hlms2:
nop ; 4
dec d ; 4
jr nz,hlms2 ; 12 20
dec e
jr nz,hlms1
dec hl
ld a,h
or l
jr nz,waithlms
ret
;Get next parameter from (de) into hl
getparm:
ex de,hl ; get address into hl
ld e,(hl) ; get lo
inc hl
ld d,(hl) ; then hi
inc hl ; bump for next
ex de,hl ; result in hl, address still in de
ret
;Get address of user-defined variables
getvars:
ld hl,uservars
ret
uservars:
dw overdrive ; .OVR etc. drive/user
dw overuser
if ($ - codebgn) gt ovsize
toobig: jp errval ; Overlay too large!
endif
end