mirror of https://github.com/wwarthen/RomWBW.git
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.
602 lines
13 KiB
602 lines
13 KiB
;===============================================================================
|
|
; PORTSCAN - Sweep Ports
|
|
;
|
|
;===============================================================================
|
|
;
|
|
; Author: Wayne Warthen (wwarthen@gmail.com)
|
|
;_______________________________________________________________________________
|
|
;
|
|
; Usage:
|
|
; PORTSCAN
|
|
;
|
|
; Operation:
|
|
; Reads all ports (multiple ways) and displays values read
|
|
;_______________________________________________________________________________
|
|
;
|
|
; Change Log:
|
|
; 2023-02-14 [WBW] Initial release
|
|
;_______________________________________________________________________________
|
|
;
|
|
; ToDo:
|
|
;_______________________________________________________________________________
|
|
;
|
|
;===============================================================================
|
|
; Definitions
|
|
;===============================================================================
|
|
;
|
|
runloc .equ $C000 ; Running location (upper memory required)
|
|
stksiz .equ $40 ; Working stack size
|
|
;
|
|
restart .equ $0000 ; CP/M restart vector
|
|
;
|
|
;
|
|
#include "../../../ver.inc"
|
|
;
|
|
#include "../../../HBIOS/hbios.inc"
|
|
;
|
|
;===============================================================================
|
|
; Code Section
|
|
;===============================================================================
|
|
;
|
|
.org $100
|
|
;
|
|
; relocate worker code to upper memory
|
|
ld hl,begin ; start of working code image
|
|
ld de,runloc ; running location
|
|
ld bc,size ; size of working code image
|
|
ldir ; copy to upper RAM
|
|
jp runloc ; and go
|
|
;
|
|
; Start of working code
|
|
;
|
|
begin .equ $ ; image loaded here
|
|
;
|
|
.org runloc ; now generate running location adresses
|
|
;
|
|
; setup stack (save old value)
|
|
ld (stksav),sp ; save stack
|
|
ld sp,stack ; set new stack
|
|
;
|
|
; initialization
|
|
call init ; initialize
|
|
jr nz,exit ; abort if init fails
|
|
;
|
|
; process
|
|
call process ; do main processing
|
|
jr nz,exit ; abort on error
|
|
;
|
|
exit: ; clean up and return to command processor
|
|
call crlf ; formatting
|
|
ld sp,(stksav) ; restore stack
|
|
;jp restart ; return to CP/M via restart
|
|
ret ; return to CP/M w/o restart
|
|
;
|
|
; Initialization
|
|
;
|
|
init:
|
|
call crlf2 ; formatting
|
|
ld de,msgban ; point to version message part 1
|
|
call prtstr ; print it
|
|
;
|
|
call idbio ; identify active BIOS
|
|
cp 1 ; check for HBIOS
|
|
jp nz,errbio ; handle BIOS error
|
|
;
|
|
ld a,rmj << 4 | rmn ; expected HBIOS ver
|
|
cp d ; compare with result above
|
|
jp nz,errbio ; handle BIOS error
|
|
;
|
|
initx
|
|
; initialization complete
|
|
xor a ; signal success
|
|
ret ; return
|
|
;
|
|
; Process
|
|
;
|
|
process:
|
|
call crlf
|
|
ld a,($FFE0) ; get current hbios bank id
|
|
ld (orgbnk),a ; and save it
|
|
ld a,0 ; start with port 0
|
|
ld (curport),a ; save it for use below
|
|
; Test for z180 using mlt
|
|
ld de,$0506 ; 5 x 6
|
|
mlt de ; de = 30 if z180
|
|
ld a,e ; result to A
|
|
cp 30 ; check if multiply happened
|
|
jr nz,prtcpu ; if invalid, then Z80
|
|
or $FF ; flag value for Z180
|
|
ld (is180),a ; save it
|
|
;
|
|
prtcpu:
|
|
ld de,msgcpu
|
|
call prtstr
|
|
ld a,(is180)
|
|
or a
|
|
ld de,msgz80
|
|
jr z,prtcpu1
|
|
ld de,msgz180
|
|
prtcpu1:
|
|
call prtstr
|
|
call crlf
|
|
;
|
|
loop:
|
|
call crlf
|
|
ld a,(curport)
|
|
call prthex
|
|
ld a,':'
|
|
call prtchr
|
|
;
|
|
di ; interrupts off
|
|
;
|
|
ld hl,vallist ; init value list pointer
|
|
call portread ; read the port
|
|
call portread ; do it again
|
|
;
|
|
; restore possibly corrupted bank registers
|
|
ld a,(orgbnk) ; get proper bank id
|
|
call $FFF3 ; restore it
|
|
;
|
|
ei ; interrupts safe now
|
|
;
|
|
ld hl,vallist ; re-init value list pointer
|
|
ld b,4 ; print 4 values
|
|
prtloop:
|
|
ld a,' '
|
|
call prtchr
|
|
ld a,(hl)
|
|
call prthex
|
|
inc hl
|
|
djnz prtloop
|
|
;
|
|
; update port and loop as needed
|
|
ld a,(curport) ; get current port
|
|
inc a ; move to next
|
|
ld (curport),a ; save it
|
|
jr z,done ; done on wraparound
|
|
jr loop ; loop until done
|
|
;
|
|
done:
|
|
;
|
|
call crlf2
|
|
ld de,msgdone ; message to print
|
|
call prtstr ; do it
|
|
ret ; all done
|
|
;
|
|
;
|
|
;
|
|
portread:
|
|
ld a,(is180)
|
|
or a
|
|
jr nz,portread_z180
|
|
;
|
|
portread_z80: ; use traditional "IN"
|
|
; read port using IN <portnum>
|
|
ld a,(curport) ; get current port
|
|
ld (pnum0),a ; modify IN instruction
|
|
nop ; defeat Z280 pipeline
|
|
nop
|
|
in a,($FF) ; read the port
|
|
pnum0 .equ $-1
|
|
ld (hl),a ; save it
|
|
inc hl ; bump value list pointer
|
|
jr portread1
|
|
;
|
|
portread_z180: ; use "IN0"
|
|
; read port using IN <portnum>
|
|
ld a,(curport) ; get current port
|
|
ld (pnum1),a ; modify IN instruction
|
|
in0 a,($FF) ; read the port
|
|
pnum1 .equ $-1
|
|
ld (hl),a ; save it
|
|
inc hl ; bump value list pointer
|
|
;
|
|
portread1:
|
|
; read port using IN (C)
|
|
ld a,(curport) ; get current port
|
|
ld b,0 ; in case 16 bits decoded
|
|
ld c,a ; move to reg C
|
|
in a,(c) ; read the port
|
|
ld (hl),a ; save it
|
|
inc hl ; bump value list pointer
|
|
ret
|
|
;
|
|
; Identify active BIOS. RomWBW HBIOS=1, UNA UBIOS=2, else 0
|
|
;
|
|
idbio:
|
|
;
|
|
; Check for UNA (UBIOS)
|
|
ld a,($FFFD) ; fixed location of UNA API vector
|
|
cp $C3 ; jp instruction?
|
|
jr nz,idbio1 ; if not, not UNA
|
|
ld hl,($FFFE) ; get jp address
|
|
ld a,(hl) ; get byte at target address
|
|
cp $FD ; first byte of UNA push ix instruction
|
|
jr nz,idbio1 ; if not, not UNA
|
|
inc hl ; point to next byte
|
|
ld a,(hl) ; get next byte
|
|
cp $E5 ; second byte of UNA push ix instruction
|
|
jr nz,idbio1 ; if not, not UNA, check others
|
|
;
|
|
ld bc,$04FA ; UNA: get BIOS date and version
|
|
rst 08 ; DE := ver, HL := date
|
|
;
|
|
ld a,2 ; UNA BIOS id = 2
|
|
ret ; and done
|
|
;
|
|
idbio1:
|
|
; Check for RomWBW (HBIOS)
|
|
ld hl,(HB_IDENT) ; HL := HBIOS ident location
|
|
ld a,'W' ; First byte of ident
|
|
cp (hl) ; Compare
|
|
jr nz,idbio2 ; Not HBIOS
|
|
inc hl ; Next byte of ident
|
|
ld a,~'W' ; Second byte of ident
|
|
cp (hl) ; Compare
|
|
jr nz,idbio2 ; Not HBIOS
|
|
;
|
|
ld b,BF_SYSVER ; HBIOS: VER function
|
|
ld c,0 ; required reserved value
|
|
rst 08 ; DE := version, L := platform id
|
|
;
|
|
ld a,1 ; HBIOS BIOS id = 1
|
|
ret ; and done
|
|
;
|
|
idbio2:
|
|
; No idea what this is
|
|
xor a ; Setup return value of 0
|
|
ret ; and done
|
|
;
|
|
; Print character in A without destroying any registers
|
|
;
|
|
prtchr:
|
|
push bc ; save registers
|
|
push de
|
|
push hl
|
|
ld e,a ; character to print in E
|
|
ld b,BF_CIOOUT ; HBIOS function to output a character
|
|
ld c,CIO_CONSOLE ; write to current console unit
|
|
call HB_INVOKE ; invoke HBIOS via call
|
|
pop hl ; restore registers
|
|
pop de
|
|
pop bc
|
|
ret
|
|
;
|
|
prtdot:
|
|
;
|
|
; shortcut to print a dot preserving all regs
|
|
push af ; save af
|
|
ld a,'.' ; load dot char
|
|
call prtchr ; print it
|
|
pop af ; restore af
|
|
ret ; done
|
|
;
|
|
prtspace:
|
|
;
|
|
; shortcut to print a space preserving all regs
|
|
push af ; save af
|
|
ld a,' ' ; load dot char
|
|
call prtchr ; print it
|
|
pop af ; restore af
|
|
ret ; done
|
|
;
|
|
prtcr:
|
|
;
|
|
; shortcut to print a dot preserving all regs
|
|
push af ; save af
|
|
ld a,13 ; load CR value
|
|
call prtchr ; print it
|
|
pop af ; restore af
|
|
ret ; done
|
|
;
|
|
; Print a zero terminated string at (DE) without destroying any registers
|
|
;
|
|
prtstr:
|
|
push de
|
|
;
|
|
prtstr1:
|
|
ld a,(de) ; get next char
|
|
or a
|
|
jr z,prtstr2
|
|
call prtchr
|
|
inc de
|
|
jr prtstr1
|
|
;
|
|
prtstr2:
|
|
pop de ; restore registers
|
|
ret
|
|
;
|
|
; Print a block of memory nicely formatted
|
|
; de=buffer address
|
|
;
|
|
dump_buffer:
|
|
call crlf
|
|
|
|
push de
|
|
pop hl
|
|
inc d
|
|
inc d
|
|
|
|
db_blkrd:
|
|
push bc
|
|
push hl
|
|
pop bc
|
|
call prthexword ; print start location
|
|
pop bc
|
|
call prtspace ;
|
|
ld c,16 ; set for 16 locs
|
|
push hl ; save starting hl
|
|
db_nxtone:
|
|
ld a,(hl) ; get byte
|
|
call prthex ; print it
|
|
call prtspace ;
|
|
db_updh:
|
|
inc hl ; point next
|
|
dec c ; dec. loc count
|
|
jr nz,db_nxtone ; if line not done
|
|
; now print 'decoded' data to right of dump
|
|
db_pcrlf:
|
|
call prtspace ; space it
|
|
ld c,16 ; set for 16 chars
|
|
pop hl ; get back start
|
|
db_pcrlf0:
|
|
ld a,(hl) ; get byte
|
|
and 060h ; see if a 'dot'
|
|
ld a,(hl) ; o.k. to get
|
|
jr nz,db_pdot ;
|
|
db_dot:
|
|
ld a,2eh ; load a dot
|
|
db_pdot:
|
|
call prtchr ; print it
|
|
inc hl ;
|
|
ld a,d ;
|
|
cp h ;
|
|
jr nz,db_updh1 ;
|
|
ld a,e ;
|
|
cp l ;
|
|
jp z,db_end ;
|
|
db_updh1:
|
|
; if block not dumped, do next character or line
|
|
dec c ; dec. char count
|
|
jr nz,db_pcrlf0 ; do next
|
|
db_contd:
|
|
call crlf ;
|
|
jp db_blkrd ;
|
|
|
|
db_end:
|
|
ret
|
|
;
|
|
; Print the value in A in hex without destroying any registers
|
|
;
|
|
prthex:
|
|
push af ; save AF
|
|
push de ; save DE
|
|
call hexascii ; convert value in A to hex chars in DE
|
|
ld a,d ; get the high order hex char
|
|
call prtchr ; print it
|
|
ld a,e ; get the low order hex char
|
|
call prtchr ; print it
|
|
pop de ; restore DE
|
|
pop af ; restore AF
|
|
ret ; done
|
|
;
|
|
; print the hex word value in bc
|
|
;
|
|
prthexword:
|
|
push af
|
|
ld a,b
|
|
call prthex
|
|
ld a,c
|
|
call prthex
|
|
pop af
|
|
ret
|
|
;
|
|
; print the hex dword value in de:hl
|
|
;
|
|
prthex32:
|
|
push bc
|
|
push de
|
|
pop bc
|
|
call prthexword
|
|
push hl
|
|
pop bc
|
|
call prthexword
|
|
pop bc
|
|
ret
|
|
;
|
|
; Convert binary value in A to ascii hex characters in DE
|
|
;
|
|
hexascii:
|
|
ld d,a ; save A in D
|
|
call hexconv ; convert low nibble of A to hex
|
|
ld e,a ; save it in E
|
|
ld a,d ; get original value back
|
|
rlca ; rotate high order nibble to low bits
|
|
rlca
|
|
rlca
|
|
rlca
|
|
call hexconv ; convert nibble
|
|
ld d,a ; save it in D
|
|
ret ; done
|
|
;
|
|
; Convert low nibble of A to ascii hex
|
|
;
|
|
hexconv:
|
|
and $0F ; low nibble only
|
|
add a,$90
|
|
daa
|
|
adc a,$40
|
|
daa
|
|
ret
|
|
;
|
|
; Print value of A or HL in decimal with leading zero suppression
|
|
; Use prtdecb for A or prtdecw for HL
|
|
;
|
|
prtdecb:
|
|
push hl
|
|
ld h,0
|
|
ld l,a
|
|
call prtdecw ; print it
|
|
pop hl
|
|
ret
|
|
;
|
|
prtdecw:
|
|
push af
|
|
push bc
|
|
push de
|
|
push hl
|
|
call prtdec0
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
pop af
|
|
ret
|
|
;
|
|
prtdec0:
|
|
ld e,'0'
|
|
ld bc,-10000
|
|
call prtdec1
|
|
ld bc,-1000
|
|
call prtdec1
|
|
ld bc,-100
|
|
call prtdec1
|
|
ld c,-10
|
|
call prtdec1
|
|
ld e,0
|
|
ld c,-1
|
|
prtdec1:
|
|
ld a,'0' - 1
|
|
prtdec2:
|
|
inc a
|
|
add hl,bc
|
|
jr c,prtdec2
|
|
sbc hl,bc
|
|
cp e
|
|
ret z
|
|
ld e,0
|
|
call prtchr
|
|
ret
|
|
;
|
|
; Start a new line
|
|
;
|
|
crlf2:
|
|
call crlf ; two of them
|
|
crlf:
|
|
push af ; preserve AF
|
|
ld a,13 ; <CR>
|
|
call prtchr ; print it
|
|
ld a,10 ; <LF>
|
|
call prtchr ; print it
|
|
pop af ; restore AF
|
|
ret
|
|
;
|
|
; Get the next non-blank character from (HL).
|
|
;
|
|
nonblank:
|
|
ld a,(hl) ; load next character
|
|
or a ; string ends with a null
|
|
ret z ; if null, return pointing to null
|
|
cp ' ' ; check for blank
|
|
ret nz ; return if not blank
|
|
inc hl ; if blank, increment character pointer
|
|
jr nonblank ; and loop
|
|
;
|
|
; Convert character in A to uppercase
|
|
;
|
|
ucase:
|
|
cp 'a' ; if below 'a'
|
|
ret c ; ... do nothing and return
|
|
cp 'z' + 1 ; if above 'z'
|
|
ret nc ; ... do nothing and return
|
|
res 5,a ; clear bit 5 to make lower case -> upper case
|
|
ret ; and return
|
|
;
|
|
; Add the value in A to HL (HL := HL + A)
|
|
;
|
|
addhl:
|
|
add a,l ; A := A + L
|
|
ld l,a ; Put result back in L
|
|
ret nc ; if no carry, we are done
|
|
inc h ; if carry, increment H
|
|
ret ; and return
|
|
;
|
|
; Jump indirect to address in HL
|
|
;
|
|
jphl:
|
|
jp (hl)
|
|
;
|
|
; Short delay functions. No clock speed compensation, so they
|
|
; will run longer on slower systems. The number indicates the
|
|
; number of call/ret invocations. a single call/ret is
|
|
; 27 t-states on a z80, 25 t-states on a z180
|
|
;
|
|
; ; Z80 Z180
|
|
; ; ---- ----
|
|
dly64: call dly32 ; 1728 1600
|
|
dly32: call dly16 ; 864 800
|
|
dly16: call dly8 ; 432 400
|
|
dly8: call dly4 ; 216 200
|
|
dly4: call dly2 ; 108 100
|
|
dly2: call dly1 ; 54 50
|
|
dly1: ret ; 27 25
|
|
|
|
;
|
|
; Errors
|
|
;
|
|
erruse: ; command usage error (syntax)
|
|
ld de,msguse
|
|
jr err
|
|
;
|
|
errprm: ; command parameter error (syntax)
|
|
ld de,msgprm
|
|
jr err
|
|
;
|
|
errbio: ; invalid BIOS or version
|
|
ld de,msgbio
|
|
jr err
|
|
;
|
|
err: ; print error string and return error signal
|
|
call crlf2 ; print newline
|
|
;
|
|
err1: ; without the leading crlf
|
|
call prtstr ; print error string
|
|
;
|
|
err2: ; without the string
|
|
; call crlf ; print newline
|
|
or $FF ; signal error
|
|
ret ; done
|
|
;
|
|
;===============================================================================
|
|
; Storage Section
|
|
;===============================================================================
|
|
;
|
|
is180 .db 0 ; non-zero for z180
|
|
orgbnk .db 0 ; original bank id
|
|
curport .db 0 ; current port being processed
|
|
vallist .fill 8,0 ; port values read
|
|
;
|
|
stksav .dw 0 ; stack pointer saved at start
|
|
.fill stksiz,0 ; stack
|
|
stack .equ $ ; stack top
|
|
;
|
|
; Messages
|
|
;
|
|
msgban .db "PORTSCAN v1.0, 16-Feb-2023",13,10
|
|
.db "Copyright (C) 2023, Wayne Warthen, GNU GPL v3",0
|
|
msguse .db "Usage: PORTSCAN",13,10
|
|
msgprm .db "Parameter error (PORTSCAN /? for usage)",0
|
|
msgbio .db "Incompatible BIOS or version, "
|
|
.db "HBIOS v", '0' + rmj, ".", '0' + rmn, " required",0
|
|
str_sep .db ": ",0
|
|
;
|
|
msgcpu .db "CPU is ",0
|
|
msgz80 .db "Z80",0
|
|
msgz180 .db "Z180",0
|
|
msgdone .db "End of Port Sweep",0
|
|
|
|
;
|
|
;
|
|
;
|
|
size .equ $ - runloc
|
|
;
|
|
.end
|
|
|