;--------------------------------------------------------------
; File Name:			  lb186.asm
; Format:			  asm source
; Base Address:	FC00h Range: FC000h - 100000h Loaded length: 4000h
; Assembler:                      borland tasm
; Copyright 2016 joseph c. lang NO RIGHTS RESERVED
;--------------------------------------------------------------
; This disassembly doesn't produce an exact binary image of
; the Ampro ROM. TASM does argument size optimization that
; the original assembler (what ever that was) didn't do.
; For example "and bx,5". The original 5 was a word. With 
; tasm it's a byte. [sx+0] becomes [sx] The instructions
; are shorter so address alignment was not preserved. 
; nops have been added to preserve address alignment.
; (marked "tasm alignment")
; It will still serve as a usefull guide and documentation
; for the Ampro ROM and produce a functional ROM image.
;--------------------------------------------------------------

	.model medium
	P186

;--------------------------------------------------------------
biosdata	segment byte public 'DATA'
;BIOS data segment offsets: (at 40:0)
bd_0h		dw ?		; serial port 0 address
		dw 3 dup (?)	; unused
bd_8h		dw ?		; lpt port 0 address
		dw 3 dup (?)	; unused
;
bd_10h		dw ?		; equipment list
		db ?		; unused
bd_13h		dw ?		; memory size
bd_15h		db ?		; busy wait flag
		db ?		; unused
bd_17h		db ?		; keyboard shift
		db 2 dup (?)	; unused
bd_1Ah		dw ?		; get pointer
bd_1Ch		dw ?		; put pointer
bd_1Eh		db 32 dup (?)	; keyboard queue 32 bytes
;
bd_3Eh		db ?		; drive recal status
bd_3Fh		db ?		; drive motor status
		db ?		; unused
bd_41h		db ?		; floppy BIOS status
bd_42h		db 6 dup (?)	; CDB 6 bytes (also data for sense)
		db 36 dup (?)	; unused
;
bd_6Ch		dw ?		; tick counter lsw
bd_6Eh		dw ?		; tick counter msw
bd_70h		dw ?		; 24 hour rollover
;
		db 2 dup (?)	; unused
bd_74h		db ?		; HD BIOS error status
bd_75h		db ?		; last HD error (SCSI bits)
bd_76h		db ?		; HD error count
		db 13 dup (?)	; unused
bd_84h		db 4 dup (?)	; floppy step rate table
bd_88h		db 4 dup (?)	; settle time table 
bd_8Ch		db ?		; status in buffer
bd_8Dh		db ?		; message out buffer
bd_8Eh		db ?		; message in buffer
;
		db ?		; unused
bd_90h		db 4 dup (?)	; floppy current track table
		db 3 dup (?)	; unused
bd_97h		db ?		; last used FD
;
;drive parameter table
bd_98h		dw ?		; HD 0 heads
bd_9Ah		dw ?		; HD 0 sectors
bd_9Ch		dw ?		; HD 0 cylinders
		dw ?		; HD 1 heads
		dw ?		; HD 1 sectors
		dw ?		; HD 1 cylinders
;
		dw 2 dup (?)	; unused
;
bd_0A8h		dw ?		; pointer to drive parameter table
bd_0AAh		dw ?		; segment
;
bd_0ACh		dw ?		; pointer to HD select bits table
bd_0AEh		dw ?		; segment
;
bd_0B0h		dw 16 dup (?)	; serial irq table 16 words
;
bd_0D0h		dw ?		; pointer to IO table
bd_0D2h		dw ?		; seg
;
;IO table
bd_0D4h		db ?		; my SCSI ID
bd_0D5h		db ?		; target ID
bd_0D6h		dw ?		; unused?
bd_0D8h		dw ?		; CDB pointer (scsi command block)
bd_0DAh		dw ?		; seg
bd_0DCh		dw ?		; data buffer pointer
bd_0DEh		dw ?		; seg
bd_0E0h		dw ?		; status buffer pointer
bd_0E2h		dw ?		; seg
bd_0E4h		dw ?		; message in buffer ptr
bd_0E6h		dw ?		; seg
bd_0E8h		dw ?		; message out buffer ptr
bd_0EAh		dw ?		; seg

biosdata	ends
;
;--------------------------------------------------------------
seg000		segment	byte public 'CODE'
		assume cs:seg000, ds:biosdata
		assume es:nothing, ss:nothing
;
start:
		jmp	bios_init
;--------------------------------------------------------------
		db '* AMPRO Little Board/186'
		db 20h
		db 'MS-DOS/Concurrent PC DOS'
		db 20h
		db 'Rom-Bios *'
		db 0Dh
		db 0Ah
		db 'Version 3.35 -'
		db 20h
		db '28 January 1987'
		db 0
		db  24h	; $
		db ' 08 mhz Version ** '
		db 20h
		db 'CCooppyyrriigghhtt  ((CC))'
		db 20h
		db ' 11998855,,11998866 '
		db 20h
		db 'AAMMPPRROO  CCoommppuutteerrss  IInncc..'
		db 20h
		db 20h
		db 'AAllll  RRiigghhttss  RReesseerrvveedd  **'
		db 90h
;--------------------------------------------------------------
;
d_seg_40	dw 40h	;segment for abs 400h
d_seg_0		dw 0	;segment for abs 0h
;
;--------------------------------------------------------------
;initialize hardware
bios_init:
		cli			;stop interrupts
;
;set chip selects
		mov	dx, 0FFA0h	;UMCS chip sel control
		mov	ax, 0FC38h	;16k@FC000 (ROM)
		out	dx, ax
		mov	dx, 0FFA8h	;MPCS chip sel control
		mov	ax, 0C0B9h
		out	dx, ax
		mov	dx, 0FFA4h	;PACS chip sel control
		mov	ax, 139h
		out	dx, ax
;
;start timer 2
;timer 2 generates refresh request to DMA channel 1
		mov	dx, 0FF62h	;timer 2 max count
		mov	ax, 20h
		out	dx, ax
		mov	dx, 0FF66h	;timer 2 control
		mov	ax, 0C001h
		out	dx, ax
;
;DMA 1 is refresh 'counter'
;it cycles through all of RAM
		mov	dx, 0FFD8h	;DMA 1 transfer count
		mov	ax, 0FFFFh	;
		out	dx, ax
;
		sub	dx, 2		;destination hi 4 bits
		xor	ax, ax		;DMA 1 address (zero)
		out	dx, ax
		sub	dx, 2		;destination 
		out	dx, ax
		sub	dx, 2		;source hi 4 bits
		out	dx, ax
		sub	dx, 2		;source 
		out	dx, ax
;
;disable DMA IRQ and start (refresh) DMA cycles
		mov	dx, 0FF34h	;DMA 0 int control
		mov	ax, 0Fh		;mask int
		out	dx, ax
		mov	dx, 0FFDAh	;DMA 1 control word
		mov	ax, 7477h
		out	dx, ax
;
;refresh is running so clear all RAM
		xor	ax, ax
		mov	bl, 10h		;block count
		mov	es, ax		;clear segment
zero_loop:
		xor	ax, ax		;clear AX
		mov	di, ax		;clear pointer
		mov	cx, 8000h	;word count
		cld	
		rep stosw		;clear words
		mov	ax, es
		add	ax, 1000h	;inc segment
		mov	es, ax
		dec	bl		;dec count
		jnz	zero_loop
;
;determine the size of memory
		mov	ds, cs:d_seg_0	;start of RAM
		assume ds:nothing
		mov	bx, ds		;zero BX
		mov	ax, 0FFFFh	;init pattern
;
;put known pattern in ram at seg:400h
;increment seg by 1000h and loop
set_RAM:
		inc	ax		;inc pattern
		mov	[bx], ax	;write pattern to RAM
		add	bx, 400h	;inc offset
		jnb	set_RAM
		mov	dx, ds
		add	dx, 1000h	;inc segment
		mov	ds, dx
		jnb	set_RAM		;loop till end of RAM
;
;check RAM at seg:400h for pattern
;increment seg by 1000h and loop
		mov	ds, cs:d_seg_0
		mov	bx, ds
		mov	ax, 0FFFFh
check_RAM:
		inc	ax		;inc pattern (and size)
		cmp	[bx], ax	;pattern match?
		jnz	got_end		;no match=end of RAM
		add	bx, 400h	;next block
		jnb	check_RAM	;end of segment
		mov	dx, ds
		add	dx, 1000h	;inc segment
		mov	ds, dx
		jnb	check_RAM	;jmp not end of RAM
;
;end of RAM found so validate and save RAM size
got_end:
		mov	bx, ax		;save size
		mov	dx, 101Ah
		in	al, dx		;input port
		test	al, 40h
		jnz	set_size
		cmp	bx, 280h	;less than 640k
		jle	set_size
		mov	bx, 280h	;more than 640k
set_size:
		mov	ax, bx		;RAM size to AX
		mov	ds, cs:d_seg_40
		assume ds:biosdata
		mov	ds:[bd_13h], ax	;save memory size
;
;initialize  DUART
		mov	dx, 1004h	;command A
		mov	al, 0Ah		;disable TX RX
		out	dx, al
;
		mov	dx, 1000h	;mode A
		mov	al, 13h		;no parity 8 bits
		out	dx, al
;
		mov	dx, 1000h	;mode A
		mov	al, 7		;1 stop
		out	dx, al
;
		mov	dx, 1002h	;clk sel A
		mov	al, 0BBh	;9600 baud
		out	dx, al
;
		mov	dx, 1008h	;aux control 
		mov	al, 60h		;hi baud,xtal osc
		out	dx, al
;
		mov	dx, 1004h	;command A
		mov	al, 15h		;en TX RX rst mode reg ptr
		out	dx, al
;
		mov	dx, 101Ah	;output port ctl
		mov	al, 0		;all outputs
		out	dx, al
;
;the output port is inverted
;setting a bit drives the pin low
		mov	dx, 101Ch	;set output bits (low)
		mov	al, 83h		;set RTS !LPR init
		out	dx, al
;
		mov	dx, 101Eh	;clr output bits (HI)
		mov	al, 4
		out	dx, al
;
		mov	ds, cs:d_seg_40		;ds=biosdata
		mov	ax, 0
		mov	es, ax			;es=zero
		assume es:nothing
		mov	al, 40h			;
		mov	ds:[bd_17h], al		;fake keyboard shift status
		mov	ax, offset bd_1Eh	;init queue pointers
		mov	ds:[bd_1Ah], ax		;'get' pointer
		mov	ds:[bd_1Ch], ax		;'put' pointer
;
		mov	word ptr ds:[bd_8h], 1280h	;set LPT1 address
		mov	word ptr ds:[bd_0h], 1016h	;set COM1 address
		mov	word ptr ds:[bd_10h], 423Dh	;set equipment list
;
;test for 1770 FDC presence
;try to write/readback the sector register
		mov	dx, 1104h		;FDC sector reg
		mov	al, 0AAh		;test pattern
		out	dx, al			;put in register
		mov	bl, al			;save pattern
;
		mov	cx, 1Eh			;delay count
loop001:
		loop	loop001			;delay
;
		in	al, dx			;read register 
		cmp	al, bl			;match test pattern?
		jz	flop_pres
;
;FDC not present
		mov	ax, 0FF3Eh
		and	ds:[bd_10h], ax		;remove floppy from equip list
		jmp	short set_vector
;
		nop	
;
;floppy controller is present 
;get drive count from jumpers
flop_pres:
		mov	dx, 1200h
		in	al, dx				;read jumpers
		and	al, 0C0h			;mask to drive bits
		xor	ah, ah				;clear hi byte
		or	ds:[bd_10h], ax			;set in equip list
		mov	cx, 1414h			;settle loop count (14h)
		mov	word ptr ds:[bd_88h], cx	;set settle time
		mov	word ptr ds:[bd_88h+2], cx	;for 4 drives
;
;copy interrupt vectors to RAM
set_vector:
		mov	di, 0				;destination (zero)
		push	cs				;copy CS to DS
		pop	ds
		assume ds:seg000
		mov	si, offset int_init		;vector init data
		mov	cx, 80h	;			;word count
		rep movsw				;move vectors
;
;copy serial irq table to RAM
		mov	es, cs:d_seg_40			;ES=biosdata
		assume es:biosdata
		mov	di, offset bd_0B0h 		;dest offset
		mov	si, offset com_irq_tab		;source offset
		mov	cx, 10h				;count
		rep movsw
;
;initialize stack
		cli			;stop interrupts
		mov	ax, 30h		;stack segment
		mov	ss, ax
		assume ss:nothing
		mov	sp, 100h	;set stack pointer
;
;enable serial interrupt
		mov	dx, 0FF38h	;int 0 control (serial)
		mov	ax, 11h		;set priority
		out	dx, ax
		mov	dx, 100Ah
		mov	al, 2
		out	dx, al
		sti			;allow interrupts
;
;setup timer 0 
;18 MS BIOS tick timer
		mov	dx, 0FF52h	;timer 0 max count
		mov	ax, 0D6Ah
		out	dx, ax
		mov	dx, 0FF56h	;timer 0 mode control
		mov	ax, 0E009h
		out	dx, ax
		mov	ax, 0
;
;enable timer,floppy, printer interrupts
		mov	dx, 0FF32h	;timer 0 ctl 
		out	dx, ax
		mov	dx, 0FF3Ch	;int 2 ctl (floppy)
		mov	ax, 3		;set priority
		out	dx, ax
		mov	dx, 0FF3Eh	;int 3 ctl (printer)
		mov	ax, 0Fh		;set priority
		out	dx, ax
		mov	dx, 0FF28h	;int mask
		mov	ax, 0ACh 	;
		out	dx, ax
;
;call BIOS to setup AUX serial port 1
		mov	ah, 0		;function
		mov	al, 0A3h 	;baud/par/stop
		mov	dx, 1		;port 1
		int	14h		;init com port
;
;detect SCSI presence
		xor	al, al
		mov	dx, 1084h	;reset 5380
		out	dx, al		;clear mode
;
		mov	cx, 20h		;loop count
loop002:
		loop	loop002		;delay
;
		in	al, dx		;read port
		or	al, al		;
		jnz	boot_DOS	;try floppy boot
;
;install hd driver
;move floppy irq to int_40
;put HD vector at int_13
		xor	ax, ax
		mov	ds, ax		;zero DS
		assume ds:nothing
		mov	ax, ds:[4Ch]	;get floppy int 
		mov	ds:[100h], ax	;move to int 40h
		mov	ax, ds:[4Eh]	;copy segment ptr
		mov	ds:[102h], ax
;
		mov	word ptr ds:[4Ch], offset hd_int13
		mov	ds:[4Eh], cs
;
;set pointer to (BIOS) HD param table
		mov	word ptr ds:[104h], offset hd_param
		mov	ds:[106h], cs
;
;get initiator SCSI ID (my ID)
		push	ds
		mov	ax, cs
		mov	ds, ax			;copy CS to DS
		assume ds:seg000
		mov	es, cs:d_seg_40		;bios data to ES
		assume es:biosdata
		mov	si, offset SEL_bits	;point to select bits
		mov	dx, 1200h
		in	al, dx			;read jumpers
		and	al, 7			;mask to ID bits
		cmp	al, 7			;my id=7?
		jz	id_eq_7			;jmp if ID=7
		mov	cx, 8			;offset to 2nd tabl
		add	si, cx
;
;set pointer to drive select table
id_eq_7:
		mov	es:[bd_0ACh], si	;save offset
		mov	es:[bd_0AEh], cs	;save segment
;
;copy default drive parameters to RAM
		mov	si, offset DRV_params	;source offset
		mov	di, offset bd_98h	;dest offset
		mov	es:[bd_0A8h], di	;set pointer
		mov	word ptr es:[bd_0AAh], 40h 	;set segment
		mov	cx, 0Ch			;count
		nop	
		rep movsb			;copy table
;
;copy default SCSI IO table to RAM
		mov	si, offset IO_table	;source offset
		mov	di, offset bd_0D4h 	;dest offset
		mov	cx, 18h			;count
		nop	
		rep movsb			;copy 24 bytes
;
;set my SCSI id in IO table
		mov	cl, al			;convert id to
		mov	al, 1			;select bit
		shl	al, cl			;shift CL times
		mov	es:[bd_0D4h], al	;set my ID
;
;call a ROM expansion stub
		pop	ds			;DS=biosdata
		assume ds:biosdata
;I cant get this far call to assemble so I'll fake it.
;		call	far 0FC00h:3FE0h	;call bios extension
		db 09ah,0E0h,03Fh,0h,0FCh
;
;--------------------------------------------------------------
;boot DOS
boot_DOS:
		int	19h		; DISK BOOT
					; int 19 doesn't return
;
;can't get here
		int	18h		; TRANSFER TO ROM BASIC
					; int 18 doesn't return
		nop	

;--------------------------------------------------------------
;DUART HW interrupt service
int_0c:
		push	ax
		push	si
		push	dx
		push	ds
		cld	
		mov	ds, cs:d_seg_40
		assume ds:biosdata
		mov	dx, 100Ah
		in	al, dx		;get irq status bits
;
;decode status bits
;AL=status  bits
;SI=index to handler
		test	al, 2		;rx A full
		mov	si, 4
		jnz	srv_comm
		test	al, 20h		;rx B full
		mov	si, 14h
		jnz	srv_comm
		test	al, 1		;tx A rdy
		mov	si, 0
		jnz	srv_comm
		test	al, 4		;break A
		mov	si, 8
		jnz	srv_comm
		test	al, 8		;counter
		mov	si, 0Ch
		jnz	srv_comm
		test	al, 10h		;tx B rdy
		mov	si, 10h
		jnz	srv_comm
		test	al, 40h		;break B
		mov	si, 18h
		jnz	srv_comm
		test	al, 80h		;port change
		mov	si, 1Ch
;
;--------------------------------------------------------------
;vector to comm port service routines
srv_comm:
		call	dword ptr [si+0B0h]
		mov	dx, 0FF22h
		mov	ax, 0Ch
		out	dx, ax		;reset irq
		pop	ds
		pop	dx
		pop	si
		pop	ax
		iret	
;
;--------------------------------------------------------------
;nothing to do just return
irq_ret:
		retf	
;
		nop	
;
;--------------------------------------------------------------
;rx A receive full irq (console)
rx_A_full:
		push	di
		mov	dx, 1006h
		in	al, dx			;get rx data
		cmp	al, 3			;ctrl C?
		jz	got_ctrlC
		mov	di, ds:[bd_1Ch]		;put pointer
		mov	si, di
		inc	di
		cmp	di, offset bd_3Eh	;end of buffer
		jb	rx_A_ful2
		mov	di, offset bd_1Eh	;then wrap pointer
rx_A_ful2:
		cmp	di, ds:[bd_1Ah]		;eq get pointer?
		jz	key_ovrn
		mov	[si], al		;save in buffer
		mov	ds:[bd_1Ch], di
		pop	di
		retf	
;
		nop	
;
; handle ctrl C
got_ctrlC:
		mov	di, offset bd_1Eh	;reset get pointer
		mov	ds:[bd_1Ah], di
		mov	si, di
		mov	[si], al		;put ^C at head of queue	
		inc	di
		mov	ds:[bd_1Ch], di		;reset put pointer
		pop	di
		retf	
;
;key buffer overrun
key_ovrn:
		mov	dx, 1002h
		in	al, dx		;get status
		test	al, 4		;tx empty?
		jz	key_ovrn1	;jmp if not
		mov	dx, 1006h
		mov	al, 7		;bell
		out	dx, al		;send it
key_ovrn1:
		pop	di
		retf	
;--------------------------------------------------------------
;
		dw 0
		dw 40h
;
boot_point	dw 7C00h	;pointer to boot location
		dw 0
;

;--------------------------------------------------------------
; DISK BOOT
; boot disk system
int_19:
		sti			;allow interrupts
		mov	ds, cs:d_seg_40
		assume ds:biosdata
		xor	al, al		;clear 5380
		mov	dx, 1084h
		out	dx, al
		mov	dx, 1082h
		out	dx, al
		mov	dx, 1086h
		out	dx, al
		mov	dx, 1088h
		out	dx, al
		mov	dx, 108Eh
		in	al, dx
		mov	dx, 1084h
		xor	al, al
		out	dx, al
;
		mov	cx, 20h
loop003:
		loop	loop003
;
		in	al, dx
		or	al, al
		mov	ds:[bd_74h], al		;set error status
		jnz	int_19c
		mov	cx, 0FFFFh
int_19a:
		mov	dx, 1080h
		in	al, dx
		cmp	al, 1
		jnz	int_19b
		loopz	int_19a
		mov	ds:[bd_74h], al		;set error status
		jmp	dsk_reset
;
int_19b:
		mov	dx, 1200h
		in	al, dx		;read jumpers
		and	al, 7
		cmp	al, 7
		jnz	int_19c
;long wait
		mov	al, 8		;8 loops
		xor	cx, cx		;of 65535 cycles
loop004:
		loop	loop004
		dec	al
		jnz	loop004
;
		mov	dx, 1082h
		mov	al, 80h		;assert SCSI reset
		out	dx, al
;
		mov	cx, 32h		;kill time 32h loops
loop005:
		loop	loop005
;
		xor	al, al		;negate reset
		out	dx, al
;
;wait for SCSI to come out of reset
int_19c:
		mov	al, 8
		xor	cx, cx
loop006:
		loop	loop006
		dec	al
		jnz	loop006
;
		test	byte ptr ds:[bd_74h], 0FFh	;any bits set
		jnz	dsk_reset
;
;
;wait for HD drive ready
		mov	cx, 78h		;loop count
wait_rdy:
		push	cx
		mov	ah, 10h		;test for ready
		mov	dx, 80h		;hd drive 0
		int	13h		;call BIOS
		pop	cx
		jnb	read_MBR	;jmp if ready
		loop	wait_rdy	;wait for ready
		jmp	short dsk_reset
;
		nop	
;
;drive is ready so read MBR
read_MBR:
		mov	ax, 201h	;read 1 sector
		mov	dx, 80h		;drive 0 head 0
		mov	cx, 1		;track 0 sector 1
		les	bx, dword ptr cs:boot_point
		assume es:nothing
		int	13h		;call BIOS read
		jb	dsk_reset
;
;validate MBR sector (magic number)
		cmp	word ptr es:[7DB4h], 0ABCDh
		jnz	dsk_reset
		cmp	word ptr es:[7DB6h], 4
		jb	dsk_reset
;
;copy hard drive params to table
		mov	ax, es:[7D9Fh]
		mov	ds:[bd_9Ah], ax		;set sectors
		mov	al, es:[7DA3h]
		xor	ah, ah
		mov	ds:[bd_98h], ax		;set heads
		mov	ax, es:[7DA1h]
		mov	ds:[bd_9Ch], ax		;set cylinders
;
dsk_reset:
		xor	ax, ax			;AH=reset disk
		mov	dx, ax			;drive zero
		int	13h			;call BIOS
;
;--------------------------------------------------------------
;attempt boot from floppy
;clear buffer for boot sector
		les	di, dword ptr cs:boot_point
		mov	cx, 100h		;word count
		xor	ax, ax			;data
		cld	
		rep stosw			;zero out buffer
;
		mov	ds, cs:d_seg_40
		test	word ptr ds:[bd_10h], 1	;no floppy disks?
		jz	no_floppy
;
;read sector 1 from floppy disk
		mov	al, 5			;retry count
fboot_rtry:
		push	ax
		mov	ax, 201h		;AH=read AL=sector
		les	bx, dword ptr cs:boot_point
		mov	dx, 0			;head 0 FD drive 0
		mov	cx, 1			;track 0, 1 sector 
		int	13h			;call BIOS
		pop	ax
		jnb	exec_boot		;jmp if good read
		dec	al			;dec retry count
		jnz	fboot_rtry		;loop for more tries
no_floppy:
		test	byte ptr ds:[bd_74h], 0FFh	;any errors set?
		jnz	boot_err
;
;--------------------------------------------------------------
;attempt boot from HD
;clear boot sector data area
		les	di, dword ptr cs:boot_point
		mov	cx, 100h
		xor	ax, ax
		cld	
		rep stosw
;
;read sector 1 from hd
		mov	ax, 201h		;read sector 1
		les	bx, dword ptr cs:boot_point
		mov	dx, 80h			;1st hd drive
		mov	cx, 1			;sector count
		int	13h			;call BIOS
		jnb	exec_boot
boot_err:
		jmp	int_19			;loop forever
;
		nop	
;
;--------------------------------------------------------------
;execute boot loader
exec_boot:
		lds	si, dword ptr cs:boot_point
		assume ds:nothing
		cmp	word ptr [si+1FEh], 0AA55h	;valid boot?
		jnz	boot_err
;this doesn't produce correct results....
;		jmp	far ptr	0:7C00h		;execute boot
		db 0EAh,0h,7ch,0,0		;
;--------------------------------------------------------------
;table of baud rate constants
baud_tbl	db 11h	;110
		db 33h	;150
		db 44h	;300
		db 55h	;600
		db 66h	;1200
		db 88h	;2400
		db 99h	;4800
		db 0BBh	;9600
;
;--------------------------------------------------------------
com_table	dw offset com_speed	;AH=0	set speed
		dw offset com_tx	;AH=1	tx character
		dw offset com_rx	;AH=2 	rx character
		dw offset com_status	;AH=3	get status
;
;--------------------------------------------------------------
; SERIAL I/O - USART PORT B
; AL = initializing parameters,	DX = port number (0)
; Return: AH = RS-232 status bits, AL = modem status bits
int_14:
		cmp	ah, 0CFh 	;special function?
		jnz	int_14a
		cmp	bx, 5555h	;valid?
		jnz	int_14a
		cmp	dx, 0AAAAh	;really really valid?
		jnz	int_14a
;
;reset interrupt table (undocumented function)
		cli			;stop interrupts
		push	ds
		push	es
		push	di
		push	si
		call	rst_vectors	;reset int table
		pop	si
		pop	di
		pop	es
		pop	ds
		mov	dx, 0CFCFh
		retf	2
;
int_14a:
		push	cx
		push	dx
		push	si
		push	ds
		enter	2, 0
		mov	[bp-2],	al	;save params or char
		cmp	ah, 3
		ja	int_14b		;out of range?
		mov	ds, cs:d_seg_40	;bios data to DS
		assume ds:biosdata
		xchg	si, dx
		mov	dx, [si+0]
		nop			;*** tasm alignmnet
		nop			;***
		or	dx, dx
		jz	int_14b
		xchg	ah, al		;funct to AL
		xor	ah, ah		;zero to AH
		shl	ax, 1		;make word offset
		xchg	ax, si
		jmp	cs:com_table[si]	;jump to function
;
int_14b:
		mov	ah, 1
int_14c:
		mov	al, [bp-2]	;get RX char
		leave	
		pop	ds
		pop	si
		pop	dx
		pop	cx
		iret	
;
		nop	
;--------------------------------------------------------------
;AH=0 serial port set baud rate, parity, stop bits
com_speed:
		push	bx
		mov	dx, 1014h	;command register
		mov	al, 1Ah
		out	dx, al		;disable TX RX
		mov	dx, 1010h	;mode register
		mov	al, [bp-2]	;get params
		and	al, 3		;mask to size
		test	byte ptr [bp-2], 8
		jnz	com_spe01	;jmp if parity enabled
		or	al, 10h		;set no parity
com_spe01:
		test	byte ptr [bp-2], 10h
		jnz	com_spe02
		or	al, 4		;set parity odd
com_spe02:
		out	dx, al		;set register
;
		mov	al, 7		;one stop bit
		test	byte ptr [bp-2], 4
		jz	com_spe03
		or	al, 8		;two stop bit
com_spe03:
		out	dx, al		;set register
		mov	bx, [bp-2]	;get params
		shr	bl, 5
		and	bx, 7		;mask to baud
		nop			;*** tasm alignment
		mov	al, cs:[bx+baud_tbl]	;get from table
		mov	dx, 1012h	;clock select
		out	dx, al		;set baud
		mov	dx, 1014h	;command reg
		mov	al, 15h		;start TX RX
		out	dx, al
		pop	bx
		call	get_Bstat	;get status
		jmp	short int_14c	;return
;--------------------------------------------------------------
;AH=1 serial port transmit
com_tx:
		xor	cx, cx		;time out count
		mov	dx, 1012h	;B status register
com_tx1:
		in	al, dx
		test	al, 8		;tx buffer empty
		jnz	com_tx2
		loop	com_tx1		;loop till empty
		call	get_Bstat	;get status
		or	ah, 80h		;set timeout bit
		jmp	short int_14c	;return
;
com_tx2:
		mov	dx, 1016h
		mov	al, [bp-2]	;get char
		out	dx, al		;send it
		call	get_Bstat	;get status
		jmp	short int_14c	;return
;--------------------------------------------------------------
;AH=2 serial port receive
com_rx:
		xor	cx, cx
		mov	dx, 1012h	;get B status reg
com_rx1:
		in	al, dx
		test	al, 1		;data ready?
		jnz	com_rx2
		loop	com_rx1		;loop till data avail
;
;input timeout
		call	get_Bstat	;get B status
		or	ah, 80h		;set timeout
		jmp	int_14c		;return
;
		nop	
com_rx2:
		mov	dx, 1016h	;B rx holding reg 
		in	al, dx
		mov	[bp-2],	al	;return value
		call	get_Bstat	;get status
		jmp	int_14c		;return
;
		nop	
;--------------------------------------------------------------
;AH=3 serial port status
com_status:
		mov	dx, 101Ah	;input port
		in	al, dx
		test	al, 2
		jz	com_stat1
		mov	al, 0
		jmp	short com_stat2
;
		nop	
		nop	
com_stat1:
		mov	al, 30h	;
com_stat2:
		mov	[bp-2],	al
		call	get_Bstat
		jmp	int_14c
;
		nop	
;
;--------------------------------------------------------------
;get SIO-b status
get_Bstat	proc near
		mov	dx, 1012h	;B status register
		in	al, dx
		shr	al, 3
		and	al, 1Eh
		mov	ah, al
		in	al, dx
		test	al, 1
		jz	get_Bsta2
		or	ah, 1
get_Bsta2:
		and	al, 0Ch
		shl	al, 3
		or	ah, al
		retn	
get_Bstat	endp
;
		nop	
;
;--------------------------------------------------------------
key_table	dw offset key_read	;AH=0 read input
		dw offset key_stat	;AH=1 get status
		dw offset key_shift	;AH=2 query shift key
;
;--------------------------------------------------------------
;keyboard services
; AH=function
int_16:
		sti			;allow  interrupts
		push	ds
		push	si
		mov	ds, cs:d_seg_40
		assume ds:biosdata
		cmp	ah, 2		;out of range?
		ja	int_16a
		mov	al, ah		;func to AL
		xor	ah, ah		;zero to AH
		shl	ax, 1		;offset to word
		xchg	ax, si
		jmp	cs:key_table[si] ;execute routine
;
		nop	
;
int_16a:
		pop	si
		pop	ds
		iret	
;
		nop	
;--------------------------------------------------------------
;AH=0 read serial console (wait for char)
key_read:
		cli			;stop interrupts
		mov	si, ds:[bd_1Ah]
		cmp	si, ds:[bd_1Ch]
		sti			;allow interrupts
		jz	key_read	;loop till char available
		mov	al, [si]	;get char
		inc	si		;advance pointer
		cmp	si, offset bd_3Eh	;end of queue?
		jb	key_read1
		mov	si, offset bd_1Eh	;reset to start of queue
key_read1:
		mov	ds:[bd_1Ah], si	;save get pointer
		pop	si
		pop	ds
		iret			;exit
;
		nop	
;--------------------------------------------------------------
;AH=1 get serial console status
key_stat:
		cli			;stop interrupts
		mov	si, ds:[bd_1Ah]
		cmp	si, ds:[bd_1Ch]
		mov	al, [si]	;get char 
		sti			;allow interrupts
;
;discard pointer (leave char on queue)
		pop	si
		pop	ds
		retf	2
;
		nop	
;--------------------------------------------------------------
;AH=2 get serial console shift status
key_shift:
		mov	al, ds:[bd_17h]	;get keyboard shift
		pop	si
		pop	ds
		iret	
;
;--------------------------------------------------------------
;floppy disk function table
flp_table	dw offset flp_reset	;AH=0	reset
		dw offset flp_status	;AH=1	get status
		dw offset flp_read	;AH=2	read sectors
		dw offset flp_write	;AH=3	write sectors
		dw offset flp_verify	;AH=4	verify sectors
		dw offset flp_format	;AH=5	format track
;
;--------------------------------------------------------------
;floppy disk IO
;AH=function DL=drive DH=head CH=track CL=sector
;AL=sector count ES:BX=buffer
;moved to int 40h if hdisk installed
int_13:	
		cmp	dl, 7Fh			;special function?
		jnz	int_13a
		cmp	ah, 1			;special function?
		jz	int_13c
int_13a:
		enter	0, 0			;
		push	ds			;save registers
		push	ax
		push	bx
		push	cx
		push	dx
		push	di
		push	si
		push	es
		sti				;allow interrupts
		mov	ds, cs:d_seg_40
		assume ds:biosdata
		cmp	ah, 5			;out of range?
		jbe	int_13b
;
		mov	byte ptr ds:[bd_41h], 1	;set illegal function
		jmp	short flp_status	;exit through status
;
		nop	
		nop	
int_13b:
		xchg	ah, al			;function to AL
		xor	ah, ah			;zero to AH
		shl	ax, 1			;word offset
		xchg	ax, si
		jmp	cs:flp_table[si]	;exec function
;
;floppy done get status
flop_done:
		mov	al, [bp-4]
		and	ax, 0FFh
		sub	ax, di
		mov	[bp-4],	al
;
;--------------------------------------------------------------
;floppy function=1 get status
flp_status:
		pop	es		;clean up stack
		pop	si
		pop	di
		pop	dx
		pop	cx
		pop	bx
		pop	ax
		mov	ah, ds:[bd_41h]	;get floppy status
		cmp	ah, 1		;set flags
		cmc	
		pop	ds
		leave	
		retf	2		;
;
		nop	

;--------------------------------------------------------------
;special function: get floppy busy flag
;DL=7fh AH=1
int_13c:
		push	ds		;save segment
		mov	ds, cs:d_seg_40	;get DS
		assume ds:biosdata
		mov	al, ds:[bd_15h]	;get flag
		cbw			;convert to words
		pop	ds		;recover DS
		iret			;exit
;
;--------------------------------------------------------------
;floppy disk HW interrupt service
int_0e:
		sti			;allow interrupts
		push	ds		;save regs
		push	dx
		push	ax
		mov	ds, cs:d_seg_40	;point to data seg
		assume ds:biosdata
		or	byte ptr ds:[bd_15h], 80h	;set done flag
		mov	ax, 9101h	;signal op complete
		int	15h		;call busy wait
		mov	dx, 0FF22h	;EOI register
		mov	ax, 0Eh		;mask off interrupt
		out	dx, ax
		pop	ax		;clean up
		pop	dx
		pop	ds
		iret			;exit

;--------------------------------------------------------------
;delay subroutine
delay	proc near
		push	cx
		mov	cx, 22h
loop007:
		loop	loop007
		pop	cx
		retn	
delay	endp
;
;--------------------------------------------------------------
;select floppy drive, spin up and test ready
flop_select	proc near
		push	cx
		push	bx
;
;convert head/drive in DX to side
; and select bits in AL
		and	dx, 103h	;mask to head and drive
		mov	cl, dl		;get drive
		mov	al, 1
		shl	al, cl		;shift sel bit
		mov	cl, 4		;shift side 4 places
		shl	dh, cl		;
		or	al, dh		;combine sel and side
;
;select drive and side
		mov	dx, 1200h	;drive sel latch
		out	dx, al		;select drive+side
		and	al, 0Fh		;strip side bit
		push	ax
		test	ds:[bd_3Eh], al	;test if recal done
		jnz	old_drive	;track should be valid
;
;uncalibrated, drive needs seek zero
		or	ds:[bd_3Eh], al	;mark drive calibrated
		mov	bx, [bp-0Ah]	;get drive
		and	bx, 3		;mask to 4 drives
		nop			;*** tasm alignment
		mov	byte ptr [bx+bd_90h], 0	;set track to zero
		mov	al, 0		;recal command
		or	al, [bx+bd_84h]	;or in step rate
		call	flop_GO		;seek trk zero
		jz	old_drive
		pop	ax
		pop	bx
		pop	cx
		retn	
;
;drive already calibrated, seek zero not needed
old_drive:
		mov	dx, 1100h
		in	al, dx			;get 1770 status
		test	al, 80h			;motor on?
		jnz	old_drv2
;
;motor not running so  spin up drive
		mov	byte ptr ds:[bd_3Fh], 0	;clear motor status
		mov	dx, 1100h
		mov	al, 0D0h	 	;force interrupt
		out	dx, al
		mov	cx, 1			;time out
		mov	ah, 4			;time out count
old_drv1:
		mov	dx, 101Ah		;read input port
		in	al, dx
		test	al, 8			;drive ready bit
		jnz	old_drv2		;ready so dont loop
		loop	old_drv1		;loop till ready
		dec	ah
		jnz	old_drv1		;or timeout
;
;motor is running
old_drv2:
		pop	ax
		test	ds:[bd_3Fh], al		;same drive?
		jnz	old_drv4		;then exit
		mov	ds:[bd_3Fh], al		;set motor status
;
		mov	al, 32h			;outer loop count
old_drv3:
		mov	cx, 215h		;inner loop count
loop008:
		loop	loop008			;delay loop
		dec	al
		jnz	old_drv3
;
		nop	
old_drv4:
		pop	bx			;clean up stack
		pop	cx
		xor	ax, ax
		retn				;exit
flop_select	endp
;
		nop	
;--------------------------------------------------------------
;get floppy controllers attention
;stop FDC operation then reset FDC
force_int	proc near
		push	ax
		push	dx
		mov	dx, 1200h
		xor	al, al
		out	dx, al		;clear FDC state
		mov	ds:[bd_3Fh], al	;clear motor status
		mov	ds:[bd_3Eh], al	;clear recal done
		mov	ds:[bd_97h], al	;clear last used FD
		mov	dx, 1100h	;FDC command reg
		mov	al, 0D0h 	;force interrupt
		out	dx, al		;stop FDC
		call	delay
		mov	dx, 101Ch	;set output bits
		mov	al, 20h		;
		out	dx, al		;FDC reset bit (low)
		call	delay
		mov	dx, 101Eh	;clr output bits
		out	dx, al		;release FDC reset
		call	delay
		pop	dx
		pop	ax
		retn	
force_int	endp
;
		nop	
;
;--------------------------------------------------------------
;start floppy operation
flop_GO	proc near
		pusha	
		cli				;stop interrupts
		mov	cx, ax
		mov	dx, 0FF3Ch		;enable FDC interrupt
		mov	ax, 3
		out	dx, ax
		mov	dx, 0FF22h
		mov	ax, 0Eh
		out	dx, ax
;
		mov	ax, cx			;
		and	byte ptr ds:[bd_15h], 7Fh	;turn off done flag
		mov	byte ptr ds:[bd_41h], 0	;clear status
		mov	dx, 1100h		;start floppy
		out	dx, al
		mov	ax, 9001h		;signal wait loop
		int	15h
		sti				;allow interrupts
		test	byte ptr ds:[bd_41h], 80h	;done?
		jnz	flop_GO2
;
;wait for done bit
;done bit is set by interrupt routine
		xor	cx, cx			;time out
		mov	bl, 4			;long time out
flop_GO1:
		test	byte ptr ds:[bd_15h], 80h	;done?
		jnz	flop_GO3
		loop	flop_GO1
		dec	bl
		jnz	flop_GO1
;
;timed out or error out
flop_GO2:
		call	force_int		;stop FDC
		or	byte ptr ds:[bd_41h], 80h	;set drive not ready
;
;good exit
flop_GO3:
		or	byte ptr ds:[bd_41h], 0	;
		mov	dx, 0FF3Ch		;turn off IRQ
		mov	ax, 0Bh
		out	dx, ax
		popa	
		retn	
flop_GO	endp
;
;--------------------------------------------------------------
;seek to track
flop_seek	proc near
		pusha	
		mov	bx, [bp-0Ah]	;get drive
		and	bx, 3		;mask to 4 drives
		nop			;*** tasm alignment
		cmp	[bx+bd_90h], ch	;same track?
		jnz	flp_seek1	;do seek if not
		mov	al, ds:[bd_3Fh]	;get motor status
		cmp	ds:[bd_97h], al	;same as last used drive? 
		jz	flp_seek3	;then skip the rest
		mov	ds:[bd_97h], al	;save last used FD
;
;update track reg in FDC
;and seek to desired track
flp_seek1:
		mov	al, [bx+bd_90h]	;get old track
		mov	dx, 1102h	;track
		out	dx, al		;set controller track
		mov	[bx+bd_90h], ch	;save target 
		mov	al, ch
		mov	dx, 1106h	;set target track
		out	dx, al
		mov	al, 18h		;floppy seek command 
		or	al, [bx+bd_84h]	;or in step rate
		call	flop_GO		;execute seek
;
;settle time delay loop
		mov	al, [bx+bd_88h]	;get settle time
flp_seek2:
		mov	cx, 215h
settlet:
		loop	settlet		;settle time on track
		dec	al		;loop till done
		jnz	flp_seek2
flp_seek3:
		popa	
		retn	
flop_seek	endp
;
;--------------------------------------------------------------
flp_rd01:
		mov	byte ptr [bp-4], 0
		jmp	flp_status
;
		nop	
;--------------------------------------------------------------
;floppy function=2 read sector(s)
flp_read:
		mov	byte ptr ds:[bd_41h], 0	;clear status
		call	flop_select		;select drive
		jnz	flp_rd01		;error?
		call	flop_seek		;seek to track
		mov	al, [bp-4]
		and	ax, 0FFh		;mask to 256 words
		mov	di, ax
		mov	ds, cs:d_seg_0
		assume ds:nothing
		lds	si, dword ptr ds:[78h]	;get floppy table
		mov	cl, [si+3]		;sector size
		or	cl, cl
		mov	dx, 80h			;128 words
		jz	flp_rd2
		shl	dx, cl
flp_rd2:
		mul	dx			;times sector size
		mov	ds, cs:d_seg_40
		assume ds:biosdata
;
;set up DMA in
		push	ax
		mov	dx, 0FFCAh	;control word
		mov	ax, 4		;stop DMA
		out	dx, ax
		mov	dx, 0FFC8h	;transfer count
		pop	ax
		out	dx, ax
;
;convert seg:offset to linear 20bit address
		mov	ax, es
		mov	cx, 10h
		mul	cx
		add	ax, bx
		adc	dl, 0
		xchg	bx, dx
;
;setup DMA controller
		mov	dx, 0FFC4h	;dest pointer
		out	dx, ax
		xchg	ax, bx
		mov	dx, 0FFC6h	;dest pointer hi 4 bits
		out	dx, ax
;
		mov	dx, 0FFC0h
		mov	ax, 1106h	;source pointer
		out	dx, ax
		add	dx, 2		;transfer count
		xor	ax, ax
		out	dx, ax
;
		mov	dx, 0FFCAh	;control word
		mov	ax, 0AEA6h
		out	dx, ax
;
;--------------------------------------------------------------
;read sector
		mov	bl, [bp-8]	;get sector num
flp_rd3:
		mov	al, bl
		mov	dx, 1104h	;set sector
		out	dx, al
		mov	al, 88h		;read sector command
		call	flop_GO		;start floppy IO
		jnz	flp_rd4		;error?
		mov	dx, 1100h	;status reg
		in	al, dx
		test	al, 1Ch		;mask error bits
		jnz	rd_s_err
		inc	bl		;next sector
		dec	di		;dec sector count
		jnz	flp_rd3		;loop if not zero
flp_rd4:
		jmp	flop_done	;exit
;
		nop	
;
;read sector error
rd_s_err:
		call	force_int	;stop FDC
		jmp	f_err_com
;
;--------------------------------------------------------------
;floppy function=0 reset floppy subsystem
flp_reset:
		mov	dx, 101Ch		;set output
		mov	al, 20h			;set reset pin low
		out	dx, al
		call	delay
		mov	dx, 101Eh		;clear output
		out	dx, al			;release reset
		call	delay
		xor	ax, ax
		mov	ds:[bd_3Eh], al		;clear recal done
		mov	ds:[bd_41h], al		;clear status
		mov	ds:[bd_97h], al		;clear last used FD
		cld	
		mov	di, offset bd_90h	;point to track table
		mov	es, cs:d_seg_40
		stosw				;zero the table
		stosw
		jmp	flp_status
;
		nop	
;--------------------------------------------------------------
flp_wr01:
		mov	byte ptr [bp-4], 0
		jmp	flp_status
;
		nop	
;
;--------------------------------------------------------------
;floppy function=3 write sector(s)
flp_write:
		mov	byte ptr ds:[bd_41h], 0	;clear status
		call	flop_select		;select floppy drive
		jnz	flp_wr01
		call	flop_seek		;seek track
		mov	al, [bp-4]
		and	ax, 0FFh
		mov	di, ax
		mov	ds, cs:d_seg_0
		assume ds:nothing
		lds	si, dword ptr ds:[78h] ;get floppy table
		mov	cl, [si+3]
		or	cl, cl
		mov	dx, 80h	;
		jz	flp_wr02
		shl	dx, cl
;
;set up DMA out
flp_wr02:
		mul	dx
		mov	ds, cs:d_seg_40
		assume ds:biosdata
		push	ax
		mov	dx, 0FFCAh		;control word
		mov	ax, 4
		out	dx, ax
		mov	dx, 0FFC8h		;transfer count
		pop	ax
		out	dx, ax
;
;convert seg:offset to 20 bit linear address
		mov	ax, es
		mov	cx, 10h
		mul	cx
		add	ax, bx
		adc	dl, 0
		xchg	bx, dx
;
		mov	dx, 0FFC0h		;source pointer
		out	dx, ax
		xchg	ax, bx
		mov	dx, 0FFC2h		;source pointer hi 4 bits
		out	dx, ax
;
		mov	dx, 0FFC4h		;dest pointer
		mov	ax, 1106h		;data reg
		out	dx, ax
		add	dx, 2			;dest pointer hi 4 bits
		xor	ax, ax
		out	dx, ax
;
		mov	dx, 0FFCAh		;control word
		mov	ax, 76A6h
		out	dx, ax
;
;--------------------------------------------------------------
;write sector
		mov	bl, [bp-8]
flp_wr03:
		mov	al, bl
		mov	dx, 1104h		;set sector
		out	dx, al
		mov	al, 0A8h 		;write
		call	flop_GO			;execute command
		jnz	flp_wr04		;error?
		mov	dx, 1100h		;get status
		in	al, dx
		and	al, 5Ch			;error bits
		jnz	wr_s_err		;error?
		inc	bl			;inc sector num
		dec	di			;dec sector count
		jnz	flp_wr03		;loop for next sector
flp_wr04:
		jmp	flop_done		;done
;
		nop	
;
;write sector error
wr_s_err:
		call	force_int		;stop FDC
		test	al, 40h
		jz	f_err_com
		or	byte ptr ds:[bd_41h], 3	;write protect error
		jmp	flop_done		;done
;
		nop	
;
;--------------------------------------------------------------
;floppy read/write/format error all come here
;convert 1770 error bits to BIOS error bits
f_err_com:
		test	al, 4
		jz	f_err_1
		or	byte ptr ds:[bd_41h], 8	;DMA overrun
		jmp	flop_done		;done
;
f_err_1:
		test	al, 8
		jz	f_err_2
		or	byte ptr ds:[bd_41h], 10h	;CRC error
		jmp	flop_done		;done
;
f_err_2:
		mov	ds, cs:d_seg_0
		assume ds:nothing
		lds	si, dword ptr ds:[78h] ;get floppy table
		mov	ah, [si+4]
		mov	ds, cs:d_seg_40
		assume ds:biosdata
		cmp	ah, bl
		jge	f_err_3
		or	byte ptr ds:[bd_41h], 2	;AM not found
		jmp	flop_done
;
		nop	
f_err_3:
		or	byte ptr ds:[bd_41h], 4	;sector not found
		jmp	flop_done
;
;--------------------------------------------------------------
flp_ver01:
		mov	byte ptr [bp-4], 0
		jmp	flp_status
;
		nop	
;
;--------------------------------------------------------------
;floppy function=4 verify sector
flp_verify:
		mov	byte ptr ds:[bd_41h], 0	;clear status
		call	flop_select		;select drive
		jnz	flp_ver01
		call	flop_seek		;seek track
		mov	al, [bp-4]
		and	ax, 0FFh
		mov	di, ax
		mov	ds, cs:d_seg_0		;point to seg 0
		lds	si, dword ptr ds:[78h]	;get floppy table
		mov	cl, [si+3]		;get sector size
		or	cl, cl
		mov	dx, 80h			;128 words
		jz	flp_vfy01
		shl	dx, cl
;
;set up DMA
flp_vfy01:
		mul	dx
		mov	ds, cs:d_seg_40
		assume ds:biosdata
		push	ax
		mov	dx, 0FFCAh		;control word
		mov	ax, 4			;stop DMA
		out	dx, ax
;
		mov	dx, 0FFC8h		;transfer count
		pop	ax
		out	dx, ax
;
;clear destination address
		xor	ax, ax
		mov	dx, 0FFC6h		;dest pointer hi 4 bits
		out	dx, ax
		mov	dx, 0FFC4h		;dest pointer
		out	dx, ax
;
		mov	dx, 0FFC0h		;source pointer
		mov	ax, 1106h
		out	dx, ax
		add	dx, 2			;source pointer hi 4 bits
		xor	ax, ax
		out	dx, ax
;
		mov	dx, 0FFCAh		;control word
		mov	ax, 6E66h
		out	dx, ax
;
;verify sector CRC
		mov	bl, [bp-8]		;get sector
flp_vfy02:
		mov	al, bl
		mov	dx, 1104h		;set sector in FDC
		out	dx, al
		mov	al, 88h			;read sector command
		call	flop_GO			;start IO
		jnz	flp_vfy03
		mov	dx, 1100h
		in	al, dx			;get status
		test	al, 1Ch			;error bits set?
		jnz	v_s_err
		inc	bl			;inc sector number
		dec	di			;dec sector count
		jnz	flp_vfy02		;loop

flp_vfy03:
		jmp	flop_done
;
;verify sector error
v_s_err:
		call	force_int		;stop FDC
		jmp	f_err_com		;common error exit
;
;--------------------------------------------------------------
;data patterns for write track
;index gap
trk_data	db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
;
;--------------------------------------------------------------
;index address mark
		db    0	;PLL sync  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db 0F6h	;write C2
		db 0F6h	;write C2
		db 0F6h	;write C2
		db 0FCh	;write FC index address mark
;
;--------------------------------------------------------------
;first sector gap
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
;
;--------------------------------------------------------------
;sector header data
trk_data2	db    0	;PLL sync
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db 0F5h	;write A1 preset CRC
		db 0F5h	;write A1 preset CRC
		db 0F5h	;write A1 preset CRC
		db 0FEh	;write FE id address mark
;sector ID inserted here (4 bytes)
;track,side,sector,size
		db 0F7h	;write CRC
;
;--------------------------------------------------------------
;sector data gap
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
		db  4Eh	;
;
;--------------------------------------------------------------
;sector data block
		db    0	;PLL sync  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db    0	;  
		db 0F5h	;write A1 preset CRC
		db 0F5h	;write A1 preset CRC
		db 0F5h	;write A1 preset CRC
		db 0FBh	;write FB data address mark
;data inserted here (512 bytes of E5)
;write CRC then loop for next sector
;
;--------------------------------------------------------------
		nop	;
;
;--------------------------------------------------------------
flp_for1:
		mov	byte ptr [bp-4], 0
		jmp	flp_status
;
		nop	
;
;--------------------------------------------------------------
;floppy function=5 format track
flp_format:
		mov	byte ptr ds:[bd_41h], 0	;clear status
		call	flop_select		;select drive
		jnz	flp_for1
		call	flop_seek		;seek track
		mov	dx, 1102h
		in	al, dx
		cmp	ch, al
		jz	flp_fmt02
		or	byte ptr ds:[bd_41h], 40h	;seek error
		jmp	flp_status
;
		nop	
flp_fmt01:
		mov	dx, 1100h
		mov	al, 0D0h 		;stop operation
		out	dx, al
		call	delay			;wait to complete
		mov	ds, cs:d_seg_40
		assume ds:biosdata
		or	byte ptr ds:[bd_41h], 3	;write protect
		sti				;allow interrupts
		leave	
		jmp	flp_status
;
flp_fmt02:
		mov	al, [bp-4]
		enter	2, 1			;reserve space
		mov	[bp-2],	al
		cld				;direction=up
		mov	dx, 0FFCAh		;control word
		mov	ax, 4			;turn off DMA
		out	dx, ax
		mov	di, bx
		mov	bx, 1106h		;fdc data reg
		mov	ax, cs
		mov	ds, ax
		assume ds:seg000
		cli				;no interrupts
;
;start the FDC write
		mov	al, 0F8h		;write track
		mov	dx, 1100h		;FDC CSR
		out	dx, al
flp_fmt03:
		in	al, dx
		test	al, 1
		jz	flp_fmt03		;loop till busy sets
		test	al, 40h			;write protect?
		jnz	flp_fmt01
;
;write index address mark
		mov	cx, 94h		;byte count
		mov	si, offset trk_data
FDRQ01:
		in	al, dx
		test	al, 2		;DRQ?
		jz	FDRQ01
		xchg	bx, dx
		outsb			;send byte
		xchg	bx, dx
		loop	FDRQ01		;loop
;
;write sector header
fmt_hdr:
		mov	si, offset trk_data2
		mov	cx, 10h		;byte count
FDRQ02:
		in	al, dx
		test	al, 2		;DRQ?
		jz	FDRQ02		;wait for DRQ
		xchg	bx, dx
		outsb			;send to FDC
		xchg	bx, dx
		loop	FDRQ02		;loop
;
;write sector id bytes
		mov	cx, 4		;byte count
		xchg	si, di
FDRQ03:
		in	al, dx
		test	al, 2		;DRQ?
		jz	FDRQ03
		xchg	bx, dx
		outs	dx, byte ptr es:[si]
		xchg	bx, dx
		loop	FDRQ03		;loop
;
;write gap and data mark
		xchg	si, di
		mov	cx, 27h		;byte count
FDRQ04:
		in	al, dx
		test	al, 2		;DRQ?
		jz	FDRQ04
		xchg	bx, dx
		outsb			;send to FDC
		xchg	bx, dx
		loop	FDRQ04		;loop
;
;write sector data
;512 bytes of 0E5h
		mov	cx, 200h	;byte count
FDRQ05:
		in	al, dx
		test	al, 2		;DRQ?
		jz	FDRQ05
		mov	al, 0E5h 	;data byte
		xchg	bx, dx
		out	dx, al		;send to FDC
		xchg	bx, dx
		loop	FDRQ05		;loop
;
;write data CRC
FDRQ06:
		in	al, dx
		test	al, 2		;DRQ?
		jz	FDRQ06
		mov	al, 0F7h 	;write CRC
		xchg	bx, dx
		out	dx, al		;send to FDC
		xchg	bx, dx
		dec	byte ptr [bp-2]	;last sector?
		jz	fmt_fill	;then fill rest of track
;
;write inter sector gap
;40 bytes of 4Eh
		mov	cx, 28h		;byte count
FDRQ07:
		in	al, dx
		test	al, 2		;DRQ?
		jz	FDRQ07
		mov	al, 4Eh		;gap character
		xchg	bx, dx
		out	dx, al		;send to FDC
		xchg	bx, dx
		loop	FDRQ07		;loop
;
		jmp	short fmt_hdr	;next sector
;
		nop	
;
;write 04Eh for rest of track
fmt_fill:
		in	al, dx
		test	al, 1		;BSY?
		jz	fmt_fil2
		test	al, 2		;DRQ?
		jz	fmt_fill
		mov	al, 4Eh		;data byte
		xchg	bx, dx
		out	dx, al		;write it
		xchg	bx, dx
		jmp	short fmt_fill	;loop
;
;delay for tunnel erase
fmt_fil2:
		mov	cx, 14D5h	;loop count
loop00a:
		loop	loop00a		;delay
		in	al, dx		;get ending status
		sti			;allow interrupts
		leave			;clean up stack
		mov	ax, 40h		;set data segment 
		mov	ds, ax
		assume ds:biosdata
		jmp	flp_status
;
;--------------------------------------------------------------
lpr_table	dw offset lpr_outch	;AH=0 print char
		dw offset lpr_setup	;AH=1 setup port
		dw offset lpr_gstat	;AH=2 get status
;
;--------------------------------------------------------------
;error bits conversion tables
lpt_tbl1	db 88h	;!paper !sel !busy
		db 88h	;!paper !sel  busy
		db 88h	;!paper  sel !busy
		db 29h	;!paper  sel  busy
		db 10h	; paper !sel !busy
		db 10h	; paper !sel  busy
		db 88h	; paper  sel !busy
		db 1	; paper  sel  busy
;
lpt_tbl2	db 88h	;!paper !sel !busy
		db 88h	;!paper !sel  busy
		db 88h	;!paper  sel !busy
		db 28h	;!paper  sel  busy
		db 90h	; paper !sel !busy
		db 10h	; paper !sel  busy
		db 88h	; paper  sel !busy
		db 0	; paper  sel  busy
;
;--------------------------------------------------------------
;line printer request
;AH=function DX=printer (0)
int_17:
		push	ds
		push	dx
		push	si
		enter	4, 0
		mov	[bp-2],	ax
		cmp	ah, 2		;out of range?
		ja	int_17a		;set error and exit
		or	dx, dx
		jnz	int_17a		;lpt!=0?
		xchg	ah, al		;func to AL
		xor	ah, ah		;zero to AH
		shl	ax, 1		;word offset
		xchg	ax, si
		jmp	cs:lpr_table[si]	;exec function
;
		nop	

int_17a:
		mov	byte ptr [bp-1], 1

int_17b:
		mov	ax, [bp-2]
		leave	
		pop	si
		pop	dx
		pop	ds
		iret	
;
;--------------------------------------------------------------
;send char to lpr
lpr_outch:
		push	cx
		push	bx
		xor	cx, cx
		mov	byte ptr [bp-4], 14h
		call	lpr_stat01
		lea	bx, lpt_tbl1
		xlat	byte ptr cs:[bx]
		mov	[bp-1],	al
		cmp	byte ptr [bp-1], 88h ;
		jz	lpr_ch03
lpr_ch01:
		mov	dx, 101Ah	;read input port
		in	al, dx
		test	al, 4		;busy?
		jz	lpr_ch02
		loop	lpr_ch01	;loop till not busy
		dec	byte ptr [bp-4]
		jnz	lpr_ch01
		or	byte ptr [bp-1], 1
		jmp	short lpr_ch03
;
		nop	
lpr_ch02:
		mov	al, [bp-2]	;get character
		mov	dx, 1280h
		out	dx, al		;send to LPT port
		mov	al, 4
		mov	dx, 101Ch	;set output bit
		out	dx, al		;assert (low) strobe
		mov	dx, 101Eh	;clear output bit
		out	dx, al
lpr_ch03:
		pop	bx
		pop	cx
		jmp	short int_17b
;
		nop	
;--------------------------------------------------------------
;setup lpr port
lpr_setup:
		push	cx
		mov	dx, 1280h
		mov	al, 0
		out	dx, al
		mov	cx, 3E8h	;loop count
		mov	al, 80h	;
		mov	dx, 101Ch	;set output bit
		out	dx, al		;assert (low) init
		mov	dx, 101Eh	;clear output bit
		out	dx, al		;release init
loop00b:
		loop	loop00b
		mov	dx, 101Ch
		out	dx, al
		call	lpr_stat01
		mov	byte ptr [bp-1], 0
		cmp	al, 1
		jnz	lpr_se01
		mov	byte ptr [bp-1], 88h ;
lpr_se01:
		pop	cx
		jmp	short int_17b
;
;--------------------------------------------------------------
;get lpr status
lpr_gstat:
		call	lpr_stat01		;get status
		push	bx
		lea	bx, cs:lpt_tbl2	;conversion table
		xlat	byte ptr cs:[bx]	;convert to BIOS
		mov	[bp-1],	al
		pop	bx
		jmp	int_17b
;
		nop	
;
;get printer status and pack bits in AL
;bit0=busy bit1=sel bit2=paper
lpr_stat01	proc near
		mov	dx, 101Ah	;read port
		in	al, dx
		mov	ah, al		;save copy
		and	ah, 30h		;sel+paper
		shr	ah, 3
		and	al, 4		;busy
		shr	al, 2
		or	al, ah		;combine
		xor	ah, ah		;clear hi byte
		retn	
lpr_stat01	endp
;
;--------------------------------------------------------------
;table of video routines
video_tbl	dw offset vid_exit	;AH=0
		dw offset vid_exit	;AH=1
		dw offset vid_exit	;AH=2
		dw offset vid_exit	;AH=3
		dw offset vid_exit	;AH=4
		dw offset vid_exit	;AH=5
		dw offset scroll_up	;AH=6 scroll up
		dw offset vid_exit	;AH=7
		dw offset vid_exit	;AH=8
		dw offset vid_exit	;AH=9
		dw offset vid_exit	;AH=a
		dw offset vid_exit	;AH=b
		dw offset vid_exit	;AH=c
		dw offset vid_exit	;AH=d
		dw offset write_tty	;AH=e write tty
		dw offset get_vmode	;AH=f read video mode
;
;--------------------------------------------------------------
; Video out (serial console out) 
; AL = character, AH = function 
int_10:
		sti			;allow interrupts
		cmp	ah, 0Fh		;out of range?
		ja	vid_noop
		enter	0, 0
		push	ds		;save registers
		push	ax
		push	bx
		push	cx
		push	dx
		push	es
		push	si
		push	di
		mov	bl, ah		;function to BL
		xor	bh, bh		;zero to BH
		shl	bx, 1		;make word offset
		jmp	cs:video_tbl[bx]	;jmp to function
;
		nop	
vid_noop:
		iret	
;
		nop	
;--------------------------------------------------------------
;function=0Eh write tty
write_tty:
		mov	dx, 1002h	;get serial status
		in	al, dx
		test	al, 4		;tx empty?
		jz	write_tty	;loop till empty
		mov	dx, 1006h
		mov	al, [bp-4]	;get character
		out	dx, al		;send
;exit
vid_exit:
		pop	di		;restore registers
		pop	si
		pop	es
		pop	dx
		pop	cx
		pop	bx
		pop	ax
		pop	ds
		leave	
		iret	
;
		nop	
;--------------------------------------------------------------
;function=0Fh get video mode
get_vmode:
		mov	byte ptr [bp-4], 7	;mode to AL
		mov	byte ptr [bp-3], 50h 	;width to AH
		mov	word ptr [bp-6], 0	;active page to BX
		jmp	short vid_exit
;
		nop	
;--------------------------------------------------------------
;function=06h scroll up 
scroll_up:
		cmp	ah, 6		;redundant?
		jnz	vid_exit
;
;validate arguments
		cmp	al, cl		;AL=0? (scroll)
		jnz	vid_exit
		cmp	ch, cl		;upper left (0,0)
		jnz	vid_exit
		cmp	dh, 18h		;line 24
		jnz	vid_exit
		cmp	dl, 4Fh		;column 80
		jnz	vid_exit
;
;do CR and 24 LFs
		mov	ah, 0Eh		;write char
		mov	al, 0Dh		;CR return to col 1
		int	10h		;
		mov	cx, 18h		;line count
scroll_loop:
		mov	al, 0Ah		;LF move down 1 line
		mov	ah, 0Eh		;write char
		int	10h		;
		loop	scroll_loop	;loop
		jmp	short vid_exit
;
;--------------------------------------------------------------
;get memory size
;AX=number of 1k blocks
int_12:
		push	ds
		mov	ds, cs:d_seg_40
		assume ds:biosdata
		mov	ax, ds:[bd_13h]	;get mem size
		pop	ds
		iret	
;
		nop	
;--------------------------------------------------------------
;get equipment list
;exit AX=equipment list
int_11:
		push	ds
		mov	ds, cs:d_seg_40
		assume ds:biosdata
		mov	ax, ds:[bd_10h]	;get equip list
		pop	ds
		iret	
;
		nop	
;--------------------------------------------------------------
;system services/cassette
;hook for operating systems to intercept busy wait loop
int_15:
		cmp	ah, 90h		;device busy loop
		jz	int_15a
		cmp	ah, 91h		;device complete
		jz	int_15c
		mov	ah, 86h		;wait
		jmp	short int_15b
;
;--------------------------------------------------------------
;AH=90
int_15a:
		clc			;flag
int_15b:
		sti			;allow intrrupts
		retf	2
;
		nop	
;--------------------------------------------------------------
;AH=91
int_15c:
		iret	
;
		nop	
;--------------------------------------------------------------
;time of day functions
int_1a:
		sti			;allow interrupts
		push	ds
		mov	ds, cs:d_seg_40
		assume ds:biosdata
		cmp	ah, 0
		jz	int_1a1		;read clock
		cmp	ah, 1
		jz	int_1a2		;set the clock
;
		sti			;allow interrupts
		pop	ds
		iret	
;--------------------------------------------------------------
;read clock AH=0
int_1a1:
		cli			;stop interrupts
		mov	dx, ds:[bd_6Ch]
		mov	cx, ds:[bd_6Eh]
		mov	al, byte ptr ds:[bd_70h]
		mov	byte ptr ds:[bd_70h], 0
		sti			;allow interrupts
		pop	ds
		iret	
;--------------------------------------------------------------
;set clock AH=1
int_1a2:
		cli			;stop interrupts
		mov	ds:[bd_6Ch], dx
		mov	ds:[bd_6Eh], cx
		mov	byte ptr ds:[bd_70h], 0
		sti			;allow interrupts
		pop	ds
		iret	
;
		nop	
;--------------------------------------------------------------
;timer interrupt handler (HW)
;increment the tick counters  and exit
int_08:
		sti				;allow interrupts
		push	ds
		push	ax
		push	dx
		mov	ax, 40h			;set segment reg
		mov	ds, ax
		assume ds:biosdata
		inc	word ptr ds:[bd_6Ch]	;inc tick counter
		jz	int_08b
		cmp	word ptr ds:[bd_6Eh],24	;hours rollover
		jz	int_08c
int_08a:
		int	1Ch			; CLOCK	TICK
		mov	dx, 0FF22h
		mov	ax, 8
		out	dx, ax			;reenable tick timer
		pop	dx
		pop	ax
		pop	ds
		iret	
;
		nop
int_08b:
		inc	word ptr ds:[bd_6Eh]
		jmp	short int_08a
;
int_08c:
		cmp	word ptr ds:[bd_6Ch], 18
		jb	int_08a
		mov	byte ptr ds:[bd_70h], 1
		mov	word ptr ds:[bd_6Ch], 0
		mov	word ptr ds:[bd_6Eh], 0
		jmp	short int_08a
;
;--------------------------------------------------------------
; TRANSFER TO ROM BASIC
; No rom-basic available so reboots 
int_18:
		jmp	far ptr	start
;
		nop	

;--------------------------------------------------------------
;dummy interrupt just returns
int_dummy:
		sti		;allow interrupts
		iret	
;
;--------------------------------------------------------------
;pointer to io table
IOT_ptr		dw offset bd_0D4h
		dw 40h
;
;pointer to drive parameter tables (unreferenced)
		dw 0A8h
		dw 40h
;--------------------------------------------------------------
;alternate HD functions (undocumented) AH=0C0h to 0CFh
;this appears to be SCSI device access that's
;not BIOS supported disks
hd_alt_tbl	dw offset SCSI_unkn	;AH=C0
		dw offset SCSI_exec	;AH=C1
		dw offset SCSI_read	;AH=C2
		dw offset SCSI_write	;AH=C3
		dw offset hd_rtn_bad	;AH=C4
		dw offset hd_rtn_bad	;AH=C5
		dw offset hd_rtn_bad	;AH=C6
		dw offset hd_rtn_bad	;AH=C7
		dw offset hd_rtn_bad	;AH=C8
		dw offset hd_rtn_bad	;AH=C9
		dw offset hd_rtn_bad	;AH=CA
		dw offset hd_rtn_bad	;AH=CB
		dw offset hd_rtn_bad	;AH=CC
		dw offset hd_rtn_bad	;AH=CD
		dw offset SCSI_get_PTR	;AH=CE
		dw offset SCSI_rst	;AH=CF
;
;--------------------------------------------------------------
;table of hard disk functions AH=0 to 15h
hd_table	dw offset hd_reset	;AH-0	disk reset
		dw offset hd_status	;AH=1	get status
		dw offset hd_read	;AH=2	read sectors
		dw offset hd_write	;AH=3	write sectors
		dw offset hd_verify	;AH=4	verify sector
		dw offset hd_rtn_good	;AH=5	format track
		dw offset hd_rtn_bad	;AH=6	flag bad track
		dw offset hd_rtn_good	;AH=7	format drive at track
		dw offset hd_gparm	;AH=8	get drive param
		dw offset hd_rtn_good	;AH=9	set drive param
		dw offset hd_rtn_bad	;AH=a	read with ecc
		dw offset hd_rtn_bad	;AH=b	write with ecc
		dw offset hd_seek	;AH=c	seek
		dw offset hd_rtn_good	;AH=d	reset disk controller
		dw offset hd_rtn_bad	;AH=e	read buffer
		dw offset hd_rtn_bad	;AH=f	write buffer
		dw offset hd_tst_rdy	;AH=10	test ready
		dw offset hd_recal	;AH=11	recalibrate
		dw offset hd_rtn_good	;AH=12	ram diag
		dw offset hd_rtn_good	;AH=13	drive diag
		dw offset hd_rtn_good	;AH=14	controller diag
		dw offset hd_rtn_bad	;AH=15	read dasd?
;
;--------------------------------------------------------------
;hard disk service (int_13)
; AH=function DL=drive CH=track CL=sector AL=sector count
; ES:BX=buffer
hd_int13:	
		sti			;allow interrrupts
		cmp	ah, 0C0h	;
		jnb	hd_int_1
		cmp	dl, 80h		;
		jb	hd2flop		;floppy?
hd_int_1:
		cmp	dl, 0FFh
		jnz	hd_int_2
		cmp	ah, 1
		jnz	hd_int_2
		push	dx
		call	SCSI_poll
		pop	dx
		iret	
;
		nop	
;
hd_int_2:
		enter	0, 0
		push	ds		;save registers
		push	ax
		push	bx
		push	cx
		push	dx
		push	es
		push	si
		push	di
		mov	ds, cs:d_seg_40
		assume ds:biosdata
		cmp	ah, 14h		;out of range?
		ja	hd_int_3
		mov	ds:[bd_0DCh], bx	;set buffer pointer 
		mov	ds:[bd_0DEh], es	;in IO table
		mov	bl, ah		;function to BL
		xor	bh, bh		;zero BH
		shl	bx, 1		;word offset
		jmp	cs:hd_table[bx]	;execute function
;
		nop	
hd_int_3:
		cmp	ah, 0C0h 	;lowest function
		jb	hd_rtn_bad
		cmp	ah, 0D0h 	;highest function
		jb	hd_alt_1	;do special function

;--------------------------------------------------------------
;return bad status
hd_rtn_bad:
		mov	byte ptr [bp-4], 0	;AL=0
		mov	byte ptr ds:[bd_74h], 1	;invalid function
		jmp	short hd_status
;
		nop	
;--------------------------------------------------------------
;return good status
hd_rtn_good:
		mov	byte ptr ds:[bd_74h], 0	;no error
		jmp	short hd_status
;
		nop	
;--------------------------------------------------------------
;goto floppy handler
hd2flop:
		int	40h	; Floppy Handler (original INT 13h)
		retf	2	; 
;
		nop	
;--------------------------------------------------------------
;vector to alternate functions (undocumented)
hd_alt_1:
		mov	al, ah		;func to AL
		and	ax, 0Fh		;mask hi bits
		shl	ax, 1		;make word offset
		mov	si, ax
		jmp	cs:hd_alt_tbl[si]	;execute 
;
;--------------------------------------------------------------
;set error and exit
hd_set_err:
		mov	byte ptr ds:[bd_74h], 80h ;timeout error
		jmp	short hd_status
;
;--------------------------------------------------------------
;read sense data and exit
hd_ck_sense:
		call	hd_sense
		jmp	short hd_status
;
		nop	
;--------------------------------------------------------------
;check error and read sense or just exit
hd_chk_err:
		or	al, al
		jnz	hd_set_err
		test	byte ptr ds:[bd_8Ch], 2
		jnz	hd_ck_sense
		mov	ds:[bd_74h],	al	;set error flag
;--------------------------------------------------------------
;return HD status (exit)
hd_status:
		pop	di		;recover registers
		pop	si
		pop	es
		pop	dx
		pop	cx
		pop	bx
		pop	ax
		mov	ah, ds:[bd_74h]	;get error status
		cmp	ah, 1
		cmc	
		pop	ds
		leave			;clean up stack
		retf	2
;
		nop	
;--------------------------------------------------------------
;reset disks skip HD just reset floppy
hd_reset:
		mov	byte ptr ds:[bd_74h], 0	;no error
		xor	ax, ax
		int	40h			; Floppy Handler
		jmp	short hd_status
;
		nop	
;--------------------------------------------------------------
;issue SCSI read
hd_read:
		mov	cl, 8		;read command
		call	make_CDB	;fill in cmd block
		les	bx, dword ptr cs:IOT_ptr
		call	SCSI_go		;execute cmd
		jmp	short hd_chk_err
;
		nop	
;--------------------------------------------------------------
;issue SCSI write
hd_write:
		mov	cl, 0Ah		;write command
		call	make_CDB	;fill in cmd block
		les	bx, dword ptr cs:IOT_ptr
		call	SCSI_go		;execute cmd
		jmp	short hd_chk_err ;check for errors
;
		nop	
;--------------------------------------------------------------
;hd verify (stub)
hd_verify:
		mov	byte ptr ds:[bd_74h], 0	;no error
		jmp	short hd_status		;exit
;
		nop	
;--------------------------------------------------------------
;get hd parameters
hd_gparm:
		mov	byte ptr ds:[bd_74h], 0	;no error
		mov	ax, [bp-0Ah]		;get drive DX
		and	ax, 7Fh			;mask
		mov	cx, 6			;size of entry
		mul	cx			;make offset
		les	si, dword ptr ds:[bd_0A8h]	;point to table
		add	si, ax			;offset to drive
		mov	ax, es:[si]		;get heads
		dec	al			;zero base
		mov	[bp-9],	al		;return in DH
		mov	byte ptr [bp-0Ah], 1	;DL=1 drive
		mov	ax, es:[si+4]		;get cyl
		xchg	ah, al
		shl	al, 6			;make room for sectors
		or	ax, es:[si+2]		;or in sectors
		mov	[bp-8],	ax		;return in CX
		jmp	short hd_status
;
;--------------------------------------------------------------
;issue SCSI seek
hd_seek:
		mov	cl, 0Bh		;seek command
		call	make_CDB	;fill in cmd block
		les	bx, dword ptr cs:IOT_ptr
		call	SCSI_go		;exec command
		jmp	hd_chk_err	;check for error
;
;--------------------------------------------------------------
;issue SCSI test ready
hd_tst_rdy:
		xor	ax, ax
		mov	dx, ax
		mov	[bp-4],	al	;clear status
		mov	cl, 0		;test drive ready cmd
		call	make_CDB1	;make cmd block
		les	bx, dword ptr cs:IOT_ptr
		call	SCSI_go		;execute cmd
		jmp	hd_chk_err
;
		nop	
;--------------------------------------------------------------
;issue SCSI recalibrate
hd_recal:
		xor	ax, ax
		mov	dx, ax
		mov	[bp-4],	al	;clear status
		mov	cl, 1		;recal command
		call	make_CDB1	;fill in cmd block
		les	bx, dword ptr cs:IOT_ptr
		call	SCSI_go		;do scsi IO
		jmp	hd_chk_err	;check for error
;
		nop	
;
;--------------------------------------------------------------
;alternate:
;I cant figure out what this is for with out some documentation
;it sets some bits/regs and returns the initiator ID in DL
SCSI_unkn:
		mov	byte ptr ds:[bd_74h], 0FFh	;sense failed
		mov	byte ptr [bp-4], 0FFh	;AL=0FFh
		mov	byte ptr [bp-8], 1	;CL=1
		mov	byte ptr [bp-7], 2	;CH=2
		les	bx, dword ptr cs:IOT_ptr
		mov	al, es:[bx]		;get initiator ID
		mov	[bp-0Ah], al		;DL=ID
		jmp	hd_status		;return
;
		nop	
;
;--------------------------------------------------------------
;alternate: SCSI read primitive
SCSI_read:
		mov	ds:[bd_0DCh], bx	;set data pointer
		mov	ds:[bd_0DEh], es
		mov	cl, 8			;SCSI read command
		call	SCSI_mkCDB		;fill in cmd block
		les	bx, dword ptr cs:IOT_ptr
		call	SCSI_go			;do SCSI IO
		jmp	hd_chk_err
;
;--------------------------------------------------------------
;alternate: SCSI write primitive
SCSI_write:
		mov	ds:[bd_0DCh], bx	;set data pointer
		mov	ds:[bd_0DEh], es
		mov	cl, 0Ah			;SCSI write command
		call	SCSI_mkCDB		;fill in cmd block
		les	bx, dword ptr cs:IOT_ptr
		call	SCSI_go			;do SCSI IO
		jmp	hd_chk_err
;
;--------------------------------------------------------------
;alternate: get pointers to drive parameter table and Select bits
SCSI_get_PTR:
		lea	di, ds:[bd_0A8h]	;get DRV tbl pointer
		mov	[bp-10h], di		;DI
		lea	si, ds:[bd_0ACh]	;get pointer to SEL_bits
		mov	[bp-0Eh], si		;SI
		mov	[bp-2],	ds		;DS
		mov	byte ptr ds:[bd_74h], 0	;no error
		jmp	hd_status
;
		nop	
;
;--------------------------------------------------------------
;alternate: reset SCSI bus
SCSI_rst:
		mov	dx, 1082h		;initiator cmd
		mov	al, 80h			;assert reset
		out	dx, al
;
		mov	cx, 32h			;short delay	
loop00c:
		loop	loop00c
;
		xor	al, al			;clear reset
		out	dx, al
		mov	ds:[bd_74h], al		;no error
		jmp	hd_status
;
;--------------------------------------------------------------
;alternate: exec user provided SCSI IO table
;ES:BX points to IO Table
SCSI_exec:
		call	SCSI_go			;do SCSI IO
		mov	ds:[bd_74h], al		;set error
		mov	byte ptr [bp-4], 0	;AL=0
		jmp	hd_status
;
		nop	
;
;--------------------------------------------------------------
;alternate: make user SCSI CDB
; SI=cylinder (65535 max)
SCSI_mkCDB	proc near
		push	cx			;save command
		mov	ax, [bp-0Ah]		;get drive DX
		and	ax, 7Fh			;strip bit 7
		add	ax, ax			;times 2
		mov	cx, ax			;
		add	ax, ax			;times 4
		add	ax, cx			;plus 2= times 6
		les	si, dword ptr ds:[bd_0A8h]	;drive table base
		add	si, ax			;make ptr to drive param
;
;convert CHS to LBN
		mov	ax, [bp-0Eh]		;get cylinder SI
		mul	word ptr es:[si]	;mult x heads
		mov	cl, [bp-9]		;get head DH
		xor	ch, ch			;zero hi byte
		add	ax, cx
		adc	dx, 0
		mov	cx, dx
		mul	word ptr es:[si+2]	;mult x sectors
		mov	bx, ax
		mov	ax, cx
		mov	cx, dx
		mul	word ptr es:[si+2]	;mult x sectors
		add	ax, cx
		mov	dx, ax
		mov	ax, [bp-8]		;get sector CX
		and	ax, 3Fh			;mask to 63
		dec	al			;zero base
		add	ax, bx			;add to LBA
		adc	dx, 0			;carry to LBA+2
		pop	cx			;recover command
		jmp	short make_CDB1
SCSI_mkCDB	endp
;
		nop	
;
;--------------------------------------------------------------
;make SCSI command descriptor block
make_CDB:
		mov	ax, [bp-0Ah]		;DX
		and	ax, 7Fh			;drive num
		add	ax, ax			;times 2
		mov	bx, ax			;
		add	ax, ax			;times 4
		add	ax, bx			;plus2= times 6
		les	si, dword ptr ds:[bd_0A8h] ;drive table base
		add	si, ax			;point to drv param
;
;convert CHS to LBN
		mov	ax, [bp-8]		;get cyl&sector
		mov	ch, al			;save sector
		xchg	ah, al
		shr	ah, 6			;shift cylhi down
		mul	word ptr es:[si]	;mult x heads
		mov	bl, [bp-9]		;get head 
		xor	bh, bh			;zero 
		add	ax, bx
		mul	word ptr es:[si+2]	;mult x sectors
		mov	bl, ch			;sector in BL
		and	bx, 3Fh			;mask value
		nop				;*** tasm alignment
		dec	bl			;zero base
		add	ax, bx			;sector to LBN
		adc	dl, bh			;carry?
;
;--------------------------------------------------------------
;fill in CDB command/ID's/LUN
make_CDB1	proc near
		mov	bx, offset bd_42h	;point to CDB
		xchg	ah, al
		mov	[bx+2],	ax		;set LBN (lo&mid)
		mov	al, [bp-4]		;sector count AL 
		xor	ah, ah			;clear option bits
		mov	[bx+4],	ax		;set count&option
		les	si, dword ptr ds:[bd_0ACh]	;point to SEL_bits
		mov	ax, [bp-0Ah]		;get drive DX
		and	ax, 7Fh			;mask bits
		shl	ax, 1			;make word offset
		add	si, ax
		mov	al, es:[si]		;get target ID
		mov	ds:[bd_0D5h], al	;set target ID
		mov	ah, es:[si+1]		;get target LUN
		and	dl, 1Fh			;mask to LBN
		or	ah, dl			;set LUN+LBA
		mov	al, cl			;get command
		mov	[bx], ax		;set cmd,lun,lba
		retn	
make_CDB1	endp
;
;--------------------------------------------------------------
;error translate table
;convert sense data status to BIOS error
tran_err	db    0	;no error
		db    4	;sector not found
		db  40h	;seek failed
		db 0CCh	;write fault
		db 0AAh	;drive not ready
		db    1	;invalid function  
		db  40h	;seek failed
		db  20h	;general error  
		db  80h	;timeout
		db  40h	;seek failed
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db  0Ah	;bad sector flag  
		db  10h	;uncorrectable  
		db    2	;address mark not found  
		db    2	;address mark not found  
		db    4	;sector not found  
		db  40h	;seek failed
		db    0	;no error  
		db    0	;no error  
		db  11h	;ecc corrected  
		db  10h	;uncorrectable  
		db    1	;invalid function  
		db  10h	;uncorrectable  
		db    7	;set parameter failed  
		db  0Ah	;bad sector flag  
		db    0	;no error  
		db 0BBh	;undefined error
		db    1	;invalid function  
		db    4	;sector not found  
		db    1	;invalid function  
		db 0BBh	;undefined error
		db    1	;invalid function  
		db 0AAh	;drive not ready
		db    1	;invalid function  
		db    3	;invalid func&AM not found  
		db    6	;media changed  
		db    0	;no error  
		db  20h	;general error  
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db    4	;sector not found  
		db    4	;sector not found  
		db    4	;sector not found  
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db 0BBh	;undefined error
		db  20h	;general error  
		db  20h	;general error  
		db  20h	;general error  
		db    1	;invalid function  
		db  20h	;general error  
		db  80h	;timeout
		db  20h	;general error  
		db  20h	;general error  
		db  20h	;general error  
		db    1	;invalid function  

;--------------------------------------------------------------
;read sense and set error code
hd_sense	proc near
		inc	word ptr ds:[bd_76h]	;inc error counter
		jnz	hd_sens1
		dec	word ptr ds:[bd_76h]	;dec if already max
hd_sens1:
		les	bx, dword ptr ds:[bd_0D8h]	;get buffer address
		mov	ds:[bd_0DCh], bx	;set buffer pointer
		mov	ds:[bd_0DEh], es
		xor	ax, ax
		mov	dx, ax
		mov	[bp-4],	al
		mov	cl, 3			;request sense
		call	make_CDB1		;make command block
		les	bx, dword ptr cs:IOT_ptr
		call	SCSI_go			;execute command
		or	al, al			;error?
		jnz	hd_sens3
		mov	al, ds:[bd_42h]		;get sense data
		mov	ds:[bd_75h], al		;save error
		and	al, 7Fh			;strip address valid
		cmp	al, 49h			;in range?
		jbe	hd_sens2
		mov	byte ptr ds:[bd_74h], 20h	;generic error code
		retn	
;
hd_sens2:
		lea	bx, tran_err		;point to translate
		xlat	byte ptr cs:[bx]	;convert to bios error
hd_sens3:
		mov	ds:[bd_74h], al		;save error code
		retn	
hd_sense	endp
;
;--------------------------------------------------------------
;initiate SCSI I/O
SCSI_go	proc near
		mov	ds:[bd_0D2h], es	;save IOT pointer
		mov	ds:[bd_0D0h], bx
		mov	dx, 1200h
		mov	al, ds:[bd_3Fh]		;get motor status 
		or	al, 40h			;set DRQ source (SCSI)
		out	dx, al
		xor	ax, ax			;stop 5380
		mov	dx, 1082h		;initiator command
		out	dx, al
		mov	dx, 1086h		;target command 
		out	dx, al
;
;SCSI arbitration
SCSI_arb:
		xor	ax, ax
		mov	dx, 1084h		;mode register
		out	dx, al			;clear mode
		mov	dx, 108Eh		;reset irq
		in	al, dx
		mov	al, es:[bx]		;get my ID
		mov	ah, al			;save it
		mov	dx, 1080h		;output data
		out	dx, al
		mov	dx, 1084h		;get mode
		in	al, dx
		or	al, 1			;drive bus
		out	dx, al
		xor	cx, cx
wait4arb:
		mov	dx, 1082h		;get initiator cmd
		in	al, dx
		test	al, 40h			;arb in progress
		jnz	arb_in_prog
		mov	dx, 108Ah		;get bus&status
		in	al, dx
		test	al, 10h			;irq active?
		jnz	SCSI_arb		;then retry
		loop	wait4arb		;loop till arb or irq
;
		mov	ah, 0FEh ;
		jmp	SCSI_err
;
;arb in progress
arb_in_prog:
		mov	cx, 2			;short delay
loop00d:
		loop	loop00d
;
		mov	dx, 1080h		;get current scsi data
		in	al, dx
		sub	al, ah
		sub	al, ah
		jns	SCSI_arb		;retry
		mov	dx, 1082h		;get initiator cmd
		in	al, dx
		test	al, 20h			;lost arb?
		jnz	SCSI_arb		;loop try again
;	
;arbitration won
		mov	al, 8
		out	dx, al			;assert bsy
		mov	dx, 1084h		;get mode reg
		in	al, dx
		and	al, 0FEh
		out	dx, al			;end arbitration
;
		mov	dx, 1082h		;get initiator cmd
		in	al, dx
		or	al, 4			;assert sel
		out	dx, al
		mov	dx, 1080h		;
		mov	al, ah			;get my ID
		or	al, es:[bx+1]		;set target ID
		out	dx, al
		mov	dx, 1082h		;get initiator cmd
		in	al, dx
		or	al, 1			;assert data (ID's)
		out	dx, al
;
		mov	cx, 2			;short delay
loop00e:
		loop	loop00e
;
		mov	dx, 1082h		;initiator cmd
		mov	al, 5
		out	dx, al			;assert sel+data
;
		mov	cx, 9368h		;time out value
loop00f:
		mov	dx, 1088h		;SCSI bus status
		in	al, dx
		test	al, 40h			;BSY set?
		jnz	targ_sel
		loop	loop00f			;loop till timeout or BSY
;
		mov	ah, 0FFh
		jmp	SCSI_err
;
;BSY is set target is selected
targ_sel:
		mov	dx, 1082h		;get initiator cmd
		mov	al, 1
		out	dx, al			;negate sel
		mov	al, 0
		out	dx, al			;negate data
		mov	dx, 1084h		;set mode register
		mov	al, 6		
		out	dx, al			;monitor BSY+DMA
;
;wait for request to set
wait_req:
		mov	dx, 108Ah		;bus & status
		in	al, dx
		test	al, 10h			;irq active?
		jnz	irq_bit_set
		mov	dx, 1088h		;get SCSI bus status
		in	al, dx
		test	al, 20h			;req?
		jnz	req_activ
		jmp	short wait_req		;loop till req
;
		db 90h
;
;--------------------------------------------------------------
;SCSI bus phase table
;offsets of SCSI phase handlers
SCSI_phase	dw offset data_out		;!io !cd !msg data out
		dw offset data_in		; io !cs !msg data in
		dw offset cmd_out		;!io  cd !msg cmd out
		dw offset status_in		; io  cd !msg status in
		dw offset unspec		;!io !cd  msg unspec
		dw offset unspec		; io !cd  msg unspec
		dw offset mess_out		;!io  cd  msg message out
		dw offset mess_in		; io  cd  msg message in
;
;--------------------------------------------------------------
;5380 irq is active
;stop current IO
;decode status
irq_bit_set:
		xor	al, al
		mov	dx, 1082h		;initiator cmd
		out	dx, al			;stop command
		mov	dx, 108Ah		;bus&status reg
		in	al, dx
		and	ax, 0Ch			;phase match or busy loss
		jnz	SCSI_err		;jmp if phase or busy
;
;req is active and phase mismatch
;decode new phase
;set 5380 phase and jump to handler
req_activ:
		xor	al, al
		mov	dx, 1084h		;clear mode reg
		out	dx, al
		mov	dx, 108Eh		;reset parity/irq
		in	al, dx
		mov	al, 6			;mon BSY+DMA
		mov	dx, 1084h		;set mode reg
		out	dx, al
		mov	dx, 1088h		;get SCSI bus status
		in	al, dx
		and	ax, 1Ch			;mask to phase
		shr	ax, 1
		mov	si, ax			;offset to phase handler
		shr	ax, 1
		mov	dx, 1086h		;target command reg
		out	dx, al			;set phase bits
		les	bx, dword ptr ds:bd_0D0h	;get IOT pointer
		jmp	cs:SCSI_phase[si]	;execute new phase
;
		nop	
;--------------------------------------------------------------
;data out phase
data_out:
		les	bx, es:[bx+8]		;get buffer pointer
		jmp	short DMA_send		;start DMA
;
		nop	
		nop	
;--------------------------------------------------------------
;data in phase
data_in:
		les	bx, es:[bx+8]		;get buffer pointer
		jmp	DMA_receive		;start DMA
;
		nop	
;--------------------------------------------------------------
;command out phase
cmd_out:
		les	bx, es:[bx+4]		;get CDB pointer
		jmp	short DMA_send		;start DMA
;
		nop	
		nop	
;--------------------------------------------------------------
;status in phase
status_in:
		les	bx, es:[bx+0Ch]		;get status pointer
		jmp	DMA_receive		;start DMA
;
		nop	
;--------------------------------------------------------------
;message out phase
mess_out:
		les	bx, es:[bx+14h]		;get message out pointer
		jmp	short DMA_send		;start DMA
;
		nop	
		nop	
;--------------------------------------------------------------
;message in phase
mess_in:
		les	bx, es:[bx+10h]		;get message in pointer
		jmp	DMA_receive		;start DMA
;
		nop	
;--------------------------------------------------------------
;undefined phase (error)
unspec:
		mov	ah, 0FDh ;error code
;
;(irq active and phase match) or busy loss
;is an error so get off the bus
SCSI_err:
		xor	al, al
		mov	dx, 1084h		;mode reg
		out	dx, al
		mov	dx, 1082h		;initiator cmd
		out	dx, al
		mov	dx, 108Eh		;reset parity/irq
		in	al, dx
		mov	dx, 1200h		;reset SCSI drq
		mov	al, ds:3Fh		;get motor status
		and	al, 0BFh
		out	dx, al
		mov	al, ah
		retn	
SCSI_go	endp
;
;--------------------------------------------------------------
;set up and start DMA send
DMA_send:
		mov	dx, 0FFC8h		;set transfer count
		mov	ax, 0FFFFh
		out	dx, ax
;
;convert segment:offset
;to linear 20 bit address
		mov	ax, es			;get segment
		mov	cx, 10h
		mul	cx			;times 16
		add	ax, bx			;add offset
		adc	dl, ch			;add carry
		mov	bx, dx
;
;setup DMA controller
		mov	dx, 0FFC0h		;source pointer
		out	dx, ax
		mov	ax, bx			;addr 0-15
		mov	dx, 0FFC2h		;src pointer hi 4 bits
		out	dx, ax			;addr 16-19
		mov	dx, 0FFC4h		;dest pointer
		mov	ax, 1180h
		out	dx, ax			;SCSI data reg
		add	dx, 2
		xor	ax, ax			;addr 16-19=0
		out	dx, ax
		mov	dx, 0FFCAh		;DMA ctrl word (go)
		mov	ax, 74A6h
		out	dx, ax
;
;start SCSI send
		mov	dx, 1082h		;initiator cmd
		mov	al, 1			;assert data
		out	dx, al
		mov	dx, 108Ah		;start DMA send
		out	dx, al
		call	SCSI_wait		;wait for SCSI done
		mov	dx, 0FFCAh		;control word
		mov	ax, 4			;turn off dma
		out	dx, ax
		jmp	irq_bit_set		;next phase
;
		nop	
;
;--------------------------------------------------------------
;wait for SCSI done
SCSI_wait	proc near
		call	SCSI_poll		;poll 5380
		jnz	wait_exit
		mov	ax, 9000h		;signal wait loop
		int	15h
wait_done:
		call	SCSI_poll		;poll 5380
		jz	wait_done
wait_exit:
		retn	
SCSI_wait	endp
;
;--------------------------------------------------------------
;check 5380 irq status
SCSI_poll	proc near
		mov	dx, 108Ah		;bus & status reg
		in	al, dx
		and	al, 10h			;irq active?
		mov	ah, al
		jz	poll_exit
		or	ah, 0FFh
poll_exit:
		retn	
SCSI_poll	endp
;
;--------------------------------------------------------------
;SCSI set up and start DMA in
DMA_receive:
		mov	dx, 0FFC8h
		mov	ax, 0FFFFh		;byte count
		out	dx, ax
;
;convert segment:offset
;to linear 20 bit address
		mov	ax, es			;get segment
		mov	cx, 10h			;times 16
		mul	cx
		add	ax, bx			;add to offset
		adc	dl, ch			;add carry
		mov	bx, dx
;
;setup DMA controller
		mov	dx, 0FFC4h		;destination
		out	dx, ax			;set addr 0-15
		mov	ax, bx
		mov	dx, 0FFC6h
		out	dx, ax			;set addr 16-19
		mov	dx, 0FFC0h
		mov	ax, 118Ch		;source port (SCSI data)
		out	dx, ax
		add	dx, 2
		xor	ax, ax			;addr 16-19=0
		out	dx, ax
		mov	dx, 0FFCAh
		mov	ax, 0AC66h		;DMA control (go)
		out	dx, ax
;
;start SCSI DMA in
		mov	dx, 108Eh
		out	dx, al			;start DMA
		call	SCSI_wait		;wait for done
;
		mov	dx, 0FFCAh
		mov	ax, 4			;turn off DMA
		out	dx, ax
		jmp	irq_bit_set		;next phase
;
		nop
;
;--------------------------------------------------------------
;hard disk param table
hd_param	dw  400h	;max cyl
		db  0Fh		;max heads  
		dw    0		;RWC cyl  
		dw 0FFFFh	;precomp cyl
		db  0Bh		;ecc length  
		db    0		;opt flags
		db    0		;timeout  
		db    0		;timeout format  
		db    0		;timeout check  
		dw 03FFh	;last cyl
		dw   17		;heads  
;  
;--------------------------------------------------------------
;floppy disk param table
;the table doesn't match bios documentation
;
fd_param	db 0DFh		;step rate, unload time
		db 2		;dma,head load time
		db 19h		;motor off time
		db 2		;sector size (512)
		db 9		;sectors per track
		db 40h		;gap
		db 0FFh		;data length (ignored)
;gap length when formatting is missing from table.... (50h)
		db 0F6h		;format filler
		db 0Fh		;settle time
		db 8		;motor start time
;
;--------------------------------------------------------------
;serial IO irq service addresses
;only rx A full and break are serviced
;the rest just iret
;16 words copied to 40:B0h
com_irq_tab	dw offset irq_ret	;tx A
		dw 0FC00h		;ROM segment
		dw offset rx_A_full	;rx A full
		dw 0FC00h
		dw offset bios_init	;break A
		dw 0FC00h
		dw offset irq_ret	;counter
		dw 0FC00h
		dw offset irq_ret	;tx B
		dw 0FC00h
		dw offset irq_ret	;rx B
		dw 0FC00h
		dw offset irq_ret	;break B
		dw 0FC00h
		dw offset irq_ret	;port change
		dw 0FC00h
;
;--------------------------------------------------------------
;irq vector initialize table
;128 words copied to 0:0
int_init	dw offset int_dummy	;0
		dw 0FC00h
		dw offset int_dummy	;4
		dw 0FC00h
		dw offset int_dummy	;8
		dw 0FC00h
		dw offset int_dummy	;c
		dw 0FC00h
		dw offset int_dummy	;10
		dw 0FC00h
		dw offset int_dummy	;14
		dw 0FC00h
		dw offset int_dummy	;18
		dw 0FC00h
		dw offset int_dummy	;1c
		dw 0FC00h
		dw offset int_08	;20
		dw 0FC00h
		dw offset int_dummy	;24
		dw 0FC00h
		dw offset int_dummy	;28
		dw 0FC00h
		dw offset int_dummy	;2c
		dw 0FC00h
		dw offset int_0c	;30
		dw 0FC00h
		dw offset int_dummy	;34
		dw 0FC00h
		dw offset int_0e	;38
		dw 0FC00h
		dw offset int_dummy	;3c
		dw 0FC00h
		dw offset int_10	;40
		dw 0FC00h
		dw offset int_11	;44
		dw 0FC00h
		dw offset int_12	;48
		dw 0FC00h
		dw offset int_13	;4c
		dw 0FC00h
		dw offset int_14	;50
		dw 0FC00h
		dw offset int_15	;54
		dw 0FC00h
		dw offset int_16	;58
		dw 0FC00h
		dw offset int_17	;5c
		dw 0FC00h
		dw offset int_18	;60
		dw 0FC00h
		dw offset int_19	;64
		dw 0FC00h
		dw offset int_1a	;68
		dw 0FC00h
		dw offset int_dummy	;6c
		dw 0FC00h
		dw offset int_dummy	;70
		dw 0FC00h
		dw offset int_dummy	;74
		dw 0FC00h
		dw offset fd_param	;78
		dw 0FC00h
		dw offset int_dummy	;7c
		dw 0FC00h
		dw 0			;80
		dw 0
		dw 0			;84
		dw 0
		dw 0			;88
		dw 0
		dw 0			;8c
		dw 0
		dw 0			;90
		dw 0
		dw 0			;94
		dw 0
		dw 0			;98
		dw 0
		dw 0			;9c
		dw 0
		dw 0			;a0
		dw 0
		dw 0			;a4
		dw 0
		dw 0			;a8
		dw 0
		dw 0			;ac
		dw 0
		dw 0			;b0
		dw 0
		dw 0			;b4
		dw 0
		dw 0			;b8
		dw 0
		dw 0			;bc
		dw 0
		dw 0			;c0
		dw 0
		dw 0			;c4
		dw 0
		dw 0			;c8
		dw 0
		dw 0			;cc
		dw 0
		dw 0			;d0
		dw 0
		dw 0			;d4
		dw 0
		dw 0			;d8
		dw 0
		dw 0			;dc
		dw 0
		dw 0			;e0
		dw 0
		dw 0			;e4
		dw 0
		dw 0			;e8
		dw 0
		dw 0			;ec
		dw 0
		dw 0			;f0
		dw 0
		dw 0			;f4
		dw 0
		dw 0			;f8
		dw 0
		dw 0			;fc
		dw 0
;
;--------------------------------------------------------------
;target select bit patterns
;first table is used for initiator id=7
SEL_bits	db    1		;drive 0 target
		db    0 	;drive 0 LUN
		db    2		;drive 1 target
		db    0		;drive 1 LUN 
		db    0
		db    0
		db    0
		db    0
;2nd. table is used for initiator id not=7 
		db  80h		;drive 0 target
		db    0		;drive 0 LUN  
		db    0
		db    0  
		db    0
		db    0  
		db    0
		db    0 
;
;--------------------------------------------------------------
;per drive parameters (for 2 drives)
;12 bytes copied to 40:98h
DRV_params	dw    4		;drive 0 heads
		dw   17		;drive 0 sectors
		dw 03FFh	;drive 0 tracks
;
		dw    0		;drive 1 (stub)
		dw    0		;  
		dw    0		;  
;
;--------------------------------------------------------------
;IO pointer table
;dword pointers to SCSI IO buffers
;12 words copied to 40:0D4h  
IO_table	db    0		;my ID
		db    0		;target ID
		dw    0		;
		dw  bd_42h	;CDB pointer
		dw  40h		;
		dw  bd_42h	;data pointer
		dw  40h		;
		dw  bd_8Ch	;status pointer
		dw  40h		;
		dw  bd_8Eh	;message in pointer
		dw  40h		;
		dw  bd_8Dh	;message out pointer
		dw  40h		;

;--------------------------------------------------------------
;(RE)install interrupt vectors
rst_vectors	proc near
		xor	di, di
		mov	es, di
		assume es:nothing
		mov	di, 0
		mov	ax, cs
		mov	ds, ax
		assume ds:seg000
		mov	si, offset int_init
		mov	cx, 0Ch
		rep movsb
		mov	cx, 70h
		mov	ax, 4
		add	si, ax
		add	di, ax
		rep movsb
		xor	al, al
;
;look for 5380
		mov	dx, 1084h
		out	dx, al
		mov	cx, 20h
loop010:
		loop	loop010
		in	al, dx
		or	al, al
		jnz	rst_vect01	;skip HD install
;
;move floppy to int 40
		mov	ax, es:[4Ch]
		mov	es:[100h], ax
		mov	ax, es:[4Eh]
		mov	es:[102h], ax
;
;install HD at int 13
		mov	word ptr es:[4Ch], offset hd_int13
		mov	es:[4Eh], cs
		mov	word ptr es:[104h], offset hd_param
		mov	es:[106h], cs

rst_vect01:
		retn	
rst_vectors	endp

;
; dummy routine for option ROM?
	org 03FE0h
far_stub	proc far
		retf	
far_stub	endp

;
		nop	
		nop	
		nop	
		nop	
		nop	
		nop	
		nop	
		nop	
		nop	
		nop	
		nop	
		nop	
		nop	
		nop	
		nop
;
;--------------------------------------------------------------
	org 03FF0h
power_on:	mov	dx, 0FFA0h
		mov	ax, 0FC3Ch	;set chip select
		out	dx, ax
		jmp	far ptr	start
;
;checksum?
		db 4Fh
		db 1
		db 0FFh
		db 0FFh

seg000		ends


		end 
