mirror of
https://github.com/wwarthen/RomWBW.git
synced 2026-02-06 14:11:48 -06:00
Jörg Linder has disassembled and thoroughly commented a great deal of the BPBIOS binaries. This was an incredible amount of work. I have added all of these to the RomWBW build scripts and will ultimately integrate them more completely.
547 lines
15 KiB
Z80 Assembly
547 lines
15 KiB
Z80 Assembly
TITLE "B/P Bios RAM size display"
|
|
;************************************************************************
|
|
;* S I Z E R A M *
|
|
;* Determine size and location of (banked) Memory *
|
|
;* by Harold F. Bower and Cameron W. Cotrill *
|
|
;*----------------------------------------------------------------------*
|
|
;* Disassembly: jxl Dec 2024 *
|
|
;* public release 1.0 Apr 2025 *
|
|
;* see remarks at the end *
|
|
;*----------------------------------------------------------------------*
|
|
;* LINK with Version 4 libraries: VLIB, Z3LIB, SYSLIB *
|
|
;* *
|
|
;* A>Z80ASM SIZERAM/RS *
|
|
;* A>SLRNK SIZERAM/N,/A:100,/D:0750,SIZERAM,VLIBS/S,Z3LIBS/S,SYSLIBS/S,/E *
|
|
;************************************************************************
|
|
|
|
VER EQU 12
|
|
REV EQU ' '
|
|
|
|
DATE MACRO
|
|
DEFB '30 Aug 01'
|
|
ENDM
|
|
|
|
|
|
BEL EQU 07H ; Bell character
|
|
LF EQU 0AH ; Line Feed character
|
|
CR EQU 0DH ; Carriage Return character
|
|
|
|
CPMBIOS EQU 0 ; CP/M BIOS warm boot (JP)
|
|
CPMBDOS EQU 5 ; CP/M BDOS entry point (JP)
|
|
CPMFCB EQU 5CH ; CP/M standard FCB #1 (+1 filename, +9 filetype)
|
|
|
|
|
|
; From Z3LIB Get..
|
|
EXTRN GETNAME, PRTNAME, Z3INIT, WHRENV
|
|
|
|
; From SYSLIB Get..
|
|
EXTRN EPRINT, CRLF, PAFDC, PHLFDC, COUT, CODEND
|
|
|
|
|
|
;::::: PROGRAM START
|
|
|
|
ORG 100H
|
|
CSEG
|
|
|
|
|
|
SIZERAM: JP START ; bypass header
|
|
DEFB 'Z3ENV' ; this is a ZCPR3 utility
|
|
DEFB 1 ; show external environment
|
|
|
|
ENVADR: DEFW 0 ; addr of Z3 environment
|
|
DEFW SIZERAM ; type 4 filler
|
|
|
|
DEFB 'SIZERAM ',0 ; configuration name
|
|
|
|
START: LD (STACK),SP
|
|
LD SP,STACK
|
|
CALL EPRINT
|
|
DEFB 'B/P Banked RAM Sizing Utility V',VER/10+'0','.'
|
|
DEFB VER MOD 10 + '0',REV,' '
|
|
DATE
|
|
DEFB CR,LF
|
|
DEFB 0
|
|
CALL CHKZ3E ; check if Z3 Environment is valid
|
|
CALL GETNAME ; get actual program name
|
|
CALL CHKHELP ; check cmdline for help request
|
|
CALL CHKSYS ; check if running under B/P Bios
|
|
CALL M$BVER ; display version # msg
|
|
LD HL,(BPCNFG) ; get addr of config area
|
|
INC HL ; move ptr fwd
|
|
INC HL
|
|
LD A,(HL) ; get OPTF1 (option flag at CONFIG+2)
|
|
AND 00000001b ; mask bit 0
|
|
LD (BPBNKD),A ; ..and store it
|
|
JP NZ,BNKDSYS ; if banked, jump to continue
|
|
|
|
|
|
;::::: NON-BANKED SYSTEM
|
|
|
|
NBNKSYS: INC HL ; move ptr to TPABNK in config area
|
|
INC HL
|
|
CALL EPRINT
|
|
DEFB CR,LF,LF,'Non-Banked System using TPA Banks = '
|
|
DEFB 0
|
|
LD A,(HL) ; get value
|
|
PUSH AF
|
|
CALL PAFDC ; ..and display
|
|
LD A,'/'
|
|
CALL COUT
|
|
POP AF ; restore value
|
|
INC A ; increase it
|
|
CALL PAFDC ; ..and display
|
|
CALL CRLF
|
|
JP EXIT
|
|
|
|
|
|
;::::: BANKED SYSTEM
|
|
|
|
BNKDSYS: INC HL ; move ptr to UABNK in config area
|
|
LD DE,UABNK ; ..and make local copies
|
|
LD BC,5 ; (5 bytes, UABNK..MAXBNK)
|
|
LDIR
|
|
CALL CODEND ; get first available page after code end
|
|
LD (WSPCBEG),HL ; and store it
|
|
EX DE,HL ; swap regs
|
|
|
|
; memory read/write tests for all ram banks at addr 0x0000
|
|
; building a map in WSPC area: b1= zero if no bank found, b2= initially read byte
|
|
|
|
; test #1: 0x00/0xFF byte - iterate over banks in forward order
|
|
LD HL,0 ; set addr 0x0000
|
|
LD C,0 ; start with bank #0 (TPA)
|
|
MEMRW: CALL GETFRB ; get byte
|
|
LD B,A ; store in B
|
|
CPL ; invert (complement) byte
|
|
CALL SETINB ; write it back
|
|
CALL GETFRB ; read again
|
|
CPL ; ..and invert
|
|
XOR B ; xor'd with initially read byte
|
|
JR NZ,MEMRW0 ; ..if no match, skip over
|
|
LD A,B ; get initially read byte
|
|
CALL SETINB ; write it
|
|
CALL GETFRB ; and read again
|
|
SUB B ; subtract initially read byte
|
|
JR NZ,MEMRW0 ; ..if not zero, skip over
|
|
|
|
; injected opcode 0xF6 (OR n) to skip following XOR A
|
|
DEFB 0F6h ; = OR 0AFH (write non-zero)
|
|
MEMRW0: XOR A ; nullify A
|
|
LD (DE),A ; store in WSPC area
|
|
INC DE ; move ptr fwd
|
|
LD A,B ; get initially read byte
|
|
LD (DE),A ; store it, too
|
|
INC DE ; move ptr fwd
|
|
INC C ; bank # +1
|
|
JR NZ,MEMRW ; ..loop till all possible (256 / 0x100) banks tested
|
|
|
|
; test #2: write no. in each bank (iterate backward), then read and compare (forward)
|
|
DEC C ; correct bank # (loop was 1 ahead)
|
|
MEMWRB: DEC DE ; move ptr back
|
|
DEC DE
|
|
LD A,(DE) ; get byte
|
|
OR A ; check if bank exists
|
|
JR Z,MEMWRB0 ; ..if not, skip over
|
|
LD A,C ; else, get bank #
|
|
CALL SETINB ; and write in bank
|
|
MEMWRB0: LD A,C ; get bank #
|
|
SUB 1 ; -1
|
|
LD C,A ; set bank #
|
|
JR NC,MEMWRB ; ..if not below zero, loop
|
|
INC C ; correct bank #
|
|
|
|
MEMRDB: LD A,(DE) ; get byte from WSPC area
|
|
OR A ; check if bank exists
|
|
JR Z,MEMRDB0 ; ..if not, skip over
|
|
CALL GETFRB ; read byte from bank
|
|
CP C ; compare to bank #
|
|
JR Z,MEMRDB0 ; ..if match, skip over
|
|
XOR A ; else, set <NUL> as indicator
|
|
LD (DE),A ; that bank doesn't exist
|
|
MEMRDB0: INC DE ; move ptr fwd
|
|
INC DE
|
|
INC C ; bank # +1
|
|
JR NZ,MEMRDB ; ..loop till all possible (256 / 0x100) banks tested
|
|
|
|
; restore bytes initially read in all banks
|
|
LD DE,(WSPCBEG) ; set ptr to start of WSPC addr
|
|
MEMRST: LD A,(DE) ; get byte
|
|
OR A ; check bank exists
|
|
JR Z,MEMRST0 ; ..if not, skip over
|
|
INC DE ; move ptr fwd
|
|
LD A,(DE) ; get initially read byte
|
|
CALL SETINB ; ..and restore it
|
|
INC DE ; ptr fwd
|
|
JR MEMRST1 ; skip over
|
|
MEMRST0: INC DE ; ptr fwd
|
|
INC DE
|
|
MEMRST1: INC C ; bank # +1
|
|
JR NZ,MEMRST ; loop till done
|
|
; ..then fall through to display collected data
|
|
|
|
|
|
; display information for Banked System
|
|
; detect ranges of continuous ram banks
|
|
;
|
|
; HL= ptr in WSPC area
|
|
; C= counter (up) bank #, B= counter (down) for outer loop
|
|
; D= bank # begin of range, E= bank # end of range
|
|
LD BC,0
|
|
LD HL,(WSPCBEG) ; set ptr to start of WSPC addr
|
|
BRANGLP: LD A,(HL) ; get byte
|
|
OR A ; check bank exists
|
|
JR NZ,BRANGE ; ..if so, skip over
|
|
INC HL ; else, use a shorter loop
|
|
INC HL ; ..move ptr fwd
|
|
INC C ; ..and bank #
|
|
JR NZ,BRANGLP ; loop until max. reached (256 / 0x100)
|
|
JR PCNFIG ; else, display Bios config report
|
|
|
|
BRANGE: LD D,C ; store start of range (bank # in D)
|
|
BRANG0: INC HL ; ptr fwd
|
|
INC HL
|
|
INC C ; bank # +1
|
|
JR NZ,BRANG1 ; ..if not max., skip over
|
|
DEC B
|
|
JR PBRANG ; else, display bank ranges
|
|
|
|
BRANG1: LD A,(HL) ; get byte
|
|
OR A ; check bank exists
|
|
JR NZ,BRANG0 ; ..if so, loop
|
|
; else, fall through
|
|
|
|
; display collected information
|
|
PBRANG: LD E,C ; get current bank # in E
|
|
DEC E ; loop is ahead, so -1
|
|
CALL EPRINT
|
|
DEFB CR,LF,'RAM Banks '
|
|
DEFB 0
|
|
LD A,D ; get begin of range
|
|
CALL PAFDC ; ..and display it
|
|
CALL EPRINT
|
|
DEFB ' - '
|
|
DEFB 0
|
|
LD A,E ; get end of range
|
|
CALL PAFDC ; ..and display it
|
|
CALL EPRINT
|
|
DEFB ' ('
|
|
DEFB 0
|
|
LD A,E ; get end of range
|
|
INC A ; adjust for correct calc
|
|
SUB D ; calc difference of begin/end
|
|
PUSH HL
|
|
LD L,A ; get value in L
|
|
LD H,0 ; ..and multiply for display
|
|
ADD HL,HL ; *2
|
|
ADD HL,HL ; *4
|
|
ADD HL,HL ; *8
|
|
ADD HL,HL ; *16
|
|
ADD HL,HL ; *32 (fixed bank size of 32k assumed)
|
|
CALL PHLFDC ; ..and display
|
|
POP HL
|
|
CALL EPRINT
|
|
DEFB 'k Bytes)'
|
|
DEFB 0
|
|
LD A,B ; check if more to go
|
|
OR A
|
|
JR Z,BRANGLP ; loop till done
|
|
|
|
; display information as stored in B/P Bios config area
|
|
PCNFIG: CALL EPRINT
|
|
DEFB CR,LF,LF,'Bios Reports:',CR,LF
|
|
DEFB ' TPA Banks = '
|
|
DEFB 0
|
|
LD A,(TPABNK) ; get TPA bank #
|
|
PUSH AF ; save regs
|
|
CALL PAFDC ; and display it
|
|
LD A,'/'
|
|
CALL COUT
|
|
POP AF ; restore value
|
|
INC A ; +1 (TPA bank is build by 2 consecutive banks)
|
|
; fixed size of 32k per bank is assumed
|
|
CALL PAFDC ; and display it
|
|
CALL EPRINT
|
|
DEFB CR,LF,' System Bank = '
|
|
DEFB 0
|
|
LD A,(SYSBNK) ; get SYSTEM bank #
|
|
CALL PAFDC ; and display it
|
|
CALL EPRINT
|
|
DEFB CR,LF,' User Bank = '
|
|
DEFB 0
|
|
LD A,(UABNK) ; get # of USER banks
|
|
OR A
|
|
JR NZ,PCNFIG0 ; ..if not zero, skip over
|
|
CALL EPRINT
|
|
DEFB '(None)'
|
|
DEFB 0
|
|
JR PCNFIG1
|
|
PCNFIG0: CALL PAFDC ; display # of USER banks
|
|
PCNFIG1: CALL EPRINT
|
|
DEFB CR,LF,' RAM Disk Start = '
|
|
DEFB 0
|
|
LD A,(RAMBNK) ; get # of begin RAM Disk
|
|
CALL PAFDC
|
|
CALL EPRINT
|
|
DEFB CR,LF,' Last Used Bank = '
|
|
DEFB 0
|
|
LD A,(MAXBNK) ; get max. available bank #
|
|
CALL PAFDC
|
|
CALL EPRINT
|
|
DEFB CR,LF,LF,' -- Scan Complete.',CR,LF
|
|
DEFB 0
|
|
JP EXIT
|
|
|
|
|
|
;::::: SUPPORT FUNCTIONS
|
|
|
|
; get first token from command line (in FCB #1)
|
|
; and check if help was requested
|
|
CHKHELP: LD HL,CPMFCB+1
|
|
LD A,(HL) ; get byte
|
|
CP '/' ; is this a help request ?
|
|
RET NZ ; ..if not, return
|
|
INC HL ; else, move ptr fwd
|
|
LD A,(HL) ; and get next byte
|
|
CP '/' ; is it also '/' ?
|
|
RET NZ ; ..if not, return
|
|
; else, fall through and display help
|
|
|
|
|
|
;::::: HELP SCREEN
|
|
|
|
HELP: CALL EPRINT
|
|
DEFB CR,LF,' '
|
|
DEFB 0
|
|
CALL PPRGNAM
|
|
CALL EPRINT
|
|
DEFB ' Determines location and Size of Banked Memory.',CR,LF
|
|
DEFB ' (Only TPA Banks printed if Non-Banked)',CR,LF,LF
|
|
DEFB ' Syntax:',CR,LF
|
|
DEFB ' '
|
|
DEFB 0
|
|
CALL PPRGNAM
|
|
CALL EPRINT
|
|
DEFB ' - Print 32k RAM banks present and B/P Allocations',CR,LF
|
|
DEFB ' '
|
|
DEFB 0
|
|
CALL PPRGNAM
|
|
CALL EPRINT
|
|
DEFB ' // - display this screen',CR,LF
|
|
DEFB 0 ; fall through and exit
|
|
|
|
|
|
;::::: EXIT PROGRAM
|
|
|
|
EXIT: CALL CRLF
|
|
LD SP,(STACK) ; restore stack pointer
|
|
RET ; ..and return to system
|
|
|
|
|
|
;::::: SUPPORT FUNCTIONS
|
|
|
|
; check if running under ZCPR3 and status of wheel byte
|
|
; terminate program if not succesful
|
|
CHKZ3E: LD HL,(CPMBDOS+1)
|
|
CALL WHRENV ; find Z3 Environment Descriptor
|
|
LD (ENVADR),HL ; store ENV ptr
|
|
LD A,H ; check if invalid (= zero)
|
|
OR L
|
|
JP Z,E$BPBIO ; ..if so, jump error and terminate
|
|
CALL Z3INIT ; else, init for Z3LIB routines
|
|
LD A,41 ; offset to addr of wheel byte (Z3WHL)
|
|
CALL ADDHLA ; adjust ptr
|
|
LD E,(HL) ; get addr in DE
|
|
INC HL
|
|
LD D,(HL)
|
|
EX DE,HL ; swap regs
|
|
LD A,(HL) ; get value of wheel byte
|
|
AND A ; check if zero
|
|
RET NZ ; ..if not (wheel on), return
|
|
CALL EPRINT ; else, display message and exit
|
|
DEFB BEL,CR,LF,'Must be wheel to Execute !',CR,LF
|
|
DEFB 0
|
|
JR EXIT
|
|
|
|
|
|
; check if running under B/P Bios
|
|
; if not, program is terminated
|
|
CHKSYS: LD HL,(CPMBIOS+1) ; get warm boot addr (BIOS fn #1)
|
|
LD L,30*3 ; adjust ptr to fn #30
|
|
LD A,(HL) ; check byte at ptr location
|
|
CP 0C3H ; is it opcode 0xC3 (JP) ?
|
|
JR NZ,E$BPBIO ; ..if not, jump error and terminate
|
|
CALL JUMPHL ; else, "call" B/P Bios fn #30 (RETBIO)
|
|
LD (BPVERS),A ; store version of B/P Bios
|
|
LD (BPADDR),BC ; " base addr
|
|
LD (BPCNFG),DE ; " config area addr
|
|
LD HL,-6 ; move ptr 6 bytes backward
|
|
ADD HL,DE ; (signature string)
|
|
LD A,(HL) ; get byte
|
|
CP 'B' ; is it 'B' ?
|
|
JR NZ,E$BPBIO ; ..if not, jump error and exit
|
|
INC HL ; ptr fwd
|
|
LD A,(HL) ; get byte
|
|
CP '/' ; is it '/' ?
|
|
JR NZ,E$BPBIO ; ..if not, jump error and exit
|
|
INC HL ; ptr fwd
|
|
LD A,(HL) ; get byte
|
|
CP 'P' ; is it 'P' ?
|
|
RET Z ; ..if so, return
|
|
; else, fall through (error and exit)
|
|
|
|
|
|
; msg aborting
|
|
E$BPBIO: CALL EPRINT
|
|
DEFB CR,LF,BEL,'Not B/P Bios, aborting...!',CR,LF
|
|
DEFB 0
|
|
RST 0
|
|
|
|
|
|
; print program name on CON: device
|
|
; (either the actual name, or fallback to default)
|
|
; only used by HELP
|
|
PPRGNAM: LD A,(ENVADR+1) ; get high byte of ENVPTR
|
|
OR A ; check if valid (<> zero)
|
|
JP NZ,PRTNAME ; ..if so, display actual name
|
|
; ..and let return from there
|
|
CALL EPRINT ; else, display default
|
|
DEFB 'SIZERAM'
|
|
DEFB 0
|
|
RET
|
|
|
|
|
|
; msg B/P Bios Vers x.x
|
|
M$BVER: CALL EPRINT
|
|
DEFB ' (B/P Bios Vers '
|
|
DEFB 0
|
|
LD A,(BPVERS) ; get version #
|
|
RRCA ; reverse nybbles in A
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
AND 00001111b ; mask lower nybble
|
|
ADD A,'0' ; make it ascii
|
|
CALL COUT ; ..and display
|
|
LD A,'.'
|
|
CALL COUT
|
|
LD A,(BPVERS) ; get version #
|
|
AND 00001111b ; mask lower nybble
|
|
ADD A,'0' ; make it ascii
|
|
CALL COUT ; ..and display
|
|
CALL EPRINT
|
|
DEFB ')',CR,LF
|
|
DEFB 0
|
|
RET
|
|
|
|
|
|
; add A to HL (result in HL)
|
|
ADDHLA: ADD A,L ; add L
|
|
LD L,A ; store result in L
|
|
RET NC ; ..if no overflow, return
|
|
INC H ; else, increment H
|
|
RET
|
|
|
|
|
|
; the following routines rearrange Top of Stack by injecting an
|
|
; intermediate return addr, and putting the Bios fn call on top
|
|
; so that HL regs are preserved
|
|
; order of steps:
|
|
; [1] HL (= addr) is pushed onto stack
|
|
; [2] intermediate return addr is swapped to Top of Stack
|
|
; [3] HL (= addr) is pushed onto stack again
|
|
; [4] Bios fn JP addr is swapped to Top of Stack
|
|
; [5] Bios is "called" through RET, and returns to intermediate addr
|
|
|
|
; get byte from ram bank - in the form LD A,(HL)
|
|
; in: C= bank #, HL= addr
|
|
; out: A= byte
|
|
GETFRB: PUSH BC
|
|
PUSH HL ; save addr
|
|
LD HL,GETFRB0 ; load return addr
|
|
EX (SP),HL ; put it on stack
|
|
PUSH HL ; save HL again (previous top of stack)
|
|
LD HL,(BPADDR) ; get B/P Bios base addr
|
|
LD L,35*3 ; adjust ptr to fn #35 (FRGETB)
|
|
EX (SP),HL ; put addr on stack
|
|
RET ; ..and "call" Bios fn through stack
|
|
|
|
GETFRB0: POP BC ; restore regs
|
|
RET ; ..and finally return
|
|
|
|
; set byte in ram bank - in the form LD (HL),A
|
|
; in: C= bank #, HL= addr, A= byte to set
|
|
SETINB: PUSH BC
|
|
PUSH HL ; save addr
|
|
LD HL,GETFRB0 ; load return addr
|
|
EX (SP),HL ; put it on stack
|
|
PUSH HL ; save HL again (previous top of stack)
|
|
LD HL,(BPADDR) ; get B/P Bios base addr
|
|
LD L,37*3 ; adjust ptr to fn #37 (FRPUTB)
|
|
EX (SP),HL ; put addr on stack
|
|
RET ; ..and "call" Bios fn through stack
|
|
|
|
|
|
; "called" as a pseudo-routine that returns to caller
|
|
; in: HL= target addr
|
|
JUMPHL: JP (HL) ; jump to addr in HL regs
|
|
|
|
|
|
;::::: LOCAL DATA (not in DSEG)
|
|
|
|
WSPCBEG: DEFW 0 ; addr begin of workspace area
|
|
; (first available page, returned by CODEND)
|
|
|
|
; data retrieved from running system
|
|
BPVERS: DEFB 0 ; B/P Bios version
|
|
BPADDR: DEFW 0 ; B/P Bios base addr
|
|
BPCNFG: DEFW 0 ; addr of Config Area
|
|
BPBNKD: DEFB 0 ; indicator banked system (bit 0 of OPTF1) --> not used
|
|
|
|
; local copies of RAM bank configuration
|
|
UABNK: DEFB 0 ; beginning of User Bank(s)
|
|
TPABNK: DEFB 0 ; TPA Bank
|
|
SYSBNK: DEFB 0 ; beginning of System Bank(s)
|
|
RAMBNK: DEFB 0 ; base bank # for Ram Disk
|
|
MAXBNK: DEFB 0 ; highest permissible Bank #
|
|
|
|
|
|
DEFS 30H ; room for stack
|
|
STACK: DEFW 0 ; stack storage location
|
|
|
|
|
|
;:::::::::::::::::::::::::::::::::::::::::::::::::::::
|
|
; Z3LIB - 0x05ae
|
|
; SYSLIB - 0x065c
|
|
; end addr 0x0750 (begin DSEG)
|
|
;:::::::::::::::::::::::::::::::::::::::::::::::::::::
|
|
|
|
|
|
;::::: RAM STORAGE
|
|
|
|
DSEG
|
|
|
|
END
|
|
|
|
|
|
;************************************************************************
|
|
; Remarks jxl:
|
|
; SIZERAM.COM, included in available B/P Bios package(s), was dis-
|
|
; assembled and extensively commented. Labels are up to seven chars long
|
|
; to comply with M-REL standards. However, it is recommended to use SLR
|
|
; tools that support labels up to sixteen chars.
|
|
; In its current state, the compiled/linked file matches exactly the
|
|
; original INITRAM.COM, i.e. no changes to the source were made.
|
|
;
|
|
; The program uses an interesting technique to read and write data in
|
|
; alternative ram banks utilizing B/P Bios functions no. 35 FRGETB and
|
|
; no. 37 FRPUTB. Top of Stack is manipulated to keep registers intact
|
|
; and "call" functions through a RET statement (instead of CALL.)
|
|
; Another specialty which is worth mentioning: Routine MEMRW (memory
|
|
; read/write) contains an "injected" opcode to alter the behaviour at
|
|
; runtime. Otherwise a more complex logic and/or additional routine
|
|
; would have been necessary.
|
|
; Storage of local data is in CSEG (code segment), not DSEG. There
|
|
; seems to be no particular reason for this. So, it can be assumed that
|
|
; this just happened by mistake.
|
|
;************************************************************************
|