; Retro UNIX 8086 v1 Terminal Program (DOS version)
; (Standalone DOS program)
; by Erdogan TAN
;
; Last Update: 29/10/2015	
;
; SERIAL3.ASM - 26/10/2015 (Retro UNIX 386 v1 - Kernel v0.2.015)
; SERIAL1.ASM 
; 08/07/2014, 23/07/2013, 27/07/2014 
; (06/07/2014, 05/07/2014, 04/07/2014, 03/07/2014)

; Assembler: MASM 6.14

.8086

CODE_SEG	segment para public
		assume  CS:CODE_SEG, DS:CODE_SEG, SS:CODE_SEG, ES:CODE_SEG

		org 100h
start:
		mov 	ax, 0600h  ; Scroll up, clear (AL=0)
		mov	bh, 07h    ; Black backround (0), 
				   ; Light gray foreground (7)
		sub	cx, cx     ; Left-Upper column, row
		mov	dx, 184Fh  ; Righ-Lower column, row
		int 	10h			 
		;
		mov	ah, 2	   ; Set cursor position 	
		xor	dx, dx	   ; Row 0 (DH), Column 0 (DL)	
		xor	bh, bh ; 0
		int 	10h
		;
		mov	si, offset StartMsg
		mov	bl, 7
		call	proc_printmsg
		;
		xor 	ax, ax
		mov	ds, ax
		mov	si, offset 27*4 
				   ; INT 1Bh vector
		mov	di, offset old_ctrlbrk
                movsw              ; Save the old ctrl+brk interrupt 
                movsw
		;
                push    cs
		pop	ds
		mov	es, ax
		mov	ax, offset ctrlbrk
		mov	di, 27*4   ; INT 1Bh vector - offset
		stosw		
		mov	ax, cs
		stosw		   ; INT 1Bh vector - segment
		;
		mov	es, ax
@@:
		xor	ah, ah
		int 	16h
		;
		cmp	al, '1'
		je	short @f
		cmp	al, '2'
		je	short _0
		;
		mov	al, 07h	   ; BEEP !
		mov	ah, 0Eh
		int	10h
		jmp	short @b
_0:
		mov	si, offset _3F8h + 1
		dec 	byte ptr [SI]	; 2F8h
		add	si, 2
		dec 	byte ptr [SI]	; 2F9h
		; 28/10/2015
		;add	si, 2
		;dec 	byte ptr [SI]	; 2FAh
		add	si, 2
		dec 	byte ptr [SI]	; 2FCh
		add	si, 2
		dec 	byte ptr [SI]	; 2FDh
		;add	si, 2
		;dec 	byte ptr [SI]	; 2FEh
		;
		mov	si, offset _EFh
		mov 	byte ptr [SI], 0F7h	
@@:
		sub	al, '1'
		mov	byte ptr [port], al
		;
		mov     si, offset ComSMsg
		add	byte ptr [SI]+4, al
		;mov	bx, 7
		call	proc_printmsg
		;
		; 26/10/2015
		call	set_spcp ; Set communication parameters
		jnc	short @f
                mov     si, offset ErrMsg
		;mov	bx, 7
		call	proc_printmsg
		jmp	_exit
@@:	
		mov 	cx, 65535
@@:
		nop
		nop
		nop
		loop	@b                 
		;
		mov     si, offset AnyKeyMsg
		call	proc_printmsg
		;
		xor	ah, ah
		int 	16h
		;
		cmp	al, 1Bh    ; ESC key
                je      _return 
		;
		mov 	ax, 0600h  ; Scroll up, clear (AL=0)
		mov	bh, 17h    ; Blue backround (1), 
				   ; Light gray foreground (7)
		sub	cx, cx     ; Left-Upper column, row
		mov	dx, 184Fh  ; Righ-Lower column, row
		int 	10h			 
		;
		mov	ah, 2	   ; Set cursor position 	
		sub	dx, dx	   ; Row 0 (DH), Column 0 (DL)	
		mov	bx, 7	
		int 	10h
		;
		mov	dl, byte ptr [port]
				   ; hook serial port interrupt
		xor	ax, ax ; 0
		mov 	ds, ax	   ; IVT base

		mov	si, 0Bh*4  ; Port 1 (COM2)
		and	dl, dl     ; Port 0 (COM1) ?
		jnz	short @f
		add	si, 4	   ; 0Ch*4 (COM1)
@@:
		push	si
                mov     di, offset old_serial
                movsw              ; Save the old serial port interrupt   
                movsw
        	;
		push	cs
		pop	ds
                mov     es, ax     ; 0
		pop	di
@@:
		cli
		mov 	ax, offset serial  ; serial port interrupt handler
		stosw		   ; INT 0Ch (0Bh) vector - offset   
		mov	ax, cs
		stosw		   ; INT 0Ch (0Bh) vector - segment	
		;mov	es, ax
		sti
		; 28/10/2015
		sub	ax, ax 	   ; null  
		jmp	short _2   ; (initialization, wakeup signal)
sendchr:
		cmp 	byte ptr [cbrk], ah ; ctrl + break
		ja	short _exit
@@:		
		mov	ah, 1
		int	16h
		jnz	short @f
		hlt
		nop
		nop
		nop
		jmp	short @b
@@:
		xor	ah, ah 	   ; 0
		int	16h	   ; Read character
		; 28/10/2015
		mov 	byte ptr [char], al
		sub	ah, ah
_1:
		; 29/10/2015
		;mov	dx, word ptr [_3FAh]
				   ;interrupt identification register
		;in	al, dx	   ;read register
		;and	al, 2	   ;is transmitter holding reg. empty?
		;jnz	short @f   ;;yes, ready to send
		;
		mov	dx, word ptr [_3FDh] ; Line status register 
		in	al, dx	   ; read register
		JMP	$+2	   ; I/O DELAY
		and	al, 20h	   ; Transmitter holding reg. empty?
		jnz	short @f   ; yes, ready to send
		;
		;xor	dh, dh
		;mov	dl, byte ptr [port]
		;mov	ah, 3
		;int	14h
		;and	ah, 32 	   ;trasmitter holding register empty
		;jnz	short @f   ;yes, ready to send
		;
		cmp	byte ptr [cbrk], ah ; exit sign
		ja	short _exit
		;
		hlt		   ;no, check status again
		nop
		nop
		jmp	short _1
@@:
		sub	ah, ah
		mov 	al, byte ptr [char]
_2:
		mov	dx, word ptr [_3F8h] ; data port
		out	dx, al	   ; send on serial port
		jmp 	short sendchr
_exit:
				   ; Restore old interrupt vectors
		;xor 	ax, ax
                ;mov    es, ax     ; 0
                mov     si, offset old_serial
		mov	di, offset 0Bh*4 ; (COM2)
		dec 	byte ptr [port] 
		jz	short @f
		add	di, 4	   ; 0Ch*4 (COM1)
@@:
		cli
                movsw              ; Restore
                movsw
		sti        
_return:
		mov	si, offset old_ctrlbrk
		mov	di, offset 27*4 
				   ; INT 1Bh vector
                movsw              ; Restore 
                movsw
		;
		int 20h
here:
		hlt
		jmp	 short here

serial:		;
                ; INT 0Ch (0Bh) serial port interrupt handler        
		;
		push	ds
		push	ax
		push	bx
		push	dx
		;
		mov	ax, cs
		mov	ds, ax
		; 28/10/2015
		;mov	dx, word ptr [_3FAh]
				   ; interrupt identification reg.
		;in	al, dx	   ; read register
		;JMP	$+2	   ; I/O DELAY
		;and	al, 4	   ; is receiver data available?
		;jz	short @f   ; no, leave interrupt handler
		;
		mov	dx, word ptr [_3FDh] ; Line status register 
		in	al, dx	   ; read register
		JMP	$+2	   ; I/O DELAY
		and	al, 01h	   ; Data ready?
		jz	short @f   ; no	
		;
		mov	dx, word ptr [_3F8h]
				   ;data register
		in	al, dx     ;read character
		JMP	$+2	   ; I/O DELAY
		;
		; 29/10/2015
		and 	al, al	   ; 0
		jz	short @f
		;
		cmp 	al, 9	   ; TAB key ?
		jne	short _4
		push	cx
		mov	bx, 7
		mov 	ah, 3	  ; Get cursor position
		int 	10h
		pop 	cx
		xor	ah, ah
		mov	al, dl	   ; row
		mov	dl, 8
		div	dl
		mov 	dl, 8
		sub	dl, ah
		mov	al, 20h
_3:
		dec	dl
		jz	short _4
		mov	ah, 0Eh
		int	10h	   ; Write character on TTY display
		jmp 	short _3	
_4:
		mov	bx, 7
		mov	ah, 0Eh
		int	10h	   ; Write character on TTY display
@@:
		mov	al, 20h
		out	20h, al	   ;end of interrupt
		;
		pop	dx
		pop	bx
		pop	ax
		pop	ds
		iret
ctrlbrk:
		;
		; INT 1Bh (control+break) handler		
		;
		inc 	byte ptr CS:[cbrk] 
		iret

proc_printmsg:
		mov	ah, 0Eh
		;mov	bx, 7
@@:
		lodsb
		and	al, al
		jz	short @f
		int 	10h
		jmp	short @b
@@:          
		retn

set_spcp: ; Set serial port communication parameters
	; 26/10/2015 (SERIAL3.ASM)
	; 29/06/2015 (Retro UNIX 386 v1 - u0.s)
	;
	;  Communication parameters (except BAUD RATE):
	;	Bit	4	3	2	1	0
	;		-PARITY--   STOP BIT  -WORD LENGTH-	 		 
	;  this one -->	00 = none    0 = 1 bit  11 = 8 bits
	;		01 = odd     1 = 2 bits	10 = 7 bits
	;		11 = even
	;  Baud rate setting bits: (29/06/2015)
	;		Retro UNIX 386 v1 feature only !
	;	Bit	7    6    5  | Baud rate
	;		------------------------
	;	value	0    0    0  | Default (Divisor = 1)
	;		0    0    1  | 9600 (12)
	;		0    1    0  | 19200 (6) 
	;		0    1	  1  | 38400 (3) 
	;		1    0	  0  | 14400 (8)
	;		1    0	  1  | 28800 (4)
	;		1    1    0  | 57600 (2)
	;		1    1    1  | 115200 (1) 
	;	
	; References:	
	; (1) IBM PC-XT Model 286 BIOS Source Code
	;     RS232.ASM --- 10/06/1985 COMMUNICATIONS BIOS (RS232)
	; (2) Award BIOS 1999 - ATORGS.ASM
	; (3) http://wiki.osdev.org/Serial_Ports
	;
	; (COM1 base port address = 3F8h, COM1 Interrupt = IRQ 4)
	; (COM2 base port address = 2F8h, COM1 Interrupt = IRQ 3)
	;
	; ((Modified registers: EAX, ECX, EDX, EBX))
	;
	;mov	cl, 1 ; divisor = 1 (115200 baud)
	;mov	ch, 3 ; communication parameters except divisor 
	mov	cx, 301h
sp_i1:
	mov	dx, 3F8h  ; COM1 Data register
	; 26/10/2015
	mov 	al, byte ptr [port]
	or	al, al
	jz	short sp_i2 ;  COM1 (AL = 0)
	;
	dec	dh ; 2F8h, COM2 Data register
sp_i2:
  	;-----	INITIALIZE THE COMMUNICATIONS PORT
	; 28/10/2015
	inc	dl	; 3F9h (2F9h)	; 3F9h, COM1 Interrupt enable register 
	mov	al, 0
	out	dx, al			; disable serial port interrupt
	JMP	$+2			; I/O DELAY
	add	dl, 2 	; 3FBh (2FBh)	; COM1 Line control register (3FBh)
	mov	al, 80h			
	out	dx, al			; SET DLAB=1 ; divisor latch access bit
	;-----	SET BAUD RATE DIVISOR
	; 26/10/2015
	sub 	dl, 3   ; 3F8h (2F8h)	; register for least significant byte
					; of the divisor value
	mov	al, cl	; 1
	out	dx, al			; 1 = 115200 baud (Retro UNIX 386 v1)
					; 2 = 57600 baud
					; 3 = 38400 baud
					; 6 = 19200 baud
					; 12 = 9600 baud (Retro UNIX 8086 v1)
	JMP	$+2			; I/O DELAY
	sub	al, al
	inc	dl      ; 3F9h (2F9h)	; register for most significant byte
					; of the divisor value
	out	dx, al ; 0
	JMP	$+2			; I/O DELAY
	;	
	mov	al, ch ; 3		; 8 data bits, 1 stop bit, no parity
	;and	al, 1Fh ; Bits 0,1,2,3,4	
	add	dl, 2	; 3FBh (2FBh)	; Line control register
	out	dx, al			
	JMP	$+2			; I/O DELAY
	; 29/10/2015
	dec 	dl 	; 3FAh (2FAh)	; FIFO Control register (16550/16750)
	xor	al, al			; 0
	out	dx, al			; Disable FIFOs (reset to 8250 mode)	
	;JMP	$+2
sp_i3:
	;-----	COMM PORT STATUS ROUTINE
	; 28/10/2015
	; 26/10/2015
	add	dl, 3	; 3FDh (2FDh)	; COM1 Line status register (3FDh) 
	mov	ah, byte ptr [error]
	or	ah, ah ; 0?
	jnz	short sp_i4 ; error! 
	in	al, dx			; GET LINE CONTROL STATUS
	test	al, 80h			; Timeout!?
	jz	short sp_i5
	inc	ah ; 1
	mov	byte ptr [error], ah
        mov     cx, 30Eh 		; Reset to 9600 baud
	jmp 	short sp_i1
sp_i4:
	stc
	retn
sp_i5:
	; 28/10/2015
	; 26/10/2015
	; 29/06/2015
	;; COM1 - enabling IRQ 4
	;; (COM2 - enabling IRQ 3)
	mov	dx, word ptr [_3FCh]	; modem control register
	in	al, dx 	   		; read register
	or	al, 8      		; enable bit 3 (OUT2)
	out	dx, al     		; write back to register
	mov	dx, word ptr [_3F9h]	; interrupt enable register
	in	al, dx     		; read register
	JMP	$+2			; I/O DELAY
	;or	al, 3      		; transmitter empty 
					; interrupt enable and
	or	al, 1			; receiver data interrupt enable
	out	dx, al 	   		; write back to register
	JMP	$+2        		; I/O DELAY
	in	al, 21h    		; read interrupt mask register
	JMP	$+2        		; I/O DELAY
        and     al, byte ptr [_EFh]     ; enable IRQ 4 (IRQ 3)
	out	21h, al    		; write back to register
	;
	retn

StartMsg:
                db 0Dh,0Ah
                db 'Terminal program for Retro UNIX 386 v1... (29/10/2015)'
		db 0Dh, 0Ah
                db "('Ctrl+Break' must be used to exit!)"
                db 0Dh, 0Ah
		db 0Dh, 0Ah
		db 'Press 1 for COM 1 or press 2 for COM2 serial port...' 
		db 0Dh, 0Ah
		db 0Dh,0Ah,0h
ComSMsg:
		db 07h
		db 'COM1 selected...'
		db  0Dh, 0Ah, 0
AnyKeyMsg:
                db "Press a key to continue. (Press 'ESC' key to cancel.)"
                db 0Dh,0Ah,0h
ErrMsg:
		db 0Dh, 0Ah
		db 0Dh, 0Ah
		db 'Serial Port (115200 baud) Error !'
		db 0Dh, 0Ah, 0

_EFh:		db 0EFh
;
_3F8h:		dw 3F8h
_3F9h:		dw 3F9h
;_3FAh:		dw 3FAh
;_3FBh:		dw 3FBh
_3FCh:		dw 3FCh
_3FDh:		dw 3FDh	
;_3FEh:		dw 3FEh	

port:		db 0
cbrk:		db 0

old_ctrlbrk:	dd 0
old_serial:	dd 0

; 26/10/2015
error:		db 0
; 28/10/2015
char:		db 0	

CODE_SEG	ends

		end	start
