mirror of
https://github.com/wwarthen/RomWBW.git
synced 2026-02-06 22:43:15 -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.
484 lines
14 KiB
Z80 Assembly
484 lines
14 KiB
Z80 Assembly
TITLE "Swap drives under B/P Bios"
|
|
;************************************************************************
|
|
;* B P S W A P *
|
|
;* Swap two drive letters in a running B/P Bios system *
|
|
;* 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 BPSWAP/RS *
|
|
;* A>SLRNK BPSWAP/N,/A:100,/D:0854,BPSWAP,VLIBS/S,Z3LIBS/S,SYSLIBS/S,/E *
|
|
;************************************************************************
|
|
|
|
VER EQU 10
|
|
REV EQU ' '
|
|
|
|
DATE MACRO
|
|
DEFB '31 Aug 92'
|
|
ENDM
|
|
|
|
|
|
CTRLC EQU 03H ; Control-C character
|
|
BEL EQU 07H ; Bell character
|
|
TAB EQU 09H ; Tab 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)
|
|
CPMDMA EQU 80H ; CP/M standard DMA buffer
|
|
|
|
|
|
; From VLIB Get..
|
|
EXTRN VPRINT, Z3VINIT
|
|
|
|
; From Z3LIB Get..
|
|
EXTRN GETNAME, PRTNAME, WHRENV
|
|
|
|
; From SYSLIB Get..
|
|
EXTRN CRLF, CAPINE, COUT
|
|
|
|
|
|
;::::: PROGRAM START
|
|
|
|
ORG 100H
|
|
CSEG
|
|
|
|
|
|
BPSWAP: JP START ; bypass header
|
|
DEFB 'Z3ENV' ; this is a ZCPR3 utility
|
|
DEFB 1 ; show external environment
|
|
|
|
ENVADR: DEFW 0 ; addr of Z3 environment
|
|
|
|
START: LD HL,(CPMBDOS) ; ##### BUG: should be CPMBDOS+1 ?
|
|
CALL WHRENV ; find Z3 Environment Descriptor
|
|
LD (ENVADR),HL ; store addr
|
|
CALL Z3VINIT ; ..and init for Z3LIB routines
|
|
CALL GETNAME ; get actual program name
|
|
CALL GQFLAG
|
|
AND A ; running in quiet mode ?
|
|
JR NZ,START0 ; ..if so, skip over
|
|
CALL VPRINT
|
|
DEFB 1,'B/P Drive Swap',2,' V',VER/10+'0','.',VER MOD 10 + '0',', '
|
|
DATE
|
|
DEFB CR,LF
|
|
DEFB 0
|
|
|
|
START0: LD (STACK),SP
|
|
LD SP,STACK
|
|
|
|
; get first token from command line (in FCB #1)
|
|
LD A,(CPMFCB+1) ; get char
|
|
CP '/' ; is this a help request ?
|
|
JP Z,HELP ; ..if so, show help screen
|
|
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 exit
|
|
CALL JUMPHL ; else, "call" B/P Bios fn #30 (RETBIO)
|
|
LD (BPBASE),BC ; store B/P Bios base 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, error and exit
|
|
INC HL ; ptr fwd
|
|
LD A,(HL) ; get byte
|
|
CP '/' ; is it '/' ?
|
|
JR NZ,E$BPBIO ; ..if not, error and exit
|
|
INC HL ; ptr fwd
|
|
LD A,(HL) ; get byte
|
|
CP 'P' ; is it 'P' ?
|
|
JR Z,EVALCMD ; ..if so, jump to continue
|
|
; else, fall through (error and exit)
|
|
|
|
E$BPBIO: CALL VPRINT
|
|
DEFB CR,LF,BEL,'+++ Not B/P Bios ... aborting +++',CR,LF
|
|
DEFB 0
|
|
JP EXIT
|
|
|
|
|
|
; evaluate command line
|
|
EVALCMD: LD HL,CPMDMA ; ptr to standard DMA buffer (holds command line)
|
|
LD A,(HL) ; get length of first token
|
|
INC HL ; +1
|
|
CALL ADDHLA ; move ptr fwd
|
|
LD (HL),0 ; set <NUL> terminator
|
|
LD HL,CPMDMA+1 ; set ptr to start of string
|
|
CALL FINDDRV ; find letter of first drive
|
|
JR C,RUNIMOD ; ..if invalid/not found, switch to interactive mode
|
|
LD (DRV1ST),A ; else, store # of first drive
|
|
LD A,(HL) ; get following byte
|
|
CALL EVALSEP ; is it a separator char ?
|
|
JP C,M$ABORT ; ..if not, abort program
|
|
CALL FINDDRV ; find letter of second drive
|
|
JR C,RUNIM0 ; ..if invalid/not found, switch to interactive mode
|
|
LD (DRV2ND),A ; else, store # of second drive
|
|
LD A,(HL) ; get following byte
|
|
CALL EVALSEP ; is it a separator char ?
|
|
JP C,M$ABORT ; ..if not, abort program
|
|
JR SWAPDRV ; else, jump to continue
|
|
|
|
|
|
; run in interactive mode
|
|
RUNIMOD: CALL VPRINT
|
|
DEFB ' First Drive to Swap [A..P] : '
|
|
DEFB 0
|
|
CALL CAPINE ; get input
|
|
CALL CRLF
|
|
CP CTRLC ; is it <Ctrl-C> ?
|
|
JP Z,M$ABORT ; ..if so, abort program
|
|
CALL EVALDRV ; check if drive letter is valid (A..P)
|
|
JR C,RUNIMOD ; ..if not, loop ask for new input
|
|
LD (DRV1ST),A ; else, store drive #
|
|
RUNIM0: CALL VPRINT
|
|
DEFB ' Second Drive to Swap [A..P] : '
|
|
DEFB 0
|
|
CALL CAPINE ; get input
|
|
CALL CRLF
|
|
CP CTRLC ; is it <Ctrl-C> ?
|
|
JP Z,M$ABORT ; ..if so, abort program
|
|
CALL EVALDRV ; check if drive letter is valid (A..P)
|
|
JR C,RUNIM0 ; ..if not, loop ask for new input
|
|
LD (DRV2ND),A ; else, store drive #
|
|
|
|
|
|
;::::: PROCESS
|
|
|
|
SWAPDRV: LD HL,(BPBASE) ; get B/P Bios base addr
|
|
LD L,22*3 ; adjust ptr to fn #22 (DRVTBL)
|
|
CALL JUMPHL ; ..and "call" fn
|
|
PUSH HL ; save ptr to DRVTBL
|
|
LD A,(DRV1ST) ; get # of first drive
|
|
ADD A,A ; *2 for 16-bit entries
|
|
CALL ADDHLA ; ..and move ptr fwd
|
|
EX DE,HL ; swap regs
|
|
POP HL ; restore ptr to DRVTBL
|
|
LD A,(DRV2ND) ; get # of second drive
|
|
ADD A,A ; *2
|
|
CALL ADDHLA ; ..and move ptr fwd
|
|
|
|
; DE= addr DPH first drive
|
|
; HL= addr DPH second drive
|
|
LD C,(HL) ; swap addr's in DRVTBL using
|
|
LD A,(DE) ; regs DE, HL as pointers
|
|
LD (HL),A ; and regs A, C holding bytes to copy
|
|
LD A,C
|
|
LD (DE),A
|
|
INC HL
|
|
INC DE
|
|
LD C,(HL)
|
|
LD A,(DE)
|
|
LD (HL),A
|
|
LD A,C
|
|
LD (DE),A
|
|
LD HL,0
|
|
LD (PDRVVCT),HL ; init new Drive Vector (pos) with 0x0000
|
|
DEC HL
|
|
LD (NDRVVCT),HL ; init new Drive Vector (neg) with 0xFFFF
|
|
LD HL,(ENVADR) ; get ENV addr
|
|
LD DE,52 ; offset to Drive Vector
|
|
ADD HL,DE ; move ptr
|
|
PUSH HL ; ..and save it
|
|
LD E,(HL) ; get Drive Vector in DE
|
|
INC HL
|
|
LD D,(HL)
|
|
LD A,(DRV1ST) ; get # of first drive
|
|
CALL MKDRMSK ; get bit mask for first drive
|
|
LD C,L ; ..and move it to BC
|
|
LD B,H
|
|
LD A,(DRV2ND) ; get # of second drive
|
|
CALL MKDRMSK ; get bit mask for second drive
|
|
EX DE,HL ; ..and move it to DE
|
|
CALL MKVCMSK ; update new Drive Vector for first drive
|
|
PUSH BC ; swap BC and DE
|
|
PUSH DE
|
|
POP BC
|
|
POP DE
|
|
CALL MKVCMSK ; update new Drive Vector for second drive
|
|
|
|
; (Stack) = addr of Drive Vector in ENV - PUSH HL
|
|
; HL= current Drive Vector, DE= bit mask first drive, BC= bit mask second drive
|
|
EX DE,HL ; swap regs (save current Drive Vector in DE)
|
|
ADD HL,BC ; add/merge bit masks
|
|
EX (SP),HL ; put merged mask on stack - used by SWAPDRX
|
|
; get addr of Drive Vector in ENV
|
|
PUSH HL ; ..and save it
|
|
EX DE,HL ; swap regs back (current Drive Vector in HL)
|
|
LD BC,(PDRVVCT) ; get new Drive Vector (pos)
|
|
LD DE,(NDRVVCT) ; and (neg)
|
|
LD A,L ; low byte of current Drive Vector
|
|
AND E ; reset bit (neg)
|
|
OR C ; set bit (pos)
|
|
LD E,A ; ..and store result in E
|
|
LD A,H ; high byte of current Drive Vector
|
|
AND D ; reset bit (neg)
|
|
OR B ; set bit (pos)
|
|
LD D,A ; ..and store result in D
|
|
POP HL ; get addr of Drive Vector in ENV
|
|
LD (HL),E ; store new Drive Vector (low byte)
|
|
INC HL
|
|
LD (HL),D ; ..and high byte
|
|
CALL GQFLAG
|
|
OR A ; check quiet flag
|
|
JR NZ,SWAPDRX ; ..if quiet mode, skip over
|
|
CALL VPRINT
|
|
DEFB ' ...Drives '
|
|
DEFB 0
|
|
LD A,(DRV1ST) ; get # of first drive
|
|
ADD A,'A' ; make ascii letter
|
|
CALL COUT ; ..and display it
|
|
CALL VPRINT
|
|
DEFB ': and '
|
|
DEFB 0
|
|
LD A,(DRV2ND) ; get # of second drive
|
|
ADD A,'A' ; make ascii letter
|
|
CALL COUT ; ..and display it
|
|
CALL VPRINT
|
|
DEFB ': exchanged',CR,LF
|
|
DEFB 0
|
|
|
|
; exit function
|
|
SWAPDRX: POP DE ; restore merged bit masked 1st+2nd drive
|
|
LD C,37 ; BDOS fn #37 Reset Drive(s)
|
|
CALL CPMBDOS
|
|
JP EXIT
|
|
|
|
|
|
M$ABORT: CALL VPRINT
|
|
DEFB ' ...aborting...',CR,LF
|
|
DEFB 0
|
|
JP EXIT
|
|
|
|
|
|
;::::: HELP SCREEN
|
|
|
|
HELP: CALL VPRINT
|
|
DEFB CR,LF,1
|
|
DEFB 0
|
|
CALL PPRGNAM
|
|
CALL VPRINT
|
|
DEFB 2,' exchanges the logical definition '
|
|
DEFB 'of two physical disk drives',CR,LF
|
|
DEFB ' or partitions. Drive letters must be '
|
|
DEFB 'in the range of "A"-"P".',CR,LF
|
|
DEFB ' The program is re-executable under '
|
|
DEFB 'ZCPR with the "GO" command',CR,LF,LF
|
|
DEFB ' Syntax: '
|
|
DEFB 0
|
|
CALL PPRGNAM
|
|
CALL VPRINT
|
|
DEFB ' <Drv1>[:] <tab| |,> <Drv2>[:]',CR,LF,LF
|
|
DEFB ' Examples:',CR,LF,' '
|
|
DEFB 0
|
|
CALL PPRGNAM
|
|
CALL VPRINT
|
|
DEFB ' A: E: - Exchange E drive with A',CR,LF
|
|
DEFB ' '
|
|
DEFB 0
|
|
CALL PPRGNAM
|
|
CALL VPRINT
|
|
DEFB ' D,H - Exchange D drive with H',CR,LF
|
|
DEFB ' '
|
|
DEFB 0
|
|
CALL PPRGNAM
|
|
CALL VPRINT
|
|
DEFB ' // - display this message',CR,LF
|
|
DEFB 0
|
|
|
|
|
|
;::::: EXIT PROGRAM
|
|
|
|
EXIT: LD SP,(STACK) ; restore stack
|
|
RET ; ..and return to system
|
|
|
|
|
|
;::::: SUPPORT FUNCTIONS
|
|
|
|
; "called" as a pseudo-routine that returns to caller
|
|
; in: HL= target addr
|
|
JUMPHL: JP (HL) ; jump to addr in HL regs
|
|
|
|
|
|
; parse nul-terminated string skipping separator chars
|
|
; then fall through and check/convert drive letter
|
|
; in: HL= ptr to string
|
|
; out: A= drive number (or <NUL> if invalid letter)
|
|
; HL= ptr to byte after end of string
|
|
; C-Flag set if <NUL> (end of string) reached
|
|
FINDDRV: LD A,(HL) ; get byte
|
|
INC HL ; move ptr fwd
|
|
OR A ; check if <NUL> (zero) = end of string
|
|
SCF ; prepare status indicator (C-Flag set)
|
|
RET Z ; ..if <NUL> byte, return
|
|
CALL EVALSEP ; check if byte is a separator
|
|
JR NC,FINDDRV ; ..if so, get next char
|
|
; else, fall through and check if letter is valid
|
|
|
|
|
|
; evaluate if letter is a valid drive (A..P) and return as number
|
|
; in: A= letter to check
|
|
; out: A= drive number
|
|
; C-Flag set if error, NC= ok
|
|
EVALDRV: CP 'A' ; is it lower than ascii 'A' ?
|
|
RET C ; ..return with C-Flag already set
|
|
CP 'P'+1 ; is it greater than ascii 'P' ?
|
|
CCF ; ..reverse C-Flag to set correct status
|
|
RET C ; and return
|
|
SUB 'A' ; else, convert to number
|
|
RET
|
|
|
|
|
|
; evaluate char in register A whether it is a separator
|
|
; (space, comma, colon, tab, zero)
|
|
; in: A= char
|
|
; out: C-Flag set if not separator, NC= char is separator
|
|
EVALSEP: CP ' ' ; is it <SP> ?
|
|
RET Z
|
|
CP ',' ; Comma ?
|
|
RET Z
|
|
CP ':' ; Colon ?
|
|
RET Z
|
|
CP TAB ; <TAB> ?
|
|
RET Z
|
|
OR A ; <NUL> (zero) ?
|
|
RET Z
|
|
SCF ; set C-Flag
|
|
RET
|
|
|
|
|
|
; make bit mask for specified drive #
|
|
; position of 1-bit represents drive in 16-bit word (similar to Drive Vector)
|
|
; in: A= drive number
|
|
; out: HL= bit mask
|
|
MKDRMSK: LD HL,1 ; set bit 0
|
|
INC A ; ahead of loop, increase A
|
|
MKDRMS0: DEC A ; decrease A
|
|
RET Z ; ..if zero, finished
|
|
ADD HL,HL ; *2 (shift 1-bit to next position)
|
|
JR MKDRMS0 ; loop
|
|
|
|
|
|
; make bit masks for new Drive Vector
|
|
; maintaining a positive (bits set) map, and a negate version (bits reset)
|
|
; in: HL= current Drive Vector (from ENV)
|
|
; BC= bit mask w/ old position
|
|
; DE= bit mask w/ new position
|
|
MKVCMSK: PUSH BC ; save regs
|
|
LD A,B
|
|
AND H ; mask high byte
|
|
LD B,A ; ..and store result back in B
|
|
LD A,C
|
|
AND L ; mask low byte
|
|
OR B ; check if invalid (= zero), ie. not mapped in Vector
|
|
POP BC ; restore regs
|
|
JR Z,MKVCMS0 ; if invalid drive, jump
|
|
|
|
; drive at new position exists in Drive Vector - set bit
|
|
PUSH HL
|
|
LD HL,(PDRVVCT)
|
|
LD A,H ; high byte first
|
|
OR D ; ..merge with new position
|
|
LD H,A ; and store result back in H
|
|
LD A,L ; low byte
|
|
OR E ; ..merge with new position
|
|
LD L,A ; and store result back in L
|
|
LD (PDRVVCT),HL ; save final result
|
|
POP HL
|
|
RET
|
|
|
|
; drive at new position does _not_ exist in Drive Vector - reset bit
|
|
MKVCMS0: PUSH HL
|
|
LD HL,(NDRVVCT)
|
|
LD A,D ; get high byte of new position
|
|
CPL ; invert it
|
|
AND H ; reset corresponding bit
|
|
LD H,A ; ..and store result in H
|
|
LD A,E ; get low byte of new position
|
|
CPL ; invert it
|
|
AND L ; reset corresponding bit
|
|
LD L,A ; ..and store result in L
|
|
LD (NDRVVCT),HL ; save final result
|
|
POP HL
|
|
RET
|
|
|
|
|
|
; get Quiet Flag from Z3 Environment
|
|
; in: -
|
|
; out: A= Quiet Flag, defaults to A= 0 (not quiet)
|
|
GQFLAG: LD HL,(ENVADR) ; get ENV addr
|
|
LD A,H ; check if invalid (= zero)
|
|
OR L
|
|
RET Z ; ..if so, return
|
|
LD A,40 ; else, move ptr forward
|
|
CALL ADDHLA ; to Quiet Flag
|
|
LD A,(HL) ; get value
|
|
RET ; ..and return
|
|
|
|
|
|
; 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
|
|
|
|
|
|
; 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 VPRINT ; else, display default
|
|
DEFB 'BPSWAP'
|
|
DEFB 0
|
|
RET
|
|
|
|
|
|
;:::::::::::::::::::::::::::::::::::::::::::::::::::::
|
|
; VLIB - 0x0536
|
|
; Z3LIB - 0x0757
|
|
; SYSLIB - 0x0805
|
|
; end addr 0x0854 (begin DSEG)
|
|
;:::::::::::::::::::::::::::::::::::::::::::::::::::::
|
|
|
|
|
|
;::::: RAM STORAGE
|
|
|
|
DSEG
|
|
|
|
PDRVVCT: DEFW 0 ; new Drive Vector
|
|
; (positive notation, bit _set_ for existing drives)
|
|
NDRVVCT: DEFW 0 ; new Drive Vector
|
|
; (negative notation, bits _reset_ for existing drives)
|
|
BPBASE: DEFW 0 ; B/P Bios base addr
|
|
DRV1ST: DEFB 0 ; # of first drive
|
|
DRV2ND: DEFB 0 ; # of second drive
|
|
|
|
DEFS 40H ; room for stack
|
|
STACK: DEFW 0 ; stack storage location
|
|
|
|
END
|
|
|
|
|
|
;************************************************************************
|
|
; Remarks jxl:
|
|
; BPSWAP.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 BPSWAP.COM, i.e. no changes to the source were made. There
|
|
; seems to be one bug (marked with "##### BUG") at the beginning of the
|
|
; program.
|
|
;************************************************************************
|