; Retro UNIX 386 v1 Terminal Program (DOS version)
; (Standalone DOS program)
; by Erdogan TAN
;
; Last Update: 26/11/2015	
;
; serial6.s - 23/11/2015
; serial5.s - 10/11/2015 NASM 2.11 (buffered read)
;
; 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: NASM 2.11 

[BITS 16]

		[ORG 100h]

		; 10/11/2015
		; clear bss section
		mov	cx, bss_end
		mov	di, bss_start
		xor	ax, ax
		sub	cx, di
		shr	cx, 1
		rep	stosw
		;
		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, StartMsg
		mov	bl, 7
		call	proc_printmsg
		;
		xor 	ax, ax
		mov	ds, ax
		mov	si, 27*4 
				   ; INT 1Bh vector
		mov	di, old_ctrlbrk
                movsw              ; Save the old ctrl+brk interrupt 
                movsw
		; 06/11/2015
		;mov	si, 28*4 
				   ; INT 1Ch vector
		;mov	di, old_timer
                movsw              ; Save the old timer interrupt 
                movsw
		;
                push    cs
		pop	ds
		cli	; 06/11/2215
		mov	es, ax
		mov	ax, 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, _timer
		stosw
  		mov	ax, cs
		stosw		   ; INT 1Ch vector - segment

		mov	es, ax
		sti	; 06/11/2015
s0:
		xor	ah, ah
		int 	16h
		;
		cmp	al, '1'
		je	short s1
		cmp	al, '2'
		je	short _0
		;
		mov	al, 07h	   ; BEEP !
		mov	ah, 0Eh
		int	10h
		jmp	short s0
_0:
		mov	si, _3F8h + 1
		dec 	byte [SI]	; 2F8h
		add	si, 2
		dec 	byte [SI]	; 2F9h
		; 28/10/2015
		;add	si, 2
		;dec 	byte [SI]	; 2FAh
		add	si, 2
		dec 	byte [SI]	; 2FCh
		add	si, 2
		dec 	byte [SI]	; 2FDh
		;add	si, 2
		;dec 	byte [SI]	; 2FEh
		;
		mov	si, _EFh
		mov 	byte [SI], 0F7h	
s1:
		sub	al, '1'
		mov	byte [port], al
		;
		mov     si, ComSMsg
                add     byte [SI+4], al
		;mov	bx, 7
		call	proc_printmsg
		;
		; 26/10/2015
		call	set_spcp ; Set communication parameters
		jnc	short s2
                mov     si, ErrMsg
		;mov	bx, 7
		call	proc_printmsg
		jmp	_exit
s2:
		; 06/11/2015
		mov	dl, byte [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 s3
		add	si, 4	   ; 0Ch*4 (COM1)
s3:
		push	si
                mov     di, old_serial
                movsw              ; Save the old serial port interrupt   
                movsw
        	;
		push	cs
		pop	ds
                mov     es, ax     ; 0
		pop	di
		;
		cli
		mov 	ax, serial  ; serial port interrupt handler
		stosw		   ; INT 0Ch (0Bh) vector -   
		mov	ax, cs
		stosw		   ; INT 0Ch (0Bh) vector - segment	
		;mov	es, ax
		sti              
		;
		mov     si, AnyKeyMsg
		call	proc_printmsg
		;
		xor	ah, ah
		int 	16h
		;
		cmp	al, 1Bh    ; ESC key
                je      _return 
		;
		push	ds
		pop	es
		;
		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 [ticks], ax ; 0 ; reset timer ticks
                jmp     short _3 ; query (initialization, wakeup signal)

sendchr:
		; 10/11/2015
		call	print  ; move and print received chars on the screen
		;
		cmp 	byte [cbrk], ah ; 0 ; ctrl + break
                ja      short _exit
                ; 06/11/2015
		mov 	al, byte [rchar]
		or	al, al
		jnz	short s4
		dec	al ; 0FFh
		cmp	byte [schar], al ; 0FFh
		je	short getchr
response:
		; the remote process is waiting for response !
		mov 	byte [schar], al ; 0FFh  ; response
		inc	al
		mov	word [ticks], ax ; 0 ; reset connection timeout
		jmp	short _2 ; _1
s4:
		cmp 	byte [schar], ah ; 0  ; query (wait for response)
		ja	short s5
		cmp	al, 0FFh ; response
		je	short getchr
query:
		; connection time out ! wait for response ...
		xor	al, al ; ax = 0
		mov	word [ticks], ax ;0 ; reset connection timeout
		jmp	short _1 ; _2	 	
s5:
		cmp	word [ticks], 417
 		jnb	short rq
getchr:	
		mov	ah, 1
		int	16h
		jnz	short s6
		;hlt
		;nop
		;nop
		;nop
		; 10/11/2015
		; Do somethings while waiting for
		; 'transmitter holding register empty' status
		;
		call 	print	; move and print received chars on the screen
		;
		jmp	short getchr
s6:
		xor	ah, ah 	   ; 0
		int	16h	   ; Read character
		; 28/10/2015
		mov 	byte [schar], al
		sub	ah, ah
_1:
		; 06/11/2015
		cmp	byte [cbrk], ah ; exit sign
		ja	short _exit
		; 29/10/2015
		mov	dx, word [_3FDh] ; Line status register 
		in	al, dx	   ; read register
		JMP	$+2	   ; I/O DELAY
		test	al, 80h	   ; time out error
		jz	short s7
		cmp	word [ticks], 417
 		jb	short _1
rq:
		mov 	byte [schar], ah ; 0  ; query (wait for response) 
		jmp	short query
s7:
		and	al, 20h	   ; Transmitter holding reg. empty?
		jnz	short _2   ; yes, ready to send
		;
		;xor	dh, dh
		;mov	dl, byte [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
		;
		; 10/11/2015
		; Do somethings while waiting for 
		; 'transmitter holding register empty' status
		;
		call 	print	; move and print received chars on the screen
		;
		jmp	short _1
_2:
		mov 	al, byte [schar]
_3:
		mov	dx, word [_3F8h] ; data port
		out	dx, al	   ; send on serial port
		; ah = 0
		; 10/11/2015
                jmp    sendchr

_exit:
				   ; Restore old interrupt vectors
		xor	ax, ax
                mov	es, ax     ; 0
                mov	si, old_serial
		mov	di, 0Bh*4 ; (COM2)
		dec	byte [port] 
		jz	short s8
		add	di, 4	   ; 0Ch*4 (COM1)
s8:
		cli
                movsw              ; Restore
                movsw
		sti        
_return:
		mov	si, old_ctrlbrk
		mov	di, 27*4 
				   ; INT 1Bh vector
                movsw              ; Restore 
                movsw
		; 06/11/2015
		;mov	si, old_timer
		;mov	di, 28*4 
				   ; INT 1Ch vector
                movsw              ; Restore 
                movsw
		;
		int 20h
here:
		hlt
		jmp	 short here

print:
		; 23/11/2015
		; 10/11/2015
		;
		; write received chars (in buffer) to screen
		;
		mov	si, rbuffer
		mov	di, wbuffer
		mov	word [wb_off], di
		pushf ; 23/11/2015
		cli	; interrupts off		
		mov	cx, word [rb_off] ; read buffer offset
		sub	cx, si
		ja	short p0
		; empty buffer !
		;sti	; interrupts on	
		popf ; 23/11/2015
		retn
p0:
		; move chars to writing buffer (from reading buffer)
		; while serial port interrupt is disabled
		;
		add	word [wb_off], cx ; byte/char count
		;
		rep	movsb
		;
		mov	word [rb_off], rbuffer ; reset
		;sti	; interrupts on
		popf ; 23/11/2015
		;
		mov	si, wbuffer
		;
		mov	bx, 7    ; video page 0, color 7
		mov	ah, 0Eh  ; write chars as teletype output
p1:	
		; print chars (in writing buffer) on the screen
		; while serial port interrupt is enabled (again)
		;
		lodsb
		;
		; 26/10/2015
		cmp 	al, 9	   ; TAB key ?
		jne	short p3
		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
p2:
		dec	dl
		jz	short p3
		int	10h	   ; Write character on TTY display
		jmp 	short p2	
p3:
		int	10h	   ; Write character on TTY display
		;
		; 10/11/2015
		cmp	si, word [wb_off]
		jb	short p1
		retn

serial:		
		; 10/11/2015
		; 06/11/2015
		; 28/10/2015
		; 26/10/2015
		;
                ; 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 [ticks], 0 ; Reset connection timeout
		;
		; 28/10/2015
		mov	dx, word [_3FDh] ; Line status register 
		in	al, dx	   ; read register
		JMP	$+2	   ; I/O DELAY
		and	al, 01h	   ; Data ready?
		jz	short _6   ; no
		; 28/10/2015
		mov	dx, word [_3F8h]
				   ;data register
		in	al, dx     ;read character
		JMP	$+2	   ; I/O DELAY
		; 06/11/2015
		mov	byte [rchar], al ; received char
		; query
		and 	al, al	   ; 0
		jnz	short _4
                ; response
		mov 	byte [schar], 0
		mov	dx, word [_3FDh] ; Line status register 
		in	al, dx	   ; read register
		JMP	$+2	   ; I/O DELAY
		and	al, 20h	   ; Transmitter holding reg. empty?
		jz	short _6   ; no
		;
		mov 	al, 0FFh   ; response			
		mov	dx, word [_3F8h] ; data port
		out	dx, al	   ; send on serial port
		;JMP	$+2	   ; I/O DELAY
		mov	byte [schar], al ; 0FFh
		jmp	short _6
_4:
		; 06/11/2015
		inc	al	  ; al = FFh -> 0
		jz	short _6  ; al = 0 (response, FFh)
		dec	al		
		; 10/11/2015 - Receive char and put it in read buffer
		mov	bx, word [rb_off] ; read buffer offset
		mov 	dx, rbuffer + 256
		cmp	bx, dx
		jb	short _5
		; 23/11/2015
		; buffer is full!
		push	ax
		call	print
		mov	bx, word [rb_off]	
		pop	ax
_5:
                mov     byte [bx], al  ; put character in buffer
		inc	bx	 ; points to next char address in buffer	
		mov	word [rb_off], bx ; read buffer offset
_6:
		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 [CS:cbrk] 
		iret
_timer:
		; 06/11/2015
		; INT 1Ch (periodic timer interrupt (18.2 times per second)		
		;
                inc     word [CS:ticks]
		;iret 
                jmp     far [CS:old_timer] 

proc_printmsg:
		mov	ah, 0Eh
		;mov	bx, 7
s9:
		lodsb
		and	al, al
		jz	short s10
		int 	10h
		jmp	short s9
s10:          
		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 [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 [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 [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 [_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 [_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 [_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... (26/11/2015)'
		db 0Dh, 0Ah
                db "('Ctrl+Break' must be used to exit!)"
                db 0Dh, 0Ah
		db 0Dh, 0Ah
		db 'Press 1 for COM1 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

_EFh:		dw 0EFh
;
_3F8h:		dw 3F8h
_3F9h:		dw 3F9h
;_3FAh:		dw 3FAh
;_3FBh:		dw 3FBh
_3FCh:		dw 3FCh
_3FDh:		dw 3FDh	
;_3FEh:		dw 3FEh	
;

; 10/11/2015
rb_off:		dw rbuffer  ; Write buffer offset
wb_off:		dw wbuffer  ; Read buffer offset

;; align 2

; 10/11/2015 (bss)
bss_start:

ABSOLUTE bss_start

port:		resb 1
cbrk:		resb 1

old_ctrlbrk:	resd 1
old_timer:	resd 1 ; 06/11/2015
old_serial:	resd 1

; 06/11/2015
schar:		resb 1
rchar:		resb 1
ticks:		resw 1

wbuffer:	resb 256 ; Print (with INT 10h) routine will use this one

rbuffer:	resb 256 ; Interrupt handler will use this one

error:		resb 1
		resb 1

bss_end:
