;***************************************************************************
;* 10-24-1983			V1.9e				     WM03  *
;* CHANGED SCREEN SIZE IN 'clear2' TO 800H TO ELMINATE THE PHANTOM CURSOR  *
;*	ATTRIBUTE. COMMENTED & LEFT ORIGINAL CALCULATION FOR DOCUMENTATION.*
;***************************************************************************
;* 10-22-1983			V1.9d				     WM02  *
;* INSERTED hrestore_&retry before call nz,wrt.err & rd.err; THE ROUTINE   *
;*	WILL RESTORE DRIVE AND RETRY FUNCTION BEFORE GOING TO ERROR ROUTINE*
;* CHANGED 18 JP'S TO JR'S, ADDED 16 BYTES, NET GAIN = 2 BYTES		   *
;***************************************************************************
;* 10-21-1983			V 1.9c					   *
;* ISOLATED HARD DISK CONTROLLER RESET FROM HARD DISK RESET, MODIFIED ROM  *
;*  SO THAT THE HARD DISK CONTROLLER RESET IS ONLY PERFORMED AT POWER-UP   *
;*  OR RESET.  FIXED A BUG IN HARD DISK READY ROUTINE THAT WAS TURNING	   *
;*  ON THE FLOPPY DRIVE MOTOR AND SETTING ALL BITPORT BITS HIGH.  ALSO     *
;*  CHANGED THE SEEK SPEED OF FLOPPY RESTORE TO MATCH THE SEEK TIME (6ms). *
;*  (M. Sherman, 21-Oct-83)						   *
;***************************************************************************
;* 10-10-1983			V 1.9b				     WM01  *
;* CHANGED 12 JP'S TO JR AND ADDED A 4 SEC DELAY TO HARD DISK INITIAL RESET*
;*   HARD DISK RESET STATE HAS BEEN COMPLEMENTED TO BE COMPATABLE 	   *
;*   WITH THE IMPROVED WD 1002 CONTROLLER BOARD.			   *
;* IN 'seekcmd' STEP RATE CHANGED TO BE 6mS, THE SAME AS THE II & THE IV.  *
;* range of program 0000 - 0FFEH, added 11 bytes, net gain = 1 byte.	   *
;***************************************************************************
;
;***************************************************************************
;* INLINE ASSEMBLY OF BIOS MODULES	W. MCKINLEY	8-12-83  16:00	   *
;***************************************************************************
;
IF1
.PRINTX / 10-22-83 INLINE BIOS MODIFIED FOR IMPROVED WD-1002 CONTROLLER, SEEK = 6mS /
.PRINTX / VERSION 1.9E /
ENDIF
;
IF2
.PRINTX / PASS 2 /
ENDIF
;
title System scratch RAM used by ROM software and OVL.  (C) 1983 By NLS
.comment %
########################################################
##                                                    ##
##      KAYPRO 10 System                              ##
##                                                    ##
##      By G. Ohnysty                                 ##
##                                                    ##
##      System scratch RAM used by ROM & OVL software ##
##                                                    ##
##      Copyright (C) 1983 By Non-Linear Systems, Inc ##
##      No warranty is made, expressed or implied.    ##
##                                                    ##
########################################################
##      Date: 04/14/83                    [01]        ##
########################################################
%
.z80


ovlram		equ	0EE00H	; overlay ram

wrt.err	equ	ovlram		; write sector error handler
rd.err	equ	wrt.err+3	; read sector error handler
pixon	equ	rd.err+3	; plot a pixel
pixoff	equ	pixon+3		; erase a pixel
lineon	equ	pixoff+3	; draw a line
lineoff	equ	lineon+3		; erase a line

ramscratch	equ	0F700H	; scratch ram

dsktyp	equ	ramscratch	;hard or floppy disk currently selected flag
adsk	equ	dsktyp+1	;hard or floppy is A: flag
sectrk	equ	adsk+1		;sectors per track
@sekdsk	equ	sectrk+1	;seek disk number
@sektrk	equ	@sekdsk+1	;seek track number
@seksec	equ	@sektrk+2	;seek sector number
@hstdsk	equ	@seksec+1	;host disk number
@hsttrk	equ	@hstdsk+1	;host track number
@hstsec	equ	@hsttrk+2	;host sector number
@sekhst	equ	@hstsec+1	;seek shr secshf
@hstact	equ	@sekhst+1	;host active flag
@hstwrt	equ	@hstact+1	;host written flag
@unacnt	equ	@hstwrt+1	;@unalloc rec cnt
@unadsk	equ	@unacnt+1	;last @unalloc disk
@unatrk	equ	@unadsk+1	;last @unalloc track
@unasec	equ	@unatrk+2	;last @unalloc sector
@erflag	equ	@unasec+1	;error reporting
@rsflag	equ	@erflag+1	;read sector flag
@readop	equ	@rsflag+1	;1 if read operation
@wrtype	equ	@readop+1	;write operation type
@dmaadr	equ	@wrtype+1	;last dma address
@hstbuf	equ	@dmaadr+2	;host buffer
@move	equ	@hstbuf+512	;move routine for deblocking
@dirbuf	equ	@move+15	;directory buffer for hard disk
@alva	equ	@dirbuf+128	; alocation map for hd A
@alvb	equ	@alva+162	; alocation map for hd B
@dpha	equ	@alvb+162	; dph for hd A
@dphb	equ	@dpha+16	; dph for hd B
@dpbh	equ	@dphb+16	; dpb for hd
sekdsk	equ	@dpbh+15	;seek disk number
sektrk	equ	sekdsk+1	;seek track number
seksec	equ	sektrk+2	;seek sector number
hstdsk	equ	seksec+1	;host disk number
hsttrk	equ	hstdsk+1	;host track number
hstsec	equ	hsttrk+2	;host sector number
sekhst	equ	hstsec+1	;seek shr secshf
hstact	equ	sekhst+1	;host active flag
hstwrt	equ	hstact+1	;host written flag
unacnt	equ	hstwrt+1	;unalloc rec cnt
unadsk	equ	unacnt+1	;last unalloc disk
unatrk	equ	unadsk+1	;last unalloc track
unasec	equ	unatrk+2	;last unalloc sector
erflag	equ	unasec+1	;error reporting
rsflag	equ	erflag+1	;read sector flag
readop	equ	rsflag+1	;1 if read operation
wrtype	equ	readop+1	;write operation type
dmaadr	equ	wrtype+1	;last dma address
hstbuf	equ	dmaadr+2	;host buffer
dsk	equ	hstbuf+512	; current disk drive
sidflg	equ	dsk+1		; single/double sided flag
csva	equ	sidflg+1	; directory check
alva	equ	csva+16		; allocation map
leadflg	equ	alva+25
; video graphics data storage
vidram	equ	leadflg			; initialization pointer
crow	equ	leadflg+1
ccol	equ	crow+1
vatt	equ	ccol+1
cursor	equ	vatt+1
vrbase	equ	cursor+2
esccmd	equ	vrbase+2
precur	equ	esccmd+1
ramlen	equ	12			; number of bytes to initialize
col	equ	precur+2
col2	equ	col+1
row	equ	col2+1
row2	equ	row+1
onoff	equ	row2+1
newc	equ	onoff+1
pix	equ	newc+1
saddr	equ	pix+1
xoff	equ	saddr+2
yoff	equ	xoff+1
difx	equ	yoff+1
dify	equ	difx+1
vgb1	equ	dify+1
dpha	equ	vgb1+1		; DPH for A
$dpb	equ	dpha+16		; single density dpb
adrbuf	equ	$dpb+15		; read address buffer
move	equ	adrbuf+6	; move logical sector from hstbuf
rd128	equ	move+15		; routine to read 128 byte sector
rd512	equ	rd128+7		; routine to read 512 byte sector
wrt128	equ	rd512+10	; routine to write 128 byte sector
wrt512	equ	wrt128+7	; routine to wrtie 512 byte sector
rdwrtend equ	rd128+145	; end of read and write routines
dirbuf	equ	rdwrtend+1	; bdos directory buffer

stack	equ	0FFFFH		; boot up stack space
title Cold start routines.    (C) 1983 By NLS
.comment %
########################################################
##                                                    ##
##      KAYPRO 10 System                              ##
##                                                    ##
##      By G. Ohnysty                                 ##
##                                                    ##
##      Cold start routine, reset and configure       ##
##      system for power up condition.                ##
##                                                    ##
##      Copyright (C) 1983 By Non-Linear Systems, Inc ##
##      No warranty is made, expressed or implied.    ##
##                                                    ##
########################################################
##      Date: 04/14/83                    [01]        ##
########################################################
#							#
#	Modified for proper error handling on first	#
#	 attempts to load the overlay (which has the	#
#	 error message reporting calls in it.)		#
#	 (M. Sherman, 17-Jun-83)			#
#	Modified for proper response to the floppy	#
#	on power-up/reset by M. Sherman on 8-Jun-83	#
#							#
#########################################################
%
;
ovlram	equ	0EE00H		; load address for overlay
hdsel	equ	0		; hard disk is A: flag
fsel	equ	-1		; floppy is A: flag
status	equ	10H		; floppy status port (to look for index)

	.z80

; ROM master jump table

jp	start		; start up computer
jp	diskinit	; disk initialize
jp	vidinit		; video initialize
jp	devinit		; device initialize

jp	home_dispatch	; home selected disk drive
jp	seldsk		; select a disk drive
jp	settrk		; seek a track
jp	setsec		; set sector number to read
jp	setdma		; set dma address
jp	read		; read logical sector
jp	write		; write logical sector
jp	sectran		; xlate sector number
jp	diskon		; turn on disk
jp	diskoff		; turn off disk

jp	kbdstat		; KeyBoarD character ready
jp	kbdin		; input from keyboard
jp	kbdout		; output to keyboard (used to ring bell)
jp	ttystat		; status of serial input port
jp	ttyin		; serial input
jp	ttyout		; serial output
jp	liststat	; list output status (Centronics)
jp	list		; list output
JP	TTYOSTAT	;TESTSTATUS OF SERIAL OUTPUT
jp	vidout		; video output
jp	thnsd		; short delay

start:	di			; stop interupts while setup
	ld	sp,stack	; rom stack point
	ld	b,20		; a delay to let the hardware stabilize
	call	thnsd		; 20 milli-seconds worth.
	call	devinit		; init device sub-system
	call	vidinit		; init video sub-system
	call	hdcinit		; hard disk controller initialization
	call	diskinit	; init disk sub-system
	jr	bootsys		; boot system
	org	66H		; nmi vector
	ret			; return from "halt", NMI sequence when in rom
page
; boot system, the first sector (1) of the first track (0)
; hold system boot information. It does NOT hold a short boot routine!
; the image is:
;	self:	jr	self	; hang if booted and run
;		defw	loadpt	; where to load the opsys image
;		defw	bios	; where to go after booting system
;		defw	length	; length of image in 128 byte sectors
;	(* the rest of the sector is not used *)
;
; This sector image is loaded and inspected at 0FA00H during the boot process

esc	equ	1BH		; ascii esc

bootsys:call	print
	defb	esc,'=',20H+10,20H+31
	defb	'* KAYPRO 10 v 1.9e *',0
;	DEFB	' BETA TEST ROM V 1.7a',0
doagain:call	check		; is floppy alive?
	ld	a,hdsel		; parms for hard disk
	ld	(adsk),a
	ld	a,68
	ld	(sectrk),a
	jr	z,loadit
	ld	a,fsel		; parms for floppy
	ld	(adsk),a
	ld	a,40
	ld	(sectrk),a
loadit:	call	ovload		; load overlay
boot:	ld	c,0
	call	seldsk		; select disk, set density, do home after diskinit
	ld	bc,0		; set track
	call	settrk
	ld	c,0		; read the first sector
	call	setsec
	ld	bc,0FA00H	; header sector to go here
	call	setdma
	call	read		; read sector to FA00
	di			; read does EI upon exit
	or	a		; trouble reading?
	jr	nz,doagain	; tell crt
	ld	bc,(0FA02H)	; where to load system image
	ld	a,c		; system image?
	cp	0E5H
	jr	z,doagain
	ld	(@dmaadr),bc
	ld	(dmaadr),bc
	ld	bc,(0FA04H)	; where to go after loading system
	push	bc		; save for latter use
	ld	bc,(0FA06H)	; length of system in 128 byte sectors
	ld	b,c		; reg B holds # of sectors to load
	ld	c,1		; initial sector (0 was header sector)
cb1:	push	bc		; save sector count and current sector
	call	setsec		; select sector
	call	read
	di			; read does EI upon exit
	pop	bc
	or	a
	jr	nz,doagain	; bad read of sector
	ld	hl,(@dmaadr)	; update dma address for next sector
	ld	de,128		; new dma address
	add	hl,de
	ld	(@dmaadr),hl
	ld	(dmaadr),hl
	dec	b
	ret	z		; done booting goto system
	inc	c		; bump sector count
	ld	a,(sectrk)	; over sectors/track?
	cp	c
	jr	nz,cb1		; fetch another sector
	ld	c,16		; first sector to read on next track
	push	bc		; save counts
	ld	bc,1		; set for next track
	call	settrk
	pop	bc
	jr	cb1

check:	call	fndidx		; find index pulse
	ret	z		; no index, abort
	ld	b,8		; delay while waiting for index to go away
	call	thnsd		; 8 MS
	in	a,(status)
	cpl
	bit	1,a		; 0=no floppy, 1=floppy
	ret

fndidx:	call	$home		; home floppy
	ld	hl,9000H
lp1:	in	a,(status)	; index pulse?
	bit	1,a
	ret	nz		; index is nz, return if true
	dec	hl		; enough tries?
	ld	a,h
	or	l
	jr	nz,lp1
	ret

ovload:	call	filhdr		; set up bogus error reporting system
	ld	a,(adsk)	; is it possible to load overlay?
	or	a		; is not possible if hard disk is drive B:
	jr	z,loadovl	; go load overlay from hard disk

filhdr:	ld	hl,ovlram	; base of overlay, fill with no.op
	ld	b,16		; *16 [or a, nop, ret]
lp2:	ld	(hl),0B7H	; [or a]
	inc	hl
	ld	(hl),0		; [nop]
	inc	hl
	ld	(hl),0C9H	; [ret]
	inc	hl
	djnz	lp2
	ret

loadovl:ld	c,1		; select drive B:
	call	seldsk
	ld	bc,0		; track = 0
	call	settrk
	ld	hl,ovlram	; set dma address
	ld	(@dmaadr),hl
	ld	bc,0		; sector #
ldlp:	push	bc
	call	setsec
	call	read		; read sector
	pop	bc
	or	a
	jr	nz,filhdr	; fault, set up bogus overlay, exit.
	ld	hl,(@dmaadr)	; update dma address
	ld	de,128
	add	hl,de
	ld	(@dmaadr),hl
	inc	c
	ld	a,c
	cp	16		; load 2K sec# 0-15
	jr	nz,ldlp
	ld	a,(ovlram)	; check for a jp inst
	cp	0C3H
	jr	nz,filhdr	; bad data in ram, fill header with default
	ret

title System device I/O routines.   (C) 1983 By NLS
.comment %
########################################################
##                                                    ##
##      KAYPRO 10 System                              ##
##                                                    ##
##      By G. Ohnysty                                 ##
##                                                    ##
##      System device I/O routines                    ##
##                                                    ##
##      Copyright (C) 1983 By Non-Linear Systems, Inc ##
##      No warranty is made, expressed or implied.    ##
##                                                    ##
########################################################
##      Date: 04/14/83                    [01]        ##
########################################################
%
.z80

	public	kbdstat, kbdin, kbdout, ttystat, ttyin, ttyout, TTYOSTAT
	public	liststat, list, devinit


;***************
;* sio equates *
;***************

sio	equ	04H	; base address of sio
sioa0	equ	sio+2	; channel a command/status
sioa1	equ	sio+0	; channel a data
siob0	equ	sio+3	; channel b command/status
siob1	equ	sio+1	; channel b data
sioc0	equ	sio+10	; channel a command/status
sioc1	equ	sio+8	; channel a data
siod0	equ	sio+11	; channel b command/status
siod1	equ	sio+9	; channel b data

; write registers 0-7 and control bits
; init registers in the following order 0,2,4,3,5,1

WR0	equ	0	; command register, crc reset, reg pointer
; bits 0-2 are register pointers to WRx and RRx
; bits 3-5 and commands as given bellow
null	equ	0	; null command
extrset	equ	10H	; reset ext/status interrupts
reset	equ	18H	; channel reset
ienrc	equ	20H	; Enable Int on Next Rx Character
rtip	equ	28H	; disable transmitter (prevents buffer empty int.)
			; and enable break (prevents under-run int.)
			; (note: since the transmitter is disabled,
			;  no break characters are transmitted.)
			; (also note:  Transmitter output is High-Z,
			;  which is neither high nor low (niether 'Mark'
			;  nor all zero's.  Value dependent upon pullup
			;  or pull down resistors or other external loading
			;  factors.) )
			; (note:  Auto Turnaround is also enabled.)
errset	equ	30H	; error reset

WR1	equ	1H	; interrupt enable and Wait/Ready modes
esie	equ	1H	; external/status interrupt enable
tie	equ	2H	; transmitter interrupt enable
tid	equ	0	; transmitter interrupt disable
statav	equ	4H	; Status affects vector (z80 mode 2) (see WR2)
; bits 3-4 affect receive interrupt mode
rid	equ	0	; receive interrupts disabled
rifc	equ	8H	; receive interrupt on first char only
riep	equ	10H	; recv interrupts enabled, parity err Special Recv Cond
rie	equ	18H	; same as riep but parity error not Special Recv Cond

WR2	equ	2	; interrupt vector address/pointer (chan b only)
; interrupt address (z80 reg I+WR2=interrupt address)
; returned as is if not statav above in wr1
; if statav then bits 1-3 are modified as bellow:
;	000	ch b transmit buffer empty
;	001	ch b external/status change
;	010	ch b receive char available
;	011	ch b special receive condition (parity error, Rx overrun,
;			framing error, end of frame(sdlc) )
;	1xx	ch a (* same vectors as for channel b above *)

WR3	equ	3	; receiver logic control and parameters
re	equ	1	; receiver enable
autoe	equ	20H	; auto enable (use dcd and cts to enable recv and xmt
; bits 6-7 are receiver bits/character
rbits5	equ	0	; 5 bits/character
rbits7	equ	40H	; 7 bits/character
rbits6	equ	80H	; 6 bits/character
rbits8	equ	0C0H	; 8 bits/character

WR4	equ	4	; control bits that affect both xmt and recv
pon	equ	1	; enable parity (parity on)
pstate	equ	2	; parity even   not pstate = parity odd
; bits 2-3 are number of stop bits
syncmd	equ	0	; sync mode is to be selected
sbits1	equ	4	; 1 stop bit
sbits5	equ	8H	; 1.5 stop bits
sbits2	equ	0CH	; 2 stop bits
; bits 6-7 control clock rate
cr1	equ	0	; data rate x1=clock rate
cr16	equ	40H	;           x16
cr32	equ	80H	;           x32
cr64	equ	0CH	;           x64

WR5	equ	5	; control bits that affect xmt
te	equ	8H	; transmit enable
break	equ	10H	; send break
; bits 5-6 are number of bits/character to transmit
tbits5	equ	0	; 5 or less bits/character
tbits7	equ	20H	; 7 bits/character
tbits6	equ	40H	; 6 bits/character
tbits8	equ	60H	; 8 bits/character
rts	equ	2	; RTS output
dtr	equ	80H	; DTR output

WR6	equ	6	; sdlc transmit sync character
WR7	equ	7	; sdlc receive sync character

; read registers 0-2 and status bits
rr0	equ	0	; general recv and xmt status
rca	equ	1	; receive character available
intped	equ	2	; interrupt pending (ch a only)
tbe	equ	4	; transmit buffer empty
synhnt	equ	10H	; sync/hunt
dcd	equ	8H	; DCD input
cts	equ	20H	; CTS input
xmtundr	equ	40H	; transmit underrun/ EOM
brk	equ	80H	; break/abort status

rr1	equ	1	; Special Receive conditions and Residue codes
; bits 4-7 are special receive conditions
rpe	equ	10H	; parity error
rovr	equ	20H	; Rx overrun error
framerr	equ	40H	; framing error

rr2	equ	2	; interrupt vector address/pointer

pdat	equ	24	; cent out data port (8 bit latch)

bitport	equ	20	; system bit port for status and control
;0 floppy drive 0 select:  0=select, 1=deselect.
;1 floppy drive 1 select / hard disk controller reset:
;  0=floppy drive 1 select / hard disk controller reset,
;  1=floppy drive 1 deselect / hard disk controller enable,
;2 floppy drive side select line:  0=side 1, 1=side 0.
;3 parallel port output line, used (for example) for centronics data strobe.
;4 floppy motor control:  0=motor off, 1=motor on.
;5 floppy controller density select, 0=double density, 1=single density.
;6 parallel port input line, used (for example) for centronics busy line.
;7 bank select:  0=64K ram only, 1=rom, video ram and upper 32k ram select.

;***************
;* baud rate   *
;***************

bauda	equ	00H	; baud rate generator for serial chan a (modem)
baudb	equ	08H	; baud rate generator for serial chan b (printer)

; baud rate factors, output to baudx to select baud rate
baud10	equ	02H	; 110 baud rate
baud30	equ	05H	; 300 baud rate
baud12	equ	07H	; 1200 baud rate
baud24	equ	0AH	; 2400 baud rate
baud48	equ	0CH	; 4800 baud rate
baud96	equ	0EH	; 9600 baud rate
baud19k	equ	0FH	; 19.2k baud rate
subttl I/O configuration tables
page

iotbint:defb	reset			; reset sio channel
	defb	wr4
	defb	sbits1 or cr16		; one stop bit, 16x clock
	defb	wr3
	defb	re or rbits8		; recv enable, 8 bits/char
	defb	wr5
	defb	te or tbits8 or dtr	; xmt enable, 8bits/char, assert dtr
	defb	wr1
	defb	tid or rid		; xmt & recv interrupts disabled
iotbend:

tblen	equ	iotbend-iotbint		; table length

devinit:ld	a,0cfh		; reset hard disk controller CF=(1100111)
	out	(bitport),a	; initialize bitport		      ^	
	ld	c,siob0
	call	tblout		; initialize channel
	ld	c,sioc0
tblout:	ld	hl,iotbint
	ld	b,tblen
	otir
	ret

subttl Device I/O handlers
page
kbdstat:in	a,(siob0)	; kbd char avail?
	and	rca
comout:	ld	a,0
	ret	z		; 0=no char
	ld	a,0FFH		; FF=char avail
	ret

kbdin:	call	kbdstat		; loop till char avail
	jr	z,kbdin
	in	a,(siob1)	; get char
	call	kbdmap		; map out funny chars of vector pad and #'s
	ret

kbdout:	in	a,(siob0)	; xmit buffer empty?
	and	tbe
	jr	z,kbdout
	ld	a,c		; out character
	out	(siob1),a
	ret

kbdmap:	ld	hl,mapin	; input map table
	ld	bc,mapout-mapin	; table length
	cpir			; search table
	ret	nz		; not found
	ld	de,mapin	; make hl=table index
	or	a		; hl-mapin=index
	sbc	hl,de
	ld	de,mapout-1	; index
	add	hl,de
	ld	a,(hl)		; get char from mapout
	ret

mapin:	defb	0F1H, 0F2H, 0F3H, 0F4H	; up, down, left, right arrows
	defb	0B1H, 0C0H, 0C1H, 0C2H	; 0,1,2,3
	defb	0D0H, 0D1H, 0D2H, 0E1H	; 4,5,6,7
	defb	0E2H, 0E3H, 0E4H, 0D3H	; 8,9, '-', ','
	defb	0C3H, 0B2H		; return, '.'
	defb	0FFH			; end of mapin table
mapout:	defb	80H, 81H, 82H, 83H	; vector pad, xlate in bios
	defb	84H, 85H, 86H, 87H
	defb	88H, 89H, 8AH, 8BH
	defb	8CH, 8DH, 8EH, 8FH
	defb	90H, 91H

ttystat:in	a,(sioc0)		; serial port status input
	and	rca
	JR	COMOUT

ttyin:	call	ttystat			; is a char ready?
	jr	z,ttyin
	in	a,(sioc1)
	ret

ttyout:	in	a,(sioc0)		; output a char to serial port
	and	tbe
	jr	z,ttyout		; xmit buffer full?
	ld	a,c
	out	(sioc1),a		; xmit character
	ret

;
TTYOSTAT:	;TEST STATUS OF SERIAL OUTPUT
	IN	A,(SIOD0)
	AND	TBE			;TX BUF FULL ?
	JR	COMOUT
;
; list port centronics equates
pready	equ	6			; bit in bit port
pstrob	equ	3			; bit in bit port

liststat:in	a,(bitport)		; centronics printer port status
	bit	pready,a
	ld	a,0
	ret	nz			; 00=busy
	ld	a,0FFH			; FF=ready
	ret

list:	call	liststat		; is printer busy?
	jr	nz,list
	ld	a,c
	out	(pdat),a		; output char to printer
	in	a,(bitport)		; strb. printer
	res	pstrob,a
	out	(bitport),a
	set	pstrob,a
	out	(bitport),a
	ret

title Dispatch to hard disk or floppy drive.   (C) 1983 By NLS
.comment %
########################################################
##                                                    ##
##      KAYPRO 10 System                              ##
##                                                    ##
##      By G. Ohnysty                                 ##
##                                                    ##
##      Dispatch to hard disk or floppy drive         ##
##                                                    ##
##      Copyright (C) 1983 By Non-Linear Systems, Inc ##
##      No warranty is made, expressed or implied.    ##
##                                                    ##
########################################################
##      Date: 04/14/83                    [01]        ##
########################################################
%
	.z80


fsel	equ	-1

dispatch macro x, y
	.xlist
	ld	a,(dsktyp)
	cp	hdsel
	jp	z,x
	jp	y
	.list
	endm

home_dispatch:	dispatch @home,$home
settrk:	dispatch @settrk,$settrk
setsec:	dispatch @setsec,$setsec
read:	dispatch @read,$read
write:	dispatch @write,$write

seldsk:	ld	a,(adsk)		; hard disk = A:?
	cp	hdsel
	jr	nz,s2			; if not then floppy=A:, hd=B: & C:
	bit	1,c			; selecting hard of floppy
	jr	nz,s1
	ld	a,hdsel			; set hard disk as selected drive
	ld	(dsktyp),a
	jp	@seldsk
s1:	ld	a,fsel			; set floppy as selected drive
	ld	(dsktyp),a
	xor	a
	ld	c,a
	jp	$seldsk
s2:	ld	a,c			; selecting floppy or hard
	or	a
	jr	z,s3			; floppy
	sub	1			; hard disk is B: or C: xlate to 0,1
	ld	c,a
	ld	a,hdsel			; set hard disk as selected drive
	ld	(dsktyp),a
	jp	@seldsk
s3:	ld	a,fsel			; set floppy disk as selected drive
	ld	(dsktyp),a
	jp	$seldsk

setdma:	push	bc		; save dmaadr
	call	@setdma		; set hd dmaadr
	pop	bc
	jp	$setdma		; now go do floppy

diskinit:call	@diskinit	; do hd
	jp	$diskinit	; now floppy

diskoff:call	@diskoff	; do hd
	jp	$diskoff	; now floppy

title Hard disk support routines.    (C) 1983 By NLS
.comment %
########################################################
##                                                    ##
##      KAYPRO 10 System                              ##
##                                                    ##
##      By G. Ohnysty & M. Sherman                    ##
##                                                    ##
##      Disk support routines (Deblocking hard disk)  ##
##                                                    ##
##      Copyright (C) 1983 By Non-Linear Systems, Inc ##
##      No warranty is made, expressed or implied.    ##
##                                                    ##
########################################################
##      Date: 04/14/83                    [01]        ##
########################################################
#							#
#	Current revision:	1.9	20-Jun-83	#
#	Previous revision:	1.8	17-Jun-83	#
#	Previous revision:	1.7a	13-Jun-83	#
#	Previous revision:	1.7	08-Jun-83	#
#	Previous revision:	1.5	15-May-83	#
#							#
#	Changes:  SEKOK tests drive ready as well as	#
#	 seek complete.  G. Ohynsty, revision 1.9	#
#	Changes:  SEKOK inserted into READY, which was	#
#	 the only routine using it.  4 bytes saved.	#
#	 M. Sherman, revision 1.8.			#
#	Changes:  DISKOFF now de-selects the hard disk	#
#	 by using a drive select mask on HDSEL to	#
#	 select drive 0, instead of using an "or 10h"	#
#	 to select drive 3.  Reasons:  Drive 3 is the	#
#	 floppy controller on the WD 1002 board, which	#
#	 isn't installed and always returns a 'drive	#
#	 ready" status.  This messes up DISKOFF, which	#
#	 then tells the WD 1002 board to seek the floppy#
#	 to track 305 (which it does) and wait until	#
#	 it's done, about .75 seconds later.		#
#	  Drive 0 was selected as the alternate because:#
#	1) We try not to use that drive for reliability	#
#	 purposes, and					#
#	2) We can't get a cable over that connector on	#
#	 the WD 1002 board, anyway.  (M. Sherman,	#
#	 version 1.7a)					#
#	Changes:  SEKOK modified to call HDBSY first,	#
#	 ( status bits are invalid if the controller	#
#	   is busy ) DISKOFF modified to call HDBSY	#
#	 instead of SEKOK.  (M. Sherman, version 1.7)	#
#	Changes:  DISKINIT now re-enables the hard	#
#	 disk controller immediately after resetting	#
#	 it.  ( A potential problem was discovered with	#
#	 holding the board in a reset state for long	#
#	 periods of time )				#
#							#
#########################################################
%
.z80


public	@home, @seldsk, @settrk, @setsec, @setdma, @read, @write
public	@diskinit, @diskoff


bitport	equ	20		; bit port (m80 does not support extrn bytes)
retcod	equ	0C9H		; return op code
nmivec	equ	0066H		; non-maskable interupt vector (used in rd/wt loop)

; Hard Disk Definitions:

;ports:
hdbase	equ	80h

hddata	equ	hdbase		; data register
hdetyp	equ	hdbase+1	; error type register
hdwrtp	equ	hdbase+1	; write precomp cylinder/4
hdscnt	equ	hdbase+2	; number of sectors count
hdsec	equ	hdbase+3	; first sector to read/write
hdclo	equ	hdbase+4	; cylinder number low byte
hdchi	equ	hdbase+5	; cylinder number high byte
hdsdh	equ	hdbase+6	; size/drive/head register
hdcmd	equ	hdbase+7	; command register
hdstat	equ	hdbase+7	; status register

;commands:

longrw	equ	00000010b	; long read/write bit
multrw	equ	00000100b	; multiple read/write bit
hddmam	equ	00001000b	; dma mode on read bit
rt35uS	equ	00000000b	; 035 uS step rate (fastest)
rt05mS	equ	00000001b	; 0.5 mS step rate (rest in inc. of this one)
rt10mS	equ	rt05mS*2	; 1.0 mS step rate
rt30mS	equ	rt05mS*6	; 3.0 mS step rate
rt60mS	equ	rt05mS*12	; 6.0 mS step rate
rt75mS	equ	rt05mS*15	; 7.5 mS step rate (slowest)
eccmod	equ	10000000b	; error correcting mode
sec512	equ	00100000b	; 512 byte sector size
hdselh	equ	10101000b	; select hard disk drive
hdsmsk	equ	11100111b	; drive select mask
hdinir	equ	00010000b+rt60mS ; restore used for initialization
hdrstr	equ	00010000b+rt05mS ; restore used for normal home command
hdseek	equ	01110000b+rt35uS ; fast seek
hdred	equ	00100000b	 ; read sector
hdredl	equ	00100000b+longrw ; long read (sector + ECC bytes)
hdwrt	equ	00110000b	 ; write sector
hdwrtl	equ	00110000b+longrw ; long write (sector + ECC bytes)

; hard disk info
lzone	equ	305	; safety zone
maxcyl	equ	305	; same as safety zone (see dsm)

; for use with bitport

hdcres	equ	00000010b	; hard disk controller reset mask	wm01
hdcsel	equ	11111101b	; hard disk controller select bit	wm01

page
; This section defines the disk parameters (dph's are images @moved to RAM)
dph0h:	defw	0,0,0,0		; dph for unit A:
	defw	@dirbuf,@dpbh	; directory buffer, Disk Parameter Block
	defw	0, @alva		; check sum pointer, allocation map pointer

	defw	0,0,0,0		; dph for unit B:
	defw	@dirbuf,@dpbh	; directory buffer, Disk Parameter Block
	defw	0, @alvb	; check sum pointer, allocation map pointer

;@dpbh:
	defw	68		; (spt) sectors per track
	defb	5		; (bsh) block shift factor
	defb	31		; (blm) block mask
	defb	1		; (exm) extent mask
	defw	1125		; (dsm) max logical block # (max 1282)
				; dsm is 1125 to allow for safety zone
	defw	1023		; (drm) max directory #
	defb	0FFH		; (al0) directory allocation map
	defb	00H		; (al1)
	defw	0		; (cks) size of directory check vector
	defw	4		; (off) reserved tracks

enddphh:
subttl Logical BIOS entry points & Deblocking
page
.8080
;*****************************************************
;*      Logical BIOS entry points                    *
;*      Sector Deblocking Algorithms                 *
;*****************************************************
blksizh	equ	4096		;CP/M allocation size
hstsiz	equ	512		;host disk sector size
hstspth	equ	17		;host disk sectors/trk
hstblk	equ	hstsiz/128	;CP/M sects/host buff
cpmspth	equ	hstblk * hstspth ;CP/M sectors/track
secmsk	equ	hstblk-1	;sector mask
secshf	equ	2		;log2(hstblk) sector mask
wrall	equ	0		;write to allocated
wrdir	equ	1		;write to directory
wrual	equ	2		;write to unallocated

.z80
hdcinit:
	; reset hard disk controller on power-up and hold it there
	; until the controller is properly powered up (100 milliseconds
	; to 2 seconds) and the hard disk unit is stabilized (1 to 3 seconds)
	in	a,(bitport)	; reset controller
	or	hdcres		; reset if bit 1 = 1			wm01
	out	(bitport),a
	push	af		; save a				wm01
;
;decrement b's A0h and dec b through [100H = (256)] - (02 - first dec) time
;
	ld	bc,0A002H	; delay four seconds  [(01x100H)+A0H)]	wm01
delay4:	call	thnsd		; delay loop (each b bit = .001 sec)	wm01
	dec	c		; done					wm01
	jr	nz,delay4	; jif not done				wm01
	pop	af		; retrieve acc				wm01
	and	hdcsel		; select if bit 1 = 0			wm01
	out	(bitport),a	; select controller
	ret

@diskinit:	;enter here on system boot to initialize
	ld	hl,ioimageh	;@move rd/wrt routines into RAM
	ld	de,@move
	ld	bc,image_length
	ldir
	ld	hl,dph0h		; set dph's
	ld	de,@dpha
	ld	bc,enddphh-dph0h
	ldir
.8080
	xra	a		;0 to accumulator
	sta	@hstact		;host buffer inactive
	sta	@unacnt		;clear unalloc count
	ret

@seldsk:			;select disk
	mov	a,c		;selected disk number
	sta	@sekdsk		;seek disk number
	lxi	h,0		;does disk exist?
	cpi	2
	rnc
	lxi	h,@dpha		;dph for drive a
	ora	a
	rz
	lxi	h,@dphb		;dph for drive b
	ret

@setsec:				;set sector given by register c 
	mov	a,c
	sta	@seksec		;sector to seek
	ret

.z80
@setdma:	ld	(@dmaadr),bc	;set dma address given by BC
	ret

@settrk:	ld	(@sektrk),bc	;set track given by registers BC
.8080
	ret

@home:	lda	@hstwrt		; (patch by DRI) host written flag
	ora	a		; written ?
.z80
	jr	nz,homedh	; jif not written			wm01
.8080
	sta	@hstact		; else store in host active
homedh:	jmp	dohomeh		; go do home disk drive

@read:				;read the selected CP/M sector
	xra	a		; a patch by DRI
	sta	@unacnt
	mvi	a,1
	sta	@readop		;read operation
	sta	@rsflag		;must read data
	mvi	a,wrual
	sta	@wrtype		;treat as unalloc
.z80
	jr	rwoperh		;to perform the read			wm01
.8080

@write:				;write the selected CP/M sector
	xra	a		;0 to accumulator
	sta	@readop		;not a read operation
	mov	a,c		;write type in c
	sta	@wrtype
	cpi	wrual		;write unallocated?
.z80
	jr	nz,chkunah	;check for unalloc			wm01
.8080

;	write to unallocated, set parameters
	mvi	a,blksizh/128	;next unalloc recs
	sta	@unacnt
	lda	@sekdsk		;disk to seek
	sta	@unadsk		;@unadsk = @sekdsk
	lhld	@sektrk
	shld	@unatrk		;@unatrk = sectrk
	lda	@seksec
	sta	@unasec		;@unasec = @seksec

chkunah:
	;check for write to unallocated sector
	lda	@unacnt		;any unalloc remain?
	ora	a
.z80
	jr	z,alloch	;skip if not				wm01
.8080

;	more unallocated records remain
	dcr	a		;@unacnt = @unacnt-1
	sta	@unacnt
	lda	@sekdsk		;same disk?
	lxi	h,@unadsk
	cmp	m		;@sekdsk = @unadsk?
.z80
	jr	nz,alloch	;skip if not				wm01
.8080

;	disks are the same
	lxi	h,@unatrk
	call	@sektrkcmp	;@sektrk = @unatrk?
.z80
	jr	nz,alloch	;skip if not				wm01
.8080

;	tracks are the same
	lda	@seksec		;same sector?
	lxi	h,@unasec
	cmp	m		;@seksec = @unasec?
.z80
	jr	nz,alloch	;skip if not				wm01
.8080

;	match, @move to next sector for future ref
	inr	m		;@unasec = @unasec+1
	mov	a,m		;end of track?
	cpi	cpmspth		;count CP/M sectors
.z80
	jr	c,noovfh	;skip if no overflow
.8080
;
;	overflow to next track
	mvi	m,0		;@unasec = 0
	lhld	@unatrk
	inx	h
	shld	@unatrk		;@unatrk = @unatrk+1

noovfh:
	;match found, mark as unnecessary read
	xra	a		;0 to accumulator
	sta	@rsflag		;@rsflag = 0
.z80
	jr	rwoperh		;to perform the write			wm02
.8080

alloch:
	;not an unallocated record, requires pre-read
	xra	a		;0 to accum
	sta	@unacnt		;@unacnt = 0
	inr	a		;1 to accum
	sta	@rsflag		;@rsflag = 1

;*	Common code for READ and WRITE follows       *;

rwoperh:
	;enter here to perform the read/write
	xra	a		;zero to accum
	sta	@erflag		;no errors (yet)
	lda	@seksec		;compute host sector
	ora	a		;carry = 0
	rar			;shift right
	ora	a		;carry = 0
	rar			;shift right
	sta	@sekhst		;host sector to seek

;	active host sector?
	lxi	h,@hstact	;host active flag
	mov	a,m
	mvi	m,1		;always becomes 1
	ora	a		;was it already?
.z80
	jr	z,filhsth	;fill host if not			wm02
.8080

;	host buffer active, same as seek buffer?
	lda	@sekdsk
	lxi	h,@hstdsk	;same disk?
	cmp	m		;@sekdsk = @hstdsk?
.z80
	jr	nz,nomatchh						;wm02
.8080

;	same disk, same track?
	lxi	h,@hsttrk
	call	@sektrkcmp	;@sektrk = @hsttrk?
.z80
	jr	nz,nomatchh						;wm02
.8080

;	same disk, same track, same buffer?
	lda	@sekhst
	lxi	h,@hstsec	;@sekhst = @hstsec?
	cmp	m
.z80
	jr	z,matchh	;skip if match				wm02
.8080

nomatchh:
	;proper disk, but not correct sector
	lda	@hstwrt		;host written?
	ora	a
	cnz	writehsth	;clear host buff

filhsth:
	;may have to fill the host buffer
	lda	@sekdsk
	sta	@hstdsk
	lhld	@sektrk
	shld	@hsttrk
	lda	@sekhst
	sta	@hstsec
	lda	@rsflag		;need to read?
	ora	a
	cnz	readhsth	;yes, if 1
	xra	a		;0 to accum
	sta	@hstwrt		;no pending write

matchh:
	;copy data to or from buffer
	lda	@seksec		;mask buffer number
	ani	secmsk		;least signif bits
	mov	l,a		;ready to shift
	mvi	h,0		;double count
	dad	h		;shift left 7
	dad	h
	dad	h
	dad	h
	dad	h
	dad	h
	dad	h
;	hl has relative host buffer address
.z80
	ld	de,@hstbuf
	add	hl,de		;hl = host address
	ld	de,(@dmaadr)	;de = dma address
	ld	bc,128		;length
	ld	a,(@readop)	;which way?
	or	a
	jr	nz,rw@move	;skip if read

;	write operation, mark and switch direction
	ld	a,1
	ld	(@hstwrt),a	;@hstwrt = 1
	ex	de,hl		;source/dest swap

rw@move:	call	@move		;@move a logical sector to/from buffer

.8080
;	data has been @moved to/from host buffer
	lda	@wrtype		;write type
	cpi	wrdir		;to directory?
	lda	@erflag		;in case of errors
	rnz			;no further processing

;	clear host buffer for directory write
	ora	a		;errors?
	rnz			;skip if so
	xra	a		;0 to accum
	sta	@hstwrt		;buffer written
	call	writehsth
	lda	@erflag
	ret

;*	Utility subroutine for 16-bit compare        *;

@sektrkcmp:			;HL = .@unatrk or .@hsttrk, compare with @sektrk
;	.z80
;	ld	bc,(@sektrk)
;	or	a		; clear carry
;	sbc	hl,bc		; hl=hl-bc
;	ret			; return status
	xchg
	lxi	h,@sektrk
	ldax	d		;low byte compare
	cmp	m		;same?
	rnz			;return if not
;	low bytes equal, test high 1s
	inx	d
	inx	h
	ldax	d
	cmp	m	;sets flags
	ret

.z80

readhsth:call	hstcomh
	call	hdread
	ret

writehsth:call	hstcomh
	call	hdwrite
	ret

hstcomh:call	readyh
	ld	bc,(@hsttrk)
	call	trkseth
	ld	a,(@hstsec)
	ld	c,a
	call	secseth
	ret

dohomeh:call	readyh
	xor	a		; seek cyl 0
	out	(hdclo),a
	out	(hdchi),a
	ld	a,hdseek
	out	(hdcmd),a	; set future (implied) seek speed

hdbsy:	in	a,(hdstat)	; controller busy?
	and	80h
	jr	nz,hdbsy
	ret

@diskoff:in	a,(hdstat)	; is it not busy and ready?
	bit	7,a
	ret	nz		; controller busy, exit.
	bit	6,a
	ret	z		; selected disk not ready, exit.
	ld	a, low lzone	; seek lzone
	out	(hdclo),a
	ld	a, high lzone
	out	(hdchi),a
	ld	a,hdseek
	out	(hdcmd),a
	call	hdbsy	; wait till controller is finished issuing seek,
	ld	a,hdselh and hdsmsk	; de-select drive
	out	(hdsdh),a
	ret

; system is on tracks 0 and 1
; spares are on tracks 2 and 3
; the dir is on 4,6,8,10
; dup dir is on 5,7,9,11
; and data starts on 12
trkseth:ld	hl,4		; track >7 then +4
	ld	a,c
	and	0F8H
	or	b
	jr	nz,.set
	ld	hl,0		; then rest are +0
	ld	a,c
	cp	4		; track <4 then no change
	jr	c,.set
	sub	4		; form 4,6,8,10
	add	a,c
	ld	c,a
.set:	add	hl,bc		; hl is track number
	srl	h		; msb is head select
	rr	l
	ld	a,l
	out	(hdclo),a	; to controller
	ld	a,h
	out	(hdchi),a
	ret

secseth:ld	a,c
	out	(hdsec),a
	ret

readyh:	in	a,(bitport)
	and	hdcsel		; select controller, clear reset
	out	(bitport),a
	call	hdbsy		; controller busy?
	ld	a,hdselh		; select drive
	out	(hdsdh),a
sekok:	call	hdbsy		; make sure controller isn't busy first
	in	a,(hdstat)	; seek done?
	cpl			; ones' complement,
	and	01010000b	; are drive ready and seek complete true?
	jr	nz,sekok	; no, keep waiting, else -
	ld	a,(@hsttrk)	; select head
	and	1
	or	hdselh
	push	bc		; save conts of bc
	ld	b,a
	ld	a,(@hstdsk)	; select heads 0-1, or 2-3
	rla			; using disk #
	and	2
	or	b
	pop	bc		; restore bc
	out	(hdsdh),a
	ret

hdwrite:call	hrdwrt		; write sector
	push	af		; save status				wm02
	call	nz,dohomeh	; seek track zero			wm02
	pop	af		; restore status			wm02
	call	nz,hrdwrt	; try again to write			wm02
	call	nz,wrt.err	; if error try to recover
	ld	(@erflag),a	; set error flag to proper status
	in	a,(hdchi)	; do dup write?
	or	a
	ret	nz		; dup write on cly<6 only
	in	a,(hdclo)
	cp	6
	ret	nc
	in	a,(hdsdh)	; get head
	xor	1		; flip to other side
	out	(hdsdh),a
	call	hrdwrt		; do write
	call	nz,wrt.err	; if error try to recover
	ex	af,af		; save error flag of 2nd write
	ld	a,(@erflag)	; was 1st an error?
	or	a
	ret	z		; no so ret ok! (avoid giving cp/m a bad sec)
	ex	af,af		; else return status of 2nd write
	ld	(@erflag),a	; as it MAY be ok.
	ret

hrdwrt:	in	a,(hdclo)
	out	(hdclo),a		; clear data request line.
	ld	bc,0000h+hddata
	ld	hl,@hstbuf
	ld	a,hdwrt		; that's right, you
	out	(hdcmd),a	; issue the command
	otir			; before the data.
	otir
	call	hdbsy
	and	01		; error flag
	ld	a,0FFH		; write ok?
	ret	nz		; return if not
	ld	a,hdred		; do read after write verify
	out	(hdcmd),a
	call	hdbsy
	in	a,(hdstat)	; get status of read
	and	1
	ret	z
	ld	a,0FFH
	ret

hdread:	call	hdrd		; read sector
	push	af		; save status				wm02
	call	nz,dohomeh	; goto track zero			wm02
	pop	af		; restore status			wm02
	call	nz,hdrd		; and try again				wm02
	call	nz,rd.err	; if error try to recover
	ld	(@erflag),a
	ret	z		; read op ok
	in	a,(hdchi)	; try other side of platter?
	or	a
	ret	nz		; cly # to big
	in	a,(hdclo)
	cp	6
	ret	nc
	in	a,(hdsdh)	; flip to other side
	xor	1
	out	(hdsdh),a
	call	hdrd
	call	nz,rd.err	; error, try to recover
	ld	(@erflag),a
	ret

hdrd:	ld	a,hdred			; read a sector
	out	(hdcmd),a
	ld	bc,0000h+hddata
	ld	hl,@hstbuf
	call	hdbsy
	inir				; get bytes before checking status
	inir				; so that even if sector is bad
	in	a,(hdstat)		; some of it may be recovered
	and	01
	ret	z		; no error
	ld	a,0FFH		; error flag
	ret
;
ioimageh:
;@move:				; block memory @move, turn rom on/off
	in	a,(bitport)	; turn rom off
	res	7,a
	out	(bitport),a
	ldir			; @move logical sector from @hstbuf
	in	a,(bitport)	; turn rom back on
	set	7,a
	out	(bitport),a
	ret			; back to rom

image_length  equ    $-ioimageh		; length of this image
title Floppy disk support routines.    (C) 1983 By NLS
.comment %
########################################################
##                                                    ##
##      KAYPRO 10 System                              ##
##                                                    ##
##      By G. Ohnysty                                 ##
##                                                    ##
##      Disk support routines (Deblocking for floppy) ##
##                                                    ##
##      Copyright (C) 1983 By Non-Linear Systems, Inc ##
##      No warranty is made, expressed or implied.    ##
##                                                    ##
########################################################
##      Date: 04/14/83                    [01]        ##
########################################################
%
.z80

public	$home, $seldsk, $settrk, $setsec, $setdma, $read, $write, sectran
public	$diskinit, diskon, $diskoff, thnsd

bitport	equ	20		; bit port (m80 does not support extrn bytes)
drvmask	equ	0FEH		; drive select mask
denmask	equ	0DFH		; density bit mask
driveA	equ	00H		; drive A select bit
ddbit	equ	00H		; double density bit
sdbit	equ	20H		; single density bit
sidmask	equ	0FBH		; side select mask
sid0	equ	4H		; side 0 bit
sid1	equ	0H		; side 1 bit
control	equ	16		; I/O port of disk controller
status	equ	control+0	; status register
cmnd	equ	control+0	; command register
track	equ	control+1	; track register
sector	equ	control+2	; sector register
data	equ	control+3	; data register
ficmd	equ	11010000B	; force interrupt (Abort current command)
rdcmd	equ	10001000B	; read command
wrtcmd	equ	10101100B	; write command
;***************************************************************************
;* seek time at a clock rate of 1 MHZ				WM01	   *
;*									   *
;* bits	1	0	seek time					   *
;*	0	0	6  ms						   *
;*	0	1	12 ms						   *
;*	1	0	20 ms						   *
;*	1	1	30 ms						   *
;***************************************************************************
seekcmd	equ	00010000B	; seek command				WM01
rstcmd	equ	00000000B	; home (restore) command
adrcmd	equ	11000100B	; read track address
rdmask	equ	10011100B	; read status mask
wrtmask	equ	11111100B	; write status mask
tries1	equ	2		; re-home on bad sector # of tries+1
tries2	equ	5		; re-read/write # of retries+1
ssmblk	equ	194
dsmblk	equ	ssmblk*2
retcod	equ	0C9H		; return op code
nmivec	equ	0066H	; non-maskable interupt vector (used in rd/wt loop)
page
; This section defines the disk parameters (dph's are images moved to RAM)
dph0:	defw	0,0,0,0		; dph for unit A:
	defw	dirbuf,$dpb	; directory buffer, Disk Parameter Block
	defw	csva, alva	; check sum pointer, allocation map pointer

;dpb	;( double density );
	defw	40		; (spt) sectors per track
	defb	3		; (bsh) block shift factor
	defb	7		; (blm) block mask
	defb	0		; (exm) extent mask
	defw	194		; (dsm) max logical block #
	defw	63		; (drm) max directory #
	defb	0F0H		; (al0) directory allocation map & BIOS space
	defb	00H		; (al1)
	defw	16		; (cks) size of directory check vector
	defw	1		; (off) reserved tracks

enddph:

dpb:   ;( double sided double density );
	defw	40		; (spt) sectors per track
	defb	4		; (bsh) block shift factor
	defb	15		; (blm) block mask
	defb	1		; (exm) extent mask
	defw	196		; (dsm) max logical block #
	defw	63		; (drm) max directory #
	defb	0C0H		; (al0) directory allocation map & BIOS space
	defb	00H		; (al1)
	defw	16		; (cks) size of directory check vector
	defw	1		; (off) reserved tracks

subttl Logical BIOS entry points & Deblocking
page
.8080
;*****************************************************
;*      Logical BIOS entry points                    *
;*      Sector Deblocking Algorithms                 *
;*****************************************************
blksiz	equ	1024		;CP/M allocation size
hstsiz	equ	512		;host disk sector size
hstspt	equ	10		;host disk sectors/trk
hstblk	equ	hstsiz/128	;CP/M sects/host buff
cpmspt	equ	hstblk * hstspt	;CP/M sectors/track
secmsk	equ	hstblk-1	;sector mask
secshf	equ	2		;log2(hstblk) sector mask
wrall	equ	0		;write to allocated
wrdir	equ	1		;write to directory
wrual	equ	2		;write to unallocated

$diskinit:	;enter here on system boot to initialize
.z80
	ld	hl,ioimage	;move rd/wrt routines into RAM
	ld	de,move
	ld	bc,imaglen
	ldir
	ld	hl,dph0		; set dph's
	ld	de,dpha
	ld	bc,enddph-dph0
	ldir
.8080
	xra	a		;0 to accumulator
	sta	hstact		;host buffer inactive
	sta	unacnt		;clear unalloc count
	cma
	sta	dsk		;clear disk number
	ret

$seldsk:			;select disk
	mov	a,c		;selected disk number
	sta	sekdsk		;seek disk number
	jmp	dsksel		;physical disk select (If needed to check den)

$setsec:			;set sector given by register c 
	mov	a,c
	sta	seksec		;sector to seek
	ret

.z80
$setdma: ld	(dmaadr),bc	;set dma address given by BC
	ret

$settrk: ld	(sektrk),bc	;set track given by registers BC
.8080
	ret

$home:	lda	hstwrt		; patch by DRI
	ora	a
.z80
	jr	nz,homed	;					wm02
.8080
	sta	hstact
homed:	jmp	dohome		; go do home disk drive

$read:				;read the selected CP/M sector
	xra	a		; a patch by DRI
	sta	unacnt
	mvi	a,1
	sta	readop		;read operation
	sta	rsflag		;must read data
	mvi	a,wrual
	sta	wrtype		;treat as unalloc
.z80
	jr	rwoper		;to perform the read			wm02
.8080

$write:				;write the selected CP/M sector
	xra	a		;0 to accumulator
	sta	readop		;not a read operation
	mov	a,c		;write type in c
	sta	wrtype
	cpi	wrual		;write unallocated?
.z80
	jr	nz,chkuna	;check for unalloc			wm02
.8080

;	write to unallocated, set parameters
	mvi	a,blksiz/128	;next unalloc recs
	sta	unacnt
	lda	sekdsk		;disk to seek
	sta	unadsk		;unadsk = sekdsk
	lhld	sektrk
	shld	unatrk		;unatrk = sectrk
	lda	seksec
	sta	unasec		;unasec = seksec

chkuna:
	;check for write to unallocated sector
	lda	unacnt		;any unalloc remain?
	ora	a
.z80
	jr	z,alloc		;skip if not				wm02

.8080
;	more unallocated records remain
	dcr	a		;unacnt = unacnt-1
	sta	unacnt
	lda	sekdsk		;same disk?
	lxi	h,unadsk
	cmp	m		;sekdsk = unadsk?
.z80
	jr	nz,alloc	;skip if not				wm02
.8080

;	disks are the same
	lxi	h,unatrk
	call	sektrkcmp	;sektrk = unatrk?
.z80
	jr	nz,alloc	;skip if not				wm02
.8080

;	tracks are the same
	lda	seksec		;same sector?
	lxi	h,unasec
	cmp	m		;seksec = unasec?
.z80
	jr	nz,alloc	;skip if not				wm02
.8080

;	match, move to next sector for future ref
	inr	m		;unasec = unasec+1
	mov	a,m		;end of track?
	cpi	cpmspt		;count CP/M sectors
.z80
	jr	c,noovf		;skip if no overflow			wm02
.8080

;	overflow to next track
	mvi	m,0		;unasec = 0
	lhld	unatrk
	inx	h
	shld	unatrk		;unatrk = unatrk+1

noovf:
	;match found, mark as unnecessary read
	xra	a		;0 to accumulator
	sta	rsflag		;rsflag = 0
.z80
	jr	rwoper		;to perform the write			wm02
.8080

alloc:
	;not an unallocated record, requires pre-read
	xra	a		;0 to accum
	sta	unacnt		;unacnt = 0
	inr	a		;1 to accum
	sta	rsflag		;rsflag = 1

;*	Common code for READ and WRITE follows       *;

rwoper:
	;enter here to perform the read/write
	xra	a		;zero to accum
	sta	erflag		;no errors (yet)
	lda	seksec		;compute host sector
	ora	a		;carry = 0
	rar			;shift right
	ora	a		;carry = 0
	rar			;shift right
	sta	sekhst		;host sector to seek

;	active host sector?
	lxi	h,hstact	;host active flag
	mov	a,m
	mvi	m,1		;always becomes 1
	ora	a		;was it already?
.z80
	jr	z,filhst	;fill host if not			wm02
.8080

;	host buffer active, same as seek buffer?
	lda	sekdsk
	lxi	h,hstdsk	;same disk?
	cmp	m		;sekdsk = hstdsk?
.z80
	jr	nz,nomatch	; 					wm02

.8080
;	same disk, same track?
	lxi	h,hsttrk
	call	sektrkcmp	;sektrk = hsttrk?
.z80
	jr	nz,nomatch	;					wm02
.8080

;	same disk, same track, same buffer?
	lda	sekhst
	lxi	h,hstsec	;sekhst = hstsec?
	cmp	m
.z80
	jr	z,match		;skip if match				wm02
.8080

nomatch:
	;proper disk, but not correct sector
	lda	hstwrt		;host written?
	ora	a
	cnz	writehst	;clear host buff

filhst:
	;may have to fill the host buffer
	lda	sekdsk
	sta	hstdsk
	lhld	sektrk
	shld	hsttrk
	lda	sekhst
	sta	hstsec
	lda	rsflag		;need to read?
	ora	a
	cnz	readhst		;yes, if 1
	xra	a		;0 to accum
	sta	hstwrt		;no pending write

match:
	;copy data to or from buffer
	lda	seksec		;mask buffer number
	ani	secmsk		;least signif bits
	mov	l,a		;ready to shift
	mvi	h,0		;double count
	dad	h		;shift left 7
	dad	h
	dad	h
	dad	h
	dad	h
	dad	h
	dad	h
;	hl has relative host buffer address
.z80
	ld	de,hstbuf
	add	hl,de		;hl = host address
	ld	de,(dmaadr)	;de = dma address
	ld	bc,128		;length
	ld	a,(readop)	;which way?
	or	a
	jr	nz,rwmove	;skip if read

;	write operation, mark and switch direction
	ld	a,1
	ld	(hstwrt),a	;hstwrt = 1
	ex	de,hl		;source/dest swap

rwmove:	call	move		;move a logical sector to/from buffer

.8080
;	data has been moved to/from host buffer
	lda	wrtype		;write type
	cpi	wrdir		;to directory?
	lda	erflag		;in case of errors
	rnz			;no further processing

;	clear host buffer for directory write
	ora	a		;errors?
	rnz			;skip if so
	xra	a		;0 to accum
	sta	hstwrt		;buffer written
	call	writehst
	lda	erflag
	ret

;*	Utility subroutine for 16-bit compare        *;

sektrkcmp:			;HL = .unatrk or .hsttrk, compare with sektrk
	xchg
	lxi	h,sektrk
	ldax	d		;low byte compare
	cmp	m		;same?
	rnz			;return if not
;	low bytes equal, test high 1s
	inx	d
	inx	h
	ldax	d
	cmp	m	;sets flags
	ret
subttl Physical disk routines
page
.z80
; select disk drive, C=drive number 0=A:, 1=B:
; return HL=dph for selected drive, or HL=0 for non-existent drive
dsksel:	ld	hl,0		; hl = 0 for non-existent drive
	ld	a,c
	or	a
	ret	nz		; drive number >B:
	ld	hl,dpha		; select proper dph for drive
	ld	a,(dsk)		; selecting disk already selected?
	cp	c
	ret	z		; yes, no further action needed
	xor	a		; set sid flag
	ld	(dsk),a		; only valid drive is A:
	ld	(sidflg),a	; single sided flag
	push	hl		; save pointer to dph
	call	dohome		; trk=0, dd-den, sid0, drvA
	in	a,(bitport)	; select side 1
	and	sidmask
	or	sid1
	out	(bitport),a
	call	dcheck		; can read?
	pop	hl		; if nz then can't
	ret	nz
	ld	a,(adrbuf+2)	; get sector number of side 1
	cp	10		; on other side? (side 1 sectors 10 to 19)
	ret	c		; if c then no
	push	hl
	ld	de,$dpb		; adjust dpb in ram
	ld	hl,dpb
	ld	bc,15
	ldir
	ld	a,0FFH		; double sided flag
	ld	(sidflg),a
	pop	hl		; pointer to dph
	ret

dcheck:	push	hl		; save hl and bc
	push	bc
	ld	hl,adrbuf	; buffer space
	ld	bc,6*256+data	; read 6 bytes from data port
	ld	a,adrcmd
	out	(cmnd),a
dchk1:	halt			; wait for drq
	ini
	jr	nz,dchk1
	call	busy		; wait for intrq
	bit	4,a		; test rnf flag
	pop	bc
	pop	hl
	ret

; home disk head ( set  trk=0, drv=A, sid=0, motor=on)
dohome:	call	ready		; make sure drive is on and ready
	in	a,(bitport)
	and	sidmask
	or	sid0
	out	(bitport),a	; select side 0
	ld	a,rstcmd	; restore command
	out	(cmnd),a	; issue command
	jr	busy		; test and wait for not busy

; seek track #, BC=Track #
trkset:	call	ready		; make sure drive is on and ready
	in	a,(bitport)	; set proper sense to side
	and	sidmask
	ld	b,a		; save in b
	ld	a,(sidflg)	; check flag
	or	a
	ld	a,sid0		; side 0 bit
	jr	z,outtrk
	srl	c		; double sided trk=trk/2
	jr	nc,outtrk	; if lsb=0 then side 0
	ld	a,sid1		; else side 1
outtrk:	or	b		; or in conts of bitport
	out	(bitport),a
	ld	a,c
	out	(data),a	; issue req. track to controller
	ld	a,seekcmd	; seek command
	out	(cmnd),a	; issue command
	jr	busy		; test and wait for not busy

; select sector #, BC=Sector #
secset:	in	a,(bitport)	; single or double sided?
	and	not sidmask
	cp	sid0
	ld	a,c		; pure sector number in a
	jr	z,secx		; single sided
	add	a,10		; double sided sector disp.
secx:	out	(sector),a	; to controller register
	ret

; perform logical to physical sector translation.
; logical sector number in BC, table address in DE
; return physical sector number in HL
sectran:ld	a,d	; table address 0?
	or	e
	ld	h,b	; if so no xlate
	ld	l,c
	ret	z
	ex	de,hl	; table address in hl
	add	hl,bc	; index by logical sector number
	ld	l,(hl)
	ld	h,0
	ret

; ready disk drive, perform physical disk select, set density bit
ready:	push	hl		; save hl
	push	de		; and de
	push	bc
	ld	a,ficmd		; abort any controller action
	out	(cmnd),a
	in	a,(bitport)	; select drive
	and	denmask and drvmask
;both	or	driveA
;are 0	or	ddbit
	out	(bitport),a	; to bit port
	call	diskon		; turn drive motor on
	pop	bc
	pop	de
	pop	hl
	ret

; turn disk motor on, delay for drive speed
diskon:	in	a,(bitport)	; get current drive motor status
	bit	4,a		; is motor on?
	ret	nz		; motor on, do nothing
	set	4,a		; motor on bit
	out	(bitport),a	; turn motor on
	ld	b,50		; delay
	call	thnsd
	ret

; turn disk motor off, de-select drive
$diskoff:in	a,(bitport)
	res	4,a		; motor off bit
	set	0,a
	out	(bitport),a
	ret

; delay for B th's @ 4Mhz (each call <=> one hundredh of a sec.)
thnsd:	ld	de,1670
tlp:	dec	de
	ld	a,d
	or	e
	jp	nz,tlp
	djnz	thnsd
	ret

; check status of controller, wait for command to finish executing
busy:	halt			; wait for command done
bsy:	in	a,(status)	; now wait for not busy
	bit	0,a
	jr	nz,bsy
	ret
subttl Writehst and Readhst logical to Physical routines
page
;*	WRITEHST performs the physical write to      *;
;*	the host disk, READHST reads the physical    *;
;*	disk.					     *;

writehst:;hstdsk = host disk #, hsttrk = host track #,
	;hstsec = host sect #. write "hstsiz" bytes
	;from hstbuf and return error flag in erflag.
	;return erflag non-zero if error
	ld	l,3		; read after write retries
chk0:	ld	de,tries1*256+tries2	; retry error counts
wrthst:	push	hl
	push	de		; save error counts
	call	hstcom		; set track and sector
	call	wrt512		; read sector
	pop	de		; restore error flags
	pop	hl		; restore r/w error count
	jr	z,wrtchk	; do read after write
	dec	e		; retry count
	jr	nz,wrthst	; try again
	dec	d		; home and reseek count
	jr	z,chk3		; can't recover
	call	dohome		; re seek
	ld	e,tries2	; reset retry count
	jr	wrthst
wrtchk:	ld	b,0		; dummy read loop to check sector
	ld	a,rdcmd
	out	(cmnd),a
chk1:	halt
	in	a,(data)
	djnz	chk1
chk2:	halt
	in	a,(data)
	djnz	chk2
	call	busy		; get status
	and	rdmask
chk3:	ld	(erflag),a	; error return flag
	ret	z
	dec	l
	jr	nz,chk0		; try again
	ld	a,0ffh		; bail out, error
	jr	chk3

readhst:;hstdsk = host disk #, hsttrk = host track #,
	;hstsec = host sect #. read "hstsiz" bytes
	;into hstbuf and return error flag in erflag.
	ld	de,tries1*256+tries2	; retry error counts
rdhst:	push	de		; save error counts
	call	hstcom		; set track and sector
	call	rd512		; read sector
	ld	(erflag),a	; error return flag
	pop	de		; restore error flags
	ret	z		; good op
	dec	e		; retry count
	jr	nz,rdhst	; try again
	dec	d		; home and reseek count
	ret	z		; can't recover
	call	dohome		; re seek
	ld	e,tries2	; reset retry count
	jr	rdhst

hstcom:	ld	a,(hstdsk)	; select disk
	ld	c,a
	call	dsksel
	ld	bc,(hsttrk)	; set track to hsttrk
	call	trkset		; physical seek
	ld	a,(hstsec)	; set physical sector
	ld	c,a		; c=sector
	call	secset
	ret
subttl Physical disk I/O, RAM image
page
ioimage:
;move:				; block memory move, turn rom on/off
	in	a,(bitport)	; turn rom off
	res	7,a
	out	(bitport),a
	ldir			; move logical sector from hstbuf
	in	a,(bitport)	; turn rom back on
	set	7,a
	out	(bitport),a
	ret			; back to rom

;rd128:
	ld	hl,(dmaadr)	; address of operation
	ld	b,1		; read a 128 byte sector
	jr	rd
;rd512:
	ld	hl,hstbuf
	ld	b,4		; read a 512 byte sector
; read a sector, return A=0 for no errors, A=1 for non-recoverable error
; if b=1 128, b=2 256, b=3 384, b=4 512   bytes/sector
rd:	ld	de,rdmask*256+rdcmd	; d=read status mask, e=read command
	jr	action

;wrt128:
	ld	hl,(dmaadr)
	ld	b,1		; write a 128 byte sector
	jr	wrt
;wrt512:
	ld	hl,hstbuf
	ld	b,4		; write a 512 byte sector
; write a sector, return as per read
wrt:	ld	de,wrtmask*256+wrtcmd ; d=status mask, e=write command
	;fall through to action

action:	call	ready			; make sure drive is on and ready
	di				; no interrupts during disk I/O operations
	in	a,(bitport)		; turn rom off
	res	7,a
	out	(bitport),a
	push	hl			; save address of disk buffer
	ld	hl,nmivec		; set up nmi vector
	ld	a,(hl)			; save current contents
	ex	af,af'
	ld	(hl),retcod		; this is a return after HALT in loop
	pop	hl			; hl = dma address
	ld	a,b			; sector multiple
	ld	bc,128*256+data		; b=sector length, c=data port
	bit	0,a			; if 0 then 256 or 512 bytes/sector
	jr	nz,actn			; b set for 128 or 384 bytes/sector
	ld	b,0			; b set for 256 or 512 bytes/sector
actn:	cp	1			; compute entry point 1st or 2nd loop
	push	psw			; save as Z flag
	ld	a,e			; i/o command
	cp	wrtcmd			; a write?
	jr	z,wstart		; start write command

	out	(cmnd),a		; fall through to read loop
	pop	psw
	jr	z,rl2
rl1:	halt				; wait for controller
	ini
	jr	nz,rl1
rl2:	halt
	ini
	jr	nz,rl2
	jr	done			; read loop done, exit

wstart:	out	(cmnd),a		; write loop
	pop	psw
	jr	z,wl2
wl1:	halt
	outi
	jr	nz,wl1
wl2:	halt
	outi
	jr	nz,wl2

done:	ex	af,af'			; byte at nmi vector address
	ld	(nmivec),a		; restore it
	in	a,(bitport)		; turn rom back on
	set	7,a
	out	(bitport),a
	ei				; turn interrupts on
	call	busy			; get status when contoller not busy
	and	d			; status mask
	ret	z			; no bit set, return operation ok
	ld	a,1			; cp/m error return
	ret

imaglen	equ	$-ioimage		; length of this image
title Video driver routines for the KAYPRO-10  (C) 1983 By NLS.
.comment %
########################################################
##                                                    ##
##      KAYPRO 10 System                              ##
##                                                    ##
##      By M. Sherman                                 ##
##                                                    ##
##      Video driver routines for the KAYPRO-10       ##
##      and the 6545 video controller chip.           ##
##                                                    ##
##      Copyright (C) 1983 By Non-Linear Systems, Inc ##
##      No warranty is made, expressed or implied.    ##
##                                                    ##
########################################################
##      Date: 04/14/83                    [01]        ##
########################################################
%


; routines for everyone else to use
public	vidout, vidinit, regrst, dtwait, clrdis, clreol, carret, putcur
public	getc, putc, getatt, putatt, print

.Z80

; conditional assembly equates

TRUE	equ	0ffffh
FALSE	equ	NOT TRUE

; video controller locations

vcbase	equ	1ch		; video controller base address
vccmd	equ	vcbase		; register select port
vcstat	equ	vcbase		; status port
vcrdat	equ	vcbase+1	; register data port
vcdata	equ	vcbase+3	; video controller data port

; command format, video controller commands:
; high byte = register to select, low byte = base addr. (register select)

curcmd	equ	0e1ch		; place cursor command
rwcmd	equ	121ch		; read/write command
strcmd	equ	01fh		; strobe, or "tickle", command
scrcmd	equ	0c1ch		; set start of display address command
				; ("scroll" command)
hiadd	equ	12h	; high byte register #, video mem. address,
loadd	equ	13h	; low byte register #, video mem. address.
cstart	equ	0ah	; cursor starting row count, cursor def. reg #
cstop	equ	0bh	; cursor ending row count.

csron	equ	60h	; cursor on, blinking at 1/32, starting row=0
csroff	equ	20h	; no cursor, starting row=0 (irrelevant)

; special character equates

space	equ	020h
nrmlatt	equ	00h

; single character control codes

belli	equ	07h	; bell code to video driver,
bello	equ	04h	; bell code to keyboard.
cr	equ	0dh	; carriage return
lf	equ	0ah	; line feed
ceol	equ	18h	; clear to end of line
ceos	equ	17h	; clear to end of screen
clrscr	equ	1ah	; clear screen
homec	equ	1eh	; home cursor
lcur	equ	08h	; left cursor (backspace)
rcur	equ	0ch	; right cursor (forespace)
ucur	equ	0bh	; up cursor
esc	equ	1bh	; escape code, initiates multi-
			; -character control sequences

; two-character commands

dline	equ	'R'	; delete line
iline	equ	'E'	; insert line

; three-character commands

atton	equ	'B'	; set attribute
attoff	equ	'C'	; clear attribute

; four-character commands

setpix	equ	'*'	; set pixel
clrpix	equ	' '	; clear pixel
lodcur	equ	'='	; load cursor address (cursor positioning)

; six-character commands

lindraw	equ	'L'	; draw a line
lineras	equ	'D'	; erase a line

; video driver equates

linesiz	equ	80		; characters per line
linesps	equ	24		; number of lines in the normal display
statlin	equ	linesps+1	; line number, status line
lastlin	equ	(linesps-1)*linesiz	; address, first chara last
					; normal display line
					; (the line above the status line)

;################################################
;#						#
;#		video drivers			#
;#						#
;################################################

	; clear to end of line
clreol:	call	caleol		; calculate end of line count
	jr	clrdis

	; clear to end of screen
clreos:	ld	c,linesps-1
	ld	a,(vatt)
	and	20h
	jr	nz,ceos22
	inc	c
ceos22:	ld	a,(crow)
	sub	c
	jr	nc,clreol	; clear to end of line if on last legal line
	neg			; two's complement, number of lines to erase
	ld	b,a
	ld	de,linesiz
	ld	hl,0
clresl:	add	hl,de
	djnz	clresl
	push	hl
	call	caleol
	pop	bc
	add	hl,bc		; total count in hl
	jr	clrdis		; do it

caleol:	ld	hl,linesiz
	ld	de,(cursor)
	ld	a,(ccol)
	ld	c,a
	xor	a		; clear a, clear flags (especially carry!)
	ld	b,a		; clear b
	sbc	hl,bc		; hl=number of bytes to move
	ret

vidinit:; Video hardware/software initialization routine.  Will set
	; video driver ram storage to reset/restart values,
	; reprogram the video controller chip,
	; clear the screen and place the cursor in the upper right corner.
	;
ramini:	ld	hl,vidram	; first, initialize the ram.
	ld	b,ramlen
	xor	a
rinilp:	ld	(hl),a
	inc	hl
	djnz	rinilp

ctrini:	ld	hl,ctrtbl	; then initialize the controller,
	ld	bc,ctblen*256+vcbase+1
	xor	a		; first register,=00
cinilp:	dec	c		; c:=base
	out	(c),a		; select register
	inc	a		; a:=register to program
	inc	c		; c:=data port
	outi			; (hl):=program data, out to (c)
	jr	nz,cinilp	; until b:=0
	ld	a,strcmd
	out	(vccmd),a	; start video chip processing.
	; fall through to clear screen
clear:	call	home		; home cursor
clear2:	ld	a,(vatt)
	and	0f0h		; clear ordinary attributes
	ld	(vatt),a	; clear attribute byte
	ld	de,(cursor)	; same as vrbase, now
	ld	hl,800h		; new screen size to eliminate phantom cursor wm03
;******************************************************************************
;*	ld	hl,statlin*linesiz	; screen size = 7d0H		      *
;******************************************************************************
	and	20h
	jr	z,clrdis
	ld	hl,linesps*linesiz
	; fall through to clrdis...

clrdis:	; clear display and associated attributes.
	; de := start address, hl := number of locations to clear
	; all registers affected...
	;
	ld	bc,hiadd*100h+loadd
cdislp:	in	a,(vcstat)
	or	a
	jp	p,cdislp	; wait until ready,
	ld	a,b		; high address byte register number,
	out	(vccmd),a	; select it
	ld	a,d		; get high byte, new address,
	and	07h		; qualify address,
	ld	d,a		; put it back,
	out	(vcrdat),a	; output it.
	ld	a,c		; select
	out	(vccmd),a	; low address byte register,
	ld	a,e		; get low address byte,
	out	(vcrdat),a	; output it.
	ld	a,strcmd
	out	(vccmd),a	; start a new cycle,
cdislp3:in	a,(vcstat)	; wait until it's ready,
	or	a
	jp	p,cdislp3
	ld	a,20h		; clear data byte,
	out	(vcdata),a
	inc	de		; set up for attr., next byte
cdislp2:in	a,(vcstat)	; go do attributes
	or	a
	jp	p,cdislp2	; jif until finished
	ld	a,b		; high address byte register number,
	out	(vccmd),a	; select it
	ld	a,d		; get high byte, new address,
	or	08h		; qualify address,
	out	(vcrdat),a	; output it.
	ld	a,c		; select
	out	(vccmd),a	; low address byte register,
	ld	a,e		; get low address byte,
	out	(vcrdat),a	; output it.
	ld	a,strcmd
	out	(vccmd),a	; start a new cycle,
cdislp4:in	a,(vcstat)	; wait until finished.
	or	a
	jp	p,cdislp4	; jif until finished
	xor	a		; clear attribute byte
	out	(vcdata),a
	dec	hl
	ld	a,h
	or	l
	jr	nz,cdislp
	ret

home:	xor	a
	ld	(ccol),a	; reset column count
	ld	(crow),a	; reset row count
	ld	hl,(vrbase)
	ex	de,hl
	jp	putcur		; place cursor and exit

; video controller initialization table, currently for a 25 by 80 display.
;
ctrtbl:	db	6ah	; reg00 total char/sweep including retrace, clocks
	db	50h	; reg01 total displayed, cclks
	db	56h
	db	99h
	db	19h
	db	0ah
	db	19h
	db	19h
	db	78h
	db	0fh
	db	60h
	db	0fh
	db	00h
	db	00h
	db	00h
	db	00h
ctblen	equ	$-ctrtbl	; table length

; main entry point.

vidout:	ld	a,(leadflg)	; set by escape sequences
	or	a
	jp	nz,escseq	; an escape sequence is in progress	
	ld	a,c
	or	a
	ret	z		; ignore nulls (requested by tech support)
	jp	m,vgmod		; video mode set? find out if negative (>80h)
	cp	space
	jp	c,spechar	; special characters
spcexe:	ld	a,c
	ld	de,(cursor)	; special character re-entry if non-control
	call	putc
	call	puta		; place attribute
vgmexe:	ld	a,(ccol)
	inc	a
	cp	linesiz
	jr	nc,crlf							;wm01
	ld	(ccol),a	; save new count
	ld	de,(cursor)
	inc	de
	jr	putcur		; reposition cursor and exit		wm01

vgmod:	ld	a,(vatt)
	and	10h
	jr	z,spcexe	; not video graphics mode if not zero
	ld	a,(vgb1)
	and	40h
	jr	z,vgmod2
	ld	a,c
	and	01
	ld	(vgb1),a
	ret
vgmod2:	ld	a,(vgb1)
	or	a
	ld	a,c
	jr	z,vgmod5
	cpl
vgmod5:	or	80h
	ld	de,(cursor)
	call	putc
	ld	a,(vgb1)
	ld	c,a
	ld	a,(vatt)
	or	c
	call	putatt
	ld	a,40h
	ld	(vgb1),a	; set first
	jr	vgmexe

; move the cursor to the beginning of the line

carret:	ld	hl,(cursor)
	ld	a,(ccol)
	ld	e,a
	xor	a		; clear flags,a
	ld	d,a
	ld	(ccol),a	; reset line count to zero
	sbc	hl,de		; hl = beginning of line
	ex	de,hl		; de = beginning of line
	jr	putcur		; place cursor and exit

; crlf places the cursor at the beginning of the next line and sets the
; character column count, ccol, to zero.

crlf:	call	carret		; carriage return
	; fall through to linefeed...

; move the cursor down one line, scroll if necc.

linefd:	ld	a,(crow)	; character row count
	cp	linesps-1	; lines per screen
	jr	c,linef2	; not last line if carry,
	cp	statlin-1	; status line?
	ret	z		; if so, don't scroll
	call	scroll		; else is last line, scroll screen
	jr	linef3		; don't update character row count.

linef2:	inc	a		; update character row count,
	ld	(crow),a

linef3:	ld	hl,(cursor)	; move the cursor down one line.
	ld	de,linesiz
	add	hl,de
	ex	de,hl
	; fall through to putcur...

; place cursor, new cursor address in de
putcur:	ld	a,d
	and	07h
	ld	d,a
	ex	de,hl
	ld	(cursor),hl
	ld	bc,(vrbase)
	sbc	hl,bc
	jr	nc,putcr2
	ld	de,0800h
	add	hl,de
putcr2:	add	hl,bc
	ex	de,hl
	ld	bc,curcmd
	jp	regrst

upcur:	ld	a,(crow)
	cp	statlin-1
	ret	z		; no cursor up from status line,
	or	a
	ret	z		; or from top line
	dec	a
	ld	(crow),a	; update row count
	ld	hl,(cursor)
	ld	de,linesiz
	sbc	hl,de
	ex	de,hl		; put new value in de
	jr	putcur

lfcur:	ld	a,(ccol)
	or	a
	jr	nz,lcur2
	ld	a,(crow)
	or	a
	ret	z		; no way can do
	cp	statlin-1	; on status line?
	jr	z,lcur3
	dec	a
	ld	(crow),a	; update row count
	ld	a,linesiz
lcur2:	dec	a
	ld	(ccol),a	; update column count
	ld	de,(cursor)
	dec	de
	jr	putcur		; place and exit
lcur3:	ld	a,linesiz-1
	ld	(ccol),a	; going to the end of the line
	ld	hl,(cursor)
	ld	de,linesiz-1
	add	hl,de
	ex	de,hl
	jr	putcur

rtcur:	ld	a,(ccol)
	cp	linesiz-1
	jp	nc,crlf		; do a cr, do a lf if not status line
	ld	de,(cursor)
	inc	de
	inc	a
	ld	(ccol),a	; reset column count
	jr	putcur

scroll:	jp	movsts		; fast scroll

setatr:	ld	hl,vatt
	ld	a,c
	sub	30h
	jr	z,revid		; set reverse video on
	dec	a
	jr	z,redint	; set reduced intensity on
	dec	a
	jr	z,sblink	; set blinking on
	dec	a
	jr	z,sunlin	; set underlining on
	dec	a
	jr	z,setcur	; set cursor on
	dec	a
	jr	z,setvid	; set video mode on
	dec	a
	jr	z,savcur	; save current cursor location
	dec	a
	jr	z,savsts	; save contents of status line during scroll
	ret			; illegal, exit

; set attributes

revid:	ld	a,(hl)
	or	01h
	ld	(hl),a
	ret
redint:	ld	a,(hl)
	or	02h
	ld	(hl),a
	ret
sblink:	ld	a,(hl)
	or	04h
	ld	(hl),a
	ret
sunlin:	ld	a,(hl)
	or	08h
	ld	(hl),a
	ret
setcur:	ld	c,csron		; cursor on, 1/16 blink
setcr2:	ld	a,cstart	; cursor select register
	out	(vccmd),a
	ld	a,c
	out	(vcdata),a	; turn on cursor, 1/16 blink
	ret
setvid:	ld	a,(vatt)	; turn on video mode.
	or	10h		; (GB1,GB2 graphics pairs)
	ld	(vatt),a
	ld	a,40h
	ld	(vgb1),a
	ret

savcur:	ld	hl,(crow)	; save, or 'remember', current cursor position
	ld	(precur),hl
	ret

savsts:	ld	a,(vatt)	; turn on status line preservation,
	or	00100000b	; protect it from scrolling.
	ld	(vatt),a
	ret

; clear attributes

clratr:	ld	hl,vatt
	ld	a,c
	sub	30h
	jr	z,nrmvid	; set normal video on
	dec	a
	jr	z,nrmint	; set normal intensity on
	dec	a
	jr	z,cblink	; set blinking off
	dec	a
	jr	z,cunlin	; set underlining off
	dec	a
	jr	z,clrcur	; set cursor off
	dec	a
	jr	z,clrvid	; set video mode off
	dec	a
	jr	z,rstcur	; restore cursor to last loc.
	dec	a
	jr	z,scrsts	; scroll contents of status line during scroll
	ret			; illegal, exit

; clear attributes:

nrmvid:	ld	a,(hl)		; set to non-inverted display mode.
	and	11111110b
	ld	(hl),a
	ret
nrmint:	ld	a,(hl)		; set to normal intensity
	and	11111101b
	ld	(hl),a
	ret
cblink:	ld	a,(hl)		; set to no blinking.
	and	11111011b
	ld	(hl),a
	ret
cunlin:	ld	a,(hl)		; set to no underlining.
	and	11110111b
	ld	(hl),a
	ret
clrcur:	ld	c,csroff	; turn cursor off
	jr	setcr2
clrvid:	ld	a,(hl)		; turn off video mode
	and	11101111b
	ld	(hl),a
	ret
rstcur:	ld	hl,(precur)	; return cursor to last remembered location.
	ld	a,h		; ccol
	add	a,space
	ld	(col),a
	ld	a,l
	add	a,space
	ld	(row),a
	jp	curpos		; restore previously saved cursor
scrsts:	ld	a,(vatt)	; turn off status line preservation,
	and	11011111b	; scroll status line on scrolls
	ld	(vatt),a
	ret

; X,Y cursor positioning routine
;
curpos:	ld	hl,0
	ld	c,l	; set c to zero, too.
	ld	a,(row)
	sub	space
	ret	c	; error, exit
	ld	b,a
	jr	z,curpo3
	cp	statlin		; lines per screen
	ret	nc		; error, exit
	ld	de,linesiz
curpo2:	add	hl,de
	djnz	curpo2
curpo3:	ld	e,a	; save row count
	ld	a,(col)
	sub	 space
	ret	c	; error, exit
	cp	linesiz
	ret	nc	; error, exit
	ld	c,a
	ld	(ccol),a	; new column count
	ld	a,e
	ld	(crow),a	; new row count
	add	hl,bc
	ld	de,(vrbase)
	add	hl,de
	ex	de,hl
	jp	putcur		; place cursor

dtwait:	ld	bc,rwcmd
rgwait:	call	regrst
	dec	c		; return c to original value
	ld	a,strcmd	; tickle the dummy
	out	(c),a
rgwt2:	in	a,(c)
	or	a
	jp	p,rgwt2
	ret

regrst:	out	(c),b
	inc	c
	out	(c),d
	dec	c
	inc	b
	out	(c),b
	inc	c
	out	(c),e
	ret

getc:	ld	a,d
	and	07h
	ld	d,a
getc2:	call	dtwait
	in	a,(vcdata)
	ret

putc:	push	af	; save data
	ld	a,d
	and	07h
	ld	d,a
putc2:	call	dtwait
	pop	af
	out	(vcdata),a
	ret

puta:	ld	a,(vatt)	; video attribute
putatt:	push	hl		; save hl
	push	af
	call	addatt
	call	dtwait
	pop	af
	out	(vcdata),a
	ex	de,hl
	pop	hl
	ret

getatt:	push	hl
	call	addatt
	call	getc2
	ex	de,hl
	pop	hl
	ret

addatt:	ld	hl,801h		; video attribute offset
	add	hl,de
	ld	a,h
	and	07h		; 00000000 to 00000111
	or	08h		; 00001000 to 00001111
	ld	h,a
	ex	de,hl
	ret

escseq:	ld	hl,leadflg
	ld	(hl),0		; clear flag
	cp	1
	jr	nz,esc2
	ld	a,c
	and	07fh
	cp	dline		; delete line?
	jp	z,dltlin
	cp	iline		; insert line?
	jp	z,inslin
	cp	'A'		; Kaypro-II display lower case?
	ret	z		; yes, ignore
	cp	'G'		; Kaypro-II display greek?
	ret	z		; yes, ignore
	ld	(esccmd),a	; set command
	ld	(hl),2
	ret

esc2:	cp	2
	jr	nz,esc3
	ld	a,(esccmd)
	cp	atton
	jp	z,setatr	; set attribute command
	cp	attoff
	jp	z,clratr	; clear attribute
	ld	a,c
	ld	(row),a
	ld	(hl),3
	ret

esc3:	cp	3
	jr	nz,esc4
	ld	a,c
	ld	(col),a
	ld	a,(esccmd)
	cp	lodcur
	jp	z,curpos	; cursor positioning
	cp	setpix
	jp	z,pixon		; pixel on
	cp	clrpix
	jp	z,pixoff	; pixel off
	ld	(hl),4
	ret
esc4:	cp	4
	jr	nz,esc5
	ld	a,c
	ld	(row2),a
	ld	(hl),5
	ret
esc5:	ld	a,c
	ld	(col2),a
	ld	a,(esccmd)
	cp	lindraw
	jp	z,lineon
	cp	lineras
	jp	z,lineoff
	ret			; illegal command, exit.

bell:	ld	c,bello		; put keyboard bell chara in c reg.,
	jp	kbdout		; ring bell

spechar:cp	cr
	jp	z,carret	; carriage return
	cp	lf
	jp	z,linefd	; line feed
	cp	belli
	jr	z,bell		; bell
	cp	ceol
	jp	z,clreol	; clear to end of line
	cp	ceos
	jp	z,clreos	; clear to end of screen
	cp	clrscr
	jp	z,clear		; clear screen
	cp	lcur
	jp	z,lfcur		; left cursor
	cp	rcur
	jp	z,rtcur		; right cursor
	cp	ucur
	jp	z,upcur		; up cursor
	cp	homec
	jp	z,home		; home cursor
	cp	esc
	jp	nz,spcexe	; not a control character, write it
	ld	a,1
	ld	(leadflg),a	; set escape in progress
	ret

; print routine

print:	pop	hl
	ld	a,(hl)
	inc	hl
	push	hl
	or	a
	ret	z
	ld	c,a
	call	vidout
	jr	print

	defw	0000h

title Block Move Routines for the 6545 CRT Controller.  (C) 1983 By NLS
.comment %
########################################################
##                                                    ##
##      KAYPRO 10 System                              ##
##                                                    ##
##      By M. Sherman                                 ##
##                                                    ##
##      block move routines for the 6545              ##
##                                                    ##
##      Copyright (C) 1983 By Non-Linear Systems, Inc ##
##      No warranty is made, expressed or implied.    ##
##                                                    ##
########################################################
##      Date: 03/28/83                    [77]        ##
########################################################

	Current revision:	7.7		28-Mar-83
	Previous revision:	7.6		11-Mar-83
	Prev. working rev.:	7.5		14-Feb-83
		Changes:  Attempt to add insert line.
		 (revision 7.5)
		Changes:  Updated scrolling (movsts), insert
		 line (revision 7.6)
		Changes:  Final modifications and debugging
		 prior to shipping (version 7.7)

includes the following routines:

MOVSTS:		move status line (if preserved=true), scroll screen
MDIR:		move data with attributes (emulates Z-80 LDIR)
MDDR:		move data with attributes (emulates Z-80 LDDR)
DLTLIN:		delete the current cursor line.
INSLIN:		insert a line at the current cursor location.
%
page


public	mdir, mddr, movsts, dltlin, inslin

vcdata	equ	1fh		; video ram data port
vccmd	equ	1ch		; register select port
vcstat	equ	1ch		; vc status port
scrcmd	equ	0c1ch		; used with regrst to alter base address
rwcmd	equ	121ch		; used with regrst to set up data address
strcmd	equ	1fh		; 'tickle', 'dummy' or strobe register.
lastlin	equ	0730h		; beginning address of last line (except stat)
linesiz	equ	80		; line length in counting numbers
bufsiz	equ	linesiz		; buffer size, if any
hiadd	equ	12h		; high byte of data address port
loadd	equ	13h		; low byte of data address port
vcrdat	equ	vccmd+1		; video controller register data port
linesps	equ	24

.Z80

page

; move status line and scroll
;

movsts:	ld	a,(vatt)	; first, check to see if the status line
	and	20h		; is to be preserved or not.
	jr	z,mvsts2	; if bit 5 is zero, no. else...
; status line preservation is TRUE.  Move the status line before doing
; anything else.
;
mvsts:	ld	hl,(vrbase)
	ld	de,lastlin+linesiz
	ld	bc,linesiz	; amount to move
	add	hl,de		; hl=source, de=statline
	ld	a,h		; qualify it
	and	07h
	ld	h,a
	ld	d,h		; copy it into de,
	ld	e,l		; de=source.
	add	hl,bc		; de=source, hl=destination
	ld	a,h		; qualify it
	and	07h
	ld	h,a
	ex	de,hl		; hl=source, de=destination
	push	hl		; save status line address
	call	mdir		; if so, move it
	pop	de		; status line address in de
	ld	hl,linesiz	; amount to clear
	call	clrdis		; clear it
	ld	hl,(vrbase)
	ld	de,linesiz
	add	hl,de
	ld	a,h
	and	07h
	ld	h,a
	ld	(vrbase),hl
	ex	de,hl
	ld	bc,scrcmd
	jp	regrst		; scroll screen and exit

; enter here for scroll if status line preservation IS NOT enabled.
; MVSTS2 scrolls the screen, then clears the status line.
;
mvsts2:	ld	hl,(vrbase)
	ld	de,linesiz
	add	hl,de
	ld	a,h
	and	07h
	ld	h,a
	ld	(vrbase),hl	; new base address
	ex	de,hl
	ld	bc,scrcmd
	call	regrst
	ld	hl,(vrbase)
	ld	de,linesps*linesiz	; starting addr., status line
	add	hl,de
	ld	a,h
	and	07h
	ld	h,a
	ex	de,hl
	ld	hl,linesiz
	jp	clrdis			; clear status line, exit.

; move a block of data, source in hl, destination in de, count in bc.
; (just like a Z-80 block move, or LDIR, command, only slower.)
;

mdir:	ld	a,b
	and	07h		; qualify the upper byte,
	or	c		; qualify the count
	ret	z		; not 65,535 please!
mdir2:	push	bc		; save the count
rdlopx:	in	a,(vcstat)
	or	a
	jp	p,rdlopx	; wait until ready to begin
	ld	bc,hiadd*100H+loadd	; address register numbers
			; change the data update address register:
	ld	a,b		; high address byte register, UA,
	out	(vccmd),a	; select it.
	ld	a,h		; get high byte, new address,
	out	(vcrdat),a	; put it in high byte, UA.
	ld	a,c		; low address byte, UA,
	out	(vccmd),a	; select it.
	ld	a,l		; new low address byte,
	out	(vcrdat),a	; set it.
	ld	a,strcmd	; strobe register
	out	(vccmd),a	; start a new cycle
rdlop1:	in	a,(vcstat)	; get status
	or	a		; set flags
	jp	p,rdlop1	; wait until vc is ready
	in	a,(vcdata)	; get a data byte
	ex	af,af'		; save it
	ld	a,b		; change address,
	out	(vccmd),a
	ld	a,d
	out	(vcrdat),a
	ld	a,c
	out	(vccmd),a
	ld	a,e
	out	(vcrdat),a
	ld	a,strcmd
	out	(vccmd),a
	ex	af,af'
	out	(vcdata),a
	inc	de
	inc	hl
	ld	a,d
	and	7h
	ld	d,a
	ld	a,h
	and	7h
	ld	h,a
; and now for the attributes
rdlop2:	in	a,(vcstat)
	or	a
	jp	p,rdlop2
	ld	a,b		; change address,
	out	(vccmd),a
	ld	a,h
	or	08h		; go to attribute ram
	out	(vcrdat),a
	ld	a,c
	out	(vccmd),a
	ld	a,l
	out	(vcrdat),a
	ld	a,strcmd
	out	(vccmd),a
rdlop3:	in	a,(vcstat)
	or	a
	jp	p,rdlop3
	in	a,(vcdata)
	ex	af,af'
	ld	a,b		; change address,
	out	(vccmd),a
	ld	a,d
	or	08h		; attribute ram
	out	(vcrdat),a
	ld	a,c
	out	(vccmd),a
	ld	a,e
	out	(vcrdat),a
	ld	a,strcmd
	out	(vccmd),a
	ex	af,af'
	out	(vcdata),a
	pop	bc
	dec	bc
	ld	a,b
	or	c
	jr	nz,mdir2						;wm01
	jp	mdexlp		; make sure last byte got moved

; move a block of data, source in hl, destination in de, count in bc.
; (just like a Z-80 block move, or LDDR, command, only slower.)
;

mddr:	ld	a,b
	and	07h		; qualify the upper byte,
	or	c		; qualify the count
	ret	z		; not 65,535 please!
mddr2:	push	bc		; save the count
ddlopx:	in	a,(vcstat)
	or	a
	jp	p,ddlopx	; wait until ready to begin
	ld	bc,hiadd*100H+loadd	; address register numbers
			; change the data update address register:
	ld	a,b		; high address byte register, UA,
	out	(vccmd),a	; select it.
	ld	a,h		; get high byte, new address,
	and	07h		; qualify it
	out	(vcrdat),a	; put it in high byte, UA.
	ld	a,c		; low address byte, UA,
	out	(vccmd),a	; select it.
	ld	a,l		; new low address byte,
	out	(vcrdat),a	; set it.
	ld	a,strcmd	; strobe register
	out	(vccmd),a	; start a new cycle
ddlop1:	in	a,(vcstat)	; get status
	or	a		; set flags
	jp	p,ddlop1	; wait until vc is ready
	in	a,(vcdata)	; get a data byte
	ex	af,af'		; save it
	ld	a,b		; change address,
	out	(vccmd),a
	ld	a,d
	and	07h
	out	(vcrdat),a
	ld	a,c
	out	(vccmd),a
	ld	a,e
	out	(vcrdat),a
	ld	a,strcmd
	out	(vccmd),a
	ex	af,af'
	out	(vcdata),a
	inc	de
	inc	hl
	ld	a,d
	and	7h
	ld	d,a
	ld	a,h
	and	7h
	ld	h,a
; and now for the attributes
ddlop2:	in	a,(vcstat)
	or	a
	jp	p,ddlop2
	ld	a,b		; change address,
	out	(vccmd),a
	ld	a,h
	or	08h		; go to attribute ram
	out	(vcrdat),a
	ld	a,c
	out	(vccmd),a
	ld	a,l
	out	(vcrdat),a
	ld	a,strcmd
	out	(vccmd),a
ddlop3:	in	a,(vcstat)
	or	a
	jp	p,ddlop3
	in	a,(vcdata)
	ex	af,af'
	ld	a,b		; change address,
	out	(vccmd),a
	ld	a,d
	or	08h		; attribute ram
	out	(vcrdat),a
	ld	a,c
	out	(vccmd),a
	ld	a,e
	out	(vcrdat),a
	ld	a,strcmd
	out	(vccmd),a
	ex	af,af'
	out	(vcdata),a
	pop	bc
	dec	hl
	dec	hl
	dec	de
	dec	de
	dec	bc
	ld	a,b
	or	c
	jp	nz,mddr2
mdexlp:	in	a,(vcstat)
	or	a
	jp	p,mdexlp
	ret

dltlin:	call	carret		; do a carriage return
	ld	a,(crow)
	or	a
	jr	z,dscroll	; special scroll			wm01
	ld	de,(cursor)
	ld	hl,linesiz
	cp	23
	jp	nc,clrdis	; clear last line or status line, exit
	cp	11
	jr	nc,dltl1a	; normal delete line, lines 11-22
	ex	de,hl		; de=linesiz, hl=cursor
	ld	bc,linesiz-1
	add	hl,bc		; hl=end of current line=dest
	ld	a,h
	and	07h		; qualify it
	ld	h,a		; hl=dest.
	ld	b,h
	ld	c,l		; bc=dest.
	sbc	hl,de		; hl=source
	ld	a,h
	and	7h		; qualify it
	ld	h,a		; source in hl
	push	hl		; save source
	ld	de,(vrbase)
	sbc	hl,de		; hl=source-vrbase
	jr	nc,dltl2b	; true count if no carry
	ld	hl,0800h
	or	a		; clear carry
	sbc	hl,de
	pop	de		; source in de
	add	hl,de		; count in hl
	ld	a,h
	and	07h
	ld	h,b
	ld	b,a
	ld	a,l
	ld	l,c
	ld	c,a
	ex	de,hl		; hl=source, de=dest., bc=count
dscrla:	inc	bc		; count=count-1
	call	mddr
dscroll:call	mvsts		; scroll, saving status line
	ld	hl,(cursor)
	ld	de,linesiz
	add	hl,de
	ex	de,hl		; new cursor position in de
	jp	putcur		; place cursor and exit

dltl2b:	ld	d,b
	ld	e,c		; de=dest.
	ld	b,h
	ld	c,l		; bc=count
	pop	hl		; hl=source
	jr	dscrla		; go do it

dltl1a:	add	hl,de		; source = linesiz+destination
	ld	a,h
	and	7h		; qualify it,
	ld	d,a
	ld	e,l		; put source in de.
	ld	hl,(vrbase)
	ld	bc,lastlin+linesiz
	add	hl,bc		; lastpos=vrbase+(lastlin+linesiz)
	ld	a,h
	and	07h		; qualify it,
	ld	h,a		; put it back in hl,
	ld	b,a
	ld	c,l		; save lastpos in bc.
	sbc	hl,de		; hl=lastpos-source
	jr	nc,dltl3a	; valid if no carry,
	ld	hl,0800h	; else put boundry in hl,
	or	a		; clear carry
	sbc	hl,de		; hl=boundry-source
	add	hl,bc		; +lastpos
dltl3a:	ld	b,h		; put count in bc
	ld	c,l
	ld	hl,(cursor)	; dest
	ex	de,hl		; in de, source in hl
	call	mdir		; move it.
	ld	hl,(vrbase)
	ld	de,lastlin
	add	hl,de
	ld	a,h
	and	07h
	ld	d,a
	ld	e,l		; last line in de
	ld	hl,linesiz
	jp	clrdis		; clear the last line

; insert a line

inslin:	ld	a,(crow)
	cp	12
	jp	nc,insln2	; 'normal' insert line
	ld	hl,(vrbase)	; source
	ld	de,linesiz
	or	a		; clear carry
	sbc	hl,de		; hl = new vrbase
	ld	a,h
	and	07h		; qualify it
	ld	h,a
	ex	de,hl		; dest in de,
	ld	bc,scrcmd	; scroll
	call	regrst
	ld	hl,(cursor)
	ld	bc,(vrbase)
	or	a
	sbc	hl,bc
	jr	nc,insl2a	; hl=amount
	ld	hl,0800h
	or	a		; clear carry flag
	sbc	hl,bc		; hl=800h-source
	ld	a,h
	and	07h
	ld	h,a
	ld	bc,(cursor)
	add	hl,bc
insl2a:	ld	a,h
	and	07h
	ld	b,a
	ld	c,l		; amount in bc
; test
	ld	hl,80+48
	add	hl,bc
	ld	a,h
	and	07h
	ld	b,a
	ld	c,l
;
	ld	hl,(vrbase)	; source in hl
; test
	ld	de,23*linesiz
	add	hl,de
	ld	a,h
	and	07h
	ld	h,a
	ex	de,hl
	ld	hl,80
	add	hl,de		; source in hl, dest in de
	ld	a,h
	and	07h
	ld	h,a
;
	call	mdir
	ld	hl,(cursor)
	ld	bc,linesiz
	or	a		; clear carry
	sbc	hl,bc
	ld	a,h
	and	07h
	ld	h,a		; qualify address
	ex	de,hl		; put in de
	ld	a,(ccol)
	ld	c,a
	ld	b,0
	ld	hl,linesiz
	sbc	hl,bc		; hl=amount
	push	de		; save new cursor address
	push	hl
	call	clrdis		; clear to end of inserted line
	pop	bc		; amount
	push	bc
	ld	hl,(cursor)
	ld	a,(ccol)
	ld	e,a
	ld	d,0
	or	a
	sbc	hl,de
	ld	a,h
	and	07h
	ld	d,a
	ld	e,l		; dest in de
	ld	hl,(cursor)	; source in hl
	call	mdir
	pop	bc		; amount
	ld	hl,linesiz
	or	a
	sbc	hl,bc
	call	nz,clrdis
	ld	hl,(vrbase)
	ld	bc,linesiz
	or	a
	sbc	hl,bc
	ld	a,h
	and	07h
	ld	h,a
	ld	(vrbase),hl	; new vr base, 
	pop	de
	jp	putcur		; put cursor and exit

insln2:	sub	22
	jr	z,inl33
	jp	nc,clreol
	neg			; two's complement, number of lines to move
	push	af
	ld	hl,(vrbase)
	ld	de,79+22*80	; source
	ld	bc,80
	add	hl,de
	ld	a,h
	and	07h
	ld	d,a
	ld	e,l
	add	hl,bc		; destination
	ld	a,h
	and	07h
	ld	h,a
	ex	de,hl		; hl:=source, de:=dest.
	pop	af
	push	hl
	ld	hl,0
inl22:	add	hl,bc
	dec	a
	jr	nz,inl22
	ld	b,h
	ld	c,l		; bc=amount
	pop	hl		; restore source to hl
	call	mddr		; move them
inl33:	ld	hl,(cursor)	; source in de,
	ld	d,h
	ld	e,l
	ld	a,(ccol)
	ld	c,a		; amount to clear, next line
	ld	a,80
	sub	c		; amount to move and distance to go
	ld	c,a
	ld	b,0
	add	hl,bc		; dest. in hl,
	ld	a,h
	and	07h
	ld	h,a
	ex	de,hl		; now hl=source, de=dest, bc=amount
	call	mdir		; move the rest to beginning of next line
	ld	a,(ccol)
	ld	l,a
	ld	h,0
	or	a
	call	nz,clrdis	; clear to the end of the next line,
	jp	clreol		; clear to the end of this one.

	defw	0000h

	end

