;                   TOON12.ASM
;A screen dump program: puts a watchdog between
;the conin jmp and CONIN itself, which simply passes
;along normal chars, but writes the screen to disk
;when it detects ^_.  The screen is written by
;bank-switching to video memory through rdvid,
;which alters the bank switch bit from the system 
;port to read bank 1 memory (video memory + prom)
;directly.  If TOON, the carTOON file for screens,
;already exists, the new screen is added on the end.
;
;In version 1.1, the sub-CCP address in high memory
;where TOON goes is hard coded, but the BDOS jump
;is altered by TOON to point to TOON's lowest addr.
;The TOON watchdog's first location is then a jump
;to the original entry point in the BDOS. Therefore, 
;load TOON first, and later programs can adjust
;their use of the TPA using the altered BDOS addr.
;
;Version 1.2 adds the deletion of space chars in
;the 80th column to clean up display on 80 char
;screens, and other minor changes.
;                      v1.2
;(c)June, 1985
;Mike Yarus * 2231 16th Street * Boulder, CO 80302
;
;TOON.ASM, TOON.COM, and TOON.DOC are released into
;the public domain, and may be used freely for any
;non-commercial purpose.  They may not be sold or
;used to enhance the value of another product 
;without the permission of the author.
;
;
bdos	equ 	0005h
wmboot	equ	0000h
cr	equ	0Dh
lf	equ	0Ah
eof	equ	1Ah
;
;the following equate is for the conin bios addr
;and the original bdos jump address
;
			;a dummy address (= 0000)
dmmy$ad	equ	wmboot	;to hold a place for real
			;address written at run time
;
;equates for the move into high memory
;
offs	equ	280h	;distance below ccp to load
ccp	equ 	0E400h	;bios-1600h=ccp start, std 64k
newstrt	equ	ccp-offs;new start, offs-et below ccp
;
	org	0100h
;
;get the bdos address and store it at bottom of dump
;
	lhld	0006h	;load bdos jmp addr
	shld	bds$trgt+1	;store at new site
	lxi	h,newstrt	;get bottom of dump
	shld	0006h	;redirect bdos jmp to dump
;
;get the address of the bios
;
	lhld	0001h	;load bios + 3
	dcx h 
	dcx h
	dcx h		;hl -> bios address
	shld	bios	;store start of bios jumps
;
;find the phrase "Warm Boot"
;
	lxi	d,0004h	;offset to warm boot address
	dad	d	;hl points to warmst addr
	call 	getwd	;warmst routine addr -> hl
;
	mvi	c,'W'	;search for 'W'
	call	find0	;begin at warmbt routine
;
	lxi	d,newmsg;address in hl from find
	call	fill0	;overwrite "Warm Boot"
;
;redirect the conin jump in the bios
;
	lhld	bios	;load addr of bios
	lxi	d,000ah	;offset to conin addr addr
	dad	d	;addr of conin routine addr
	shld	cinadd	;store it, must overwrite
	call	getwd	;conin address into hl
	shld	conin	;store address of conin routine
;
	lhld 	cinadd	;addr of conin addr
	lxi	d,dump	;addr of dump in de
	call	putwd	;alter conout addr to dump addr
;
;write in the calculated address of the conin routine
;
	lhld 	conin		;load conin addr
	xchg			;conin addr-> de
	lxi	h,cin$trgt+1	;addr for call to conin
	call 	putwd		;write addr in de to hl
;
	jmp 	launch0	;move the business end under ccp
;
;fills memory at h with string stored at d, term by $
;used to fill open memory at fixed, dedicated location
;
fill0	mvi	c,'$'
	ldax	d	;get char into a
	cmp	c	;is the new char the end char?
	rz		;if so, have done string
	mov	m,a	;if not, store next char
;
	inx	d
	inx	h	;increment both string counters
	jmp	fill0	
;
;find the character in c after the address supplied in h
;returns the address of the char in h
;
find0	mov	a,m	;get char in a
	cmp	c	;is it the target char?
	rz		;char found, address in h
	inx	h	;inc address and
	jmp	find0	;get the next char
;
;get word at addr in hl into hl: orig addr + 1 -> de
;
getwd	mov	a,m	;get low order byte
	mov	e,a	;move to e
	inx	h	;reset for high order byte
	mov	a,m	;get it in a
	mov	d,a	;put high order byte in d
	xchg		;exchange number and address
	ret
;
;put word in de to address in hl
;
putwd	mov	a,e	;lsb to a
	mov	m,a	;lsb to address in hl
	inx	h	;address for msb
	mov	a,d	;msb to a
	mov	m,a	;msb to address
	ret
;
;the routine to move the code below it to high memory
;
launch0	lxi	b,endmv-begin+1	;bytes to move
	lxi	d,begin	;start addr for move
	lxi	h,newstrt	;new start after move
;
launch	ldax	d	;get a char from lo mem
	mov	m,a	;put char in high mem
	inx	d	;increment lo addr
	inx	h	;increment hi mem addr
	dcx	b	;decrement # bytes to move
;
	mov	a,b	;test if b&c = 0
	ora	c	;a&c = 0?
	jnz	launch	;no, get another byte
;
	ret		;done, back to cpm
;
newmsg	db	'Toon = ^_$'
bios	ds	2	;addr of bios jump table
cinadd	ds	2	;addr of conout jmp addr
conin	ds	2	;addr of the conout routine
;
;***************** all below is moved *****************
begin	equ 	$
;
distnce	equ	newstrt-begin	;hi mem addr - lo mem
;
;redirect the bdos jmp to its original address
;
bds$trgt
	jmp	dmmy$ad		;bdos addr at run time
;
;and now, the dump routine,...
;
dump	equ	$+distnce	;<<< entry, high mem
cin$trgt			;conin address target
	call 	dmmy$ad		;addr put in at run time
	mvi	c,1fh		;ctrl underline, ^_
	cmp	c		;compare to char in c
	jz	dmpfl		;if ^_, go to routine
	ret			;otherwise just return
;
;open a file, put something in it after the end of the
;last entry, then close it, OK?
;
;row and col must be initialized for each screen written
;
dmpfl	equ	$+distnce
	mvi	a,1
	sta	col	;col = 1
	sta 	row	;row = 1
;
;set up a local stack
;
	lxi	h,0	;clear hl
	dad	sp	;sp -> hl
	shld	cpmstk	;store original stack pointer
	lxi	sp,locstk	;new top of stack
;
;define new dma
;
	mvi	c,1ah
	lxi	d,dma	;use the equate
	call 	bdos
;
;initialize address for next write to dma
;
	lxi	h,dma	;start of dma
	shld	dmaddr	;start at first addr in dma
;
;prepare the filename in the FCB
;
	lxi	h,fcb+1	;destination, for fill
	lxi	d,flnm	;string address, for fill
	call	fill	;put filename in fcb
;
;open or make file, go to end of a previous file 
;
openf	equ 	$+distnce
	mvi	c,0fh	;open file
	lxi	d,fcb
	call 	bdos	;A = 0,1,2,3, or ffh
	cpi	0ffh
	jnz	empty	;is a file, test & find end 

makef	equ	$+distnce	;req'd for era'd Toon
	lxi	d,filler	;string of 0's
	lxi	h,fcb+0Ch 	;put them after fname
	call	fill		;zero the fcb
;
	mvi	c,16h	;make file via bdos
	lxi	d,fcb
	call 	bdos
	jmp	scrloop	;skip search to end of file
;
;find end record of file
;
fend	equ	$+distnce
	mvi	c,14h	;read sequential
	lxi	d,fcb	
	call 	bdos	;a=0 if read successful
	ori	0
	jz	fend	;not at end, read again
;
;reset the record number, delete the ^Z at eof
;
deleof	equ	$+distnce
	lda	fcb+20h	;the current record number
	dcr	a	;back to last record
	sta	fcb+20h	;put decremented cr back
	call	rdseq	;last rec-> dma: advances rec #!
	lda	fcb+20h	;the current record number
	dcr	a	;back to last record
	sta	fcb+20h	;decremented cr, can rewrite
;
;find the eof and overwrite it
;
	mvi	c,1ah	;eof in c
	lxi	h,dma	;address for search
	call 	find	;returns addr of char in c in h
	shld	dmaddr	;store active address in dma
	jmp 	scrloop	;append new text to end of file
;
;measure out a screenfull, write byte by byte from video
;
scrloop	equ	$+distnce
;
	lda	col	;get col, set up to read video
	mov	c,a	;col into c
	mvi	b,0	;zero out b
	push 	b	;onto stack
	lda	row	;get row
	mov	c,a	;row into c
	mvi	b,0	;zero b
	push 	b	;onto stack
	call	rdvid	;return onto stack, get char!
;
;process the char from video memory, returned in c
;
	lhld	dmaddr
	call 	dmaout	;char in c, dmaddr in hl
	lda	col	;get current column
	inr 	a	;increment col, note col -> 81
	sta	col	;restore col
	mvi	d,81	;test, last col?
	cmp	d	;compare to col
	jnz	scrloop	;if 80th not written, next col
;
	lda 	row	;row -> a
	mov	d,a	;row -> d
	lda 	col	;col -> a
	add	d	;col + row in a
	cpi	105	;done? col + row = 105
	jz	endf	;yes, done with screen		
;
	inr	d	;no, increment row
	mov	a,d	;row + 1 -> a
	sta	row	;restore row
	call 	eoln	;put in crlf
	mvi	a,1
	sta	col	;col -> 1 for new row
	jmp	scrloop	;fill next col
;
;read the next record
;
rdseq	equ	$+distnce
	mvi	c,14h	;read sequential
	lxi	d,fcb
	call 	bdos
	ret		;done, go back
;
;write the dma to current record
;
wrtseq	equ	$+distnce
	mvi	c,15h	;write sequential
	lxi	d,fcb
	call 	bdos
	ret		;end
;
;close file
;
clsfl	equ 	$+distnce
	mvi	c,10h	;close file
	lxi	d,fcb
	call 	bdos
	ret		;done
;
;test to see if ex=rc=0, eg, empty Toon
;
empty	equ	$+distnce	;test for empty Toon
				;and delete, if so
	lda	fcb+0Fh	;load the # rec in extent
	mov	b,a	;curr record to b
	lda	fcb+0Ch	;locd curr extent to a
	ora	b	;rec & extent 0?
	jnz	fend	;not empty, find end
;
;erase file
;
erafl	equ	$+distnce	;erase empty file
	mvi	c,13h	;bdos erase file
	lxi	d,fcb
	call 	bdos
	jmp 	makef	;now make new file
;
;fills memory at h with string stored at d, term by $
;used to fill open memory at fixed, dedicated location
;
fill	equ 	$+distnce
	mvi	c,'$'
	ldax	d	;get char into a
	cmp	c	;is the new char the end char?
	rz		;if so, have done string
	mov	m,a	;if not, store next char
;
	inx	d
	inx	h	;increment both string counters
	jmp	fill		
;
;find the character in c after the address supplied in h
;returns the address of the char in h
;
find	equ 	$+distnce
	mov	a,m	;get char in a
	cmp	c	;is it the target char?
	rz		;char found, address in h
	inx	h	;inc address and
	jmp	find	;get the next char
;
;write to dma from c, which has char
;dmaddr is in hl
;
dmaout	equ 	$+distnce
	call 	ckdma	;decides to write when dma full
	mov	m,c	;byte -> dma
	call 	incr	;increment dmaddr
	ret		;done
;
;dma full? yes->reset and restart dma
;gets and returns current char in c, dmaddr in hl
;
ckdma	equ	$+distnce
	lxi	d,dma+80h	;get dma end+1
	mov	a,e	;lsb of dma end in a
	cmp	l	;at end of dma?
	rnz		;ret if not done yet
;
	push 	b	;save char
	call 	wrtseq	;full dma, write it
	pop	b	;get char back
	lxi	h,dma	;reset dmaddr
	shld	dmaddr	;store dmaddr
	ret		;done
;
;increment the address for current write to dma
;
incr	equ	$+distnce
	lhld	dmaddr	;load next write addr
	inx	h	;+ 1
	shld	dmaddr	;restore incr addr
	ret		;done
;
;writes crlf after every 80 screen chars
;deletes blank at eoln to make 79 char lines
;in order to inprove screen appearance
;
eoln	equ	$+distnce
	lhld	dmaddr	;get next location
	dcx	h	;last written char
	mov	a,m	;get the char
	cpi	20h	;was it a blank?
	jnz	eoln1	;no, terminate line
	shld	dmaddr	;yes, update address
	jmp	eoln2	;cr & lf
;
eoln1	equ	$+distnce
	inx	h	;skip non-blank
eoln2	equ 	$+distnce
	mvi	c,cr	
	call 	dmaout	;check address in hl
	mvi	c,lf	
	call 	dmaout	;check address in hl
	ret
;
;puts in last section of file, eof,...Note that 
;in general, would have to worry about hitting 
;record end during eorec, but a screenful will 
;usually end within a record
;
endf	equ	$+distnce
	call	eoln	;cr & lf
	lxi	d,eorec	;addr of string for fill
	lhld	dmaddr	;fill here
	call	fill	;with some eof's
	call 	wrtseq	;write the last dma
	call 	clsfl	;close file
;
	lhld 	cpmstk	;replace the orig stack ptr
	sphl		;hl -> sp
	ret
;
;################ MACHINE DEPENDENT #################
;                 vvvvvvv vvvvvvvvv
;
;this routine switches from normal memory (bank 0) to
;video memory and prom (bank 1).  it reads one char at 
;the address calculated for the screen col and row
;supplied on the stack when rdvid is called, then
;returns that char in register c
;
;reset for input from bank 1, video memory and prom
;
rdvid	equ	$+distnce
	in      1Ch 	;get system byte from the Z80
    	ori     80h     ;force msb -> 1 for bank one
    	out     1Ch     ;set port
;
;stack on entry: rtn addr,row,col
;
    	pop     h       ;get rtn addr
    	pop     b       ;row in BC
    	pop     d       ;column in DE
	push	h	;restore rtn addr
;
;calculate the address in video mem
;
    	lxi     h,3000h ;set up hl
    	mov     a,c     ;move row -> A
    	mvi     c,80h   ;BC = 0080, note B=00 assumed
;
;multiplication routine to get (row-1)*80h
;
rowx	equ	$+distnce
	dcr	a	;row-1
	jz	donex	;through multiplying ?
	dad	b	;addr + 80h
	jnz	rowx	;not yet HL=3000h+(row-1)*80h
			;uses flag from dcr
;
donex	equ	$+distnce
	dcx	d	;col-1
	dad	d	;addr+(col-1) in HL
;
	mov	c,m	;get the byte from memory in c
;
;reset for bank 0 memory
;
    	in      1Ch     ;get system byte
    	xri     80h     ;msb -> 0, bank zero
    	out     1Ch     ;set it
;
	ret		;end of rdvid
;
;                 ^^^^^^^ ^^^^^^^^^
;################ MACHINE DEPENDENT ################
;
flnm	equ	$+distnce
	db	'TOON       $'
eorec	equ	$+distnce
	db	eof,eof,eof,eof,'$'
dmaddr	equ	$+distnce
	ds	2	;address of next write to dma
row	equ	$+distnce
	ds	1	;current screen column, 1->80
col	equ	$+distnce
	ds	1	;current screen row,1->24
filler	equ	$+distnce
	db	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,'$'
			;used to reinit fcb 
fcb	equ	$+distnce
	db	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 
	db	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 
			;36 bytes init to 0
dma	equ	$+distnce
	ds	128	;space for sector buffer
	ds	32	;local stack
locstk	equ	$+distnce-1	;top of stack
cpmstk	equ	$+distnce
	ds	2	;cpm's stack pointer on entry
;
endmv	equ	$
;local stack
locstk	equ	$+distnce-1	;top of stack
cpmstk	equ	$+distnce
	ds	2	;c