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
/
CPM3
/
BANKSWAP.LBR
/
BANKSWAP.ASM
< prev
next >
Wrap
Assembly Source File
|
2000-06-30
|
11KB
|
493 lines
; BANKSWAP.ASM
;
; A. S. Woodhull 28 June 83
; rev 1 July 85 -- minor editing
; 20 Oct 83
;
; This is designed to be run as a subprogram under DDT
; or SID, in a banked version of CP/M 3.0 The function
; of BANKSWAP is to copy blocks of memory from other
; banks to and from bank 1, using a buffer in the common
; area. Normal DDT or SID functions can then be
; performed, using the copy of the code in bank 1.
; (Of course, you cannot trace through program segments
; that switch banks or access memory mapped I/O in
; another bank.)
;
; BANKSWAP must reside in the common area of memory, and
; a buffer area through which data can be copied must
; also be present in the common area. BANKSWAP is to be
; assembled as a Resident System Extension (RSX), which
; will automatically be relocated to the top of the TPA.
; It is assumed that the common area is large enough to
; allow an RSX to fit--if this is not true BANKSWAP will
; not work in its present form.
;
; Because the location of BANKSWAP in memory is not fixed
; a jump through a fixed location is set up when the BANK-
; SWAP code is installed. In this version the RST 5 vector
; at 28H is used, but any convenient location on page zero
; may be used.
;
; Under CP/M 3 direct access of BIOS routines is generally
; to be avoided by user programs, since BIOS routines may
; have expectations about which bank is selected when they
; are called. We will, however, do bank switching through
; the BIOS selmem routine. For generality we will use the
; BIOS vector at location 1.
;
true: equ 0ffffh
false: equ not true
;
alone: equ false ;make false if attached to DDT/SID
;
biosv: equ 1 ;address of wboot in BIOS found here
selmem: equ 4eh ;offset from wboot
;
buflen: equ 100h ;move block size
;
; default parameters
movcnt: equ 1000h ;to move 16 pages (4K) at a time
b0dft: equ 100h ;start of block in bank 0
b1dft: equ 100h ;start of block in bank 1
;
; zero page addresses
bdos: equ 5 ;bdos entry point
rstv: equ 28h ;RST 5 used as entry vector
;
; BDOS functions used
conin: equ 1 ;get a char
printf: equ 9 ;print a string
rdbuf: equ 10 ;read a line
;
; This is standard prefix for a Resident System Extension
; see CP/M 3 Programmer's Guide, 1st ed., sec. 4.4, p.168
;
serial: db 0,0,0,0,0,0
start: jmp install ;one-time routine
next: jmp 0 ;altered at installation
prev: dw 0
;
if alone
rmvflg: db 0 ;keep this in memory
else
rmvflg: db 0ffh ;remove when main program ends
endif
;
nonbnk: db 0 ;banked system
db 'BANKSWAP'
loader: db 0
db 0,0
; *** note: If BANKSWAP is to be attached directly to SID.COM
; or DDT.COM then make rmvflg 0ffh to force removal
;
bankexam:
lxi h,0 ;get stack pointer
dad sp ;...and hold it for return
shld holdsp ;...reset SP to a location
lxi sp,locstk ;...in common memory
;
; Main loop--exit by Quit or Remove command
bankex2:
call prompt ;this returns address of sub
call doit ;do subroutine addressed by HL
jmp bankex2
;
prompt: lxi d,menu ;get ready for menu
lda quietflag
ora a ;suppress menu?
jz prmpt2
prmpt1: lxi d,query ;set for prompt only
prmpt2: mvi c,printf
call bdos
; Get a character
mvi c,conin
call bdos
call crlf
; make upper case, reject non-alpha
ani 5fh
cpi 'A'
jc prmpt1 ;ask again if invalid
cpi 'Z'+1
jnc prmpt1 ;ask again if invalid
; find match in alphtbl
lxi b,altblen ;length of table
lxi h,alphtbl+altblen ;work back
try: cmp m
jz match
dcx h
dcr c ;count down
jnz try
; if c= 0 no match found. Now form address
match: lxi h,addrtbl
dad b ;add offset
dad b ;again, 2 bytes per table entry
; get the command address
mov a,m
inx h
mov h,m
mov l,a
ret ;HL has action address
;
doit: pchl ;call here to use action address
;
getbank:
lhld b0start ;setup addresses
shld source
lhld b1start
shld dest
lda length+1 ;low byte ignored
sta count
; page by page take a chunk of bank 0 to buffer, then move
; to bank 1 destination
get2:
di ;be sure no interrupts
mvi a,0
lhld selmv
call doit
call getbuf
mvi a,1
lhld selmv ;point to BIOS selmem routine
call doit
ei ;interrupts safe again
call putbuf
lxi d,buflen
lhld source
dad d
shld source
lhld dest
dad d
shld dest
lxi h,count ;repeat for required # of pages
dcr m
jnz get2
ret
;
putbank:
lhld b1start
shld source
lhld b0start
shld dest
lda length+1 ;low byte ignored
sta count
; page by page, move bank 1 data to buffer, then move it
; to bank 0
put2:
call getbuf
di ;be sure no interrupts
mvi a,0
lhld selmv
call doit
call putbuf
mvi a,1
lhld selmv
call doit
ei ;interrupts safe again
lxi d,buflen
lhld source
dad d
shld source
lhld dest
dad d
shld dest
lxi h,count
dcr m
jnz put2
ret
; source to buffer
getbuf:
lhld source
lxi d,buffer
jmp pb1
; buffer to dest
putbuf:
lhld dest
lxi d,buffer
xchg
; common code for getbuf and putbuf
pb1: lxi b,buflen
; move (BC) bytes from (HL) to (DE) (could use BIOS move
; routine for this)
move: mov a,m
stax d
inx h
inx d
dcx b
mov a,b
ora c
jnz move
ret
;
; Go back to SID/DDT
quit: lxi d,qmsg ;say how to get back
mvi c,printf
call bdos
lhld holdsp ;restore stack pointer
sphl
rst 7 ;back to DDT or SID
;
if alone
; Set for removal on termination of SID or DDT
remove: lxi h,rmvflg ;set the remove flag
mvi m,0ffh ;...in the RSX prefix
lxi h,rstv ;then wipe out the entry
mvi m,0ffh ;...JMP with an RST 7
rst 7 ;leave via the debugger
endif ;alone
;
; Set up addresses for move, also set up length
adset: lxi d,b0id ;tell current bank 0 addr
mvi c,printf
call bdos
lhld b0start ;get the address
call addro ;and print it
lxi h,b0start
call update ;enter hex to (HL)
lxi d,b1id ;do it again for bank 1
mvi c,printf
call bdos ;tell
lhld b1start
call addro
lxi h,b1start
call update ;get new address, if any
; Can fall through from adset or enter directly here to set
; length of block moved
lnset: lxi d,lnmsg ;tell current length
mvi c,printf
call bdos
lhld length
call addro
lxi h,length
call update ;offer to change it
call crlf
ret
;
; Toggle menu off/on
xpert: lda quietflag
cma ;toggle
sta quietflag
ret
;
; Get here on invalid command
na: lxi d,namsg ;say can't do it
mvi c,printf
call bdos
ret
;
crlf: push b
push d
push h
push psw
lxi d,crlfstring
mvi c,printf
call bdos
pop psw
pop h
pop d
pop b
ret
;
; get string, do nothing if null, else convert, store at (HL)
update:
push h ;save the location
; loop back here if input is not valid
upd1: lxi h,0 ;initial buffer
shld inbuf+1
lxi d,chquery ;say what's up
mvi c,printf
call bdos
lxi d,inbuf
mvi c,rdbuf ;read console until <ret>
call bdos
call crlf
lda inbuf+1 ;get length of hex string
ora a ;check for 0 length input
jnz convert
; null string, go back
pop h ;retrieve value at entry
ret
;
; Convert the hex string in the buffer to binary
convert:
lxi h,0 ;start with a zero
mov b,a ;hold length in B
lxi d,inbuf+2
conv2: ldax d ;get first (or next) char
inx d
cpi 60h
jc conv3
ani 5fh ;make lower case if necessary
conv3: sui '0'
jm upd1 ;must be valid hex, 0..9, A..F
cpi 0ah
jc num ;jump if a good numeric
sui 7
cpi 0ah
jc upd1 ;error if not good alpha
cpi 10h
jnc upd1 ;error if not good alpha
num: dad h ;multiply current val by 16
dad h
dad h
dad h
add l ;add new least significant digit
mov l,a
dcr b ;countdown the digits
jnz conv2
xchg ;result to DE
pop h ;HL at entry says where to it
mov m,e
inx h
mov m,d
ret
;
; Print HL as hex
addro:
push d
push h
xchg
lxi h,hexstr ;where to build string
mov a,d
call byte ;get A as 2 ASCII chars at (HL)
mov a,e
call byte ;again, low byte
lxi d,hexstr
mvi c,printf
call bdos ;print it
pop h
pop d
ret
;
; Convert byte to hex ASCII chars, put at (HL)
byte: push psw
rar ;get high nybble
rar
rar
rar
call nybble
pop psw ;fall through for low nybble
; nybble makes 1 char, advances output pointer
nybble: ani 0fh
adi '0'
cpi 3ah
jc nput
adi 7
nput: mov m,a
inx h
ret
;
; Acceptable command inputs go in this table
alphtbl:
db 0 ;dummy for no match
db 'A'
db 'G'
db 'L'
db 'P'
db 'Q'
;
if alone
db 'R'
endif ;alone
;
db 'X'
altblen: equ $-alphtbl
; addresses of action routines, same order as alphtabl
addrtbl:
dw na ;not available
dw adset ;address set
dw getbank
dw lnset ;length set
dw putbank
dw quit
;
if alone
dw remove
endif
;
dw xpert ;expert mode, no menu
;
menu: db 'Bankswap 1.0 by A. S. Woodhull 10/20/83',0dh,0ah
db 'functions available:',0dh,0ah
db ' A...set move Addresses',0dh,0ah
db ' G...Get alternate bank',0dh,0ah
db ' L...set move Length',0dh,0ah
db ' P...Put alternate bank',0dh,0ah
db ' Q...Quit to SID or DDT',0dh,0ah
;
if alone
db ' R...Remove BANKSWAP',0dh,0ah
endif
;
db ' X...eXpert mode (no menu)',0dh,0ah
query: db 0dh,0ah,'?? $'
namsg: db '...function not available.'
crlfstring:
db 0dh,0ah,'$'
b0id: db 'Bank 0 addr: $'
b1id: db 'Bank 1 addr: $'
lnmsg: db 'Length is now $'
chquery:
db 'Change to? (CR to keep): $'
hexstr ds 4
db 'H',0dh,0ah,'$'
qmsg: db 'Re-enter BANKSWAP from DDT or SID by "G28"'
db 0dh,0ah,'$'
;
quietflag:
db 0 ;initialized off
inbuf: db 8 ;max length of buffer
ds 9
;
; default parameters: alter by Set and Length commands
b0start: dw b0dft
b1start: dw b1dft
length: dw movcnt
;
; One-time routine, on 1st BDOS call intercepted
install:
push b ;keep everything as it was
push d ;...so BDOS function can be
push h ;...completed
push psw
; set up restart vector for re-entry
mvi a,0c3h ;a JMP instruction
sta rstv
lxi h,bankexam
shld rstv+1
; set up address of BIOS routine accessed directly
lhld biosv ;find where BIOS is
lxi d,selmem ;...and add offset
dad d
shld selmv
; then patch the RSX prefix to prevent reinstallation
lhld next+1
shld start+1
; tell 'em we're ready
mvi c,printf
lxi d,imsg
call next
pop psw ;continue with the task that
pop h ;...was so rudely interrupted
pop d
pop b
jmp next
;
imsg: db 'BANKSWAP loaded. To access from DDT or '
db 'SID type "G28"'
db 0dh,0ah,'$'
;
; reuse installation code area for stack space
locstk:
;
; uninitialized storage
selmv: ds 2 ;used for BIOS call to selmem
source: ds 2 ;for moves to buffer
dest: ds 2 ;for moves from buffer
count: ds 1 ;blocks to move
holdsp: ds 2 ;stack pointer from DDT or SID
;
buffer:
ds buflen
endinstall:
push b ;keep everything as it was
p