home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
simtel
/
sigm
/
vols000
/
vol054
/
i-o-cap.asm
< prev
next >
Wrap
Assembly Source File
|
1984-04-29
|
19KB
|
745 lines
; I/O-CAP.ASM Version 1.0 as of September 13, 1981
;
; By: Kelly Smith, CP/M-Net
;
; Version 1.0: Initial release by Kelly Smith
;
; Note: Please append any changes to I/O-CAP with a
; 'Version Log' and change comments (your name would be nice
; too) top-down from the initial Version 1.0 release.
;
; Running I/O-CAP (the first time) will relocate the
; CONIN, and CONOUT jump vectors to high memory and then ALL
; subsequent input or output (depending on conditional
; assembly switches) will be buffered (16 sectors) for
; eventual output to disk with a filename and type called
; USER.LOG. This file will then be updated as long as I/O-CAP
; is active in the system. Note: I/O-CAP may be made
; inactive by just type I/O-CAP again, to toggle it off (or
; on).
;
; I/O-CAP requires an unusually LARGE amount of
; relocation memory, because it must buffer the entire CP/M
; system image to high memory to save and restore all CCP and
; BDOS pointers as it captures each 16 sector block of console
; input or output...crude, but the ONLY other wait to do this
; would be to 'hot-patch' the CCP and BDOS 'on-the-fly' and
; that get's just a bit gruesome from a UNIVERSAL
; applications standpoint...It's much easier to just SYSGEN a
; smaller CP/M system than your actual maximum available
; memory, and then have I/O-CAP relocate itself above your
; BIOS. If anyone has a better way to do this WITHOUT this
; relocation crap, I would be eager to see it...I just got
; frustrated with trying to figure out what CP/M was doing
; internally to pursue it further...local stacks, pointers,
; etc.,...ARGH...so I took the easy way out.
;
; Thanks to Mike Karas for discovering the BDOS CALL
; contention problems when the BDOS stack was not saved and
; restored when saving the console buffer to disk.
;
; - Applications -
;
; (1) Don't have a printer, and want 'Hard Copy' of a user
; dialog with the system? Use I/O-CAP to creat it as a disk
; file...for instance, when making patches into 'uncertain'
; areas of the system with DDT (or SID, or whatever) you can
; keep a running history on disk of the patches AND their
; effects.
;
; (2) Need to show example of console dialog for an article
; or book and you don't want to 'hand type' it in? Use I/O-CAP
; to creat the examples for you, and edit the dialog to suit
; your needs.
;
; (3) Need to know all the events in 'history form' leading
; to some bizarre system blow-up? Use I/O-CAP to record that
; history for you (but only if the blow-up is recoverable by
; NOT COLD BOOTING the system).
;
; (4) Want to (secretly) monitor the activities of other
; users of the system? Use I/O-CAP to record user console
; input, and check in from time-to-time to look at the system
; activity. Used in conjunction with BYE on a remote CP/M
; system, you can finally figure-out how that 'twit' clobbers
; your system from 3000 miles away...and NOT fill a room full
; of paper by logging all input to your printer. Note: Run
; I/O-CAP first, then BYE.COM to 'grab' the vectors set-up by
; the I/O-CAP program.The capture of incoming data will appear
; to be transparent to the user, with a slight pause when it
; updates the USER.LOG file...but this only happens every 2048
; character entrys, so it should generally go un-noticed.
;
; - Using I/O-CAP -
;
; Examine the various conditional assembly switches and
; set TRUE or FALSE depending on your requirements with your
; editor (ED.COM). Then assemble with ASM.COM (or MAC.COM),
; load it to creat a .COM file and then run. The conditional
; assembly switches allow the following options:
;
; (1) DEBUG - I/O-CAP runs at 8000 Hex...about right for
; most small applications programs that use memory from 100 to
; 7FFF Hex in a 56K CP/M system. If FALSE, I/O-CAP runs above
; a 48K CP/M system (C800 Hex), with no restrictions on
; applications programs.
;
; (2) QUIET - If FALSE, rings the console bell just before it
; writes 2048 bytes of captured console INPUT or OUTPUT.
;
; (3) ERRDISP - If TRUE, I/O-CAP will display an 'OOPS...'
; message on the console if the disk or directory is full.
;
; (4) INPUT - If TRUE, only console keyboard INPUT is
; captured. Note: OUTPUT must be FALSE if INPUT is TRUE.
;
; (5) OUTPUT - If TRUE, both console keyboard INPUT and
; OUTPUT will be captured...uses 'gobs' of disk storage if you
; let I/O-CAP run for any length of time. Note: INPUT must be
; FALSE if OUTPUT is TRUE.
;
; (6) QUIT - If TRUE, when a Control-Z and Carriage Return are
; entered at the console keyboard, I/O-CAP will append the
; USER.LOG file with a physical end-of-file (i.e., no further
; data will be displayed in USER.LOG although it may be
; physically appended to it)...Note: You must type I/O-CAP<cr>
; to CLOSE the current USER.LOG, and reset the disk to normal
; R/W status. Failure to do so will result in a R/O BDOS
; Error on any subsequent attempt to write to the disk by
; means other than I/O-CAP.
;
; (7) SYSLOG - If TRUE, creates USER.LOG as a $SYS (invisible
; to directory) file, so that 'secrecy' is maintained when
; capturing user input...be sure and rename USER.LOG to your
; 'private' name, or replace the TYPE command with MLIST.COM.
;
; Please send any changes, 'bug' reports, suggestions,
; comments, gripes or bitches to the CP/M-Net system, (805)
; 527-9321...have fun with this program. It's in the public
; domain, but NOT TO BE USED for COMMERCIAL BENEFIT.
;
; Best regards,
;
; Kelly Smith, CP/M-Net
;
;
;
; define TRUE/FALSE assembly parameters
;
true equ -1 ; define TRUE
false equ not true; define FALSE
debug equ true ; define DEBUG
quiet equ false ; define QUIET (ring BELL, if not true)
errdisp equ true ; define ERRDISP (display errors, if true)
quit equ true ; define QUIT (EOF, if Control-Z found)
syslog equ false ; define SYSLOG (make USER.LOG a $SYS file)
;
; >>> Note: only one of the following two assembly switches may be true <<<
;
input equ false ; define INPUT (I/O-CAP console input)
output equ true ; define OUTPUT (I/O-CAP console output)
;
if DEBUG
dest equ 08000h ; running location of code
endif ; DEBUG
if not DEBUG
dest equ 0c800h ; running location of code
endif ; DEBUG
;
; BDOS entry point and function codes
;
base equ 0 ; <<-- set to offset of CP/M for your
; system, standard systems are 0, some
; 'alternate' systems are 4200H
;
bdos equ base+5
resdsk equ 13 ; reset disk system
offc equ 15 ; open file
cffc equ 16 ; close file
dffc equ 19 ; delete file
rrfc equ 20 ; read record
wrfc equ 21 ; write record
mffc equ 22 ; make file
sdma equ 26 ; set dma address
;
; secondary FCB field definitions
;
fn equ 1 ; file name field (rel)
ft equ 9 ; file type field (rel)
ex equ 12 ; file extent field (rel)
frc equ 15 ; file record count (rel)
nr equ 32 ; next record field (rel)
;
; ASCII control characters
;
cr equ 0dh ; carriage return
lf equ 0ah ; line feed
bel equ 07h ; bell signal
;
; This program runs up in high ram. It gets there, by being
; moved there when 'I/O-CAP' is typed. Change the following
; equate to an area in your high memory where this program may
; patch itself in. Approximate memory requirements: 2k bytes
; or more, depending upon the options selected. a marker has
; been placed at the end to deliberately print an error
; message during assembly in order to determine the actual
; ending address of the program. The error message will not
; affect the assembly. make sure you have memory available up
; to the address shown.
;
org base+100h
;
; Move 'I/O-CAP' program up to high ram and jump to it
;
lxi h,0 ; save old stack pointer
dad sp
shld oldstk
lxi sp,stack; make a new stack pointer
lxi h,tbuf ; set pointer to tbuf
shld ptr
lxi h,0 ; set size = 0
shld size
lhld base+1 ; get BIOS pointer
lxi d,5 ; add bias to console status address
dad d
mov d,m ; save in [d]
lhld newjtbl+1 ; see if vector addresses active
mov a,h ; been patched by previous execution?
cmp d
jz unpatch ; un-patch, if so
lxi b,pend-start+1 ; number of bytes to move
lxi h,dest+pend-start+1 ; end of moved code
lxi d,source+pend-start ; end of source code
;
mvlp ldax d ; get byte
dcx h ; bump pointers
mov m,a ; new home
dcx d
dcx b ; bump byte count
mov a,b ; check if zero
ora c
jnz mvlp ; if not, do some more
pchl ; do it, to it...
;
source equ $ ; boundary memory marker
;
offset equ dest-source ; relocation amount
;
; The following code gets moved to high ram located at "dest",
; where it is executed. C A U T I O N : if modifying anything
; in this program from here on: ALL labels must be of the form:
;
; label equ $+offset
;
; ...in order that the relocation to high ram work
; successfully. Forgetting to specify '$+offset' will cause
; the program to jmp into whatever is currently in low memory,
; with unpredictable results. Be careful....
;
start equ $+offset
;
; patch in the new jump table (saving the old)
;
patch equ $+offset
call tbladdr ; calc [hl] = CP/M jmp table
lxi d,vcstat ; point to save location
call move ; move it
;
; now move new jump table to CP/M
;
call tbladdr ; calc [hl] = CP/M's jmp table
xchg ; move to de
lxi h,newjtbl ; point to new
call move ; move it
lxi h,active$message
call msgout
lhld oldstk ; get old CP/M stack pointer
sphl
ret
;
unpatch equ $+offset
call reset ; reset disk in case it's R/O
lxi h,inactive$message
call msgout
call tbladdr ; [hl] = CP/M's jmp table
xchg ; move to de
lxi h,vcstat ; get saved table
call move ; move orig back
lhld oldstk ; get old CP/M stack pointer
sphl
ret
;
; calculate [hl] = CP/M's jump table, [b] = length
;
tbladdr equ $+offset
lhld base+1 ; get BIOS pointer
inx h ; ..skip
inx h ; ..to
inx h ; ..console status
mvi b,9 ; move console jump vectors
ret
;
; move [hl] to [de], length in [b]
;
move equ $+offset
mov a,m ; get a byte
stax d ; put at new home
inx d ; bump pointers
inx h
dcr b ; decrement byte count
jnz move ; if more, do it
ret ; if not, return
;
; move [hl] to [de], length in [bc]
;
movecpm equ $+offset
mov a,m ; get a byte
stax d ; put at new home
inx d ; bump pointers
inx h
dcx b ; decrement byte count
mov a,b
ora c
jnz movecpm ; if more, do it
ret ; if not, return
;
msgout equ $+offset
mov a,m ; get character from message string
ora a ; all of string displayed?
rz ; return, if so
inx h ; no, bump pointer for next character
mov c,a ; pass character to 'old' BIOS vector
call conout
jmp msgout ; display next character in message string
;
; This area is used for vectoring calls to the user's CBIOS,
; but saving the registers first in case they are destroyed.
;
constat equ $+offset
push b
push d
push h
call vcstat
pop h
pop d
pop b
ret
;
conin equ $+offset
push b
push d
push h
call vcin
pop h
pop d
pop b
ret
;
conout equ $+offset
push b
push d
push h
call vcout
pop h
pop d
pop b
ret
;
; This is the jump table which is copied on top of the one
; pointed to by location 1 in CP/M
;
newjtbl equ $+offset
jmp constat ; console status test
if INPUT
jmp capture ; console input I/O-CAP routine
endif ; INPUT
if OUTPUT
jmp conin ; console input routine
endif ; OUTPUT
if INPUT
jmp conout ; console output routine
endif ; INPUT
if OUTPUT
jmp capture ; console I/O-CAP output routine
endif ; OUTPUT
;
capture equ $+offset
push h
push d
push b
lxi h,0 ; save old stack pointer
dad sp
shld oldstk
lxi sp,stack; make a new stack pointer
if INPUT
call vcin ; get console input
mov c,a ; save in [c] for 'save'
push psw ; and save on the stack
endif ; INPUT
call save ; save characters in buffer
if INPUT
pop psw ; get console input of the stack
endif ; INPUT
lhld oldstk ; get old CP/M stack pointer
sphl
pop b
pop d
pop h
if OUTPUT
jmp vcout
endif ; OUTPUT
if INPUT
ret
endif ; INPUT
;
save: equ $+offset
lhld size ; size = size + 1
inx h
shld size
lhld ptr
mov m,c
inx h
if INPUT
mov a,c
cpi cr ; carriage return?
jnz notcr
mvi m,lf ; yes, so add line feed because CP/M does not
inx h ; bump pointer, for next time thru
endif ; INPUT
notcr equ $+offset; make label for next instruction...
if QUIT
mov a,c
cpi 'Z'-40h ; control-Z?
jz wtb ; write buffer, if so
endif ; QUIT
shld ptr
lxi d,endmark ; get 'endmark' for buffer top address
mov a,d
cmp h ; getting near the end of buffer yet?
rnz ; if not, just return
mov a,e ; very near the top now, final address loaded?
cmp l
rnz ; if not, just return
if not QUIET
mvi c,bel ; warn user that we need to write to disk
call conout
endif ; QUIET
;
; wtb - write text buffer to disk
;
wtb: equ $+offset
lhld base+6 ; get warm boot address for next bias to CCP
lxi d,0fffah; make bias to CCP
dad d ; add bias to [hl]
lxi b,1600h ; make quantity to move
lxi d,cpmbuf; buffer all of CP/M system
call movecpm ; move it...
call reset ; reset disk in case it's R/O
call open ; attempt to open USER.LOG
inr a ; check CP/M return code
jnz makeok ; USER.LOG already exist?
;
nolog: equ $+offset
;
call make ; make new file
inr a ; check CP/M return code
jnz makeok
if ERRDISP
lxi h,dirful; oops...can't make file, return to CP/M
call msgout
endif ; ERRDISP
jmp base
;
; USER.LOG exists, so set the FCB entry for next append to file
;
makeok: equ $+offset
;
lda fcb+frc ; get record count
sta fcb+nr ; make next record
lhld size ; [de] = tbuf size
xchg
lxi h,dbuf ; top of stack points to dbuf
push h
lxi h,tbuf ; [hl] points to tbuf
;
wtb1: equ $+offset
mvi c,128 ; disk buffer size
;
wtb2: equ $+offset
mov a,m ; fetch next byte of tbuf
inx h
xthl
mov m,a ; store in dbuf
inx h
xthl
dcx d ; size = size - 1
mov a,d ; exit loop if size = 0
ora e
jz wtb3
dcr c ; loop until dbuf full
jnz wtb2
call setdma ; set dma to dbuf
call write ; write full dbuf to disk
push psw ; save possible error code
lda fcb+frc ; get record count
sta fcb+nr ; make next record
pop psw ; get possible error code
ora a ; check CP/M return code
jz nextbuf
if ERRDISP
lxi h,dskful; oops...disk is full
call msgout
endif ; ERRDISP
jmp base
;
nextbuf equ $+offset
;
xthl ; top of stack points to dbuf
lxi h,dbuf
xthl
jmp wtb1 ; loop until end of tbuf
;
wtb3: equ $+offset
pop h ; [hl] points to current place in dbuf
;
wtb4: equ $+offset
mvi m,'Z'-40h ; store eof code
inx h
dcr c ; loop thru rest if dbuf
jnz wtb4
call setdma ; set dma to dbuf
call write ; write last sector to disk
push psw ; save possible error code
lda fcb+frc ; get record count
sta fcb+nr ; make next record
pop psw ; get possible error code
ora a ; check CP/M return code
jz closeup
if ERRDISP
lxi h,dskful; oops...disk is full
call msgout
endif ; ERRDISP
jmp base
;
closeup equ $+offset
;
call close ; clean up act and go home
lxi h,tbuf ; clear text buffer
shld ptr
lxi h,0
shld size
wtb5: equ $+offset
lhld base+6 ; get warm boot address for next bias to CCP
lxi d,0fffah; make bias to CCP
dad d ; add bias to [hl]
lxi b,1600h ; make quantity to move
lxi d,cpmbuf; buffer all of CP/M system
xchg ; swap
call movecpm ; move it...
ret
;
; reset - reset disk
;
reset: equ $+offset
push h
push d
push b
mvi c,resdsk
call bdos
pop b
pop d
pop h
ret
;
; open - open disk file
;
open: equ $+offset
push h
push d
push b
lxi d,fcb
mvi c,offc
call bdos
pop b
pop d
pop h
ret
;
; read - read record from disk file
;
read: equ $+offset
push h
push d
push b
lxi d,fcb
mvi c,rrfc
call bdos
pop b
pop d
pop h
ret
;
; close - close disk file
;
close: equ $+offset
push h
push d
push b
lxi d,fcb
mvi c,cffc
call bdos
pop b
pop d
pop h
ret
;
; delt - delete disk file
;
delt: equ $+offset
push h
push d
push b
lxi d,fcb
mvi c,dffc
call bdos
pop b
pop d
pop h
ret
;
; write - write record to disk
;
write: equ $+offset
push h
push d
push b
lxi d,fcb
mvi c,wrfc
call bdos
pop b
pop d
pop h
ret
;
; make - make new disk file
;
make: equ $+offset
push h
push d
push b
lxi d,fcb
mvi c,mffc
call bdos
pop b
pop d
pop h
ret
;
; setdma - set dma address for disk file
;
setdma: equ $+offset
push h
push d
push b
lxi d,dbuf
mvi c,sdma
call bdos
pop b
pop d
pop h
ret
;
if ERRDISP
dskful: equ $+offset
db cr,lf,bel,'OOPS...disk is full!',0
;
dirful: equ $+offset
db cr,lf,bel,'OOPS...directory is full!',0
endif ; ERRDISP
;
active$message equ $+offset
db ' (Active)',0
;
inactive$message equ $+offset
db ' (Inactive)',0
;
fcb equ $+offset
db 0 ; default drive specifier
if SYSLOG
db 'USER L','O'+80h,'G'
endif ; SYSLOG
if not SYSLOG
db 'USER LOG'
endif ; SYSLOG
db 0,0,0,0,0,0,0,0,0,0
;
pend equ $+offset; end of relocated code
;
; data area
;
ds 128 ; 64 level stack
stack equ $+offset;local stack
;
ptr: equ $+offset
ds 2 ; text buffer pointer
;
size: equ $+offset
ds 2 ; text buffer size
;
; Save the CP/M jump table here
;
vcstat equ $+offset
ds 3
;
vcin equ $+offset
ds 3
;
vcout equ $+offset
ds 3
;
oldstk equ $+offset
ds 2 ; storage for old CP/M stack pointer
;
cpmbuf equ $+offset
ds 1600h ; storage CP/M system image
;
dbuf equ $+offset
ds 128 ; secondary disk buffer address
;
tbuf: equ $+offset
ds 16*128 ; I/O-CAP storage for 16 sectors (2048 bytes)
;
endmark equ $+offset;! ignore error - this marks end of program
;
end