;**
;** bimo.asm:
;**	Interrupt handlers for real- and protected-mode INT 0xC, or 0x0B
;** if the -2 command-line option is used.  See the bimodal.c example
;** program for details.
;**

.286

_TEXT16	SEGMENT	BYTE PUBLIC 'CODE'
	ASSUME	cs:_TEXT16

	;**
	;** The real-mode interrupt handler is in a 16-bit code segment
	;** so that the assembler will generate the right code.
	;**
	;** We will copy this code to a 16-bit segment in low memory 
	;** and install it as a real mode interrupt handler before it
	;** gets executed.  (There's no distinction between code and
	;** data in real mode.)
	;**
	;** Since the handler doesn't get executed before it gets copied,
	;** putting it in this segment makes the C code simpler.
	;**
	;** The buffer into which we will store the data being sent
	;** to us is also in this segment, for simplicity.  (It's
	;** possible to read and write code segments in real mode.)
	;** We will lay out this segment using fixed offsets, so that
	;** we can omit the data in the executable file.  The symbolic
	;** offsets are only used to reference the segment BEFORE
	;** it has been copied low.
	;**
	;** Both the real mode and protected mode handlers will store
	;** their data in this same buffer (that is, in the copy in
	;** low memory).
	;**
	;** OFFSET    LENGTH     CONTENTS
	;**
	;** 0         <128       Real mode interrupt handler
	;** 128       4          Next available slot in buffer
	;** 132       1024       Data receive buffer
	;** 132+1024  1          Overflow indicator
	;**

maxbuf	EQU	1024

	PUBLIC	rmhandler_, _com_port_low
rmhandler_:
	push	ds
	push	bx
	mov	bx, 0B800h
	mov	ds, bx				; DS = 0xB800
	sub	bx, bx				; BX = 0
	mov	WORD PTR [bx], 0720h		; Clear 2 character cells
	mov	WORD PTR [bx+2], 0720h

	push	cx
	push	ax
	mov	cx, 10h
rmdelay:
	mov	ax, 1
	mul	cl
	loop	rmdelay				; RM delay loop (flicker makes
	pop	ax				;   screen effects understandable)
	pop	cx

	mov	BYTE PTR [bx],'R'		; Write 'R' to memory map

	db	0BBh				; mov bx,...
_com_port_low	DW	?			;   com port base address
	push	ax
	push	dx
	lea	dx, [bx+2]			; int id register
	in	al, dx				; Read ports so interrupts
	mov	dx, bx				; can continue to be
	in	al, dx				; generated; get data in AL

	mov	bx, cs:[128]			; get buffer index
	mov	cs:[132+bx], al			; store data
	inc	bx				; increment count, which
	cmp	bx, maxbuf+1			;   can overflow by one,
	jl	no_ov1				;   resetting it to zero
	sub	bx, bx				;   on an overflow so we
no_ov1:						;   don't clobber memory --
	mov	cs:[128], bx			;   and store new value

	mov	dx,020h
	mov	al,dl
	out	dx,al				; Send EOI
	pop	dx
	pop	ax
	pop	bx
	pop	ds
	iret
	ASSUME	cs:NOTHING

; These variables only exist in the real mode copy of this segment
; (the one which actually gets executed)
;
;		ORG	128
;next		DD	0
;databuf	DB	maxbuf dup(0)
;overflow	DB	0

_TEXT16	ENDS

.386p

_DATA	SEGMENT DWORD PUBLIC 'DATA'
	EXTRN	_com_port:WORD
	EXTRN	_com_id:BYTE
	EXTRN	_lowmem_seg:WORD
	EXTRN	_pm_count:DWORD
_DATA	ENDS

_TEXT	SEGMENT BYTE PUBLIC 'CODE'
	ASSUME	cs:_TEXT, ds:_DATA

	PUBLIC	com_init_
com_init_:
	mov	ax,0F3h				; 9600,n,8,1
	mov	dl,[_com_id]
	sub	dh,dh
	dec	dx				; 0 = COM1, 1 = COM2
	int	14h				; Initialize device
	mov	bx,[_com_port]			; Base of port space
	lea	dx,[bx+5]			; line status register
	in	al,dx
	lea	dx,[bx+4]			; modem control register
	in	al,dx
	or	al,8				; enable OUT2 interrupt
	out	dx,al
	lea	dx,[bx+2]			; int id register
	in	al,dx
	mov	dx,bx				; data receive register
	in	al,dx
	mov	dl,NOT 10h			; mask for IRQ4
	cmp	[_com_id],1
	je	maskit
	mov	dl,NOT 8h			; mask for IRQ3
maskit:
	in	al,21h				; interrupt mask register
	and	al,dl				; force IRQ unmasked
	out	21h,al
	lea	dx,[bx+1]			; int enable register
	mov	al,1
	out	dx,al				; enable received-data int
	ret

	ASSUME	ds:NOTHING

	;**
	;** The protected-mode interrupt handler is in a 32-bit code
	;** segment.  Even so, we have to be sure to force an IRETD
	;** at the end of the handler, because MASM doesn't generate
	;** one.  This handler will be called on a 32-bit stack by
	;** DOS/4GW.
	;**

	PUBLIC	pmhandler_
pmhandler_:
	push	ds
	push	ebx
	mov	bx,_DATA
	mov	ds,bx

	ASSUME	ds:_DATA

	mov	ebx,0B8000h			; DS:EBX = flat:0B8000h
	mov	DWORD PTR [ebx],07200720h	; Clear 2 character cells

	push	eax
	push	ecx
	mov	ecx, 10h
pmdelay:
	mov	ax, 1
	mul	cl
	loop	pmdelay				; PM delay loop (for clarity
	pop	ecx				;   of display only)

	mov	BYTE PTR [ebx+2],'P'		; Write 'P' to memory map

	push	edx
	mov	bx,[_com_port]
	lea	dx,[bx+2]
	in	al,dx				; Read ports so interrupts
	mov	dx,bx				; can continue to be
	in	al,dx				; generated

	inc	[_pm_count]			; Count interrupts handled in PM
	mov	bx, [_lowmem_seg]
	mov	ds, bx				; Get DS addressability to buffer

	ASSUME	ds:NOTHING

	mov	ebx, ds:[128]			; get buffer index
	mov	ds:[132+ebx], al		; store data
	inc	ebx				; increment count, which
	cmp	ebx, maxbuf+1			;   can overflow by one,
	jl	no_ov2				;   resetting it to zero
	sub	ebx, ebx			;   on an overflow so we
no_ov2:						;   don't clobber memory --
	mov	ds:[128], ebx			;   and store new value

	mov	dx,020h
	mov	al,dl
	out	dx,al				; Send EOI
	pop	edx
	pop	eax
	pop	ebx
	pop	ds
	iretd

_TEXT	ENDS

END
