	title	'MP/M II V2.0  DSC-2 Basic & Extended I/O Systems'
	cseg

false	equ	0
;true	equ	-1
true	equ	not false

	maclib	z80

Banked	equ	false
;Banked	equ	true

debug	equ	true
;debug	equ	false

;ldcmd	equ	true
ldcmd	equ	false
;
; disk io is in separate module

	extrn	home,seldsk,settrk,setsec,setdma,read,write,sectran

	public	swtuser,swtsys,pdisp,xdos,sysdat

;bios jump vector table

wboot:
	jmp	warmstart	;warm start
	jmp	const		;console status
	jmp	conin		;console character in
	jmp	conout		;console character out
	jmp	list		;list character out
	jmp	rtnempty	;punch not implemented
	jmp	rtnempty	;reader not implemented
	jmp	home		;move head to home
	jmp	seldsk		;select disk
	jmp	settrk		;set track number
	jmp	setsec		;set sector number
	jmp	setdma		;set dma address
	jmp	read		;read disk
	jmp	write		;write disk
	jmp	pollpt		;list status
	jmp	sectran		;sector translate

	jmp	selmemory	; select memory
	jmp	polldevice	; poll device
	jmp	startclock	; start clock
	jmp	stopclock	; stop clock
	jmp	exitregion	; exit region
	jmp	maxconsole	; maximum console number
	jmp	systeminit	; system initialization
	db	0,0,0		; force use of internal dispatch @ idle
;	jmp	idle		; idle procedure
;
commonbase:
	 jmp	coldstart
swtuser: jmp	$-$
swtsys:  jmp	$-$
pdisp:   jmp	$-$
xdos:	 jmp	$-$
sysdat:  dw	$-$

coldstart:
warmstart:
	mvi	c,0
	jmp	xdos		; system reset, terminate process


;I/O handlers
;
;
;  MP/M II V2.0   Console Bios
;
;
;nmbcns	equ	1	; number of consoles
nmbcns	equ	3	; number of consoles

poll	equ	131	; XDOS poll function
makeque	equ	134	; XDOS make queue function
readque	equ	137	; XDOS read queue function
writeque equ	139	; XDOS write queue function
xdelay	equ	141	; XDOS delay function
create	equ	144	; XDOS create process function

pllpt	equ	0	; poll printer
plco0	equ	1	; poll console out #0
plco2	equ	2	; poll console out #1
plco3	equ	3	; poll console out #2 (Port 3)
plci3	equ	4	; poll console in #2 (Port 3)
	if	debug
plci0	equ	5	; poll console in #0
	endif

;
const:			; Console Status
	call	ptbljmp	; compute and jump to hndlr
	dw	pt0st	; console #0 status routine
	dw	pt2st	; console #1 (Port 2) status rt
	dw	pt3st	; console #2 (Port 3) status rt

conin:			; Console Input
	call	ptbljmp	; compute and jump to hndlr
	dw	pt0in	; console #0 input
	dw	pt2in	; console #1 (Port 2) input
	dw	pt3in	; console #2 (Port 3) input

conout:			; Console Output
	call	ptbljmp	; compute and jump to hndlr
	dw	pt0out	; console #0 output
	dw	pt2out	; console #1 (Port 2) output
	dw	pt3out	; console #2 (Port 3) output

;
ptbljmp:		; compute and jump to handler
			; d = console #
			; do not destroy d !
	mov	a,d
	cpi	nmbcns
	jc	tbljmp
	pop	psw	; throw away table address
rtnempty:
	xra	a
	ret
tbljmp:			; compute and jump to handler
			; a = table index
	add	a	; double table index for adr offst
	pop	h	; return adr points to jump tbl
	mov	e,a
	mvi	d,0
	dad	d	; add table index * 2 to tbl base
	mov	e,m	; get handler address
	inx	h
	mov	d,m
	xchg
	pchl		; jump to computed cns handler

;
; ASCII Character Equates
;
uline	equ	5fh
rubout	equ	7fh
space	equ	20h
backsp	equ	8h
altrub	equ	uline
;
; Input / Output Port Address Equates
;
data0	equ	7ah	;crt
sts0	equ	data0+1
cd0	equ	sts0
data1	equ	7eh	;printer
sts1	equ	data1+1
cd1	equ	sts1
data2	equ	7ch	;tty
sts2	equ	data2+1
cd2	equ	sts2
data3	equ	70h	;serial a
sts3	equ	data3+1
cd3	equ	sts3

;
; Poll Console #0 Input
;
	if	debug
polci0:
pt0st:
	if	ldcmd
	lda	pt0cntr
	ora	a
	mvi	a,0
	rnz
	endif

	in	sts0
	ani	2
	rz
	mvi	a,0ffh
	ret
;
pt0in:
	if	ldcmd
	lxi	h,pt0cntr
	mov	a,m
	ora	a
	jz	ldcmd0empty
	dcr	m
	lhld	pt0ptr
	mov	a,m
	inx	h
	shld	pt0ptr
	ret
pt0cntr:
	db	ldcmd0empty-pt0ldcmd
pt0ptr:
	dw	pt0ldcmd
pt0ldcmd:
	db	'tod '
ldcmd0empty:
	endif

	mvi	c,poll
	mvi	e,plci0
	call	xdos
	in	data0
	ani	7fh
	ret
;
	else
pt0st:
			; return 0ffh if ready,
			;        000h if not
	lda	c0inmsgcnt
	ora	a
	rz
	mvi	a,0ffh
	ret
;
; Console #0 Input
;
c0inpd:
	dw	c2inpd	; pl
	db	0	; status
	db	32	; priority
	dw	c0instk+18 ; stkptr
	db	'c0in    '  ; name
	db	0	; console
	db	0ffh	; memseg
	ds	36

c0instk:
	dw	0c7c7h,0c7c7h,0c7c7h
	dw	0c7c7h,0c7c7h,0c7c7h
	dw	0c7c7h,0c7c7h,0c7c7h
	dw	c0inp	; starting address

c0inq:
	dw	0	; ql
	db	'c0inque ' ; name
	dw	1	; msglen
	dw	4	; nmbmsgs
	ds	8
c0inmsgcnt:
	ds	2	; msgcnt
	ds	4	; buffer

c0inqcb:
	dw	c0inq	; pointer
	dw	ch0in ; msgadr
ch0in:
	db	0

c0inuqcb:
	dw	c0inq	; pointer
	dw	char0in ; msgadr
char0in:
	db	0

c0inp:
	mvi	c,makeque
	lxi	d,c0inq
	call	xdos	; make the c0inq

c0inloop:
	mvi	c,flagwait
	mvi	e,6
	call	xdos	; wait for c0 in intr flag
	mvi	c,writeque
	lxi	d,c0inqcb
	call	xdos	; write c0in queue
	jmp	c0inloop


pt0in:
			; return character in reg A
	mvi	c,readque
	lxi	d,c0inuqcb
	call	xdos		; read from c0 in queue
	lda	char0in		; get character
	ani	7fh		; strip parity bit
	ret
;
	endif
;
; Console #0 Output
;
pt0out:
			; Reg C = character to output
	in	sts0
	ani	01h
	jnz	tx0rdy
	push	b
	mvi	c,poll
	mvi	e,plco0
	call	xdos	; poll console #0 output
	pop	b
tx0rdy:
	mov	a,c
	out	data0
	ret
;
; poll console #0 output
;
polco0:
	in	sts0
	ani	01h
	rz
	mvi	a,0ffh
	ret
;
;
; Line Printer Driver:  TI 810 Serial Printer
;			TTY Model 40
;
initflag:
	db	0	; printer initialization flag

list:			; List Output
pt1out:
			; Reg c = Character to print
	lda	initflag
	ora	a
	jnz	pt1xx
	mvi	a,27h
	out	49h		; TTY Model 40 init
	sta	initflag
pt1xx:
	in	sts1
	ani	01h
	jnz	tx1rdy
	push	b
	mvi	c,poll
	mvi	e,pllpt
	call	xdos		; poll printer output
	pop	b
tx1rdy:
	mov	a,c		; char to register a
	out	data1
	ret
;
; Poll Printer Output
;
pollpt:
			; return 0ffh if ready,
			;        000h if not
	in	sts1
	ani	01h
	rz
	mvi	a,0ffh
	ret
;
; Poll Console #1 (Port 2) Input
;
pt2st:
			; return 0ffh if ready,
			;        000h if not
	lda	c2inmsgcnt
	ora	a
	rz
	mvi	a,0ffh
	ret
;
; Console #1 (Port 2) Input
;
c2inpd:
	dw	0	; pl
	db	0	; status
	db	34	; priority
	dw	c2instk+18 ; stkptr
	db	'c2in    '  ; name
	db	2	; console
	db	0ffh	; memseg
	ds	36

c2instk:
	dw	0c7c7h,0c7c7h,0c7c7h
	dw	0c7c7h,0c7c7h,0c7c7h
	dw	0c7c7h,0c7c7h,0c7c7h
	dw	c2inp	; starting address

c2inq:
	dw	0	; ql
	db	'c2inque ' ; name
	dw	1	; msglen
	dw	4	; nmbmsgs
	ds	8
c2inmsgcnt:
	ds	2	; msgcnt
	ds	4	; buffer

c2inqcb:
	dw	c2inq	; pointer
	dw	ch2in ; msgadr
ch2in:
	db	0

c2inuqcb:
	dw	c2inq	; pointer
	dw	char2in ; msgadr
char2in:
	db	0

c2inp:
	mvi	c,makeque
	lxi	d,c2inq
	call	xdos	; make the c2inq

c2inloop:
	mvi	c,flagwait
	mvi	e,8
	call	xdos	; wait for c2 in intr flag
	mvi	c,writeque
	lxi	d,c2inqcb
	call	xdos	; write c2in queue
	jmp	c2inloop


pt2in:
			; return character in reg A
	mvi	c,readque
	lxi	d,c2inuqcb
	call	xdos		; read from c2 in queue
	lda	char2in		; get character
	ani	7fh		; strip parity bit
	ret
;
; Console #1 (Port 2) Output
;
pt2out:
			; Reg C = character to output
	in	sts2
	ani	01h
	jnz	tx2rdy
	push	b
	mvi	c,poll
	mvi	e,plco2
	call	xdos	; poll console #1 output
	pop	b
tx2rdy:
	mov	a,c
	out	data2
	ret
;
; poll console #1 output
;
polco2:
	in	sts2
	ani	01h
	rz
	mvi	a,0ffh
	ret
;
; Poll Console #2 (Port 3) Input
;
polci3:
pt3st:			; return 0ffh if ready,
			;        000h if not
	in	sts3
	ani	2
	rz
	mvi	a,0ffh
	ret
;
; Console #2 (Port 3) Input
;
pt3in:			; return character in reg A
	mvi	c,poll
	mvi	e,plci3
	call	xdos		; poll console #0 input
	in	data3		; read character
	ani	7fh		; strip parity bit
	ret
;
; Console #2 (Port 3) Output
;
pt3out:			; Reg C = character to output
	in	sts3
	ani	01h
	jnz	tx3rdy
	push	b
	mvi	c,poll
	mvi	e,plco3
	call	xdos		; poll console #2 (Port 3) output
	pop	b
tx3rdy:
	mov	a,c
	out	data3		; transmit character
	ret
;
; Poll Console #2 (Port 3) Output
;
polco3:
			; return 0ffh if ready,
			;        000h if not
	in	sts3
	ani	01h
	rz
	mvi	a,0ffh
	ret
;
;
;  MP/M II V2.0   Xios
;
;
polldevice:
			; Reg C = device # to be polled
			; return 0ffh if ready,
			;        000h if not
	mov	a,c
	cpi	nmbdev
	jc	devok
	mvi	a,nmbdev; if dev # >= nmbdev,
			; set to nmbdev
devok:
	call	tbljmp	; jump to dev poll code

devtbl:
	dw	pollpt	; poll printer output
	dw	polco0	; poll console #0 output
	dw	polco2	; poll console #1 output
	dw	polco3	; poll console #2 output
	dw	polci3	; poll console #2 input
	if	debug
	dw	polci0	; poll console #0 input
	endif
nmbdev	equ	($-devtbl)/2	; number of devices to poll
	dw	rtnempty; bad device handler
;


; Select / Protect Memory
;
selmemory:
			; Reg BC = adr of mem descriptor
			; BC ->  base   1 byte,
			;        size   1 byte,
			;        attrib 1 byte,
			;        bank   1 byte.
	push	h
	mov  	h,b
	mov	l,c
	inx h ! inx h ! inx h!
	mov	a,m
	out 	0c0h
	pop	h

	ret
;
; Start Clock
;
startclock:
			; will cause flag #1 to be set
			;  at each system time unit tick
	mvi	a,0ffh
	sta	tickn
	ret
;
; Stop Clock
;
stopclock:
			; will stop flag #1 setting at
			;  system time unit tick
	xra	a
	sta	tickn
	ret
;
; Exit Region
;
exitregion:
			; EI if not preempted or in dispatcher
	lda	preemp
	ora	a
	rnz
	ei
	ret
;
; Maximum Console Number
;
maxconsole:
	mvi	a,nmbcns
	ret
;
; System Initialization
;
systeminit:
;
;  This is the place to insert code to initialize
;  the time of day clock, if it is desired on each
;  booting of the system.
;	C= BREAKPOINT RESTART NUMBER
;	DE= BREAKPOINT RESTART HANDLER ADDRESS
;	HL= DIRECT XIOS INTERCEPT JUMP TABLE ADDRESS

	mvi	a,0c3h
	sta	0038h
	lxi	h,inthnd
	shld	0039h		; JMP INTHND at 0038H

	mvi	a,0h	
	out	0E2h	; source bank 0
	out	0e5h	; source high 0
	out	0e7h	; dest high 0
	out	0e4h	; source low 0
	out	0e6h	; dest low 0
	out	0e9h	; count high 0
	mvi	a,40h	; move 40h bytes
	out	0e8h	; count low 0

	mvi	a,01h	
	out	0E3h	; destination bank
	; cmd=move s to d, but only to/from common, above boundary
	mvi	a,04h	
	out	0e0h	; single thread, no need to wait

	mvi	a,02h	
	out	0E3h	; destination bank
	mvi	a,04h	
	out	0e0h	; single thread, no need to wait

	mvi	a,03h	
	out	0E3h	; destination bank
	mvi	a,04h	
	out	0e0h	; single thread, no need to wait

	mvi	a,04h	
	out	0E3h	; destination bank
	mvi	a,04h	
	out	0e0h	; single thread, no need to wait

	mvi	a,05h	
	out	0E3h	; destination bank
	mvi	a,04h	
	out	0e0h	; single thread, no need to wait

	mvi	a,06h	
	out	0E3h	; destination bank
	mvi	a,04h	
	out	0e0h	; single thread, no need to wait

	mvi	a,07h	
	out	0E3h	; destination bank
	mvi	a,04h	
	out	0e0h	; single thread, no need to wait

	mvi	c,create
	if	debug
	lxi	d,c2inpd
	else
	lxi	d,c0inpd
	endif
	call	xdos

	lda	081h		; enable int timer 0
	out	0b8h		; init interrupt mask

	db	0edh,056h	; Interrupt Mode 1
				; ** Z80 Instruction **
	ei
;	call	home
;	mvi	c,flagwait
;	mvi	e,5
;	jmp	xdos		; clear first disk interrupt

	ret			;   & return

;
; Idle procedure
;
;idle:
;	ret

;	-or-

;	ei
;	hlt
;	ret			; for full interrupt system

;
;  MP/M II V2.0   Interrupt Handlers
;

flagwait equ	132
flagset	equ	133
dsptch	equ	142

inthnd:
			; Interrupt handler entry point
			;  All interrupts gen a RST 7
			;  Location 0038H contains a jmp
			;  to INTHND.
	shld	svdhl
	pop	h
	shld	svdret
	push	psw
	lxi	h,0
	dad	sp
	shld	svdsp		; save users stk ptr
	lxi	sp,lstintstk	; lcl stk for intr hndl
	push	d
	push	b

	mvi	a,0ffh
	sta	preemp	; set preempted flag

	in	0b8h		; read timer 0  
	ani	01000000b	; test & jump if clk int
	jnz	clk60hz
;
;	in	stat		; read disk status port
;	ani	08h
;	jnz	diskintr
;
	if	not debug
	in	sts0
	ani	2
	jnz	con0in
	endif

	in	sts2
	ani	2
	jnz	con2in

;	...			; test/handle other ints
;
	jmp	intdone

;diskintr:
;	xra	a
;	out	cmd1		; reset disk interrupt
;	mvi	e,5
;	jmp	concmn		; set flag #5
;
	if	not debug
con0in:
	in	data0
	sta	ch0in
	mvi	e,6
	jmp	concmn		; set flag #6
	endif

con2in:
	in	data2
	sta	ch2in
	mvi	e,8
;	jmp	concmn		; set flag #8

concmn:
	mvi	c,flagset
	call	xdos
	jmp	intdone

clk60hz:
				; 60 Hz clock interrupt
	lda	tickn
	ora	a		; test tickn, indicates
				;  delayed process(es)
	jz	notickn
	mvi	c,flagset
	mvi	e,1
	call	xdos		; set flag #1 each tick
notickn:
	lxi	h,cnt60
	dcr	m		; dec 60 tick cntr
	jnz	not1sec
	mvi	m,50
	mvi	c,flagset
	mvi	e,2
	call	xdos		; set flag #2 @ 1 sec
not1sec:
;	xra	a
;	out	60h
;	lda	intmsk
;	out	60h		; ack clock interrupt
;	jmp	intdone
;
;	...
; Other interrupt handlers
;	...
;
intdone:
	xra	a
	sta	preemp	; clear preempted flag
	pop	b
	pop	d
	lhld	svdsp
	sphl			; restore stk ptr
	pop	psw
	lhld	svdret
	push	h
	lhld	svdhl
; The following dispatch call will force round robin
;  scheduling of processes executing at the same priority
;  each 1/60th of a second.
; Note: Interrupts are not enabled until the dispatcher
;  resumes the next process.  This prevents interrupt
;  over-run of the stacks when stuck or high frequency
;  interrupts are encountered.
	jmp	pdisp		; MP/M dispatch
;
;
;
; BIOS Data Segment
;
cnt60:	db	50	; 50 tick cntr = 1 sec
intstk:			; local intrpt stk
	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h
	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h
	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h
	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h
lstintstk:
svdhl:	dw	0	; saved Regs HL during int hndl
svdsp:	dw	0	; saved SP during int hndl
svdret:	dw	0	; saved return during int hndl
tickn:	db	0	; ticking boolean,true = delayed
	if	debug
intmsk:	db	44h	; intrpt msk, enables clk intrpt, & con2
	else
intmsk:	db	54h	; intrpt msk, enables clk intrpt, & con0/2
	endif
preemp:	db	0	; preempted boolean
;

	db	0	;force out last byte in hex file

	end
