; Retro UNIX 386 v1 Kernel (v0.2) - SYS9.INC
; Last Modification: 09/12/2015
; ----------------------------------------------------------------------------
; Derived from 'Retro UNIX 8086 v1' source code by Erdogan Tan
; (v0.1 - Beginning: 11/07/2012)
;
; Derived from UNIX Operating System (v1.0 for PDP-11) 
; (Original) Source Code by Ken Thompson (1971-1972)
; <Bell Laboratories (17/3/1972)>
; <Preliminary Release of UNIX Implementation Document>
;
; Retro UNIX 8086 v1 - U9.ASM (01/09/2014) //// UNIX v1 -> u9.s
;
; ****************************************************************************

getch:
	; 30/06/2015
	; 18/02/2015 - Retro UNIX 386 v1 - feature only!
	sub	al, al ; 0
getch_q: ; 06/08/2015
	mov 	ah, [ptty] ; active (current) video page
        jmp     short getc_n

getc: 
	; 12/11/2015
	; 15/09/2015
	; 01/07/2015
	; 30/06/2015
	; 18/02/2015 (Retro UNIX 386 v1 - Beginning)
	; 13/05/2013 - 04/07/2014 (Retro UNIX 8086 v1)
	;
	; Retro UNIX 8086 v1 modification !
	; 
	; 'getc' gets (next) character 
	;	 from requested TTY (keyboard) buffer 
	; INPUTS ->
	;     [u.ttyn] = tty number (0 to 7) (8 is COM1, 9 is COM2)	
	;     AL=0 -> Get (next) character from requested TTY buffer
	;	(Keyboard buffer will point to 
	;			next character at next call)
	;     AL=1 -> Test a key is available in requested TTY buffer
	;	(Keyboard buffer will point to 
	;			current character at next call)
	; OUTPUTS ->
	;     (If AL input is 1) ZF=1 -> 'empty buffer' (no chars)
	;     			 ZF=0 -> AX has (current) character
	;      AL = ascii code
	;      AH = scan code	(AH = line status for COM1 or COM2)	 			
	; 		        (cf=1 -> error code/flags in AH)
	; Original UNIX V1 'getc': 
	;		get a character off character list
	;
	; ((Modified registers: eAX, eBX, eCX, eDX, eSI, eDI))	
	;
	; 30/06/20045 (32 bit modifications)
	; 16/07/2013
	; mov 	[getctty], ah
	;

	mov	ah, [u.ttyn] 	; 28/07/2013
getc_n:
	; 30/06/2015
	or	ah, ah
	jz	short getc0 
	shl	ah, 1
	movzx	ebx, ah
	add	ebx, ttychr
	jmp	short getc1
getc0:
	mov	ebx, ttychr
getc1:
	mov	cx, [ebx] 	; ascii & scan code
				; (by kb_int)	
	or	cx, cx
	jnz	short getc2
	and 	al, al
	jz	short getc_s
	xor	ax, ax
	retn
getc2:	
	and	al, al
	mov	ax, cx
	mov	cx, 0
	jnz	short getc3
getc_sn:
	mov	[ebx], cx ; 0, reset
	cmp	ax, cx  ; zf = 0
getc3:
	retn
getc_s:
	; 12/11/2015
	; 15/09/2015
	; 01/07/2015
	; 30/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 16/07/2013 - 14/02/2014 (Retro UNIX 8086 v1)
	;
	; tty  of the current process is not 
	; current tty (ptty); so, current process only 
	; can use keyboard input when its tty becomes 
	; current tty (ptty).
	; 'sleep' is for preventing an endless lock
	; during this tty input request.
	; (Because, the user is not looking at the video page
	; of the process to undersand there is a keyboard
	; input request.)
	;
	;((Modified registers: eAX, eBX, eCX, eDX, eSI, eDI))
	;
	; 05/10/2013
	; ah = byte ptr [u.ttyn] ; (tty number)
	;
	; 10/10/2013
gcw0:
	mov	cl, 10 ; ch = 0
gcw1:	
	; 12/11/2015
	call intract ; jumps to 'sysexit' if [u.quit] = FFFFh
	; 10/10/2013
	call	idle
	mov	ax, [ebx] 	; ascii & scan code
				; (by kb_int)
	or	ax, ax
;	jnz	short gcw3
	jnz	short gcw2 ; 15/09/2015
	; 30/06/2015
	dec	cl
	jnz	short gcw1
	;
	mov	ah, [u.ttyn] 	; 20/10/2013
;	; 10/12/2013
;	cmp 	ah, [ptty]
;	jne	short gcw2
;	; 14/02/2014
;	cmp	byte [u.uno], 1
;	jna	short gcw0		
;gcw2:
	call	sleep
	;
	; 20/09/2013
	mov	ah, [u.ttyn]
	xor 	al, al
	jmp	short getc_n
;gcw3:
gcw2: 	; 15/09/2015
	; 10/10/2013
	xor	cl, cl
	jmp	short getc_sn

sndc:   ; <Send character>
	;
	; 17/11/2015
	; 16/11/2015
	; 11/11/2015
	; 10/11/2015
	; 09/11/2015
	; 08/11/2015
	; 07/11/2015
	; 06/11/2015 (serial4.asm, 'sendchr')	
	; 29/10/2015
	; 30/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 14/05/2013 - 28/07/2014 (Retro UNIX 8086 v1)
	;
	; Retro UNIX 8086 v1 feature only !
	;
	; ah = [u.ttyn]
	;
	; 30/06/2015
	sub	ah, 8 ; ; 0 = tty8 or 1 = tty9
	; 07/11/2015
	movzx	ebx, ah ; serial port index (0 or 1)
sndc0:
	; 07/11/2015
	call	isintr ; quit (ctrl+break) check
	jz	short sndc1
	call	intract ; quit (ctrl+break) check
	; CPU will jump to 'sysexit' if 'u.quit' = 0FFFFh (yes)
sndc1:
	; 16/11/2015
	mov	cx, ax	; *** al = character (to be sent)
sndcx:
	mov	al, [ebx+schar] ; last sent character
	mov	ah, [ebx+rchar] ; last received character
	;
	; 17/11/2015
	; check 'request for response' status
	cmp	byte [ebx+req_resp], 0
	jz	short query
response:
	inc 	byte [comqr] ; query or response status
	mov	al, 0FFh	 
	jmp	short sndc3
query:
	or 	al, al  ; 0 = query (also end of text)
	jnz 	short sndc2 ; normal character
	;cmp 	ah, 0FFh     ; is it responded by terminal ?
	;je	short sndc2  ; yes, already responded
	; 16/11/2015
	; query: request for response (again)
	mov	[ebx+rchar], al ; 0 ; reset
	inc 	byte [comqr] ; query or response status
	jmp	short sndc3
sndc2:
	mov	al, cl 	; *** character (to be sent)
sndc3:
	mov	[ebx+schar], al ; current character (to be sent)
	mov	al, bl ; 0 or 1 (serial port index)
	; 30/06/2015
	call	sp_status ; get serial port status
	; AL = Line status, AH = Modem status
	; 07/11/2015
	test	al, 80h
	jnz	short sndc4
	test	al, 20h	; Transmitter holding register empty ?
	jnz	short sndc5
sndc4: 	; Check line status again
	; 16/11/2015
	push	cx
	mov	ecx, 6 ; 6*30 micro seconds (~5556 chars/second)
	call	WAITF
	pop	cx
	;
	mov	al, bl ; 0 or 1 (serial port index)
	call	sp_status ; get serial port status
	; 16/11/2015
	; 09/11/2015
	; 08/11/2015
	test	al, 80h	; time out error
        jnz     short sndc7
	test	al, 20h	; Transmitter holding register empty ?
        jz	short sndc7
sndc5:  
	mov	al, [ebx+schar] ; character (to be sent)
	mov	dx, 3F8h   ; data port (COM2)
	sub	dh, bl
	out	dx, al	   ; send on serial port
	; 10/11/2015
	; delay for 3*30 (3*(15..80)) micro seconds
	; (to improve text flow to the terminal)
	; ('diskette.inc': 'WAITF')
	; Uses port 61h, bit 4 to have CPU speed independent waiting.
	; (refresh periods = 1 per 30 microseconds on most machines)
	push	cx 
	mov	ecx, 6 ; 6*30 micro seconds (~5556 chars/second)
	call	WAITF
	pop	cx
    	;
	; 07/11/2015
	mov	al, bl ; al = 0 (tty8) or 1 (tty9)
	;
	call	sp_status ; get serial port status
	; AL = Line status, AH = Modem status
	;
	call	isintr ; quit (ctrl+break) check
	jz	short sndc6
	call	intract ; quit (ctrl+break) check
	; CPU will jump to 'sysexit' if 'u.quit' = 0FFFFh (yes)
sndc6:
	cmp	al, 80h
	jnb	short sndc7		
	;
	cmp	byte [comqr], 1 ; 'query or response' ?
	jb	short sndc8 	; no, normal character
	mov 	byte [comqr], bh ; 0 ; reset
	; 17/11/2015
	call	idle
	;
	cmp	[ebx+schar], bh ; 0 ; query ?
        ja      sndc2       ; response (will be followed by
			    ; a normal character)
	; Query request must be responded by the terminal
	; before sending a normal character !
	push	ebx
	push	cx ; *** cl = character (to be sent)
	mov	ah, [u.ttyn]
	call	sleep ; this process will be awakened by
		      ; received data available interrupt
	pop	cx ; *** cl = character (to be sent)
	pop	ebx
        jmp	sndcx
sndc7:
	 ; 16/11/2015
	cmp	byte [comqr], 1 ; 'query or response' ?
	jb	short sndc9 	; no
	;
	mov	[ebx+rchar], bh ; 0 ; reset
	mov	[ebx+schar], bh ; 0 ; reset
	;
	mov	byte [comqr], bh ; 0 ; reset  
sndc8:
	cmc  ; jnc -> jc, jb -> jnb
sndc9:
	; AL = Line status, AH = Modem status
	retn

putc:	
	; 13/08/2015
	; 30/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 15/05/2013 - 27/07/2014 (Retro UNIX 8086 v1)
	;
	; Retro UNIX 8086 v1 modification !
	; 
	; 'putc' puts a character 
	;	 onto requested (tty) video page or
	;	 serial port
	; INPUTS ->
	;     AL = ascii code of the character
	;     AH = video page (tty) number (0 to 7)
	;			  (8 is COM1, 9 is COM2)	
	; OUTPUTS ->
	;    (If AL input is 1) ZF=1 -> 'empty buffer' (no chars)
	;      			ZF=0 -> AX has (current) character
	;     cf=0 and AH = 0 -> no error
	;     cf=1 and AH > 0 -> error (only for COM1 and COM2)		 			
	; 
	; Original UNIX V1 'putc': 
	;     put a character at the end of character list
	;
	; ((Modified registers: eAX, eBX, eCX, eDX, eSI, eDI))
	;
	cmp	ah, 7
        ja      sndc
	; 30/06/2015
	movzx	ebx, ah
	; 13/08/2015
	mov	ah, 07h ; black background, light gray character color
	jmp	write_tty ; 'video.inc'

get_cpos:
	; 29/06/2015 (Retro UNIX 386 v1)
	; 04/12/2013 (Retro UNIX 8086 v1 - 'sysgtty')
	;
	; INPUT -> bl = video page number
	; RETURN -> dx = cursor position

	push	ebx
	and	ebx, 0Fh ; 07h ; tty0 to tty7
	shl	bl, 1
	add	ebx, cursor_posn
	mov	dx, [ebx]
	pop	ebx
	retn

read_ac_current:
	; 29/06/2015 (Retro UNIX 386 v1)
	; 04/12/2013 (Retro UNIX 8086 v1 - 'sysgtty')
	;
	; INPUT -> bl = video page number
	; RETURN -> ax = character (al) and attribute (ah)

	call 	find_position ; 'video.inc'
	; dx = status port
	; esi = cursor location/address
	add	esi, 0B8000h	; 30/08/2014 (Retro UNIX 386 v1)
	mov 	ax, [esi]	; get the character and attribute
	retn

syssleep:
	; 29/06/2015 - (Retro UNIX 386 v1)
	; 11/06/2014 - (Retro UNIX 8086 v1)
	;
	; Retro UNIX 8086 v1 feature only
	; (INPUT -> none)
	;
	movzx	ebx, byte [u.uno] ; process number
	mov	ah, [ebx+p.ttyc-1] ; current/console tty
	call	sleep
	jmp	sysret

vp_clr:
	; Reset/Clear Video Page
	;
	; 30/06/2015 - (Retro UNIX 386 v1)
	; 21/05/2013 - 30/10/2013(Retro UNIX 8086 v1) (U0.ASM)
	;
	; Retro UNIX 8086 v1 feature only !
	;
	; INPUTS -> 
	;   BL = video page number	 
	;
	; OUTPUT ->
	;   none
	; ((Modified registers: eAX, BH, eCX, eDX, eSI, eDI))
	;
	; 04/12/2013
	sub	al, al
	; al = 0 (clear video page)
	; bl = video page
	mov	ah, 07h
	; ah = 7 (attribute/color)
	xor 	cx, cx ; 0, left upper column (cl) & row (cl)
	mov	dx, 184Fh ; right lower column & row (dl=24, dh=79)
	call	scroll_up
	; bl = video page
	xor	dx, dx ; 0 (cursor position) 
	jmp 	set_cpos

sysmsg:
	; 11/11/2015
	; 01/07/2015 - (Retro UNIX 386 v1 feature only!)
	; Print user-application message on user's console tty
	;
	; Input -> EBX = Message address
	;	   ECX = Message length (max. 255)
	;	   DL = Color (IBM PC Rombios color attributes)
	;
	cmp	ecx, MAX_MSG_LEN ; 255
	ja	sysret ; nothing to do with big message size
	or	cl, cl
	jz	sysret
	and	dl, dl
	jnz	short sysmsg0
	mov	dl, 07h ; default color
		; (black background, light gray character) 
sysmsg0:
	mov	[u.base], ebx
	mov	[ccolor], dl ; color attributes
	mov	ebp, esp
	xor	ebx, ebx ; 0
	mov	[u.nread], ebx ; 0
	;
	cmp	[u.kcall], bl ; 0
	ja	short sysmsgk ; Temporary (01/07/2015)
	;
	mov	[u.count], ecx
	inc	ecx ; + 00h ; ASCIZZ
	sub	esp, ecx
	mov	edi, esp
	mov	esi, esp
	mov	[u.pcount], bx ; reset page (phy. addr.) counter
	; 11/11/2015
	mov 	ah, [u.ttyp] ; recent open tty
	; 0 = none
	dec	ah
	jns	short sysmsg1 
	mov	bl, [u.uno] ; process number	
	mov	ah, [ebx+p.ttyc-1] ; user's (process's) console tty
sysmsg1:
	mov	[u.ttyn], ah
sysmsg2:
	call	cpass
	jz	short sysmsg5
	stosb
	and	al, al
	jnz	short sysmsg2
sysmsg3:
	cmp	ah, 7 ; tty number
	ja	short sysmsg6 ; serial port
	call	print_cmsg
sysmsg4:
	mov	esp, ebp	
	jmp	sysret
sysmsg5:
	mov	byte [edi], 0
	jmp	short sysmsg3
sysmsg6:
	mov	al, [esi]
	call	sndc
	jc	short sysmsg4
	cmp	byte [esi], 0  ; 0 is stop character
	jna	short sysmsg4
	inc 	esi
	mov	ah, [u.ttyn]
	jmp	short sysmsg6

sysmsgk: ; Temporary (01/07/2015)
	; The message has been sent by Kernel (ASCIIZ string)
	; (ECX -character count- will not be considered)
	mov	esi, [u.base]
	mov	ah, [ptty] ; present/current screen (video page)
	mov	[u.ttyn], ah
	mov	byte [u.kcall], 0
	jmp	short sysmsg3
	

print_cmsg: 
	; 01/07/2015 (retro UNIX 386 v1 feature only !)
	;
	; print message (on user's console tty) 
	;	with requested color
	;
	; INPUTS:
	;	esi = message address
	;	[u.ttyn] = tty number (0 to 7)
	;	[ccolor] = color attributes (IBM PC BIOS colors)
	;
	lodsb
pcmsg1:
	push 	esi
        movzx   ebx, byte [u.ttyn]
	mov	ah, [ccolor]
	call 	write_tty
	pop	esi
	lodsb
	and 	al, al  ; 0
	jnz 	short pcmsg1
	retn

sysgeterr:
	; 09/12/2015
	; 21/09/2015 - (Retro UNIX 386 v1 feature only!)
	; Get last error number or page fault count
	; (for debugging)
	;
	; Input -> EBX = return type
	;	   0 = last error code (which is in 'u.error')	
	;	   FFFFFFFFh = page fault count for running process
	;	   FFFFFFFEh = total page fault count
	;	   1 .. FFFFFFFDh = undefined 
	;
	; Output -> EAX = last error number or page fault count
	;	   (depending on EBX input)
	; 	
	and 	ebx, ebx
	jnz	short glerr_2
glerr_0:
	mov	eax, [u.error]
glerr_1:
	mov	[u.r0], eax
 	retn
glerr_2:
	inc	ebx ; FFFFFFFFh -> 0, FFFFFFFEh -> FFFFFFFFh
	jz	short glerr_2 ; page fault count for process
	inc	ebx ; FFFFFFFFh -> 0	
	jnz	short glerr_0
	mov	eax, [PF_Count] ; total page fault count
        jmp     short glerr_1
glerr_3:
	mov 	eax, [u.pfcount]
	jmp	short glerr_1
