; cmonml.dsm
; L. Stewart, September 24, 1982  10:30 AM
; clkxx -> cmkxx , September 10, 1982  11:42 AM
; L. Stewart, December 3, 1982  3:02 PM, new initial I/O setup code
; L. Stewart, January 21, 1983  1:04 PM, initial I/O fixup

SPACE   SEGMENT

C←GROUP GROUP SPACE, C←CODE, C←DATA

; 	DB	2 DUP(?)

SPACE	ENDS

C←CODE	SEGMENT

$INCLUDE(Lark.d)

; stack initial value
SPIN	EQU	0DFF0H

deiploc	EQU	000H
decsloc	EQU	002H
triploc	EQU	004H
trcsloc	EQU	006H
nmiploc	EQU	008H
nmcsloc	EQU	00AH
bkiploc	EQU	00CH
bkcsloc	EQU	00EH
oviploc	EQU	010H
ovcsloc	EQU	012H

;  boot reasons
cDIVERR	EQU	0
cTRACE	EQU	1
cNMI	EQU	2
cBREAK	EQU	3
cOVERR	EQU	4
cEXTINT	EQU	254
cRESET	EQU	255

C←DATA	SEGMENT

←mstate	LABEL	BYTE
rax	DW	?
rbx	DW	?
rcx	DW	?
rdx	DW	?
rsp	DW	?
rbp	DW	?
rsi	DW	?
rdi	DW	?
rcs	DW	?
rds	DW	?
rss	DW	?
res	DW	?
rip	DW	?
rfl	DW	?
←lip	DW	?	; single step use
←cmklo	DW	00H
←cmkhi	DW	00H
←refa	DW	?
←refb	DW	?
←refc	DW	?
←refd	DW	?
←savea	DW	?
←saveb	DW	?
←lnmi	DW	?
←eitype	DW	?

C←DATA ENDS

EXTRN	←main:NEAR
EXTRN	←portstr:NEAR

ASSUME CS:C←CODE, DS:C←DATA
←cstart:
	MOV	AL,0FFH
	OUT	TimCtl,AL
	MOV	AL,083H		; Port A, C4-7 Out, Port B, C0-3 In, mode 0
	OUT	pioctl,AL
	MOV	AL,068H
	OUT	pioa,AL
	MOV	AL,0E1H
	OUT	pioa,AL
	MOV	AL,030H		; A and HS relays OFF
	OUT	pioc,AL
	MOV	AL,000H
	OUT	piob,AL
	MOV	AL,088H		; Port A, B, C0-3 Out, Port C4-7 In, mode 0
	OUT	apioctl,AL
	MOV	AL,0C0H		; revert and off-hook relays OFF
	OUT	apioa,AL
	XOR	BX,BX
cmmt:
	IN	AL,pioc
	TEST	AL,4
	JNZ	cmst
	IN	AL,piob
	MOV	[BX+0AA55H],AL
	MOV	AL,[BX+0AA55H]
	MOV	[BX+055AAH],AL
	MOV	AL,[BX+055AAH]
	JMP	cmmt
cmst:
	MOV	BX,cRESET
←crestart:
	MOV	AX,SPIN
	MOV	SP,AX
	MOV	AX,CS
	MOV	SS,AX
	MOV	ES,AX
	MOV	DS,AX
	CALL	←main
	JMP	DWORD PTR JHUNK

JHUNK	DW	00H
	 DW	0FFFFH

; set timer, address of timer in BX, interval in CX
; call is	settmr(interval, &timer)
; interval is in 1000ths of a second
←settmr	PROC	NEAR
	ADD	CX,←cmklo
	MOV	WORD PTR [BX],CX
	RET
←settmr	ENDP

; has timer expired? Address of timer in BX
; returns BX=1 if so, BX=0 if not
; call is	if (tmrexp(&timer)) { ... }
←tmrexp	PROC	NEAR
	MOV	AX,WORD PTR [BX]
	XOR	BX,BX
	CMP	AX,←cmklo	; expired will leave sign bit set
	JS	teret
	RET
teret:
	INC	BX
	RET
←tmrexp	ENDP

; Enable interrupts
←inton	PROC	NEAR
	STI
	RET
←inton	ENDP

; Disable interrupts
←intoff	PROC	NEAR
	CLI
	RET
←intoff	ENDP

wasnmi:
	MOV	rbx,BX
	MOV	BX,cNMI
	JMP	intcom

possnmi:	; if old, juggle stack
	MOV	AX,1
	XCHG	AX,←lnmi
	OR	AL,AL
	JNZ	wasntnmi
; if we reach here, fix stack so IRET returns to secret place!
	MOV	BX,0F000H
	PUSH	BX		; push flags
	XOR	BX,BX
	PUSH	BX		; push cs for this routine
	MOV	BX,OFFSET wasnmi
	PUSH	BX		; push ip for this routine
	JMP	wasntnmi

; Refresh Interrupt
RefA:
	MOV	←savea,AX		; 3 bytes
	MOV	←saveb,BX		; 4 bytes
	XOR	BX,BX			; 2 bytes
	MOV	AX,←refb		; 3 bytes
	MOV	[nmiploc+BX],AX		; 4 bytes
	MOV	AX,←cmklo		; 3 bytes
	ADD	AX,1			; 3 bytes
	MOV	←cmklo,AX		; 3 bytes
	MOV	AX,←cmkhi		; 3 bytes
	ADC	AX,0			; 3 bytes
	MOV	←cmkhi,AX		; 3 bytes

	IN	AL,pioc			; 2 bytes check for NMI
	TEST	AL,1			; 2 bytes
	JZ	possnmi			; 2 bytes
wasntnmi:
	MOV	AX,←savea		; 3 bytes
	MOV	BX,←saveb		; 3 bytes

; total 46, we need 18 more, counting IRET

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP

	IRET

RefB:
	MOV	←savea,AX		; 3 bytes
	MOV	←saveb,BX		; 4 bytes
	XOR	BX,BX			; 2 bytes
	MOV	AX,←refc		; 3 bytes
	MOV	[nmiploc+BX],AX		; 4 bytes
	MOV	AX,←cmklo		; 3 bytes
	ADD	AX,1			; 3 bytes
	MOV	←cmklo,AX		; 3 bytes
	MOV	AX,←cmkhi		; 3 bytes
	ADC	AX,0			; 3 bytes
	MOV	←cmkhi,AX		; 3 bytes

	MOV	AX,←savea		; 3 bytes
	MOV	BX,←saveb		; 3 bytes

; total 40, we need 24 more, counting IRET

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP
	NOP

	IRET
RefC:
	MOV	←savea,AX		; 3 bytes
	MOV	←saveb,BX		; 4 bytes
	XOR	BX,BX			; 2 bytes
	MOV	AX,←refd		; 3 bytes
	MOV	[nmiploc+BX],AX		; 4 bytes
	MOV	AX,←cmklo		; 3 bytes
	ADD	AX,1			; 3 bytes
	MOV	←cmklo,AX		; 3 bytes
	MOV	AX,←cmkhi		; 3 bytes
	ADC	AX,0			; 3 bytes
	MOV	←cmkhi,AX		; 3 bytes

	MOV	AX,←savea		; 3 bytes
	MOV	BX,←saveb		; 3 bytes

; total 50, we need 24 more, counting IRET

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP
	NOP

	IRET
RefD:
	MOV	←savea,AX		; 3 bytes
	MOV	←saveb,BX		; 4 bytes
	XOR	BX,BX			; 2 bytes
	MOV	AX,←refa		; 3 bytes
	MOV	[nmiploc+BX],AX		; 4 bytes
	MOV	AX,←cmklo		; 3 bytes
	ADD	AX,1			; 3 bytes
	MOV	←cmklo,AX		; 3 bytes
	MOV	AX,←cmkhi		; 3 bytes
	ADC	AX,0			; 3 bytes
	MOV	←cmkhi,AX		; 3 bytes

	MOV	AX,←savea		; 3 bytes
	MOV	BX,←saveb		; 3 bytes

; total 50, we need 24 more, counting IRET

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP
	NOP
	NOP
	NOP

	NOP
	NOP
	NOP

	IRET


; initialize interrupt vectors
←intinit	PROC	NEAR
	MOV	AX,OFFSET RefA
	MOV	←refa,AX
	MOV	AX,OFFSET RefB
	MOV	←refb,AX
	MOV	AX,OFFSET RefC
	MOV	←refc,AX
	MOV	AX,OFFSET RefD
	MOV	←refd,AX
	XOR	BX,BX
	XOR	AX,AX
	MOV	[nmcsloc+BX],AX
	MOV	AX,←refa
	MOV	[nmiploc+BX],AX
	RET
←intinit	ENDP


; The format of this stuff is <port address><byte count><bytes>
; port FF terminates the list

; first set up the 8255 and reset all its outputs
←I8255 LABEL BYTE
	DB	pioctl
	DB	1
	DB	083H	; Port A, C[4..7] output, B, C[0..3] input, all mode 0

	DB	pioa
	DB	1
	DB	0E1H

	DB	piob
	DB	1
	DB	000H

	DB	pioc
	DB	1
	DB	030H

	DB	apioctl
	DB	1
	DB	088H	; Port A, B, C0-3 Out, Port C4-7 In, mode 0

	DB	apioa
	DB	1
	DB	0C0H	; revert and off-hook relays OFF

	DB	0FFH

←I8237 LABEL BYTE
; reset DMA controller
	DB	dmamask
	DB	1
	DB	00FH

	DB	dmaclr
	DB	1
	DB	000H

; Set up SLC controller only in cascade mode

	DB	dmastat
	DB	1
	DB	020H

	DB	dmamode
	DB	1
	DB	0C1H

	DB	dmamask
	DB	1
	DB	00DH

	DB	0FFH

; See Lark9513.txt for explanations
←I9513 DB TimCtl
	DB	5
	DB	0FFH
	DB	05FH
	DB	001H
	DB	0E0H
	DB	017H

	DB	TimData
	DB	2
	DB	0C0H
	DB	088H

	DB	TimCtl
	DB	6
	DB	0E9H		; set SLCHoldoff' high (was E1)
	DB	0E2H		; everyone else low
	DB	0E3H
	DB	0E4H
	DB	0E5H

	DB	001H

; SLCHoldoff
	DB	TimData
	DB	30

;new
	DB	062H		; mode L, toggle
	DB	0DBH		; active high edge gate N, F1
	DB	059H		; delay 192/2 -7
	DB	000H
	DB	02EH		; low for 46
	DB	000H

;old
;	DB	062H		; mode J
;	DB	01BH
;	DB	02EH
;	DB	000H
;	DB	092H
;	DB	000H

; TSN
; new
	DB	062H		; mode J, free run
	DB	01BH		; no gate, F1
	DB	058H		; low for 88
	DB	000H
	DB	008H		; high for 8
	DB	000H
; old
;	DB	062H		; mode L
;	DB	0FBH
;	DB	007H		; delay 7
;	DB	000H
;	DB	008H		; high for 8
;	DB	000H

; Framesync
	DB	0A5H		; Mode R
	DB	0FBH		; active low edge gate N, F1
	DB	005H		; delay 5
	DB	000H
	DB	000H
	DB	000H

; 8274 ch. B baud rate, 1200 baud
	DB	062H		; Mode J
	DB	01BH
	DB	028H
	DB	000H
	DB	028H
	DB	000H

; Refresh generator
; Programmable timer, set up for toggling every 8 ticks of SynTSN
; It triggers nmi on the low to high transition of output
	DB	022H		; Mode D
	DB	014H
	DB	008H
	DB	000H
	DB	000H
	DB	000H

	DB	TimCtl
	DB	2
	DB	05FH
	DB	03FH

	DB	0FFH


←I8274 DB	sioctla
	DB	2
	DB	018H	; channel reset
	DB	0F0H	; reset Tx underrun, error reset

	DB	sioctlb
	DB	2
	DB	018H	; channel reset
	DB	0F0H	; reset Tx underrun, error reset

	DB	sioctla
	DB	14
	DB	004H	; WR 4 next
	DB	04CH	; 16 x clock, 2 stop, no parity
	DB	001H	; WR 1 next
	DB	004H	; no interrupts, variable vector
;	DB	01EH	; rx int all chars, variable vector, txint, no ext int
	DB	002H	; WR 2 next
	DB	014H	; 8088 mode, non-vector int. rx high pri., both int.
	DB	003H	; WR 3 next
	DB	0C0H	; 8 bits/rxchar
	DB	005H	; WR 5 next
	DB	060H	; 8 bits/txchar
	DB	006H	; WR 6 next
	DB	000H	; junk
	DB	007H	; WR 7 next
	DB	000H	; junk

	DB	sioctlb
	DB	14
	DB	004H	; WR 4 next
	DB	04CH	; 16 x clock, 2 stop, no parity
	DB	001H	; WR 1 next
	DB	004H	; no interrupts, variable vector
;	DB	01EH	; rx int all chars, variable vector, txint, no ext int
	DB	002H	; WR 2 next
	DB	000H	; base vector
	DB	003H	; WR 3 next
	DB	0C0H	; 8 bits/rxchar
	DB	005H	; WR 5 next
	DB	060H	; 8 bits/txchar
	DB	006H	; WR 6 next
	DB	000H	; junk
	DB	007H	; WR 7 next
	DB	000H	; junk

; now enable 8274
	DB	sioctla
	DB	4
	DB	003H
	DB	0C1H	; 8 bits/rxchar, rx enable
	DB	005H
	DB	068H	; 8 bits/txchar, tx enable

	DB	sioctlb
	DB	4
	DB	003H
	DB	0C1H	; 8 bits/rxchar, rx enable
	DB	005H
	DB	068H	; 8 bits/txchar, tx enable


	DB	0FFH

; set up 8259
; Mask all interrupts
←I8259	DB	intctl
	DB	1
	DB	01BH

	DB	intdata
	DB	3
	DB	070H
	DB	001H
	DB	0FFH

; end of string
	DB	0FFH


←startm LABEL	NEAR
	CMP	rsp,020H
	JAE	sok
	MOV	rsp,01F0H
sok:
	MOV	AX,rip
	MOV	←lip,AX		; save current ip
	MOV	AX,rss
	MOV	SS,AX
	MOV	AX,rsp
	MOV	SP,AX
	PUSH	rfl
	PUSH	rcs
	PUSH	rip
	MOV	BX,rbx
	MOV	CX,rcx
	MOV	DX,rdx
	MOV	SI,rsi
	MOV	DI,rdi
	MOV	BP,rbp
	MOV	AX,rax
	PUSH	res
	PUSH	rds
	POP	DS
	POP	ES
	IRET

brkpt	LABEL	NEAR
	MOV	rbx,BX
	MOV	BX,cBREAK
intcom:
	MOV	rax,AX
	MOV	rcx,CX
	MOV	rdx,DX
	MOV	rsi,SI
	MOV	rdi,DI
	MOV	rbp,BP
	PUSH	DS
	POP	rds
	PUSH	ES
	POP	res
	POP	rip
	POP	rcs
	POP	rfl
	MOV	AX,SS
	MOV	rss,AX
	MOV	AX,SP
	MOV	rsp,AX
	JMP	←crestart

trace	LABEL	NEAR
; determine if interrupt happened first!
; if trace flag is off in the flags on the stack then reorder.

	MOV	rax,AX
	MOV	rbx,BX
	MOV	rcx,CX
	MOV	rdx,DX
	POP	DX		; pop ip
	POP	CX		; pop cs
	POP	AX		; pop flags
	PUSH	AX		; push them back
; note:	asm bug, TEST AH,1 doesn't work!
	TEST	AX,0100H	; tf set?
	JNZ	trok		; if so, this is the trace trap
	XOR	BX,BX		; otherwise
	PUSH	BX		; push cs for this routine
	MOV	BX,OFFSET trace	; push ip for this routine
	PUSH	BX
	PUSH	AX		; push original flags
	PUSH	CX		; push original cs
	PUSH	DX		; push original ip
	MOV	AX,rax		; restore registers
	MOV	BX,rbx
	MOV	CX,rcx
	MOV	DX,rdx
	IRET
trok:
	PUSH	CX		; push original cs
	PUSH	DX		; push original ip
	MOV	AX,rax		; restore registers
	MOV	CX,rcx		; except BX
	MOV	DX,rdx
	MOV	BX,cTRACE
	JMP	intcom

cmov	LABEL	NEAR
	MOV	rbx,BX
	MOV	BX,cOVERR
	JMP	intcom

cmde	LABEL	NEAR
	MOV	rbx,BX
	MOV	BX,cDIVERR
	JMP	intcom

; All other interrupts

UnkInt:
; in principle, CS contains the interrupt type and IP has 
; whatever it takes to get control here.
	PUSH	BX
	MOV	BX,CS
	MOV	←eitype,BX
	POP	BX
	MOV	AL,020H  ; 8259 eoi
	OUT	intctl,AL
	MOV	rbx,BX
	MOV	BX,cEXTINT
	DB	0EAH	; long jump to
	DW	OFFSET intcom	; intcom
	DW	0

←setup	PROC	NEAR
	CALL	←intinit
	MOV	BX,OFFSET ←I8255
	CALL	←portstr
	MOV	BX,OFFSET ←I9513
	CALL	←portstr
	MOV	BX,OFFSET ←I8237
	CALL	←portstr
	MOV	BX,OFFSET ←I8274
	CALL	←portstr
	MOV	BX,OFFSET ←I8259
	CALL	←portstr
; now set up single step and breakpoint
	XOR	AX,AX
	XOR	BX,BX
	MOV	←lnmi,AX
	MOV	[trcsloc+BX],AX
	MOV	[bkcsloc+BX],AX
	MOV	[decsloc+BX],AX
	MOV	[ovcsloc+BX],AX
	MOV	[triploc+BX],OFFSET trace
	MOV	[bkiploc+BX],OFFSET brkpt
	MOV	[deiploc+BX],OFFSET cmde
	MOV	[oviploc+BX],OFFSET cmov

; set up all interrupts to point to UnkInt
; The idea is that the interrupt type is encoded in the CS:IP pair
; so that CS holds the type and IP holds UnkInt-16*CS
	MOV	CX,251
	MOV	DI,014H
	MOV	BX,5
	MOV	DX,OFFSET UnkInt
	SUB	DX,050H
	CLD
suih:
	MOV	AX,DX
	STOSW
	MOV	AX,BX
	STOSW
	INC	BX
	SUB	DX,010H
	LOOP	suih
; initialize save area
	MOV	rip,0200H
	MOV	rsp,01F0H
	XOR	AX,AX
	MOV	rcs,AX
	MOV	res,AX
	MOV	rds,AX
	MOV	rss,AX
	MOV	rfl,0F000H	 ; interrupts off
	RET
←setup	ENDP

PUBLIC	←settmr
PUBLIC	←tmrexp
; PUBLIC	←inton
; PUBLIC	←intoff
PUBLIC	←cmklo
PUBLIC	←cmkhi
PUBLIC	←mstate
PUBLIC	←startm
PUBLIC	←setup
PUBLIC	←lip
PUBLIC	←lnmi
PUBLIC	←eitype

C←CODE ENDS
	END