	title	'8086 Hardware Interface'

;*****************************************************
;*
;*	           X  I  O  S  -  8  6
;*                 ===================
;*
;*	       MP/M-86 eXtended I/O System
;*			   for
;*	 Intel SBC 204 Floppy Diskette Interface
;*
;*		 Copyright (C) 1980, 1981
;*		 Digital Research, Inc.
;*		 Box 579, Pacific Grove
;*		 California, 93950
;*
;*	The XIOS can be assembled in two forms
;*	that are acceptable to GENSYS in building
;*	an MP/M-86 II system.
;*
;*	8080 model:
;*	-----------
;*	  Mixed code and data.  The Code and Data
;*	segments are the same.  The code segment
;*	is ORG'd at 1000h relative to the System
;*	Data Area
;*
;*	high	+-----------------+\
;*		|  System Tables  | |
;*		+-----------------+ |
;*		|  XIOS (C and D) |  >  System Data
;*		+-----------------+ |
;*		|  Sysdat         | |
;*		+-----------------+X
;*		|  System Code    | >	System Code
;*	low	+-----------------+/
;*
;*	Separate Code and Data:
;*	----------------------
;*
;*	  The Code segment is separate from the
;*	Data.  The Code is ORG'd at 0000h and the
;*	Data is ORG'd at 1000h.
;*
;*	high	+-----------------+\
;*		|  System Tables  | |
;*		+-----------------+ |
;*		|  XIOS Data      |  >  System Data
;*		+-----------------+ |	   Area
;*		|  Sysdat         | |
;*		+-----------------+X
;*		|  XIOS Code      | |
;*		+-----------------+  >	System Code
;*		|  System Code    | |     Modules
;*	low	+-----------------+/
;*
;*	This XIOS is presented as an example
;*	hardware interface to an MP/M-86 system.
;*	In many places in the code, more efficient
;*	methods can be used.
;*
;*	(Permission is hereby granted to use or
;*	 abstract the following program in  the
;*	 implementation of CP/M, MP/M or CP/NET
;*	 for the 8086 or 8088  Micro-processor.)
;*
;*****************************************************
;*
;* 	REGISTER USAGE FOR XIOS INTERFACE ROUTINES:
;*
;*	input:	AL = function # (in entry)
;*		CX = parameter
;*		DX = second parameter
;*		DS = sysdat (in entry and init)
;*		   = CS elsewhere
;*		ES = User's Data Area
;*
;*	output:	AX = return
;*		BX = AX (in exit)
;*		ES,DS must be preserved though call
;*
;*	NOTE: Some changes have been made in the
;*		argument/return register usage from
;*		the CP/M-86 BIOS.
;*
;*****************************************************
;*
;*	SYSTEM EQUATES
;*
;*****************************************************
;
include system.lib
;
;	conditional assembly switches
;
debug		equ	false
;*****MDISK SUPPORT*****
memdisk		equ	false
;***********************

cr		equ	0dh	;carriage return
lf		equ	0ah	;line feed

;*****************************************************
;*
;*	CHARACTER I/O EQUATES
;*
;*****************************************************
;
;
	;base address of serial board
serbase	equ	040h
;
nconsoles	equ	4
nlists		equ	1
;
;
; using Intel SBC 534 serial board
;
	;port address to reset entire board
sereset		equ	serbase+0fh
;
	;port address to set test mode
setestmode	equ	serbase+0eh
;
	;port address to enable counter/timer
ctcenable	equ	serbase+0ch
;
	;port address to enable uarts
uartenable	equ	serbase+0dh
;
	;counter/timer mode addresses
ctc0to2md	equ	serbase+03h
ctc3to5md	equ	serbase+07h
ctc0mode	equ	036h
ctc1mode	equ	076h
ctc2mode	equ	0b6h
ctc3mode	equ	036h
;
	;counter/timer load addresses
	; and count values
ctc0ld		equ	serbase+00h
ctc1ld		equ	serbase+01h
ctc2ld		equ	serbase+02h
ctc3ld		equ	serbase+04h
cnt0vall	equ	008h
cnt0valh	equ	000h
cnt1vall	equ	008h
cnt1valh	equ	000h
cnt2vall	equ	008h
cnt2valh	equ	000h
cnt3vall	equ	008h
cnt3valh	equ	000h
	;
	;uart mode and command
	;
u0mode		equ	04eh
u1mode		equ	04eh
u2mode		equ	04eh
u3mode		equ	04eh
u0cmd		equ	037h
u1cmd		equ	037h
u2cmd		equ	037h
u3cmd		equ	037h
;
; console i/o and status ports
;	in and out status masks
;
	;
	; console 0
	;
c0ioport    equ	0d8h
c0stport    equ 0dah
c0inmsk	    equ 02h
c0outmsk    equ 01h
	;
	; console 1
	;
c1ioport	equ	serbase+02h
c1stport	equ	serbase+03h
c1inmsk		equ	002h
c1outmsk	equ	001h
	;
	; console 2
	;
c2ioport	equ	serbase+04h
c2stport	equ	serbase+05h
c2inmsk		equ	002h
c2outmsk	equ	001h
	;
	; console 3
	;
c3ioport	equ	serbase+06h
c3stport	equ	serbase+07h
c3inmsk		equ	002h
c3outmsk	equ	001h
	;
	; list 0
	;
l0ioport	equ	serbase+00h
l0stport	equ	serbase+01h
l0inmsk		equ	002h
l0outmsk	equ	081h
;
;
;
;*****************************************************
;*
;*	DISK I/O EQUATES
;*
;*    Intel iSBC 204 Disk Controller Ports
;*
;*****************************************************

base204		equ 0a0h	;SBC204 assigned addr

fdc_com		equ base204+0	;8271 FDC out command
fdc_stat	equ base204+0	;8271 in status
fdc_parm	equ base204+1	;8271 out parameter
fdc_rslt	equ base204+1	;8271 in result
fdc_rst		equ base204+2	;8271 out reset
dmac_adr	equ base204+4	;8257 DMA base adr out
dmac_cont	equ base204+5	;8257 out control
dmac_scan	equ base204+6	;8257 out scan control
dmac_sadr	equ base204+7	;8257 out scan address
dmac_mode	equ base204+8	;8257 out mode
dmac_stat	equ base204+8	;8257 in status
fdc_sel		equ base204+9	;FDC select port
fdc_segment	equ base204+10	;segment addr register 
reset_204	equ base204+15	;reset interface

max_retries	equ 10		;retries on disk i/o
				;before perm error
;*****MDISK SUPPORT*****
mdiskbase	equ 2000h	;base address of mdisk
;***********************

;*****************************************************
;*
;*	SUP/RTM EQUATES
;*
;*****************************************************

tracebit	equ	0100H

f_dispatch  equ	142	; MPM dispatch func #
f_terminate equ	143	; MPM terminate func #
f_polldev   equ	131	; MPM polldevice func #
f_flagset   equ	133	; MPM flagset func #

p_flag  equ     word ptr 06H	; PD flag field
p_name	equ	byte ptr 08H	; PD Name field
p_cns	equ	byte ptr 020H	; PD console field
pf_keep	equ	02H		; KEEP bit in p_flag

	;flag assignments

tick_flag   equ	1
sec_flag    equ	2

	;device # assignments for POLL DEV

c0indev	    equ	00h	;console 0 input device
c1indev	    equ	01h	;console 1 input device
c2indev	    equ	02h	;console 2 input device
c3indev	    equ	03h	;console 3 input device
c0outdev    equ	04h	;console 0 output device
c1outdev    equ	05h	;console 1 output device
c2outdev    equ	06h	;console 2 output device
c3outdev    equ	07h	;console 3 output device
l0outdev    equ 08h	;list 0 output device
flpy_poll_dev equ 09h	;floppy disk poll device

;
;	system data area must preceed code
;	area for 8080 model of the XIOS
;
include sysdat.lib

endsysdat	equ	((offset $)+0fh) AND 0fff0h

	CSEG
	org	offset endsysdat

;*****************************************************
;*
;*	SYSTEM CODE AREA
;*
;*	XIOS JUMP TABLE
;*
;*****************************************************

	jmp init	;system initialization
	jmp entry	;xios entry point

sysdat		dw	0	;Sysdat Segment
supervisor	equ	offset $
		rw	2

;*****************************************************
;*
;*	UTILITY SUBROUTINES
;*
;*****************************************************

;====
pmsg:
;====
; print message on current console until null (char 0)
;	input:	BX = address of message

		;put running processes console
		;number in DL

	pushf ! cli
	push stoppoll
	mov stoppoll,true
	push ds ! mov ds,sysdat	;DS = system data area
	mov si,rlr		;SI -> current pd
	mov dl,p_cns[si]	;DL = def console #
	pop ds	
ploop:
	mov al,[bx]		; get next char
	cmp al,0 ! jz pmsg_ret	; return if zero
	    mov cl,al		; CL = character
	    push dx ! push bx	; save console,posit.
	    call conout		; print it
	    pop bx ! pop dx	; restore posit.,cons.
	    inc bx ! jmps ploop	; inc and loop
pmsg_ret:
	pop stoppoll
	popf ! ret		; end of message


;*****************************************************
;*
;*	INTERRUPT ROUTINES
;*
;*****************************************************

	;these variables must be in code segment

tickint_ss	dw	0
tickint_sp	dw	0
ax_save		dw	0
zero		dw	0

;=======
tickint:
;=======
; Interrupt handler for tick interrupts

		;save context
	push ds ! mov ds,sysdat
	mov tickint_ss,ss
	mov tickint_sp,sp
	mov ax_save,ax
	mov ax,cs
	mov ss,ax
	mov sp,offset tickint_tos
	push ax_save
	push bx ! push cx ! push dx
	push bp ! push si ! push di ! push es
	mov ds,ax

		; check to set second flag

	dec tick_count ! jnz do_tick_flag
	    mov tick_count,60
	    mov dx,sec_flag
	    mov cl,f_flagset ! call supif
do_tick_flag:
		; check to set tick flag

	cmp clockon,true ! jne tick_done
	    mov dx,tick_flag
	    mov cl,f_flagset ! call supif

tick_done:	;restore context
	pop es ! pop di ! pop si ! pop bp
	pop dx ! pop cx ! pop bx ! pop ax
	mov ss,tickint_ss
	mov sp,tickint_sp
		;force dispatch
	pop ds
	; jmp intdisp

;=======
intdisp:
;=======

	jmpf cs:dword ptr .dispatcher

;========
int_trap:	;unknown interrupts go here ...
;========
;  We will terminate the process that caused this
;  after writing a message to the process's default
;  console.  If the process is in KEEP mode, we will
;  force it to terminate anyway...
;
;  We don't need to save any registers since we are
;  not going to return to the process.


	mov ax,cs
	mov ds,sysdat

		; print first 6 chars of PD Name
	mov bx,rlr
	add bx,p_name
	mov byte ptr 6[bx],':'
	mov byte ptr 7[bx],0
	call pmsg

		; print Illegal Interrupt message
	mov bx,offset int_trp
	call pmsg

		; terminate process
	mov bx,rlr
	and p_flag[bx],not pf_keep
	mov cx,f_terminate
	mov dx,0ffffh
	int 224
	hlt	;hard stop
		;the terminate returned !!!!

;*****************************************************
;*
;*	INITIALIZATION CODE AREA
;*
;*	Inter-Module Interface Routines
;*
;*****************************************************

;====
init:		; XIOS system initialization routine.
;====
;	The INIT routine initializes all necessary
;	hardware
;
; -called from SUP init. routine with CALLF
;
; -Interrupt 224 is reinitialized by SUP later
; -It is okay to turn on interrupts at any time
; 	a STI is performed immediately after RETF
;
; -Current Stack has about 10 levels here.  Must do a
;	local stack switch if more is needed.
;
; -If assembled (GENCMD'd) with 8080 model,
;	CS=DS=Sysdat
; -If assembled with separate Code and Data,
;	CS=Code (ORGed at 0) DS=Sysdat
;
; -This example shows 8080 model
;
;	input:	DS = sysdat segment address
;
;-----------------------------------------------------
;	SYSTEM INITIALIZATION AREA
;-----------------------------------------------------
		;Save sysdat seg addr in case we need
		;to see system data area.  Set DS=CS

	push ds			;save DS on stack for
				;exit

		;initialize segment registers

	mov sysdat,ds		; save sysdat for
				; sysdat access

		;place copy of SUPMOD in data segment
		;into Code Segment (supervisor)

	mov bx,offset supmod
	mov si,supervisor
	mov ax,[bx] ! mov cs:[si],ax
	mov ax,2[bx] ! mov cs:2[si],ax

		;Make copy of Interrupt Routines
		;access point to dispatcher in
		;Code Segment

	push cs ! pop ds	; DS = CS
	cld		;set forward direction

		;stack switch since we are doing
		; i/o with polled devices when
		; printing login message
		; interrupts are known to be off
		; here so no need to save flags,
		; disable, and restore flags

	mov initss,ss
	mov initsp,sp
	mov ax,cs ! mov ss,ax
	mov sp,offset initstack

	
		; Setup all interrupt vectors in low
		; memory to address trap

	if not debug

	push ds
	push es		;ES must be saved
	mov ax,0
	mov ds,ax
	mov es,ax 	;set ES and DS to zero

		;setup interrupt 0 to trap routine

	mov .0,offset int_trap
	mov .2,CS
	mov di,4
	mov si,0	;then propagate
	mov cx,510	;trap vector to
	rep movsw	;int 255

	pop es
	pop ds		;restore DS,ES

	endif

;-----------------------------------------------------
;	CHARACTER I/O INITIALIZATION
;-----------------------------------------------------
;
;
	;*********************************************
	;*
	;*    sbc 534 serial board initialization
	;*
	;*********************************************
;
	mov al,0
	out sereset,al		;reset serial board
	out setestmode,al	;set test mode to off
	out ctcenable,al	;enable ctc addressing
;
		;start clock for port 0
	mov al,ctc0mode ! out ctc0to2md,al
	mov al,cnt0vall ! out ctc0ld,al
	mov al,cnt0valh ! out ctc0ld,al
;
		;start clock for port 1
	mov al,ctc1mode ! out ctc0to2md,al
	mov al,cnt1vall ! out ctc1ld,al
	mov al,cnt1valh ! out ctc1ld,al
;
		;start clock for port 2
	mov al,ctc2mode ! out ctc0to2md,al
	mov al,cnt2vall ! out ctc2ld,al
	mov al,cnt2valh ! out ctc2ld,al
;
		;start clock for port 3
	mov al,ctc3mode ! out ctc3to5md,al
	mov al,cnt3vall ! out ctc3ld,al
	mov al,cnt3valh ! out ctc3ld,al
;
	out uartenable,al	;enable uart addressing
;
		;initialize port 0
	mov al,u0mode ! out l0stport,al
	mov al,u0cmd ! out l0stport,al
;
		;initialize port 1
	mov al,u1mode ! out c1stport,al
	mov al,u1cmd ! out c1stport,al
;
		;initialize port 2
	mov al,u2mode ! out c2stport,al
	mov al,u2cmd ! out c2stport,al
;
		;initialize port 3
	mov al,u3mode ! out c3stport,al
	mov al,u3cmd ! out c3stport,al
;
;
;-----------------------------------------------------
;	DISK I/O INITIALIZATION
;-----------------------------------------------------
	;***** MDISK SUPPORT *****
	if memdisk
	;initialize MDISK

	mov cx,mdiskbase
	push es ! mov es,cx
	mov di,0 ! mov ax,0e5e5h
	cmp es:[di],ax ! je mdisk_end
	    mov cx,2000h
	    rep stos ax
mdisk_end:
	pop es
	;
	endif
	;*************************

;-----------------------------------------------------
;	SUP/RTM INITIALIZATION
;-----------------------------------------------------
		;Initialize Clock Tick

	if not debug
			;set up tick interrupt vector
			;tick causes interrupt 22
	sub ax,ax ! push ds ! mov ds,ax
	mov word ptr .088h,offset tick_int
	mov word ptr .08ah,cs
	pop ds
			;setup ticks to occur every
			; 1/60th of a second
	mov tick_count,60
	mov al,034h ! out 0d6h,al  ;PIT ch.0=mode 2
			;set # of 1/1.2288e6 seconds
			;20480=5000h=1/60th second
	mov al,000h ! out 0d0h,al  ;low count
	mov al,050h ! out 0d0h,al  ;high count
			;set up interrupt controller
	mov al,013h ! out 0c0h,al  ;ICW1
	mov al,020h ! out 0c2h,al  ;ICW2
				   ;  = base interrupt
	mov al,00fh ! out 0c2h,al  ;ICW4,
				   ; auto EOI,
				   ; 8086 mode
	mov al,0fbh ! out 0c2h,al  ;OCW2,
				   ; interrupt mask,
				   ; only 2
	endif
;-----------------------------------------------------
;	INITIALIZATION EXIT
;-----------------------------------------------------
		;allow poll_device mechanism to work

	mov stoppoll,false

		;print optional message on Console 0

	mov bx,offset signon
	call pmsg

		;restore stack
		; all stack switches must be in
		; critical areas (interrupts off).

	pushf ! pop ax
	cli
	mov ss,initss
	mov sp,initsp
	push ax
	popf

		;return back to BDOS
	pop ds
	retf

;*****************************************************
;*
;*	ENTRY POINT CODE AREA
;*
;*****************************************************
;=====		==================
entry:		; XIOS Entry Point
;=====		==================
; All calls to the XIOS routines enter through here
;	with a CALLF.  Must return with a RETF
;	input:	AL = function number
;		CX = parameter
;		DX = 2nd parameter
;	output:	AX = BX = return

	cld			;clear D flag
	mov bx,cs ! mov ds,bx	; (only 8080 model)
	mov ah,0 ! shl ax,1	;call routine
	mov bx,ax ! call functab[bx]
	mov bx,ax		;BX=AX
	retf			;All Done

;=====		======================
supif:		; Supervisor Interface
;=====		======================
;
;	input:	CX = function #
;		DX = parameter
;		DS = parameter segment if address
;		ES = user data area
;	output:	BX = AX = return
;		CX = error code for RTM functions
;		ES = return segment if address

	mov ch,0
	callf cs:dword ptr .supervisor ! ret


;*****************************************************
;*
;*	MP/M XIOS functions
;*
;*****************************************************

;*****************************************************
;*
;*	CHARACTER I/O CODE AREA
;*
;*****************************************************

;=====
const:	    ; Function 0:  Console Status
;=====
;	input:	CL = console device number
;	output:	AL = 0ffh if ready
;		   = 000h if not ready

	mov ch,0 ! shl cx,1
	mov bx,cx
	mov dx,consttbl[bx]
	mov bl,dh	;BL = status mask
	mov dh,0	;DX = status port address

; find input status for console device

	in al,dx
	and al,bl
	mov al,0
	jz badstatus
	    mov al,0ffh
badstatus:
	ret

;=====
conin:	    ; Function 1:  Console Input
;=====
;	input:	CL = console device number
;	output:	AL = character

	mov ch,0 ! shl cx,1
	mov bx,cx
	mov dx,conintbl[bx]
	mov bl,dh	;BL = poll device no.
	mov dh,0	;DX = i/o port address

; input routine for console device

	mov bh,0 ! push dx
	call rtm_poll ! pop dx
	in al,dx
	;and al,07fh	;CP/NET uses parity bit
	ret

;======
conout:	    ; Function 2:  Console Output
;======
;	input:	CL = character
;		DL = console device #
;	output:	None

	mov dh,0 ! shl dx,1
	mov bx,dx
	mov dx,conouttbl[bx]
	mov bl,dh	;BL = poll device no.
	mov dh,0	;DX = i/o port address

; output routine for console device

	push cx ! push dx
	mov bh,0 ! call rtm_poll
	pop dx ! pop ax
	out dx,al ! ret

;=====
plist:	    ; Function 3: List Output
;=====
;	input:	CL = character
;		DL = list device #
;	output:	None

	mov dh,0 ! shl dx,1
	mov bx,dx
	mov dx,louttbl[bx]
	mov bl,dh	;BL = poll device no.
	mov dh,0	;DX = list port address

; output routine for list device

	push cx ! push dx
	mov bh,0 ! call rtm_poll
	pop dx ! pop ax
	out dx,al ! ret

;=====
punch:	    ; Function 4:  Punch Output
reader:	    ; Function 5:  Reader Output
;======
;	PUNCH and READER devices are not supported
;		under MP/M-86
;	input:	CL = character
;	output:	AL = character (control Z)

	mov al,1ah ! ret		; return EOF

;------
plstst:	    ; convert poll device no.
;------	    ; to list device no.

	sub cl,nconsoles * 2
	; fall into list status

;======
listst:	    ; Function 13:  List Status
;======
;	input:	CL = list device number
;	output:	AL = 0ffh if ready
;		   = 000h if not ready

	mov ch,0 ! shl cx,1
	mov bx,cx
	mov dx,loutsttbl[bx]
	mov bl,dh	;BL = status mask
	mov dh,0	;DX = output port address

;	find output status of List device

	in al,dx
	and al,bl
	cmp al,bl
	mov al,0ffh
	jz gstat
	    mov al,0
gstat:	ret

;==========
maxconsole: ; Function 20:  Maximum Consoles
;==========
;	input:	None
;	output:	AL = number of consoles

	mov ax,nconsoles ! ret

;=======
maxlist:    ; Function 21:  Maximum List Devices
;=======
;	input:	None
;	output:	AL = number of consoles

	mov ax,nlists ! ret

;*****************************************************
;*
;*	DISK I/O CODE AREA
;*
;*****************************************************

;====
HOME:	    ; Function 6:  Home
;====
; move selected disk to home position (Track 0)
;	If there is Hardware home function, it should
;	be done here otherwise, do a settrk to 0
;	input:	None
;	output:	None

	mov trk,0	;set disk i/o to track zero
	xor bx,bx
	mov bl,disk	;index into disk home routine
	shl bx,1
	jmp dskhomtbl[bx]

flpy_home:
	mov bx,offset hom_com
	call execute	;home drive
hom_ret:
	ret		; and return

;======
SELDSK:	    ; Function 7:  Select Disk
;======
;	input:	CL = disk to be selected
;	output:	AX = 0 if illegal disk
;		   = offset of DPH relative from
;			XIOS Data Segment

	xor ax,ax	;zero registers ax and bx
	mov bx,ax
	mov ch,0	;translate logical disk
	mov si,cx	;to physical disk device
	mov al,dtrntbl[si]
	cmp al,0ffh	;valid disk select?
	jz sel_ret	;if not valid return
			;else compute the
			; disk parameter address
	    mov disk,al	;save physical drive no.
	    mov cl,4
	    shl al,cl	;multiply by 16
	    add ax,offset dp_base
	    mov bx,ax
sel_ret:
	mov ax,bx
	ret

;======
SETTRK:	    ; Function 8:  Set Track
;======
;	input:	CX = Track Number
;	output:	None

	mov trk,cl	;we only use 8 bits of
			; track address
	ret

;======
SETSEC:	    ; Function 9:  Set Sector
;======
;	input:	CX = Sector Number
;	output:	None

	mov sect,cl	;we only use 8 bits of
			; sector address
	ret

;======
SETDMA:	    ; Function 10:  Set DMA Offset
;======
;	input:	CX = Offset of DMA buffer
;	output:	None

	mov dma_adr,CX
	ret

;*********************************************
;*                                           *
;*  All disk I/O parameters are setup:  the  *
;*  Read and Write entry points transfer one *
;*  sector of 128 bytes to/from the current  *
;*  DMA address using the current disk drive *
;*                                           *
;*********************************************

;====
READ:	    ; Function 11:  Read
;====
;	input:	None
;	output:	AL = 00h if no error occured
;		   = 01h if error occured

	xor bx,bx
	mov bl,disk	;index into disk read routine
	shl bx,1
	jmp dskrdtbl[bx]

	;***** MDISK SUPPORT *****
	if memdisk
	;
mdsk_read:
	    call mdisk_calc
	    mov di,dma_adr ! mov si,0
	    push es ! mov es,dma_seg
	    push ds ! mov ds,ax
	    mov cx,64 ! rep movs ax,ax
	    pop ds ! pop es
	    mov ax,0 ! ret
	;
	endif
	;*************************

flpy_read:
	mov al,12h	;basic read sector command
	jmps r_w_common

	;***** MDISK SUPPORT *****
	if memdisk
	;
mdisk_calc:
	mov bh,0 ! mov bl,trk
	mov ax,26 ! mul bx
	mov bh,0 ! mov bl,sect
	add ax,bx ! mov cl,3
	shl ax,cl ! add ax,mdiskbase+1
	ret
	;
	endif
	;*************************

;=====
WRITE:	    ; Function 12:  Write
;=====
;	input:	CL = 0 - deferred write
;		     1 - non-deferred write
;		     2 - def wrt 1st sect unalloc blk
;		     3 - non-def 1st sect unalloc blk
;	output:	AL = 00h if no error occured
;		   = 01h if error occured
;		   = 02h if read/only disk

	xor bx,bx
	mov bl,disk	;index into disk write routine
	shl bx,1
	jmp dskwrttbl[bx]

	;***** MDISK SUPPORT *****
	if memdisk
	;
mdsk_write:
	    call mdisk_calc
	    mov di,0 ! mov si,dma_adr
	    push es ! mov es,ax
	    push ds ! mov ds,dma_seg
	    mov cx,64 ! rep movs ax,ax
	    pop ds ! pop es
	    mov ax,0 ! ret
	;
	endif
	;*************************

flpy_write:
	mov al,0ah 	;basic write sector command

r_w_common:
	mov bx,offset io_com ;point to command string
	mov byte ptr 1[BX],al ;put command into string

	;	fall into execute and return

execute:  ;execute command string.
	;[BX] points to length,
	;	followed by Command byte,
	;	followed by length-1 parameter bytes

	mov al,80h
	test disk,1	;A drive is even phys. drive
	jnz exec1	;B drive is odd phys. drive
	    mov al,40h
exec1:	mov sel_mask,al
	mov last_com,BX	;save command address
			;for retries
outer_retry:
	;allow some retrying
	mov rtry_cnt,max_retries
retry:
	mov BX,last_com
	call send_com	;transmit command to i8271
;	check status poll

	mov BX,last_com
	mov al,1[bx]	;get command op code
	mov cx,0800h	;mask if it will be "int req"
	cmp al,2ch
	jb flpy_poll	;ok if it is an interrupt type
	    mov cx,8080h  ;else use "not command busy"
	    and al,0fh
	    cmp al,0ch	  ; unless there isn't
	    mov al,0
	    ja exec_exit  ; any result
			;poll for bits in CH,
flpy_poll:		;  toggled with bits in CL
	mov status_mask,cx
	mov bx,flpy_poll_dev ! call rtm_poll
			;Operation complete,
	in al,fdc_rslt	; see if result indicates
	and al,1eh	; an error
	jz exec_exit	;no error, then exit
			;some type of error occurred
	cmp al,10h
	jne dr_rdy	;was it a not ready drive ?
	    ;yes, here to wait for drive ready
	    call test_ready
	    jnz dr_rdy	;if ready try again
	        call test_ready
		;if not ready twice in row,
dr_rdy:	; then we just retry read or write
	dec rtry_cnt
	jnz retry	; up to 10 times

;	retries do not recover from the
;	hard error

	cmp al,012h	; check if r/o disk
	jne err_exit
	    mov al,02h	; return r/o error code
	    ret
err_exit:
	mov ah,0	;setup error table index
	mov bx,ax	;make error code 16 bits
	mov bx,errtbl[BX]
	call pmsg	;print appropriate message
	mov al,01h	;set code for permanent error
exec_exit:
	ret

;*********************************************
;*                                           *
;*  The i8271 requires a read status command *
;*  to reset a drive-not-ready after the     *
;*  drive becomes ready                      *
;*                                           *
;*********************************************

test_ready:
	mov dh, 40h  	;proper mask if dr 1
	test sel_mask,80h
	jnz nrdy2
	    mov dh, 04h	;mask for dr 0 status bit
nrdy2:
	mov bx,offset rds_com
	call send_com
dr_poll:
	    in al,fdc_stat  ;get status word
	    test al,80h
	    jnz dr_poll ;wait for not command busy
	in al,fdc_rslt	;get "special result"
	test al,dh	;look at bit for this drive
	and al,01eh
	ret		;return status of ready

;*********************************************
;*                                           *
;*  Send_com sends a command and parameters  *
;*  to the i8271:  BX addresses parameters.  *
;*  The DMA controller is also initialized   *
;*  if this is a read or write               *
;*                                           *
;*********************************************

send_com:
	    in al,fdc_stat
	    test al,80h	    ;insure command not busy
	    jnz send_com    ;loop until ready

	;check to initialize for a DMA operation

	mov al,1[bx]	;get command byte
	cmp al,12h
	jne write_maybe	;if not read it maybe write
	    mov cl,40h	;is a read command, go set DMA
	    jmps init_dma
write_maybe:
	cmp al,0ah
	jne dma_exit	;leave DMA if not read/write
	    mov cl,80h	;we have write, not read
init_dma:
; read or write operation, setup DMA controller
;	(CL contains proper direction bit)
	mov al,04h
	out dmac_mode,al    ;enable dmac
	mov al,00
	out dmac_cont,al    ;1st byte to ctrl port
	mov al,cl
	out dmac_cont,al    ;load direction register
	mov ax,dma_adr
	out dmac_adr,al     ;send low byte of DMA
	mov al,ah
	out dmac_adr,al	    ;send high byte
	mov ax,dma_seg
	out fdc_segment,al  ;send low byte of seg addr
	mov al,ah
	out fdc_segment,al  ;then high segment address
dma_exit:
	mov cl,[BX]	;get count
	inc BX
	mov al,[BX]	;get command
	or al,sel_mask 	;merge command and drive code
	out fdc_com,al 	;send command byte
parm_loop:
	dec cl
	jz exec_exit	;no (more) parameters, return
	inc BX		;point to (next) parameter
parm_poll:
	    in al,fdc_stat
	    test al,20h	    ;"parameter register full" bit
	    jnz parm_poll   ;idle until parm reg not full
	mov al,[BX]
	out fdc_parm,al	;send next parameter
	jmps parm_loop	;see if more parameters

flpy_poll_stat:
;--------------
; See if current operation complete

	mov cx,cs:status_mask
	in al,fdc_stat	;read status
	and al,ch
	xor al,cl	;isolate what we want to poll
	mov al,0
	jz flpy_poll_nrdy
	    mov al,0ffh
flpy_poll_nrdy:
	ret

;=======
SECTRAN:    ; Function 14:  Sector Translate
;=======
; Translate sector number given a translate table
; If the translate table address is 0, don't translate
;	input:	CX = Sector Number
;		DX = Offset of Translate Table
;	output:	AX = Translated Sector Number
 
	cmp dx,0 ! jne sectran1
	    mov ax,cx ! ret
sectran1:
	mov bx,cx
	add bx,dx	;add sector to tran table addr
	mov bl,[bx]	;get logical sector
	xor bh,bh
	mov ax,bx
	ret

;=======
SETDMAB:    ; Function 15:  Set DMA Base
;=======
; set DMA segment
;	input:	CX = Segment of DMA buffer
;	output:	None

	mov dma_seg,CX
	ret

;========
flushbuf:   ; Function 24: Flush Buffer
;========
;	input:	None
;	output:	AL = 00h if no error occurs
;		   = 01h if error occurs
;		   = 02h if read/only disk

	xor al,al	;no need to flush buffer
	ret		;with no blocking/deblocking

;*****************************************************
;*
;*	SUP/RTM CODE AREA
;*
;*****************************************************
;--------
rtm_poll:	;check dev, if not ready, do rtm poll
;--------
; Check poll condition.
; If not ready,Call MPM Poll Device Routine
;	input:	BX = device #	

	push bx ! mov cx,bx
	call polldev ! pop bx
	cmp al,0ffh ! je pllret
	    cmp stoppoll,true ! je rtm_poll
;---------
rtm_poll1:	; do poll_dev with no pretest
;---------
	    mov dx,bx ! mov cl,f_polldev
	    jmp supif
pllret:	ret

;=======
polldev:    ; Function 17:  Poll Device
;=======
;	input:	CL = device number
;	output:	AL = 000h if not ready
;		   = 0ffh if ready

	mov bh,0 ! mov bl,cl
	shl bx,1
	jmp polltbl[bx]

;=======
strtclk:    ; Function 18:  Start Clock
;=======
; Enable Flagsets on Tick Interrupts
;	input:	None
;	output:	None

	mov clockon,true
	ret

;=======
stopclk:    ; Function 19:  Stop Clock
;=======
; Disable Flagsets on Tick Interrupts
;	input:	None
;	output:	None

	mov clockon,false
	ret

;=======
getsegt:    ; Function 16:  Get Segment Table
;=======
;	Not supported by MP/M-86
;	input:	None
;	output:	AX = 0ffffh

	mov ax,0ffffh ! ret

;=========
selmemory:  ; Function 22:  Select Memory
;=========
;	input:	None
;	output:	None

	ret

;====
idle:	    ; Function 23:  Idle
;====
;	input:	None
;	output:	None
;
; The Idle routine is called by the Idle Process.
; Since the Idle routine has the worst priority (255)
; in the system, It will run only when nothing else
; can run.  This routine cannot use any resources that
; may take it off the Ready List.  This includes any
; kind of I/O that uses Poll Device or System Flags.

	mov cl,f_dispatch
	call supif
	jmp idle

; If all devices are Interrupt Driven then the Idle
; Routine can be the following instead:
;	halt ! jmp idle
; This cannot be used if any I/O uses POLL DEVICE
; since polling is only done during dispatches.

;=====
patch:
;=====

	nop ! nop ! nop ! nop ! nop ! nop
	nop ! nop ! nop ! nop ! nop ! nop
	nop ! nop ! nop ! nop ! nop ! nop
	nop ! nop ! nop ! nop ! nop ! nop
	nop ! nop ! nop ! nop ! nop ! nop
	nop ! nop

;*****************************************************
;*
;*	SYSTEM DATA AREA
;*
;*****************************************************

endcode	rw	0

	DSEG
	org	(offset endcode + 1) and 0fffeh
		;org to an even word offset
		;current UDA for MPM calls
udaseg		rw	1

;-----------------------------------------------------
;	XIOS FUNCTION TABLE
;-----------------------------------------------------
functab	dw	const		; 0-console status
	dw	conin		; 1-console input
	dw	conout		; 2-console output
	dw	plist		; 3-list output
	dw	punch		; 4-punch output
	dw	reader		; 5-reader input
	dw	HOME		; 6-home
	dw	SELDSK  	; 7-select disk
	dw	SETTRK  	; 8-set track
	dw	SETSEC  	; 9-set sector
	dw	SETDMA  	;10-set DMA offset
	dw	READ		;11-read
	dw	WRITE		;12-write
	dw	listst		;13-list status
	dw	sectran		;14-sector translate
	dw	setdmab		;15-set DMA base
	dw	getsegt		;16-get segment table
	dw	polldev		;17-poll device
	dw	strtclk		;18-start clock
	dw	stopclk		;19-stop clock
	dw	maxconsole	;20-maximum consoles
	dw	maxlist		;21-max list devices
	dw	selmemory	;22-select memory
	dw	idle		;23-idle
	dw	flushbuf	;24-flush buffer

;-----------------------------------------------------
		;
		; routines to find device status
		; on poll device calls
		;
polltbl		dw	const		;00-con 0 in
		dw	const		;01-con 1 in
		dw	const		;02-con 2 in
		dw	const		;03-con 3 in
		dw	const		;04-con 0 out
		dw	const		;05-con 1 out
		dw	const		;06-con 2 out
		dw	const		;07-con 3 out
		dw	plstst		;08-list 0 out
		dw	flpy_poll_stat	;09-dsk status

signon	db	cr,lf
	db	'MP/M-86 V2.0 for SBC 8612',cr,lf,0

int_trp	db	' Uninitialized Interrupt',cr,lf,0

loc_stk	rw  32	;local stack for initialization
stkbase	equ offset $
tickint_tos	rw	0

initss		rw	1
initsp		rw	1
		rw	32
initstack	rw	0


;*****************************************************
;*
;*	CHARACTER I/O DATA AREA
;*
;*****************************************************
		;
		org	((offset $) + 1) and 0fffeh
		;
		; console i/o table for
		; status mask and port address
		;
consttbl	dw	(c0inmsk shl 8) or c0stport
		dw	(c1inmsk shl 8) or c1stport
		dw	(c2inmsk shl 8) or c2stport
		dw	(c3inmsk shl 8) or c3stport
		dw	(c0outmsk shl 8) or c0stport
		dw	(c1outmsk shl 8) or c1stport
		dw	(c2outmsk shl 8) or c2stport
		dw	(c3outmsk shl 8) or c3stport
		;
		; console input table for
		; poll device no. and port address
		;
conintbl	dw	(c0indev shl 8) or c0ioport
		dw	(c1indev shl 8) or c1ioport
		dw	(c2indev shl 8) or c2ioport
		dw	(c3indev shl 8) or c3ioport
		;
		; console output table for
		; poll device no. and port address
		;
conouttbl	dw	(c0outdev shl 8) or c0ioport
		dw	(c1outdev shl 8) or c1ioport
		dw	(c2outdev shl 8) or c2ioport
		dw	(c3outdev shl 8) or c3ioport
		;
		; list i/o table for
		; status mask and port address
		;
loutsttbl	dw	(l0outmsk shl 8) or l0stport
		;
		; list output for
		; poll device no. and port address
		;
louttbl		dw	(l0outdev shl 8) or l0ioport

;*****************************************************
;*
;*	DISK DATA AREA
;*
;*****************************************************

errtbl  dw er0,er1,er2,er3
	dw er4,er5,er6,er7
	dw er8,er9,erA,erB
	dw erC,erD,erE,erF

er0 	db  cr,lf,'Null Error ??',0
er1 	equ er0
er2 	equ er0
er3 	equ er0
er4 	db  cr,lf,'Clock Error :',0
er5 	db  cr,lf,'Late DMA :',0
er6 	db  cr,lf,'ID CRC Error :',0
er7 	db  cr,lf,'Data CRC Error :',0
er8 	db  cr,lf,'Drive Not Ready :',0
er9 	db  cr,lf,'Write Protect :',0
erA 	db  cr,lf,'Trk 00 Not Found :',0
erB 	db  cr,lf,'Write Fault :',0
erC 	db  cr,lf,'Sector Not Found :',0
erD 	equ er0
erE 	equ er0
erF 	equ er0
nrdymsg	equ er8

rtry_cnt db 0 	;disk error retry counter
last_com dw 0 	;address of last command string
dma_adr  dw 0 	;dma offset stored here
dma_seg  dw 0 	;dma segment stored here
sel_mask db 40h ;select mask, 40h or 80h

;	Various command strings for i8271

io_com	db 3	;length
rd_wr	db 0	;read/write function code
trk	db 0	;track #
sect	db 0	;sector #

hom_com	db 2,29h,0	;home drive command
rds_com	db 1,2ch	;read status command

status_mask	dw	0	;mask for flpy_poll


disk		db	0	;physical disk selected

	;	logical to physical disk
	;	translation table
	;	 A    B    C    D
dtrntbl	db	000h,001h,0ffh,0ffh
	;	 E    F    G    H
	db	0ffh,0ffh,0ffh,0ffh
	;	 I    J    K    L
	db	0ffh,0ffh,0ffh,0ffh
	;	 M    N    O    P
	;*****MDISK SUPPORT*****
	if memdisk
	db	002h,0ffh,0ffh,0ffh
	endif
	;***********************
	if not memdisk
	db	0ffh,0ffh,0ffh,0ffh
	endif

	;	disk home routine table
dskhomtbl	dw	flpy_home	;A drive
		dw	flpy_home	;B drive
	;*****MDISK SUPPORT*****
	if memdisk
		dw	hom_ret		;M drive
	endif
	;***********************

	;	disk read routine table
dskrdtbl	dw	flpy_read	;A drive
		dw	flpy_read	;B drive
	;*****MDISK SUPPORT*****
	if memdisk
		dw	mdsk_read	;M drive
	endif
	;***********************

	;	disk write routine table
dskwrttbl	dw	flpy_write	;A drive
		dw	flpy_write	;B drive
	;*****MDISK SUPPORT*****
	if memdisk
		dw	mdsk_write	;M drive
	endif
	;***********************

	include singles.lib ;read in disk definitions

;*****************************************************
;*
;*	SUP/RTM DATA AREA
;*
;*****************************************************
stoppoll	dw	false	;disallows poll_device
				;for internal printing
				;of strings when true

clockon		db	false	;if true, Tick flag is
				;set on Tick interrupts

tick_count	db	60
;
;	scratch area
;
		dw	0cccch,0cccch,0cccch
		dw	0cccch,0cccch,0cccch
		dw	0cccch,0cccch,0cccch
		dw	0cccch,0cccch,0cccch
		dw	0cccch,0cccch,0cccch
		dw	0cccch,0cccch,0cccch
		dw	0cccch,0cccch,0cccch

	db 0	;fill last address for GENCMD

