; Retro UNIX 386 v1 Terminal Program (DOS version)
; (Standalone DOS program)
; by Erdogan TAN
;
; Last Update: 06/11/2015	
;
; SERIAL4.ASM - 26/10/2015 (Retro UNIX 386 v1 - Kernel v0.2.0.15)
; SERIAL1.ASM (Retro UNIX 8086 v1)
; 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
		; 06/11/2015
		;mov	si, offset 28*4 
				   ; INT 1Ch vector
		;mov	di, offset old_timer
                movsw              ; Save the old timer interrupt 
                movsw
		;
                push    cs
		pop	ds
		cli	; 06/11/2215
		mov	es, ax
		mov	ax, offset ctrlbrk
		mov	di, 27*4   ; INT 1Bh vector - offset
		stosw		
		mov	ax, cs
		stosw		   ; INT 1Bh vector - segment
		; 06/11/2015
		; INT 1Ch periodic timer interrupt (18.2 times per second)
		;mov	di, 28*4   ; INT 1Ch vector - offset
		;cli	; 06/11/2015
		mov 	ax, offset timer
		stosw
  		mov	ax, cs
		stosw		   ; INT 1Ch vector - segment
		;
		mov	es, ax
		sti	; 06/11/2015
@@:
		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
@@:
		; 06/11/2015
		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              
		;
		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
		;
		; 28/10/2015
		sub	ax, ax 	   ; null
		; 06/11/2015
		mov	word ptr [ticks], ax ; 0 ; reset timer ticks
                jmp     short _3 ; query (initialization, wakeup signal)
sendchr:
		cmp 	byte ptr [cbrk], ah ; 0 ; ctrl + break
                ja      short _exit
                ; 06/11/2015
		mov 	al, byte ptr [rchar]
		or	al, al
		jnz	short @f
		dec	al ; 0FFh
		cmp	byte ptr [schar], al ; 0FFh
		je	short getchr
response:
		; the remote process is waiting for response !
		mov 	byte ptr [schar], al ; 0FFh  ; response
		inc	al
		mov	word ptr [ticks], ax ; 0 ; reset connection timeout
		jmp	short _2 ; _1
@@:
		cmp 	byte ptr [schar], ah ; 0  ; query (wait for response)
		ja	short @f
		cmp	al, 0FFh ; response
		je	short getchr
query:
		; connection time out ! wait for response ...
		xor	al, al ; ax = 0
		mov	word ptr [ticks], ax ;0 ; reset connection timeout
		jmp	short _1 ; _2	 	
@@:
		cmp	word ptr [ticks], 417
 		jnb	short rq
getchr:	
		mov	ah, 1
		int	16h
		jnz	short @f
		hlt
		nop
		nop
		nop
		jmp	short getchr
@@:
		xor	ah, ah 	   ; 0
		int	16h	   ; Read character
		; 28/10/2015
		mov 	byte ptr [schar], al
		sub	ah, ah
_1:
		; 06/11/2015
		cmp	byte ptr [cbrk], ah ; exit sign
		ja	short _exit
		; 29/10/2015
		mov	dx, word ptr [_3FDh] ; Line status register 
		in	al, dx	   ; read register
		JMP	$+2	   ; I/O DELAY
		test	al, 80h	   ; time out error
		jz	short @f
		cmp	word ptr [ticks], 417
 		jb	short _1
rq:
		mov 	byte ptr [schar], ah ; 0  ; query (wait for response) 
		jmp	short query
@@:
		and	al, 20h	   ; Transmitter holding reg. empty?
		jnz	short _2   ; 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 _2   ;yes, ready to send
		;
		hlt		   ;no, check status again
		nop
		nop
		jmp	short _1
_2:
		mov 	al, byte ptr [schar]
_3:
		mov	dx, word ptr [_3F8h] ; data port
		out	dx, al	   ; send on serial port
		; ah = 0
                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
		; 06/11/2015
		;mov	si, offset old_timer
		;mov	di, offset 28*4 
				   ; INT 1Ch 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
		;
		; 06/11/2015
		mov	word ptr [ticks], 0 ; Reset connection timeout
		;
		; 28/10/2015
		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 _7   ; no
		; 28/10/2015
		mov	dx, word ptr [_3F8h]
				   ;data register
		in	al, dx     ;read character
		JMP	$+2	   ; I/O DELAY
		; 06/11/2015
		mov	byte ptr [rchar], al ; received char
		; query
		and 	al, al	   ; 0
		jnz	short _4
                ; response
		mov 	byte ptr [schar], 0
		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?
		jz	short _7   ; no
		;
		mov 	al, 0FFh   ; response			
		mov	dx, word ptr [_3F8h] ; data port
		out	dx, al	   ; send on serial port
		;JMP	$+2	   ; I/O DELAY
		mov	byte ptr [schar], al ; 0FFh
		jmp	short _7
_4:
		; 06/11/2015
		inc	al	  ; al = FFh -> 0
		jz	short _7  ; al = 0 (response, FFh)
		dec	al		
		;
		mov	bx, 7
		mov	ah, 0Eh
		;
		; 26/10/2015
		cmp 	al, 9	   ; TAB key ?
		jne	short _6
		push	cx
		mov 	ah, 3	  ; Get cursor position
		;mov	bh, 0
		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
		mov	ah, 0Eh
		;mov	bx, 7
_5:
		dec	dl
		jz	short _6
		int	10h	   ; Write character on TTY display
		jmp 	short _5	
_6:
		int	10h	   ; Write character on TTY display
_7:
		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
timer:
		; 06/11/2015
		; INT 1Ch (periodic timer interrupt (18.2 times per second)		
		;
		inc 	word ptr CS:[ticks]
		;iret 
		jmp 	dword ptr CS:[old_timer] 

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
	JMP	$+2			; I/O DELAY
	;-----	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
	JMP	$+2			; I/O DELAY
	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:
	; 02/11/2015
	; 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
	JMP	$+2			; I/O DELAY
	or	al, 8      		; enable bit 3 (OUT2)
	out	dx, al     		; write back to register
	JMP	$+2			; I/O DELAY
	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... (06/11/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
align 2

;26/10/2015
error:		db 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_timer:	dd 0 ; 06/11/2015
old_serial:	dd 0

; 06/11/2015
schar:		db 0
rchar:		db 0
ticks:		dw 0	

CODE_SEG	ends

		end	start
