; ****************************************************************************
; TRDOS386.ASM (TRDOS 386 Kernel - v2.0.0) - MAIN PROGRAM : trdosk6.s
; ----------------------------------------------------------------------------
; Last Update: 11/07/2016
; ----------------------------------------------------------------------------
; Beginning: 24/01/2016
; ----------------------------------------------------------------------------
; Assembler: NASM version 2.11 (trdos386.s)
; ----------------------------------------------------------------------------
; Derived from 'Retro UNIX 386 Kernel - v0.2.1.0' source code by Erdogan Tan
; u1.s (27/17/2015), u2.s (03/01/2016)
; ****************************************************************************

sysent: ; < enter to system call >
	; 06/06/2016
	; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 16/04/2015 - 19/10/2015 (Retro UNIX 386 v1)
	; 10/04/2013 - 18/01/2014 (Retro UNIX 8086 v1)
	;
	; 'unkni' or 'sysent' is sytem entry from various traps. 
	; The trap type is determined and an indirect jump is made to 
	; the appropriate system call handler. If there is a trap inside
	; the system a jump to panic is made. All user registers are saved 
	; and u.sp points to the end of the users stack. The sys (trap)
	; instructor is decoded to get the the system code part (see
	; trap instruction in the PDP-11 handbook) and from this 
	; the indirect jump address is calculated. If a bad system call is
	; made, i.e., the limits of the jump table are exceeded, 'badsys'
	; is called. If the call is legitimate control passes to the
	; appropriate system routine.
	;
	; Calling sequence:
	;	Through a trap caused by any sys call outside the system.
	; Arguments:
	;	Arguments of particular system call.	
	; ...............................................................
	;	
	; Retro UNIX 8086 v1 modification: 
	;       System call number is in EAX register.
	;
	;       Other parameters are in EDX, EBX, ECX, ESI, EDI, EBP
	;	registers depending of function details.
  	;
	; 16/04/2015
        mov     [ss:u.sp], esp ; Kernel stack points to return address
	; save user registers
	push	ds
	push	es
	push	fs
	push	gs
	pushad  ; eax, ecx, edx, ebx, esp -before pushad-, ebp, esi, edi
	;
	; ESPACE = [ss:u.sp] - esp ; 4*12 = 48 ; 17/09/2015 ; 06/06/2016
	; 	(ESPACE is size of space in kernel stack 
	;	for saving/restoring user registers.)
	;
	push	eax ; 01/07/2015
	mov     ax, KDATA
        mov     ds, ax
        mov     es, ax
        mov     fs, ax
        mov     gs, ax
	mov	eax, [k_page_dir]
	mov	cr3, eax
	pop	eax ; 01/07/2015
	; 19/10/2015
	cld
	;
	inc	byte [sysflg]
		; incb sysflg / indicate a system routine is in progress
        sti 	; 18/01/2014
	jnz     panic ; 24/05/2013
		; beq 1f
		; jmp panic ; / called if trap inside system
;1:
	; 16/04/2015
	mov	[u.r0], eax
	mov	[u.usp], esp ; kernel stack points to user's registers
	;
		; mov $s.syst+2,clockp
		; mov r0,-(sp) / save user registers 
		; mov sp,u.r0 / pointer to bottom of users stack 
			   ; / in u.r0
		; mov r1,-(sp)
		; mov r2,-(sp)
		; mov r3,-(sp)
		; mov r4,-(sp)
		; mov r5,-(sp)
		; mov ac,-(sp) / "accumulator" register for extended
		             ; / arithmetic unit
		; mov mq,-(sp) / "multiplier quotient" register for the
		             ; / extended arithmetic unit
		; mov sc,-(sp) / "step count" register for the extended
		             ; / arithmetic unit
		; mov sp,u.sp / u.sp points to top of users stack
		; mov 18.(sp),r0 / store pc in r0
		; mov -(r0),r0 / sys inst in r0      10400xxx
		; sub $sys,r0 / get xxx code
	shl	eax, 2
		; asl r0 / multiply by 2 to jump indirect in bytes
	cmp	eax, end_of_syscalls - syscalls
		; cmp r0,$2f-1f / limit of table (35) exceeded
	;jnb	short badsys
		; bhis badsys / yes, bad system call
	cmc
	pushf	
	push	eax
 	mov 	ebp, [u.sp] ; Kernel stack at the beginning of sys call
	mov	al, 0FEh ; 11111110b
	adc	al, 0 ; al = al + cf
	and	[ebp+8], al ; flags (reset carry flag)
		; bic $341,20.(sp) / set users processor priority to 0 
				 ; / and clear carry bit
	pop	ebp ; eax
	popf
        jc      badsys
	mov	eax, [u.r0]
	; system call registers: EAX, EDX, ECX, EBX, ESI, EDI
	jmp	dword [ebp+syscalls]
		; jmp *1f(r0) / jump indirect thru table of addresses
		            ; / to proper system routine.
syscalls: ; 1:
	; 20/05/2016
	; 19/05/2016
	; 16/05/2016
	; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 21/09/2015
	; 01/07/2015
	; 16/04/2015 (32 bit address modification) 
	;dd sysrele	; / 0
	dd sysver	; 0 ; Get TRDOS 386 version number (v2.0)	
	dd sysexit 	; / 1
	dd sysfork 	; / 2
	dd sysread 	; / 3
	dd syswrite 	; / 4
	dd sysopen 	; / 5
	dd sysclose 	; / 6
	dd syswait 	; / 7
	dd syscreat 	; / 8
	dd syslink 	; / 9
	dd sysunlink 	; / 10
	dd sysexec 	; / 11
	dd syschdir 	; / 12
	dd systime 	; / 13
	dd sysmkdir 	; / 14
	dd syschmod 	; / 15
	dd syschown 	; / 16
	dd sysbreak 	; / 17
	dd sysstat 	; / 18
	dd sysseek 	; / 19
	dd systell 	; / 20
	dd sysmount 	; / 21
	dd sysumount 	; / 22
	dd syssetuid 	; / 23
	dd sysgetuid 	; / 24
	dd sysstime 	; / 25
	dd sysquit 	; / 26
	dd sysintr 	; / 27
	dd sysfstat 	; / 28
	dd sysemt 	; / 29
	dd sysmdate 	; / 30
	;dd sysstty	; / 31
	dd sysvideo 	; 31 ; TRDOS 386 Video Functions (16/05/2016)
	;dd sysgtty	; / 32
	dd sysaudio 	; 32 ; TRDOS 386 Audio Functions (16/05/2016)
	;dd sysilgins	; / 33
	dd systimer 	; 33 ; TRDOS 386 Timer Functions (18/05/2016)
	dd syssleep 	; 34 ; Retro UNIX 8086 v1 feature only !
			     ; 11/06/2014
	dd sysmsg	; 35 ; Retro UNIX 386 v1 feature only !
			     ; 01/07/2015
	dd sysgeterr	; 36 ; Retro UNIX 386 v1 feature only !
			     ; 21/09/2015 - get last error number
	dd sysreserved1 ; 37 ;; TRDOS 386 (19/05/2016)
	dd syspri 	; 38 ; change priority - TRDOS 386 (20/05/2016)
	dd sysrele	; 39 ; TRDOS 386 (19/05/2016) (0 -> 39)

end_of_syscalls:

error:
	; 18/05/2016
	; 13/05/2016
	; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 16/04/2015 - 17/09/2015 (Retro UNIX 386 v1)
	; 10/04/2013 - 07/08/2013 (Retro UNIX 8086 v1)
	;
	; 'error' merely sets the error bit off the processor status (c-bit)
	; then falls right into the 'sysret', 'sysrele' return sequence.
	;
	; INPUTS -> none
	; OUTPUTS ->
	;	processor status - carry (c) bit is set (means error)
	;
	; 26/05/2013 (Stack pointer must be reset here! 
	; 	      Because, jumps to error procedure
	;	      disrupts push-pop nesting balance)
	;
	mov	ebp, [u.sp] ; interrupt (system call) return (iretd) address
	or	byte [ebp+8], 1  ; set carry bit of flags register
				 ; (system call will return with cf = 1)
		; bis $1,20.(r1) / set c bit in processor status word below
		               ; / users stack
	; 17/09/2015
	sub	ebp, ESPACE ; 48 ; total size of stack frame ('sysdefs.inc')
				 ; for saving/restoring user registers	
	;cmp	ebp, [u.usp]
	;je	short err0	
	mov	[u.usp], ebp
;err0:
	; 01/09/2015
	mov	esp, [u.usp] 	    ; Retro Unix 8086 v1 modification!
				    ; 10/04/2013
				    ; (If an I/O error occurs during disk I/O,
				    ; related procedures will jump to 'error'
				    ; procedure directly without returning to 
				    ; the caller procedure. So, stack pointer
                                    ; must be restored here.)
	; 13/05/2016
	; NOTE: (The last) error code is in 'u.error', it can be retrieved by
	;	'get last error' system call later. 	

	; 03/09/2015 - 09/06/2015 - 07/08/2013
	mov 	byte [u.kcall], 0 ; namei_r, mkdir_w reset

sysret: ; < return from system call>
	; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 16/04/2015 - 10/09/2015 (Retro UNIX 386 v1)
	; 10/04/2013 - 23/02/2014 (Retro UNIX 8086 v1)
	;
	; 'sysret' first checks to see if process is about to be 
	; terminated (u.bsys). If it is, 'sysexit' is called.
	; If not, following happens:	 
	; 	1) The user's stack pointer is restored.
	;	2) r1=0 and 'iget' is called to see if last mentioned
	;	   i-node has been modified. If it has, it is written out
	;	   via 'ppoke'.
	;	3) If the super block has been modified, it is written out
	;	   via 'ppoke'.				
	;	4) If the dismountable file system's super block has been
	;	   modified, it is written out to the specified device
	;	   via 'ppoke'.
	;	5) A check is made if user's time quantum (uquant) ran out
	;	   during his execution. If so, 'tswap' is called to give
	;	   another user a chance to run.
	;	6) 'sysret' now goes into 'sysrele'. 
	;	    (See 'sysrele' for conclusion.)		
	;
	; Calling sequence:
	;	jump table or 'br sysret'
	; Arguments: 
	;	-	
	; ...............................................................
	;	
	; ((AX=r1 for 'iget' input))
	;	
	xor	ax, ax ; 04/05/2013
sysret0: ; 29/07/2015 (eax = 0, jump from sysexec)
	inc	al ; 04/05/2013
	cmp	[u.bsys], al ; 1
		; tstb u.bsys / is a process about to be terminated because
        jnb     sysexit ; 04/05/2013
		; bne sysexit / of an error? yes, go to sysexit
	;mov	esp, [u.usp] ; 24/05/2013 (that is not needed here)
		; mov u.sp,sp / no point stack to users stack
	dec 	al ; mov ax, 0
		; clr r1 / zero r1 to check last mentioned i-node
	call	iget
		; jsr r0,iget / if last mentioned i-node has been modified
		            ; / it is written out
	xor 	ax, ax ; 0
	cmp	[smod], al ; 0
		; tstb	smod / has the super block been modified
	jna	short sysret1
		; beq	1f / no, 1f
	mov	[smod], al ; 0
		; clrb smod / yes, clear smod
	mov	ebx, sb0 ;; 07/08//2013
   	or	word [ebx], 200h ;;
	;or	word [sb0], 200h ; write bit, bit 9
		; bis $1000,sb0 / set write bit in I/O queue for super block
		      	      ; / output
	; AX = 0
	call 	poke ; 07/08/2013
	; call	ppoke
	; AX = 0
		; jsr r0,ppoke / write out modified super block to disk
sysret1: ;1:
	cmp	[mmod], al ; 0
		; tstb	mmod / has the super block for the dismountable file
		           ; / system
	jna	short sysrel0
		; beq 1f / been modified?  no, 1f
	mov	[mmod], al ; 0	
		; clrb	mmod / yes, clear mmod
        ;mov    ax, [mntd]
        ;;mov   al, [mdev] ; 26/04/2013
	mov	ebx, sb1 ;; 07/08//2013
        ;;mov	[ebx], al
	;mov    [sb1], al
		; movb	mntd,sb1 / set the I/O queue
	or	word [ebx], 200h
	;or	word [sb1], 200h ; write bit, bit 9
		; bis $1000,sb1 / set write bit in I/O queue for detached sb
	call	poke ; 07/08/2013
	;call	ppoke 
		; jsr r0,ppoke / write it out to its device
        ;xor    al, al ; 26/04/2013       
;1:
		; tstb uquant / is the time quantum 0?
		; bne 1f / no, don't swap it out

sysrele: ; < release >
	; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 16/04/2015 - 14/10/2015 (Retro UNIX 386 v1)
	; 10/04/2013 - 07/03/2014 (Retro UNIX 8086 v1)
	;
	; 'sysrele' first calls 'tswap' if the time quantum for a user is
	;  zero (see 'sysret'). It then restores the user's registers and
	; turns off the system flag. It then checked to see if there is
	; an interrupt from the user by calling 'isintr'. If there is, 
	; the output gets flashed (see isintr) and interrupt action is
	; taken by a branch to 'intract'. If there is no interrupt from
	; the user, a rti is made.
	;
	; Calling sequence:
	;	Fall through a 'bne' in 'sysret' & ?
	; Arguments:
	;	-	
	; ...............................................................
	;	
	; 23/02/2014 (swapret)
	; 22/09/2013
sysrel0: ;1:
	cmp	byte [u.quant], 0 ; 16/05/2013
		; tstb uquant / is the time quantum 0?
        ja      short swapret
		; bne 1f / no, don't swap it out
sysrelease: ; 07/12/2013 (jump from 'clock')
	call	tswap
		; jsr r0,tswap / yes, swap it out
	
; Retro Unix 8086 v1 feature: return from 'swap' to 'swapret' address.
swapret: ;1:
	; 10/09/2015
	; 01/09/2015
	; 14/05/2015
	; 16/04/2015 (Retro UNIX 386 v1 - 32 bit, pm modifications)
	; 26/05/2013 (Retro UNIX 8086 v1)
	; cli
	; 24/07/2015
	;
	;; 'esp' must be already equal to '[u.usp]' here ! 
	;; mov	esp, [u.usp]

	; 22/09/2013
	call	isintr
	; 20/10/2013
	jz	short sysrel1
	call	intract
		; jsr r0,isintr / is there an interrupt from the user
		;     br intract / yes, output gets flushed, take interrupt
		               ; / action
sysrel1:
	cli ; 14/10/2015
	dec	byte [sysflg]
		; decb sysflg / turn system flag off
	mov     eax, [u.pgdir]
	mov	cr3, eax  ; 1st PDE points to Kernel Page Table 0 (1st 4 MB)
			  ; (others are different than kernel page tables) 
	; 10/09/2015
	popad ; edi, esi, ebp, temp (icrement esp by 4), ebx, edx, ecx, eax
		; mov (sp)+,sc / restore user registers
		; mov (sp)+,mq
		; mov (sp)+,ac
		; mov (sp)+,r5
		; mov (sp)+,r4
		; mov (sp)+,r3
		; mov (sp)+,r2
	;
	mov	eax, [u.r0]  ; ((return value in EAX))
	pop	gs
	pop	fs
	pop	es
	pop	ds
	iretd	
		; rti / no, return from interrupt

badsys:
	; 18/04/2016 (TRDOS 386 = TRDOS v2.0)
	; 17/04/2011 (TRDOS v1.0, 'IFC.ASM')
	; 03/02/2011 ('trdos_ifc_routine')
	;
	; 16/04/2015 (Retro UNIX 386 v1, 'badsys')
	; (EIP, EAX values will be shown on screen with error message)
	; (EIP = 'CD 40h' instruction address -INT 40h-)
	; (EAX = Function number)  
	;
	inc	byte [u.bsys]
	;
	mov	ebx, [u.sp] ; esp at the beginning of 'sysent'
	mov	eax, [ebx] ; EIP (return address, not 'INT 30h' address)
	sub	eax, 2 ; CDh, ##h
	call	dwordtohex
	mov	[eip_str], edx
	mov	[eip_str+4], eax
	mov	eax, [u.r0]
	call	dwordtohex
	mov	[eax_str], edx
	mov	[eax_str+4], eax

	mov	byte [int_num_str], 40h

	mov	esi, ifc_msg ; "invalid funtion call !" msg (trdosk9.s)
	call	print_msg

	jmp	sysexit

intract: ; / interrupt action
	; 14/10/2015
	; 16/04/2015 (Retro UNIX 386 v1 - Beginning)
	; 09/05/2013 - 07/12/2013 (Retro UNIX 8086 v1)
	;
	; Retro UNIX 8086 v1 modification !
	; (Process/task switching and quit routine by using
	; Retro UNIX 8086 v1 keyboard interrupt output.))
	;
	; input -> 'u.quit'  (also value of 'u.intr' > 0)
	; output -> If value of 'u.quit' = FFFFh ('ctrl+brk' sign)
	;		'intract' will jump to 'sysexit'.
	;	    Intract will return to the caller 
	;		if value of 'u.quit' <> FFFFh. 	 
	; 14/10/2015
	sti
	; 07/12/2013	
	inc 	word [u.quit]
	jz	short intrct0 ; FFFFh -> 0
	dec	word [u.quit]
	; 16/04/2015
	retn
intrct0:	
	pop	eax ; call intract -> retn
	;
	xor 	eax, eax
	inc	al  ; mov ax, 1
;;;
	; UNIX v1 original 'intract' routine... 
	; / interrupt action
		;cmp *(sp),$rti / are you in a clock interrupt?
		; bne 1f / no, 1f
		; cmp (sp)+,(sp)+ / pop clock pointer
	; 1: / now in user area
		; mov r1,-(sp) / save r1
		; mov u.ttyp,r1 
			; / pointer to tty buffer in control-to r1
		; cmpb 6(r1),$177
			; / is the interrupt char equal to "del"
		; beq 1f / yes, 1f
		; clrb 6(r1) 
		        ; / no, clear the byte 
			; / (must be a quit character)
		; mov (sp)+,r1 / restore r1
		; clr u.quit / clear quit flag
		; bis $20,2(sp) 
		    	; / set trace for quit (sets t bit of 
			; / ps-trace trap)
		; rti   ;  / return from interrupt
	; 1: / interrupt char = del
		; clrb 6(r1) / clear the interrupt byte 
			   ; / in the buffer
		; mov (sp)+,r1 / restore r1
		; cmp u.intr,$core / should control be 
				; / transferred to loc core?
		; blo 1f
		; jmp *u.intr / user to do rti yes, 
				; / transfer to loc core
	; 1:
		; sys 1 / exit

sysexit: ; <terminate process>
	; 10/06/2016
	; 06/06/2016
	; 23/05/2016
	; 19/05/2016
	; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 16/04/2015 - 01/09/2015 (Retro UNIX 386 v1)
	; 19/04/2013 - 14/02/2014 (Retro UNIX 8086 v1)
	;
	; 'sysexit' terminates a process. First each file that
	; the process has opened is closed by 'flose'. The process
	; status is then set to unused. The 'p.pid' table is then
	; searched to find children of the dying process. If any of
	; children are zombies (died by not waited for), they are
	; set free. The 'p.pid' table is then searched to find the
	; dying process's parent. When the parent is found, it is
	; checked to see if it is free or it is a zombie. If it is
	; one of these, the dying process just dies. If it is waiting
	; for a child process to die, it notified that it doesn't 
	; have to wait anymore by setting it's status from 2 to 1
	; (waiting to active). It is awakened and put on runq by
	; 'putlu'. The dying process enters a zombie state in which
	; it will never be run again but stays around until a 'wait'
	; is completed by it's parent process. If the parent is not
	; found, process just dies. This means 'swap' is called with
	; 'u.uno=0'. What this does is the 'wswap' is not called
	; to write out the process and 'rswap' reads the new process
	; over the one that dies..i.e., the dying process is 
	; overwritten and destroyed.	
 	;
	; Calling sequence:
	;	sysexit or conditional branch.
	; Arguments:
	;	-	
	; ...............................................................
	;	
	; Retro UNIX 8086 v1 modification: 
	;       System call number (=1) is in EAX register.
	;
	;       Other parameters are in EDX, EBX, ECX, ESI, EDI, EBP
	;       registers depending of function details.
	;
	; ('swap' procedure is mostly different than original UNIX v1.)
	;
; / terminate process
	; AX = 1
	dec 	ax ; 0
	mov	[u.intr], ax ; 0
		; clr u.intr / clear interrupt control word
		; clr r1 / clear r1
	; AX = 0
sysexit_1: ; 1:
	; AX = File descriptor
		; / r1 has file descriptor (index to u.fp list)
		; / Search the whole list
	call	fclose
		; jsr r0,fclose / close all files the process opened
	;; ignore error return
		; br .+2 / ignore error return
	;inc	ax
	inc	al
		; inc r1 / increment file descriptor
	;cmp	ax, 10
	cmp	al, 10
		; cmp r1,$10. / end of u.fp list?
	jb	short sysexit_1
		; blt 1b / no, go back
	movzx	ebx, byte [u.uno]
		; movb	u.uno,r1 / yes, move dying process's number to r1
	mov	[ebx+p.stat-1], ah ; 0, SFREE
		; clrb p.stat-1(r1) / free the process
	;shl	bx, 1
	shl	bl, 1
		; asl r1 / use r1 for index into the below tables
	mov	cx, [ebx+p.pid-2]
		; mov p.pid-2(r1),r3 / move dying process's name to r3
	mov	dx, [ebx+p.ppid-2]
		; mov p.ppid-2(r1),r4 / move its parents name to r4
	; xor 	bx, bx ; 0
	xor	bl, bl ; 0
		; clr r2
	xor	esi, esi ; 0
		; clr r5 / initialize reg
sysexit_2: ; 1:
	        ; / find children of this dying process, 
		; / if they are zombies, free them
	;add	bx, 2
	add	bl, 2
		; add $2,r2 / search parent process table 
		          ; / for dying process's name
	cmp	[ebx+p.ppid-2], cx
		; cmp p.ppid-2(r2),r3 / found it?
	jne	short sysexit_4
		; bne 3f / no
	;shr	bx, 1
	shr	bl, 1
		; asr r2 / yes, it is a parent
	cmp	byte [ebx+p.stat-1], 3 ; SZOMB
		; cmpb p.stat-1(r2),$3 / is the child of this 
				     ; / dying process a zombie
	jne	short sysexit_3 
		; bne 2f / no
	mov	[ebx+p.stat-1], ah ; 0, SFREE
		; clrb p.stat-1(r2) / yes, free the child process
sysexit_3: ; 2:
	;shr	bx, 1
	shl	bl, 1
		; asl r2
sysexit_4: ; 3:
		; / search the process name table 
		; / for the dying process's parent
	cmp	[ebx+p.pid-2], dx
		; cmp p.pid-2(r2),r4 / found it?
	jne	short sysexit_5
		; bne 3f / no
	mov	esi, ebx
		; mov r2,r5 / yes, put index to p.pid table (parents
		          ; / process # x2) in r5
sysexit_5: ; 3:
	;cmp	bx, nproc + nproc
	cmp	bl, nproc + nproc
		; cmp r2,$nproc+nproc / has whole table been searched?
	jb	short sysexit_2
		; blt 1b / no, go back
		; mov r5,r1 / yes, r1 now has parents process # x2
	and	esi, esi ; r5=r1
	jz	short sysexit_6
		; beq 2f / no parent has been found. 
		       ; / The process just dies
	shr	si, 1
		; asr r1 / set up index to p.stat
	mov	al, [esi+p.stat-1]
		; movb p.stat-1(r1),r2 / move status of parent to r2
	and	al, al
	jz	short sysexit_6
		; beq 2f / if its been freed, 2f
	cmp	al, 3
		; cmp r2,$3 / is parent a zombie?
	je	short sysexit_6
		; beq 2f / yes, 2f
	; BH = 0
	mov	bl, [u.uno]
		; movb u.uno,r3 / move dying process's number to r3
	mov	byte [ebx+p.stat-1], 3  ; SZOMB
		; movb $3,p.stat-1(r3) / make the process a zombie
	cmp	al, 1 ; SRUN
	je	short sysexit_6
	;cmp	al, 2
		; cmp r2,$2 / is the parent waiting for 
			  ; / this child to die
	;jne	short sysexit_6	
		; bne 2f / yes, notify parent not to wait any more
	; p.stat = 2 --> waiting
	; p.stat = 4 --> sleeping
	mov	byte [esi+p.stat-1], 1 ; SRUN
	;dec	byte [esi+p.stat-1]
		; decb	p.stat-1(r1) / awaken it by putting it (parent)
	mov	ax, si ; r1  (process number in AL)
	; 
	;mov	ebx, runq + 4
		; mov $runq+4,r2 / on the runq
	call	putlu
		; jsr r0, putlu
sysexit_6: 
	; 10/06/2016
	; 06/06/2016
	; 23/05/2016
	; 19/05/2016 - TRDOS 386 (TRDOS v2.0)
	; Check and stop/clear timer event(s) of this (dying) process
	; if there is.
	mov 	al, [esi+p.timer-1]
	and	al, al
	jz	short sysexit_12 ; no timer events for this process
	mov	byte [esi+p.timer-1], 0 ; reset
	mov	al, [timer_events]
	;or	al, al
 	;jz	short sysexit_12 ; no timer events
	mov	cl, al
	mov	ah, [u.uno]
	cli	; disable interrupts 
	mov	bl, 16
	mov	esi, timer_set ; beginning address of timer events
sysexit_7:
	mov	al, [esi] ; process number (of timer event)
	cmp	al, ah ; process number comparison
	je	short sysexit_10
	and	al, al
	jz	short sysexit_9
sysexit_8:
	dec	cl
	jz	short sysexit_11
sysexit_9:
	dec	bl
	jz	short sysexit_11
	add	esi, 16
	jmp	short sysexit_7

sysexit_10:
	;mov	byte [esi], 0
	mov	word [esi], 0
	;mov	dword [esi+12], 0
	jmp	short sysexit_8

sysexit_11:
	xor	al, al
sysexit_12: ; 2:
	sti	; enable interrupts 
	;
	mov	[u.uno], al ; 0
		; / the process dies
	;mov	byte [u.uno], 0
		; clrb u.uno / put zero as the process number, 
	           ; / so "swap" will
	call	swap
		; jsr r0,swap / overwrite process with another process
hlt_sys:
	;sti
hlts0:
	hlt
	jmp	short hlts0
		; 0 / and thereby kill it; halt?

syswait: ; < wait for a processs to die >
	; 17/09/2015
	; 02/09/2015
	; 01/09/2015
	; 16/04/2015 (Retro UNIX 386 v1 - Beginning)
	; 24/05/2013 - 05/02/2014 (Retro UNIX 8086 v1)
	;
	; 'syswait' waits for a process die. 
	; It works in following way:
	;    1) From the parent process number, the parent's 
	; 	process name is found. The p.ppid table of parent
	;	names is then searched for this process name.
	;	If a match occurs, r2 contains child's process
	;	number. The child status is checked to see if it is
	;	a zombie, i.e; dead but not waited for (p.stat=3)
	;	If it is, the child process is freed and it's name
	;	is put in (u.r0). A return is then made via 'sysret'.
	;	If the child is not a zombie, nothing happens and
	;	the search goes on through the p.ppid table until
	;	all processes are checked or a zombie is found.
	;    2) If no zombies are found, a check is made to see if
	;	there are any children at all. If there are none,
	;	an error return is made. If there are, the parent's
	;	status is set to 2 (waiting for child to die),
	;	the parent is swapped out, and a branch to 'syswait'
	;	is made to wait on the next process.
	;
	; Calling sequence:
	;	?
	; Arguments:
	;	-
	; Inputs: - 
	; Outputs: if zombie found, it's name put in u.r0.	
	; ...............................................................
	;				
	
; / wait for a process to die

syswait_0:
	movzx	ebx, byte [u.uno] ; 01/09/2015
		; movb u.uno,r1 / put parents process number in r1
	shl	bl, 1
	;shl	bx, 1
		; asl r1 / x2 to get index into p.pid table
	mov	ax, [ebx+p.pid-2]
		; mov p.pid-2(r1),r1 / get the name of this process
	xor	esi, esi
		; clr r2
	xor	ecx, ecx ; 30/10/2013
	;xor 	cl, cl
		; clr r3 / initialize reg 3
syswait_1: ; 1:
	add	si, 2
		; add $2,r2 / use r2 for index into p.ppid table
			  ; / search table of parent processes 
			  ; / for this process name
	cmp	ax, [esi+p.ppid-2]
		; cmp p.ppid-2(r2),r1 / r2 will contain the childs 
			            ; / process number
	jne	short syswait_3
		;bne 3f / branch if no match of parent process name
	;inc	cx
	inc	cl
		;inc r3 / yes, a match, r3 indicates number of children
	shr	si, 1
		; asr r2 / r2/2 to get index to p.stat table
	; The possible states ('p.stat' values) of a process are:
	;	0 = free or unused
	;	1 = active
	;	2 = waiting for a child process to die
	;	3 = terminated, but not yet waited for (zombie).	
	cmp	byte [esi+p.stat-1], 3 ; SZOMB, 05/02/2014
		; cmpb p.stat-1(r2),$3 / is the child process a zombie?
	jne	short syswait_2
		; bne 2f / no, skip it
	mov	[esi+p.stat-1], bh ; 0
		; clrb p.stat-1(r2) / yes, free it
	shl	si, 1
		; asl r2 / r2x2 to get index into p.pid table
	movzx	eax, word [esi+p.pid-2]
	mov	[u.r0], eax
		; mov p.pid-2(r2),*u.r0 
			      ; / put childs process name in (u.r0)
	;
	; Retro UNIX 386 v1 modification ! (17/09/2015)
	;
	; Parent process ID -p.ppid- field (of the child process)
	; must be cleared in order to prevent infinitive 'syswait'
	; system call loop from the application/program if it calls
	; 'syswait' again (mistakenly) while there is not a zombie
	; or running child process to wait. ('forktest.s', 17/09/2015)
	;
	; Note: syswait will return with error if there is not a
	;       zombie or running process to wait.	
	;
	sub	ax, ax
	mov 	[esi+p.ppid-2], ax ; 0 ; 17/09/2015
	jmp	sysret0 ; ax = 0
	;
	;jmp	sysret
		; br sysret1 / return cause child is dead
syswait_2: ; 2:
	shl	si, 1
		; asl r2 / r2x2 to get index into p.ppid table
syswait_3: ; 3:
	cmp	si, nproc+nproc
		; cmp r2,$nproc+nproc / have all processes been checked?
	jb	short syswait_1
		; blt 1b / no, continue search
	;and	cx, cx
	and	cl, cl
		; tst r3 / one gets here if there are no children 
		       ; / or children that are still active
	; 30/10/2013
	jnz	short syswait_4
	;jz	error
		; beq error1 / there are no children, error
	mov	[u.r0], ecx ; 0
	jmp	error
syswait_4:
	mov	bl, [u.uno]
		; movb u.uno,r1 / there are children so put 
			      ; / parent process number in r1
	inc	byte [ebx+p.stat-1] ; 2, SWAIT, 05/02/2014
		; incb p.stat-1(r1) / it is waiting for 
				  ; / other children to die
	; 04/11/2013
	call	swap
		; jsr r0,swap / swap it out, because it's waiting
	jmp	syswait_0
		; br syswait / wait on next process

sysfork: ; < create a new process >
	; 18/09/2015
	; 04/09/2015
	; 02/09/2015
	; 01/09/2015
	; 28/08/2015
	; 14/05/2015
	; 10/05/2015
	; 09/05/2015
	; 06/05/2015 (Retro UNIX 386 v1 - Beginning)
	; 24/05/2013 - 14/02/2014 (Retro UNIX 8086 v1)
	;
	; 'sysfork' creates a new process. This process is referred
	; to as the child process. This new process core image is
	; a copy of that of the caller of 'sysfork'. The only
	; distinction is the return location and the fact that (u.r0)
	; in the old process (parent) contains the process id (p.pid)
	; of the new process (child). This id is used by 'syswait'.
	; 'sysfork' works in the following manner: 	
	;    1) The process status table (p.stat) is searched to find
	;	a process number that is unused. If none are found
	;	an error occurs.
	;    2) when one is found, it becomes the child process number
	;	and it's status (p.stat) is set to active.
	;    3) If the parent had a control tty, the interrupt 
	;	character in that tty buffer is cleared.
	;    4) The child process is put on the lowest priority run 
	;	queue via 'putlu'.
	;    5) A new process name is gotten from 'mpid' (actually 
	;	it is a unique number) and is put in the child's unique
	;	identifier; process id (p.pid).
	;    6) The process name of the parent is then obtained and
	;	placed in the unique identifier of the parent process
	;	name is then put in 'u.r0'.	
	;    7) The child process is then written out on disk by
	;	'wswap',i.e., the parent process is copied onto disk
	;	and the child is born. (The child process is written 
	;	out on disk/drum with 'u.uno' being the child process
	;	number.)
	;    8) The parent process number is then restored to 'u.uno'.
	;    9) The child process name is put in 'u.r0'.
	;   10) The pc on the stack sp + 18 is incremented by 2 to
	;	create the return address for the parent process.
	;   11) The 'u.fp' list as then searched to see what files
	;	the parent has opened. For each file the parent has
	;	opened, the corresponding 'fsp' entry must be updated
	;	to indicate that the child process also has opened
	;	the file. A branch to 'sysret' is then made.	 			 				
	;
	; Calling sequence:
	;	from shell ?
	; Arguments:
	;	-
	; Inputs: -
	; Outputs: *u.r0 - child process name
	; ...............................................................
	;	
	; Retro UNIX 8086 v1 modification: 
	;	AX = r0 = PID (>0) (at the return of 'sysfork')
	;	= process id of child a parent process returns
	;	= process id of parent when a child process returns
	;
	;       In original UNIX v1, sysfork is called and returns as
	;	in following manner: (with an example: c library, fork)
	;	
	;	1:
	;		sys	fork
	;			br 1f  / child process returns here
	;		bes	2f     / parent process returns here
	;		/ pid of new process in r0
	;		rts	pc
	;	2: / parent process condionally branches here
	;		mov	$-1,r0 / pid = -1 means error return
	;		rts	pc
	;
	;	1: / child process brances here
	;		clr	r0   / pid = 0 in child process
	;		rts	pc
	;
	;	In UNIX v7x86 (386) by Robert Nordier (1999)
	;		// pid = fork();
	;		//
	;		// pid == 0 in child process; 
	;		// pid == -1 means error return
	;		// in child, 
	;		//	parents id is in par_uid if needed
	;		
	;		_fork:
	;			mov	$.fork,eax
	;			int	$0x30
	;			jmp	1f
	;			jnc	2f
	;			jmp	cerror
	;		1:
	;			mov	eax,_par_uid
	;			xor	eax,eax
	;		2:
	;			ret
	;
	;	In Retro UNIX 8086 v1,
	;	'sysfork' returns in following manner:
	;	
	;		mov	ax, sys_fork
	;		mov	bx, offset @f ; routine for child
	;		int	20h
	;		jc	error
	;		
	;	; Routine for parent process here (just after 'jc')
	;		mov	word ptr [pid_of_child], ax
	;		jmp	next_routine_for_parent	
	;
	;	@@: ; routine for child process here				
	;		....	
	;	NOTE: 'sysfork' returns to specified offset
	;	       for child process by using BX input.
	;	      (at first, parent process will return then 
	;	      child process will return -after swapped in-
	;	      'syswait' is needed in parent process
	;	      if return from child process will be waited for.)
	;	  				
	
; / create a new process
	; EBX = return address for child process 
	     ; (Retro UNIX 8086 v1 modification !)
	xor 	esi, esi
		; clr r1
sysfork_1: ; 1: / search p.stat table for unused process number
	inc	esi
		; inc r1
	cmp	byte [esi+p.stat-1], 0 ; SFREE, 05/02/2014
		; tstb p.stat-1(r1) / is process active, unused, dead
	jna	short sysfork_2	
		; beq 1f / it's unused so branch
	cmp	si, nproc
		; cmp r1,$nproc / all processes checked
	jb	short sysfork_1
		; blt 1b / no, branch back
	;
	; Retro UNIX 8086 v1. modification:
	;	Parent process returns from 'sysfork' to address 
	;	which is just after 'sysfork' system call in parent
	;	process. Child process returns to address which is put
	;	in BX register by parent process for 'sysfork'. 
	;
		;add $2,18.(sp) / add 2 to pc when trap occured, points
		             ; / to old process return
		; br error1 / no room for a new process
	jmp	error
sysfork_2: ; 1:
	call	allocate_page
	jc	error
	push	eax   ; UPAGE (user structure page) address
	; Retro UNIX 386 v1 modification!
	call	duplicate_page_dir
		; EAX = New page directory 
	jnc	short sysfork_3
	pop	eax   ; UPAGE (user structure page) address
	call 	deallocate_page
	jmp	error
sysfork_3:
	; Retro UNIX 386 v1 modification !
	push	esi
	call	wswap ; save current user (u) structure, user registers
		      ; and interrupt return components (for IRET)
	xchg	eax, [u.pgdir] ; page directory of the child process
	mov	[u.ppgdir], eax ; page directory of the parent process
	pop	esi
	pop	eax   ; UPAGE (user structure page) address
		; [u.usp] = esp
	mov	edi, esi
	shl	di, 2
	mov	[edi+p.upage-4], eax ; memory page for 'user' struct
	mov	[u.upage], eax ; memory page for 'user' struct (child)
	; 28/08/2015
	movzx	eax, byte [u.uno] ; parent process number
		; movb u.uno,-(sp) / save parent process number
	mov	edi, eax
        push	eax ; ** 
	mov     al, [edi+p.ttyc-1] ; console tty (parent)
	; 18/09/2015
	;mov     [esi+p.ttyc-1], al ; set child's console tty
	;mov     [esi+p.waitc-1], ah ; 0 ; reset child's wait channel
	mov     [esi+p.ttyc-1], ax ; al - set child's console tty
				   ; ah - reset child's wait channel	
	mov	eax, esi
	mov	[u.uno], al ; child process number
		;movb r1,u.uno / set child process number to r1
        inc     byte [esi+p.stat-1] ; 1, SRUN, 05/02/2014
		; incb p.stat-1(r1) / set p.stat entry for child 
				; / process to active status
		; mov u.ttyp,r2 / put pointer to parent process' 
			      ; / control tty buffer in r2
                ; beq 2f / branch, if no such tty assigned
		; clrb 6(r2) / clear interrupt character in tty buffer
	; 2:
	push	ebx  ; * return address for the child process
		     ; * Retro UNIX 8086 v1 feature only !	
	; (Retro UNIX 8086 v1 modification!)
		; mov $runq+4,r2
	call	putlu 
 		; jsr r0,putlu / put child process on lowest priority 
			   ; / run queue
	shl	si, 1
		; asl r1 / multiply r1 by 2 to get index 
		       ; / into p.pid table
	inc	word [mpid]
		; inc mpid / increment m.pid; get a new process name
	mov	ax, [mpid]
	mov	[esi+p.pid-2], ax
		;mov mpid,p.pid-2(r1) / put new process name 
				    ; / in child process' name slot
	pop	edx  ; * return address for the child process
		     ; * Retro UNIX 8086 v1 feature only !	
  	pop	ebx  ; **
	;mov	ebx, [esp] ; ** parent process number
		; movb (sp),r2 / put parent process number in r2
	shl 	bx, 1
		;asl r2 / multiply by 2 to get index into below tables
	;movzx eax, word [ebx+p.pid-2]
	mov	ax, [ebx+p.pid-2]
		; mov p.pid-2(r2),r2 / get process name of parent
				   ; / process
	mov	[esi+p.ppid-2], ax
		; mov r2,p.ppid-2(r1) / put parent process name 
			  ; / in parent process slot for child
	mov	[u.r0], eax	
		; mov r2,*u.r0 / put parent process name on stack 
			     ; / at location where r0 was saved
	mov 	ebp, [u.sp] ; points to return address (EIP for IRET)
	mov	[ebp], edx ; *, CS:EIP -> EIP
			   ; * return address for the child process
		; mov $sysret1,-(sp) /
		; mov sp,u.usp / contents of sp at the time when 
			      ; / user is swapped out
		; mov $sstack,sp / point sp to swapping stack space
	; 04/09/2015 - 01/09/2015
	; [u.usp] = esp
	push	sysret ; ***
	mov	[u.usp], esp ; points to 'sysret' address (***)
			     ; (for child process)	
	xor 	eax, eax
	mov 	[u.ttyp], ax ; 0
	;
	call	wswap ; Retro UNIX 8086 v1 modification !
		;jsr r0,wswap / put child process out on drum
		;jsr r0,unpack / unpack user stack
		;mov u.usp,sp / restore user stack pointer
		; tst (sp)+ / bump stack pointer
	; Retro UNIX 386 v1 modification !
	pop	eax ; ***
	shl	bx, 1
	mov     eax, [ebx+p.upage-4] ; UPAGE address ; 14/05/2015
	call	rswap ; restore parent process 'u' structure, 
		      ; registers and return address (for IRET)
		;movb (sp)+,u.uno / put parent process number in u.uno
        movzx   eax, word [mpid]
	mov	[u.r0], eax
		; mov mpid,*u.r0 / put child process name on stack 
			       ; / where r0 was saved
		; add $2,18.(sp) / add 2 to pc on stack; gives parent
			          ; / process return
	;xor	ebx, ebx
	xor     esi, esi
		;clr r1
sysfork_4: ; 1: / search u.fp list to find the files 
	      ; / opened by the parent process
	; 01/09/2015
	;xor	bh, bh
	;mov 	bl, [esi+u.fp]
	mov 	al, [esi+u.fp]
		; movb u.fp(r1),r2 / get an open file for this process
        ;or      bl, bl
	or	al, al
	jz	short sysfork_5	
		; beq 2f / file has not been opened by parent, 
		       ; / so branch
	mov	ah, 10 ; Retro UNIX 386 v1 fsp structure size = 10 bytes
	mul	ah
	;movzx	ebx, ax
	mov	bx, ax
	;shl     bx, 3
		; asl r2 / multiply by 8
       		; asl r2 / to get index into fsp table
       		; asl r2
  	inc     byte [ebx+fsp-2]
		; incb fsp-2(r2) / increment number of processes
			     ; / using file, because child will now be
			     ; / using this file
sysfork_5: ; 2:
        inc     esi
		; inc r1 / get next open file
        cmp     si, 10
		; cmp r1,$10. / 10. files is the maximum number which
			  ; / can be opened
	jb	short sysfork_4	
		; blt 1b / check next entry
	jmp	sysret
		; br sysret1

sysread: ; < read from file >
	; 13/05/2015
	; 11/05/2015 (Retro UNIX 386 v1 - Beginning)
	; 23/05/2013 (Retro UNIX 8086 v1)
	;
	; 'sysread' is given a buffer to read into and the number of
	; characters to be read. If finds the file from the file
	; descriptor located in *u.r0 (r0). This file descriptor
	; is returned from a successful open call (sysopen).
	; The i-number of file is obtained via 'rw1' and the data
	; is read into core via 'readi'.
	;
	; Calling sequence:
	;	sysread; buffer; nchars
	; Arguments:
	;	buffer - location of contiguous bytes where 
	;		 input will be placed.
	;	nchars - number of bytes or characters to be read.
	; Inputs: *u.r0 - file descriptor (& arguments)
	; Outputs: *u.r0 - number of bytes read.	
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification: 
	;       'sysread' system call has three arguments; so,
	;	* 1st argument, file descriptor is in BX register
	;	* 2nd argument, buffer address/offset in CX register
	;	* 3rd argument, number of bytes is in DX register
	;
	;	AX register (will be restored via 'u.r0') will return
	;	to the user with number of bytes read. 
	;
	call	rw1
	jc	error ; 13/05/2015, ax < 1
		; jsr r0,rw1 / get i-number of file to be read into r1
	test	ah, 80h
		; tst r1 / negative i-number?
	jnz	error
		; ble error1 / yes, error 1 to read
			   ; / it should be positive
	call	readi
		; jsr r0,readi / read data into core
	jmp	short rw0
		; br 1f
syswrite: ; < write to file >
	; 13/05/2015
	; 11/05/2015 (Retro UNIX 386 v1 - Beginning)
	; 23/05/2013 (Retro UNIX 8086 v1)
	;
	; 'syswrite' is given a buffer to write onto an output file
	; and the number of characters to write. If finds the file
	; from the file descriptor located in *u.r0 (r0). This file 
	; descriptor is returned from a successful open or create call
	; (sysopen or syscreat). The i-number of file is obtained via
	; 'rw1' and buffer is written on the output file via 'write'.
	;
	; Calling sequence:
	;	syswrite; buffer; nchars
	; Arguments:
	;	buffer - location of contiguous bytes to be writtten.
	;	nchars - number of characters to be written.
	; Inputs: *u.r0 - file descriptor (& arguments)
	; Outputs: *u.r0 - number of bytes written.	
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification: 
	;       'syswrite' system call has three arguments; so,
	;	* 1st argument, file descriptor is in BX register
	;	* 2nd argument, buffer address/offset in CX register
	;	* 3rd argument, number of bytes is in DX register
	;
	;	AX register (will be restored via 'u.r0') will return
	;	to the user with number of bytes written. 
	;
	call	rw1
	jc	error ; 13/05/2015, ax < 1
		; jsr r0,rw1 / get i-number in r1 of file to write
        test	ah, 80h
		; tst r1 / positive i-number ?
        jz	short rw3 ; 13/05/2015
	;jz	error
		; bge error1 / yes, error 1 
			   ; / negative i-number means write
        neg	ax
		; neg r1 / make it positive
	call	writei
        	; jsr r0,writei / write data
rw0: ; 1:
        mov	eax, [u.nread]
	mov	[u.r0], eax
		; mov u.nread,*u.r0 / put no. of bytes transferred
				  ; / into (u.r0)
	jmp	sysret
        	; br sysret1
rw1:	
	; 14/05/2015
	; 13/05/2015
	; 11/05/2015 (Retro UNIX 386 v1 - Beginning)
	; 23/05/2013 - 24/05/2013 (Retro UNIX 8086 v1)
	; System call registers: bx, cx, dx (through 'sysenter')
	;
	;mov	[u.base], ecx 	; buffer address/offset 
				;(in the user's virtual memory space)
	;mov	[u.count], edx 
		; jsr r0,arg; u.base / get buffer pointer
        	; jsr r0,arg; u.count / get no. of characters
	;;mov	eax, ebx ; file descriptor
		; mov *u.r0,r1 / put file descriptor 
		             ; / (index to u.fp table) in r1
	; 13/05/2015
	mov	dword [u.r0], 0 ; r/w transfer count = 0 (reset)
	;
	;; call	getf
        ; eBX = File descriptor
	call	getf1 ; calling point in 'getf' from 'rw1'
		; jsr r0,getf / get i-number of the file in r1
	; AX = I-number of the file ; negative i-number means write
	; 13/05/2015
	cmp 	ax, 1
	jb	short rw2
	;
	mov	[u.base], ecx 	; buffer address/offset 
				;(in the user's virtual memory space)
	mov	[u.count], edx 
	; 14/05/2015
        mov     dword [u.error], 0 ; reset the last error code
	retn
        	; rts r0
rw2:
	; 13/05/2015
	mov	dword [u.error], ERR_FILE_NOT_OPEN ; file not open !
	retn
rw3: 
	; 13/05/2015
	mov	dword [u.error], ERR_FILE_ACCESS ; permission denied !
	stc
	retn

sysopen: ;<open file>
	; 14/05/2015 (Retro UNIX 386 v1 - Beginning)
	; 22/05/2013 - 27/05/2013 (Retro UNIX 8086 v1)
	;
	; 'sysopen' opens a file in following manner:
	;    1) The second argument in a sysopen says whether to
	;	open the file ro read (0) or write (>0).
	;    2) I-node of the particular file is obtained via 'namei'.
	;    3) The file is opened by 'iopen'.
	;    4) Next housekeeping is performed on the fsp table
	;	and the user's open file list - u.fp.
	;	a) u.fp and fsp are scanned for the next available slot.
	;	b) An entry for the file is created in the fsp table.
	;	c) The number of this entry is put on u.fp list.
	;	d) The file descriptor index to u.fp list is pointed
	;	   to by u.r0.
	;
	; Calling sequence:
	;	sysopen; name; mode
	; Arguments:
	;	name - file name or path name
	;	mode - 0 to open for reading
	;	       1 to open for writing
	; Inputs: (arguments)
	; Outputs: *u.r0 - index to u.fp list (the file descriptor)
	;		  is put into r0's location on the stack.	
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification: 
	;       'sysopen' system call has two arguments; so,
	;	* 1st argument, name is pointed to by BX register
	;	* 2nd argument, mode is in CX register
	;
	;	AX register (will be restored via 'u.r0') will return
	;	to the user with the file descriptor/number 
	;	(index to u.fp list).
	;
	;call	arg2
	; * name - 'u.namep' points to address of file/path name
	;          in the user's program segment ('u.segmnt')
	;          with offset in BX register (as sysopen argument 1).
	; * mode - sysopen argument 2 is in CX register 
	;          which is on top of stack.
	;
	; jsr r0,arg2 / get sys args into u.namep and on stack
	;
       	; system call registers: ebx, ecx (through 'sysenter')

	mov	[u.namep], ebx
	push	cx
	call	namei
		; jsr r0,namei / i-number of file in r1
     	;and	ax, ax
	;jz	error ; File not found
	jc	short fnotfound ; 14/05/2015
	;jc	error ; 27/05/2013
		; br  error2 / file not found
   	pop	dx ; mode
	push	dx
	;or	dx, dx
	or	dl, dl
		; tst (sp) / is mode = 0 (2nd arg of call; 
		         ; / 0 means, open for read)
	jz	short sysopen_0
		; beq 1f / yes, leave i-number positive
syscreat_0: ; 27/12/2015
	neg	ax
        	; neg r1 / open for writing so make i-number negative
sysopen_0: ;1:
	call	iopen
		;jsr r0,iopen / open file whose i-number is in r1
	pop	dx
	;and	dx, dx
	and	dl, dl
        	; tst (sp)+ / pop the stack and test the mode
	jz	short sysopen_2
        	; beq op1 / is open for read op1
sysopen_1: ;op0:
	neg	ax
        	; neg r1 
		     ;/ make i-number positive if open for writing [???]
	;; NOTE: iopen always make i-number positive.
	;; Here i-number becomes negative again. [22/05/2013]
sysopen_2: ;op1:
        xor     esi, esi
        	; clr r2 / clear registers
        xor     ebx, ebx
		; clr r3
sysopen_3: ;1: / scan the list of entries in fsp table
        cmp     [esi+u.fp], bl ; 0
		; tstb u.fp(r2) / test the entry in the u.fp list
        jna      short sysopen_4
		; beq 1f / if byte in list is 0 branch
        inc     esi
		; inc r2 / bump r2 so next byte can be checked
        cmp     si, 10
		; cmp r2,$10. / reached end of list?
	jb	short sysopen_3
		; blt 1b / no, go back
toomanyf:
	; 14/05/2015
	mov	dword [u.error], ERR_TOO_MANY_FILES ; too many open files !
	jmp	error
        	; br error2 / yes, error (no files open)
fnotfound: 
	; 14/05/2015
	mov	dword [u.error], ERR_FILE_NOT_FOUND ; file not found !
	jmp	error

sysopen_4: ; 1:
        cmp     word [ebx+fsp], 0
		; tst fsp(r3) / scan fsp entries
        jna     short sysopen_5
		; beq 1f / if 0 branch
	; 14/05/2015 - Retro UNIX 386 v1 modification !
        add     bx, 10 ; fsp structure size = 10 bytes/entry
		; add $8.,r3 / add 8 to r3 
			; / to bump it to next entry mfsp table
        cmp     bx, nfiles*10
		; cmp r3,$[nfiles*8.] / done scanning
	jb	short sysopen_4
       		; blt 1b / no, back
	jmp	error
        	; br error2 / yes, error
sysopen_5: ; 1: / r2 has index to u.fp list; r3, has index to fsp table
        mov     [ebx+fsp], ax
		; mov r1,fsp(r3) / put i-number of open file 
			; / into next available entry in fsp table,
	mov	di, [cdev] ; word ? byte ?
        mov     [ebx+fsp+2], di ; device number
		; mov cdev,fsp+2(r3) / put # of device in next word
        xor	edi, edi
        mov     [ebx+fsp+4], edi ; offset pointer (0)
		; clr fsp+4(r3)
        mov     [ebx+fsp+8], di ; open count (0), deleted flag (0)
       		; clr fsp+6(r3) / clear the next two words
  	mov	eax, ebx
	mov	bl, 10
	div	bl 
		; asr r3
		; asr r3 / divide by 8 
		; asr r3 ; / to get number of the fsp entry-1
	inc	al
        	; inc r3 / add 1 to get fsp entry number
        mov     [esi+u.fp], al
		; movb r3,u.fp(r2) / move entry number into 
			; / next available slot in u.fp list
        mov     [u.r0], esi
		; mov r2,*u.r0 / move index to u.fp list 
			     ; / into r0 loc on stack
        jmp	sysret
		; br sysret2

	;
	; 'fsp' table (10 bytes/entry)
	; bit 15				   bit 0
	; ---|-------------------------------------------
	; r/w|		i-number of open file
	; ---|-------------------------------------------
	;		   device number
	; -----------------------------------------------
	; offset pointer, r/w pointer to file (bit 0-15)
	; -----------------------------------------------
	; offset pointer, r/w pointer to file (bit 16-31)
	; ----------------------|------------------------
	;  flag that says file 	| number of processes
	;   has been deleted	| that have file open 
	; ----------------------|------------------------
	;

syscreat: ; < create file >
	; 27/12/2015 (Retro UNIX 386 v1.1)
	; 14/05/2015 (Retro UNIX 386 v1 - Beginning)
	; 27/05/2013 (Retro UNIX 8086 v1)
	;
	; 'syscreat' called with two arguments; name and mode.
	; u.namep points to name of the file and mode is put
	; on the stack. 'namei' is called to get i-number of the file.		
	; If the file aready exists, it's mode and owner remain 
	; unchanged, but it is truncated to zero length. If the file
	; did not exist, an i-node is created with the new mode via
	; 'maknod' whether or not the file already existed, it is
	; open for writing. The fsp table is then searched for a free
	; entry. When a free entry is found, proper data is placed
	; in it and the number of this entry is put in the u.fp list.
	; The index to the u.fp (also know as the file descriptor)
	; is put in the user's r0. 			
	;
	; Calling sequence:
	;	syscreate; name; mode
	; Arguments:
	;	name - name of the file to be created
	;	mode - mode of the file to be created
	; Inputs: (arguments)
	; Outputs: *u.r0 - index to u.fp list 
	;		   (the file descriptor of new file)
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification: 
	;       'syscreate' system call has two arguments; so,
	;	* 1st argument, name is pointed to by BX register
	;	* 2nd argument, mode is in CX register
	;
	;	AX register (will be restored via 'u.r0') will return
	;	to the user with the file descriptor/number 
	;	(index to u.fp list).
	;
	;call	arg2
	; * name - 'u.namep' points to address of file/path name
	;          in the user's program segment ('u.segmnt')
	;          with offset in BX register (as sysopen argument 1).
	; * mode - sysopen argument 2 is in CX register 
	;          which is on top of stack.
	;
        	; jsr r0,arg2 / put file name in u.namep put mode 
			    ; / on stack
	mov	[u.namep], ebx ; file name address
	push	cx ; mode
	call 	namei        	
		; jsr r0,namei / get the i-number
        ;and	ax, ax
	;jz	short syscreat_1	       	
	jc	short syscreat_1
		; br  2f / if file doesn't exist 2f
	; 27/12/2015
	cmp	ax, 41 ; device inode ?
        jb      syscreat_0 ; yes
	;
	neg 	ax
        	; neg r1 / if file already exists make i-number 
		       ; / negative (open for writing)
	call	iopen
        	; jsr r0,iopen /
	call	itrunc
        	; jsr r0,itrunc / truncate to 0 length
	pop	cx ; pop mode (did not exist in original Unix v1 !?)
        jmp     sysopen_1
        	; br op0
syscreat_1: ; 2: / file doesn't exist
	pop	ax
        	; mov (sp)+,r1 / put the mode in r1
	xor	ah, ah	
        	; bic $!377,r1 / clear upper byte
	call 	maknod
        	; jsr r0,maknod / make an i-node for this file
	mov	ax, [u.dirbuf]
        	; mov u.dirbuf,r1 / put i-number 
			        ; / for this new file in r1
        jmp     sysopen_1
        	; br op0 / open the file

sysmkdir: ; < make directory >
	; 14/05/2015 (Retro UNIX 386 v1 - Beginning)
	; 27/05/2013 - 02/08/2013 (Retro UNIX 8086 v1)
	;
	; 'sysmkdir' creates an empty directory whose name is
	; pointed to by arg 1. The mode of the directory is arg 2.	
	; The special entries '.' and '..' are not present.
	; Errors are indicated if the directory already exists or		
	; user is not the super user. 
	;
	; Calling sequence:
	;	sysmkdir; name; mode
	; Arguments:
	;	name - points to the name of the directory
	;	mode - mode of the directory
	; Inputs: (arguments)
	; Outputs: -
	;    (sets 'directory' flag to 1; 
	;    'set user id on execution' and 'executable' flags to 0)
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification: 
	;       'sysmkdir' system call has two arguments; so,
	;	* 1st argument, name is pointed to by BX register
	;	* 2nd argument, mode is in CX register
	;
		
; / make a directory

	;call	arg2
	; * name - 'u.namep' points to address of file/path name
	;          in the user's program segment ('u.segmnt')
	;          with offset in BX register (as sysopen argument 1).
	; * mode - sysopen argument 2 is in CX register 
	;          which is on top of stack.

		; jsr r0,arg2 / put file name in u.namep put mode 
			    ; / on stack
	mov	[u.namep], ebx
	push	cx ; mode
	call	namei
        	; jsr r0,namei / get the i-number
        	;     br .+4 / if file not found branch around error
        ;xor 	ax, ax
	;jnz	error
	jnc	short dir_exists ; 14/05/2015
	;jnc	error	
		; br  error2 / directory already exists (error)
	cmp	byte [u.uid], 0 ; 02/08/2013
        	;tstb u.uid / is user the super user
	jna	short dir_access_err ; 14/05/2015
	;jna	error
        	;bne error2 / no, not allowed
	pop	ax
        	;mov (sp)+,r1 / put the mode in r1
	and	ax, 0FFCFh ; 1111111111001111b
        	;bic $!317,r1 / all but su and ex
	;or	ax , 4000h ; 1011111111111111b
	or	ah, 40h ; Set bit 14 to 1
        	;bis $40000,r1 / directory flag
	call	maknod
        	;jsr r0,maknod / make the i-node for the directory
	jmp	sysret
        	;br sysret2 /
dir_exists:
	; 14/05/2015
	mov	dword [u.error], ERR_DIR_EXISTS ; dir. already exists !
	jmp	error
dir_access_err:
	; 14/05/2015
	mov	dword [u.error], ERR_DIR_ACCESS ; permission denied !
	jmp	error

sysclose: ;<close file>
	; 14/05/2015 (Retro UNIX 386 v1 - Beginning)
	; 22/05/2013 - 26/05/2013 (Retro UNIX 8086 v1)
	;
	; 'sysclose', given a file descriptor in 'u.r0', closes the
	; associated file. The file descriptor (index to 'u.fp' list)
	; is put in r1 and 'fclose' is called.
	;
	; Calling sequence:
	;	sysclose
	; Arguments:
	;	-  
	; Inputs: *u.r0 - file descriptor
	; Outputs: -
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification:
	;	 The user/application program puts file descriptor
	;        in BX register as 'sysclose' system call argument.
	; 	 (argument transfer method 1)

	; / close the file
	
	mov 	eax, ebx
	call 	fclose
		; mov *u.r0,r1 / move index to u.fp list into r1
		; jsr r0,fclose / close the file
               	; br error2 / unknown file descriptor
		; br sysret2
	; 14/05/2015
	jnc	sysret
	mov	dword [u.error], ERR_FILE_NOT_OPEN ; file not open !
	jmp	error

sysemt: ; enable (or disable) multi tasking -time sharing-
	;
	; 23/05/2016 - TRDOS 386 (TRDOS v2.0)
	; 14/05/2015 (Retro UNIX 386 v1)
	; 10/12/2013 - 20/04/2014 (Retro UNIX 8086 v1)
	;
	; Retro UNIX 8086 v1 modification: 
	;	'Enable Multi Tasking'  system call instead 
	;	of 'Emulator Trap' in original UNIX v1 for PDP-11.
	;
	; Retro UNIX 8086 v1 feature only!
	;	Using purpose: Kernel will start without time-out
	;	(internal clock/timer) functionality.
	;	Then etc/init will enable clock/timer for
	;	multi tasking. 
	;
	; INPUT ->
	;	BL = 0 -> disable multi tasking
	;	BL > 1 -> enable multi tasking (time sharing) 
	; OUTPUT ->
	;	none	
	;
	;  Note: Multi tasking is disabled during system
	;	 initialization, it must be enabled by using
	;	 this system call. (Otherwise, running proces 
	;	 will not be changed by another process within
	;	 run time sequence/schedule, if running process
	;	 will not 'release' itself. Only 'wakeup' procedure
	;	 for waiting processes and programmed timer events
	;	 for other processes can change running process 
	;	 while multi tasking is disabled.) ** 23/05/2016 **

	cmp	byte [u.uid], 0 ; root ?
	;ja	error
	ja	badsys ; 14/05/2015
	;
	cli
	mov	[multi_tasking], bl ; 0 to disable, >0 to enable
	jmp	sysret

systimer:
	; 10/06/2016
	; 07/06/2016
	; 06/06/2016
	; 21/05/2016
	; 19/05/2016
	; 18/05/2016 - TRDOS 386 (TRDOS v2.0)
	; (TRDOS 386 feature only!)
	;
	; (start or stop timer event(s))	
	;
	; INPUT ->
	;	BL = Signal return byte (response byte)
	;	     (Any requested value between 0 and 255)
	;	     (Kernel will put it at the requested address)    	
	;	BH = Time count unit
	;	     0 = Stop timer event
	;	     1 = 18.2 ticks per second
	;	     2 = 10 milliseconds  		
	;	     3 = 1 second (for real time clock interrupt) 	 
	;	     4 to 255 = undefined
	;	BH = 0 -> Stop timer event
	;	BL = Timer event number (1 to 255) if BH = 0     	
	;	     If BL = 0, all timer events (which are belongs
	;	      to running process) will be stopped 		    
	;	ECX = Time/Tick count (depending on time count unit)
	;	EDX = Signal return (Response) byte address
	;	      (virtual address in user's memory space)
	; OUTPUT ->
	;	AL = Timer event number	(1 to 255) (max. value = 16)
	;	IF BH Input = 0 & CF = 0 & AL = 0 -> 
	;	     timer event(s) has/have been stopped/finished
	;	CF = 1 & AL = 0 -> no timer setting space to set
	;	CF = 1 & AL > 0 -> timer count unit is not usable
	;
	;	NOTE: To modify a time count for a user function,
	;	      at first, current timer event must be stopped
	;	      then a new timer event (which is related with
	;	      same user function) must be started.
	;		
	;	      Signal return (response) byte may be used for 
	;	      several purposes. Kernel will put this value 
	;	      to requested address during timer interrupt,
	;	      program/user can check this value to understand
	;	      which event has been occurred and what is changed.
	;	      (Multi timer events can share same signal address)
	;	
	;	NOTE: If the process is running while the time count
	;	      is reached, kernel will put signal return (response)
	;	      byte value at requested address during timer
	;	      interrupt and the process will continue to run.
	;	      Program/process must call (jump to) it's timer event
	;	      function as required, for checking the timer event
	;	      status via signal return (response) byte address. 
	;
	;	      If the process is not running (waiting or sleeping
	;	      or released) while the time count is reached,
	;	      it is restarted from where it left, to ensure
	;	      proper multi media (video, audio, clock, timer)
	;	      functionality.
	;
	;	      (It is better to use 'syswait' or 'syssleep',
	;	      or 'sysrele' system call just after the timer
	;	      function. Otherwise, timer events may block other
	;	      processes which are not using timer events.)  	 		 			 		 	
	;	     	      		 			
	; Timer Event Structure: (max. 16 timer events, 16*16 bytes)
	;       Owner:	        resb 1 ; 0 = free
	;		  	       ;>0 = process number (u.uno)
	;	Reserved:	resb 1 ; 0
	;	Interrupt:      resb 1 ; 0 = Timer interrupt (or none)
	;		   	       ; 1 = Real Time Clock interrupt 
	;	Response:       resb 1 ; 0 to 255, signal return value
	;	Count Limit:	resd 1 ; count of ticks (total/set)
	;	Current Count: 	resd 1 ; count of ticks (current)
	;	Response Addr:  resd 1 ; response byte (pointer) address
	;

	cmp	bh, 2
        je      short systimer_5  ; only 18.2 ticks per second is usable
				  ; 10 milliseconds (100 Hertz) timer 
				  ; will be set later (18/05/2016)
        ja      short systimer_6 

	and	bh, bh
        jz      systimer_9        ; stop timer event(s)

	; bh = 1 (timer interrupt, 18.2 Hz, IBM PC/AT ROMBIOS default)

	mov	al, 10 ; (*)

systimer_0:
	mov	bh, 16
	;
	cmp	[timer_events], bh ; 16 ; 07/06/2016
	jnb 	short systimer_3  ; max. 16 timer events
	;
	push	eax ; (*)

	mov	edi, timer_set  ; beginning address of timer events
				; setting space
	xor	al, al ; 0
systimer_1:
	inc	al
	cmp	byte [edi], 0 	; is it free space ?
	jna	short systimer_7 ; yes
	dec	bh
	jz	short systimer_2
	add	edi, 16
	jmp	short  systimer_1 ; next event space

systimer_2:
	pop	eax ; (*) discard
systimer_3:
	mov	byte [u.r0], 0
systimer_4:
        mov     dword [u.error], ERR_MISC
                                ; one of miscellaneous/other errors
	jmp	error ; cf -> 1

systimer_5:
	mov	[u.r0], bh ; Time count unit (=2 or >3)
	jmp	short systimer_4 ; 07/06/2016

systimer_6:
	cmp	bh, 3
        ja      short systimer_5  ; undefined time count unit

	; bh = 3
	; timer event via real time clock interrupt
	; interrupt/update frequency: 1 Hz (1 tick per second)
	
	mov	al, 182 ; (*) ; 18.2 * 10
        jmp     short systimer_0

systimer_7:
	mov	[u.r0], al ; timer event number
	;
	; edi = address of empty timer event area
	mov	al, [u.uno]
	cli 	; disable interrupts 
	stosb	; process number
	shl	al, 1
	mov	esi, eax
	sub	al, al
	stosb 	; reserved (=0)
	mov	al, 1 ; this is for real time clock interrupt
	stosb	; interrupt type
	mov	al, bl ; Signal return (Response) value
	stosb  ; response byte
	pop	eax ; (*) ; 10 or 182
	mov	ebx, edx ; virtual address for response/signal byte
	mul	ecx
	; (eax = 10 * count of 18.2 Hz timer ticks)
	; (count down step = 10)
	stosd  ; count limit (reset value)
	stosd  ; current count value
	; ebx = virtual address
	; [u.pgdir] = page directory's physical address
	call	get_physical_addr
	jc	short systimer_8 ; 07/06/2016
	; eax = physical address of the virtual address in user's space
	stosd	; response address (physical)
	inc	byte [timer_events] ; 07/06/201
	sti 	; enable interrupts
	jmp	sysret

systimer_8:
	; 10/06/2016
	; 07/06/2016
	sub	al, al ; 0
	mov	[edi-12], al ; clear process nummber (free timer event)
	;mov	dword [edi], eax ; 0
	sti
	mov	[u.r0], al ; 0
	jmp	error

systimer_9:
	; 10/06/2016
	; 07/06/2016
	sub	al, al
	mov	byte [u.r0], al ; 0
	cmp     byte [timer_events], al ;  0
	jna	short systimer_12

	; Note: ecx and edx are undefined here
	;	(for stop timer function)

	mov	esi, timer_set  ; beginning address of timer events
				; setting space	 
	mov	al, [u.uno]
	
	mov	bh, 16

	or	bl, bl
	jnz	short systimer_15

	; clear timer event areas belong to current process
	; (for stopping all timer events belong to current process) 
	cli 	; disable interrupts
systimer_10:
	; 10/06/2016
	; 07/06/2016 	
	mov	ah, [esi]
	or	ah, ah ; 0 ?
	jz	short systimer_11
	cmp	ah, al ; is the process ID (owner) same ?
        jne     short systimer_11 ; no

	;mov	byte [esi], 0
	mov	word [esi], 0 ; clear
	;mov	dword [esi+12], 0 ; clear

	dec	byte [timer_events]
	jz	short systimer_12

systimer_11:
	dec	bh
	jz	short systimer_12
	add	esi, 16
	jmp	short systimer_10

systimer_12:
	movzx	esi, byte [u.uno]
	or	bl, bl ; all timer events or one timer event ?
	jz	short systimer_13
	mov	bl, [esi+p.timer-1]
	and	bl, bl	; previous number of timer events for the process
	jz	short systimer_14
	dec	bl  ; previous number of timer events for the process - 1
systimer_13:
	mov	[esi+p.timer-1], bl ; 0 ; no timer events for process
systimer_14:
	sti	; enable interrupts
	jmp	sysret

systimer_15:
	cmp	bl, bh ; 16
        ja      systimer_4      ; max. 16 timer events !
	;
	mov	dl, bl
	dec	dl  ; 16 -> 15 ... 1 -> 0 
	shl	dl, 4 ; * 16
	movzx	edi, dl
	add	edi, esi ; timer_set 
	
	cmp	al, [edi] ; process number
        jne     systimer_4
	
	; same process ID
	cli	; disable interrupts
 	; 10/06/2016
	;mov	byte [esi], 0 
	mov	word [esi], 0 ; clear
	;mov	dword [esi+12], 0 ; clear
	dec	byte [timer_events]
	jmp	short systimer_12

sysmdate: ; < change the modification time of a file >
	; 16/05/2015 (Retro UNIX 386 v1 - Beginning)
	; 03/06/2013 - 02/08/2013 (Retro UNIX 8086 v1)
	;
	; 'sysmdate' is given a file name. It gets inode of this 
	; file into core. The user is checked if he is the owner 
	; or super user. If he is neither an error occurs.
	; 'setimod' is then called to set the i-node modification
	; byte and the modification time, but the modification time
	; is overwritten by whatever get put on the stack during
	; a 'systime' system call. This calls are restricted to
	; the super user.		
	;
	; Calling sequence:
	;	sysmdate; name
	; Arguments:
	;	name - points to the name of file
	; Inputs: (arguments)
	; Outputs: -
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification: 
	;	 The user/application program puts address 
	;	 of the file name in BX register 
	;	 as 'sysmdate' system call argument.
	;
; / change the modification time of a file
		; jsr r0,arg; u.namep / point u.namep to the file name
        mov	[u.namep], ebx
	call	namei
		; jsr r0,namei / get its i-number
        jc	fnotfound ; file not found !
	;jc	error       
		; br error2 / no, such file
	call	iget
		; jsr r0,iget / get i-node into core
	mov	al, [u.uid]
	cmp	al, [i.uid]
        	; cmpb u.uid,i.uid / is user same as owner
	je	short mdate_1
        	; beq 1f / yes
	and	al, al
		; tstb u.uid / no, is user the super user
	;jnz	error
		; bne error2 / no, error
	jz	short mdate_1
	mov	dword [u.error], ERR_FILE_ACCESS ; permission denied !
	jmp	error
mdate_1: ;1:
	call	setimod
        	; jsr r0,setimod / fill in modification data,
		               ; / time etc.
	mov	esi, p_time
	mov	edi, i.mtim
	movsd
		; mov 4(sp),i.mtim / move present time to
        	; mov 2(sp),i.mtim+2 / modification time
        jmp	sysret
		; br sysret2

sysvideo: ; VIDEO DATA TRANSFER FUNCTIONS
	; 11/07/2016
	; 13/06/2016
	; 16/05/2016 - TRDOS 386 (TRDOS v2.0)
	;
	;
	; VIDEO DATA TRANSFER FUNCTIONS:
	;
	; Inputs:
	;	BH = 0 = VIDEO BIOS Mode 3, tty/text mode data transfers
	;	     BL = 
	;		Bits 0&1, Transfer direction
	;	     	 	0 - System to system
	;			1 - User to system
	;			2 - System to user
	;			3 - User to user
	;		Bits 2&3, Transfer Type
	;			0 - Display page transfer	
	;	     		1 - Display page window transfer
	;	     		2 - Frame/Viewport/Window address transfer
	;			3 - Window handle transfer		
	;
	;	     /// BL = 0 -> System to system (display page) transfer
	;		 CL = Source page 
	;		 DL = Destination page
	;	     /// BL = 1&2 -> user to system & system to user transfer
	;		 ECX = User buffer
	;		 DL = Video page
	;	     /// BL = 5&6 -> user to system, system to user transfer 
	;		(window in current display page and in current mode)	 	 		
	;		 ESI = User's buffer address
	;		 ECX Low 16 bits = Top left column (X1 position)
	;		 ECX High 16 bits = Top row (Y1 position)
	;		 EDX Low 16 bits = Bottom right column (X2 position)
	;		 EDX High 16 bits = Bottom row (Y2 position)
        ;                If BL = 5 ->
	;		 EDI = Swap address (in user's memory space)
	;		 (If swap address > 0, previous content of the window
	;		 will be saved into swap area in user's memory space)		
	;	     /// BL = 4 -> system to system transfer 
	;		 ESI = System's source buffer (video page) address
	;		 ECX Low 16 bits = Top left column (X1 position)
	;		 ECX High 16 bits = Top row (Y1 position)
	;		 EDX Low 16 bits = Bottom right column (X2 position)
	;		 EDX High 16 bits = Bottom row (Y2 position)
	;		 EDI = System's destination buffer (video page) address
	;
	;	BH = 1 = CGA Graphics (0B8000h) data transfers
	;	     BL = 
	;	     	0 = Fill color (color in CL] (32K)
	;		1 = User to system display page transfer
	;		2 = System to user display page transfer
	;		3 = NOT bits in window (ECX, EDX)
	;		4 = Window copy (system to system)	
	;	     	5 = User to system window transfer
	;	     	6 = System to user window transfer
	;		7 = AND display page bytes with CL
	;		8 = OR display page bytes with CL
	;		9 = XOR display page bytes with CL
	;
	;	     /// BL = 0 -> Fill color  (all screen pixels)
	;		 CL = Color value 
	;	     /// BL = 1&2 -> user to system & system to user transfer
	;		 ECX = User buffer
	;	     /// BL = 5&6 -> user to system, system to user transfer 
	;		(window in current display page and in current mode)	 	 		
	;		 ESI = User's buffer address
	;		 ECX Low 16 bits = Top left column (X1 position)
	;		 ECX High 16 bits = Top row (Y1 position)
	;		 EDX Low 16 bits = Bottom right column (X2 position)
	;		 EDX High 16 bits = Bottom row (Y2 position)
        ;	     /// BL = 4 -> system to system (window) transfer 
	;		 ESI = System's source buffer (video page) address
	;		 ECX Low 16 bits = Top left column (X1 position)
	;		 ECX High 16 bits = Top row (Y1 position)
	;		 EDX Low 16 bits = Bottom right column (X2 position)
	;		 EDX High 16 bits = Bottom row (Y2 position)
	;		 EDI = System's destination buffer (video page) address
	;	     /// BL = 3 -> NOT byte in display page/memory 
	;		 ECX Low 16 bits = Top left column (X1 position)
	;		 ECX High 16 bits = Top row (Y1 position)
	;		 EDX Low 16 bits = Bottom right column (X2 position)
	;		 EDX High 16 bits = Bottom row (Y2 position)
	;
	;	BH = 2 = VGA Graphics (0A0000h) data transfers
	;	     BL = 
	;	     	x0h = Fill color (color in CL] (64K)
	;		x1h = User to system display page transfer
	;		x2h = System to user display page transfer
	;		x3h = NOT bits in window (ECX, EDX)
	;		x4h = Window copy (system to system)	
	;	     	x5h = User to system window transfer
	;	     	x6h = System to user window transfer
	;		x7h = AND display page bytes with CL
	;		x8h = OR display page bytes with CL
	;		x9h = XOR display page bytes with CL
	;		x = 0 -> screen width = 320
	;		x = 1 -> screen width = 640
	;		x = 2 -> screen width = 800
	;
	;	     /// BL = 0 -> Fill color  (all screen pixels)
	;		 CL = Color value 
	;	     /// BL = 1&2 -> user to system & system to user transfer
	;		 ECX = User buffer
	;	     /// BL = 5&6 -> user to system, system to user transfer 
	;		(window in current display page and in current mode)	 	 		
	;		 ESI = User's buffer address
	;		 ECX Low 16 bits = Top left column (X1 position)
	;		 ECX High 16 bits = Top row (Y1 position)
	;		 EDX Low 16 bits = Bottom right column (X2 position)
	;		 EDX High 16 bits = Bottom row (Y2 position)
       	;	     /// BL = 4 -> system to system (window) transfer 
	;		 ESI = System's source buffer (video page) address
	;		 ECX Low 16 bits = Top left column (X1 position)
	;		 ECX High 16 bits = Top row (Y1 position)
	;		 EDX Low 16 bits = Bottom right column (X2 position)
	;		 EDX High 16 bits = Bottom row (Y2 position)
	;		 EDI = System's destination buffer (video page) address
	;	     /// BL = 3 -> NOT byte in display page/memory 
	;		 ECX Low 16 bits = Top left column (X1 position)
	;		 ECX High 16 bits = Top row (Y1 position)
	;		 EDX Low 16 bits = Bottom right column (X2 position)
	;		 EDX High 16 bits = Bottom row (Y2 position)
	;
	;	BH = 3 = Super VGA, LINEAR FRAME BUFFER data transfers
	;	     BL = 
	;	     	0 = Fill color (color in ECX] (Frame buffer size)
	;		1 = User to system display page transfer
	;		2 = System to user display page transfer
	;		3 = NOT bits in window (ECX, EDX)
	;		4 = Window copy (system to system)	
	;	     	5 = User to system window transfer
	;	     	6 = System to user window transfer
	;		7 = AND display page bytes with ECX
	;		8 = OR display page bytes with ECX
	;		9 = XOR display page bytes with ECX
	;
	;	     /// BL = 0 -> Fill color  (all screen pixels)
	;		 CL = Color value 
	;	     /// BL = 1&2 -> user to system & system to user transfer
	;		 ECX = User buffer
	;	     /// BL = 5&6 -> user to system, system to user transfer 
	;		(window in current display page and in current mode)	 	 		
	;		 ESI = User's buffer address
	;		 ECX Low 16 bits = Top left column (X1 position)
	;		 ECX High 16 bits = Top row (Y1 position)
	;		 EDX Low 16 bits = Bottom right column (X2 position)
	;		 EDX High 16 bits = Bottom row (Y2 position)
	;	     /// BL = 4 -> system to system (window) transfer 
	;		 ESI = System's source buffer (video page) address
	;		 ECX Low 16 bits = Top left column (X1 position)
	;		 ECX High 16 bits = Top row (Y1 position)
	;		 EDX Low 16 bits = Bottom right column (X2 position)
	;		 EDX High 16 bits = Bottom row (Y2 position)
	;		 EDI = System's destination buffer (video page) address
	;	     /// BL = 3 -> NOT byte in display page/memory 
	;		 ECX Low 16 bits = Top left column (X1 position)
	;		 ECX High 16 bits = Top row (Y1 position)
	;		 EDX Low 16 bits = Bottom right column (X2 position)
	;		 EDX High 16 bits = Bottom row (Y2 position)
	;
	; Outputs:
	;	EAX = transfer/byte count
	;
	;	NOTE: If the source or destination address passes out of
	;	video pages (display memory limits), data will not be transferred
	;	and EAX will return as 0.
	;
	;
	; DIRECT (STANDARD VGA/CGA) DISPLAY MEMORY ACCESS FUNCTIONS:
	;
	;	BH = 4 = CGA direct video memory (0B8000h, 32K) access
	;		Page directory & page tables of the user's
	;		program will be updated to direct access to
	;		0B8000h (32K) video (CGA, color) memory; if
	;		there is not a permission  conflict or lock!  
	;	        (User's program/process will have permision to
	;		access locked display memory if the owner is
	;		it's parent.)
        ;
	;	    Screen width = 320	
	;
	;	BH = 5 = VGA direct video memory (0A0000h, 64K) access
	;		Page directory & page tables of the user's
	;		program will be updated to direct access to
	;		0A0000h (64K) video (VGA) memory; if there is not
	;		a permission conflict or lock!  
	;	        (User's program/process will have permision to
	;		access locked display memory if the owner is
	;		it's parent.)
	;		
	;	    BL = Screen width (320, 640, 800) 
	;			
	; Outputs:
	;	EAX = Display mmory address for direct access
	;	      0A0000h for VGA, 0B8000h for CGA	
	;	(Display memory size: 32K for CGA, 64K for VGA)
	; 	EAX = 0 if display page access permission has been denied. 
	;	      (Locked!) 	
	;	      	 	
	; LINEAR FRAME BUFFER ACCESS FUNCTIONS:
	;
	;	BH = 6 = Linear Frame Buffer direct video memory access
	;
	;		Page directory & page tables of the user's
	;		program will be updated to direct access to
	;		the configured LFB (Linear Frame Buffer) address,
	;		if there is not a permission conflict or lock!  
	;	        (User's program/process will have permision to
	;		access locked display memory if the owner is
	;		it's parent.)
	;			
	;		Return: EAX = Linear Frame Buffer address
	;			EDX = Frame Buffer Size in bytes	
	;	
	;	BH = 7 = Get Linear Frame Buffer info (for current mode)
	;		
	;		Return:
	;		EAX = Frame Buffer Address (0 = is not in use)
	;		EDX = Frame Buffer Size in bytes
	;		BL = Current Video Mode
	;		     BL = 0FFh -> Super VGA (Extended VGA)
	;		     If BL = 0FFh, 
        ;                       BH = 0 = 16 colors
	;			BH = 1 = 256 colors
	;			BH = 2 = 66536 colors
	;			BH = 3 = 24 bits TRUE (16M) colors
	;			BH = 4 = 32 bits TRUE (16M) colors
	;		ECX = Pixel resolution
	;		      CX = Width (640, 800, 1024, 1366, 1920)
	;		      High 16 bits of ECX = Height  
	;
	;	NOTE: Each process will have it's own frame buffer
	;	      address and resolution parameters in 'u' area.
	;	      Then, if the current frame buffer & resolution
	;	      is different, frame buffer r/w functions
	;	      will use scale factor to convert process's
        ;             pixel coordinates to actual screen coordinates.                            
	;	      resolution -> dimensional scale
	;	      color size -> color scale
	;	     * RGB (TRUE) colors to 256 colors conversion:	
        ;             TRUE Colors -> 8,8,8 (R,G,B; byte 0 is R)            
	;	      256 colors -> 2,2,2,2 (R,G,B,L; bit 0&1 is R)
	; 		  bit 6&7 -> luminosity base level (0,1,2,3)
	;		  bit 4&5 -> blue level (0,1,2,3)
	;		  bit 2%3 -> green level (0,1,2,3)
	;		  bit 0&1 -> red level (0,1,2,3)
	;	      Example: total red level : luminosity + red level   				
	;	      Luminosity base level: 0 -> 16
	;		 		     1 -> 32
	;				     2 -> 64
	;				     3 -> 128
	;	      Color level:	
	;				    0 -> 0
	;				    1 -> luminosity level
	;				    2 -> luminosity level + 64
	;				    3 -> 255
	;	     Luminosity base level = min (R,G,B)
	;			if it is <16, it will be set to 16
	;	     Color levels: Color values are fixed to (nearest) 
	;		   one of all possible set level (step) values
	;		   (according to luminosity base level); then
	;		   color levels are set to R-L, G-L, B-L.
	;	 For example: If luminosity base level is 32
	;		  all possible set values are 0, 32, 96, 255. 	 			
	;
	;	    * RGB (TRUE) colors to 16 colors conversion:
	;	    16 colors: R, B,G, L bits (4 bits)
	;	    	   If any one of R,G,B >= 128 L = 1 		     	
 	;		   If max. value of (R,G,B) >= 32, it is 1
	;		      else all color bits (R&G&B&L) are 0
	;		   If the second value >= max. value / 2
	;		      it is 1
	;		   If third value value >= max. value / 2
	;		      it is 1
	;	    Example: R = 132, G = 64, B = 78
	;		     L = 1, R = 1
	;		     G < 66 --> G = 0
	;		     B >= 66 --> B = 1	 		 	  								 	 					

	; 16/05/2016
	xor	eax, eax
	mov	[u.r0], eax

	and	bh, bh
	jnz	sysvideo_13 ; 11/07/2016
	
	; Video mode 0, 80*25 text mode, CGA 16 colors  ; [CRT_MODE] = 3
	mov	bh, bl
	shr	bh, 2
	and	bh, bh
        jnz     sysvideo_4
	mov	edi, 0B8000h
	and	dl, dl
	jz	short sysvideo_1
	cmp	dl, 7
	ja	sysret
sysvideo_0:
	add	edi, 80*25*2
	dec	dl
	jnz	short sysvideo_0	
sysvideo_1:	
	and	bl, 3
	jnz	short sysvideo_2
	cmp	cl, 7
	ja	sysret
	; system to system video/display page transfer (mode 0)
	mov	esi, 0B8000h
	movzx	eax, cl
	mov	edx, 80*25*2
	mul	edx
	add	esi, eax
	mov	ecx, (80*25*2)
	mov	[u.r0], ecx
	shr	cx, 2 ; /4
	rep	movsd
	jmp	sysret	
sysvideo_2:  
	cmp	bl, 2
        ja      sysret
	jb	short sysvideo_3
	; system to user video/display page transfer (mode 0)
	mov	esi, edi
	mov	edi, ecx ; user buffer
	mov	ecx, 80*25*2
	call	transfer_to_user_buffer ; fast transfer
	jc	sysret
	mov	[u.r0], ecx
	jmp	sysret 	
sysvideo_3: 
	; user to system video/display page transfer (mode 0)	
	mov	esi, ecx ; user buffer
	; edi = video page address
	mov	ecx, 80*25*2
	call	transfer_from_user_buffer ; fast transfer
	jc	sysret
	mov	[u.r0], ecx		
	jmp	sysret
sysvideo_4:
	and	bl, 3
        jnz     sysvideo_9
	cmp	cl, 7
	ja	sysret
	; system to system video/display page window transfer (mode 0)
	cmp	esi, 0B8000h
	jb	sysret
	cmp	esi, 0B8000h+(80*25*2*8)
	jnb	sysret
	cmp	edi, 0B8000h
	jb	sysret
        cmp     edi, 0B8000h+(80*25*2*8)
	jnb	sysret
	;
	push	ecx
	push	edx
	movzx	eax, cx ; top left column
	push	eax
	shr	ecx, 16 ; top row
	mov	ax, 80*2 ; 80 colums, 160 bytes per row
	mul	ecx
	add	esi, eax
	add	edi, eax
	pop	eax
	shl	ax, 1 ; *2 	
	add	esi, eax
	add	edi, eax
	pop	edx
	pop	ecx
	mov	eax, 0B8000h+(80*25*2*8)
	cmp	esi, eax
	jnb	sysret
	cmp	esi, eax
	jnb	sysret	

	push	esi ; ****
	push	edi ; ***
	push	edx ; **
	push	ecx ; *
	shr	ecx, 16 ; top row
	shr	edx, 16 ; bottom row
	cmp	ecx, 24 ; max. 25 rows
	ja	short sysvideo_6
	cmp	edx, 24 ; max. 25 rows
	ja	short sysvideo_6		
	sub	dl, cl
	jc	short sysvideo_6
	push	eax ; *****
	mov	ebx, edx ; row count - 1
	mov	eax, 80*2
	mul	eax
	add	esi, eax
	add	edi, eax
	pop	eax ; *****
	cmp	esi, eax
	ja	short sysvideo_6
	cmp	edi, eax
	ja	short sysvideo_6
	pop	ecx ; *
	pop	edx ; **
	and	ecx, 0FFFFh
	and	edx, 0FFFFh
	cmp	ecx, 79 ; max. 80 columns
	ja	short sysvideo_7
	cmp	edx, 79 ; max. 80 columns
	ja	short sysvideo_7
	sub	dl, cl
	jna	short sysvideo_7
	; edx = column count (width) - 1
	shl	dl, 1
	add	esi, edx
	add	edi, edx
	cmp	esi, eax
	ja	short sysvideo_7
	cmp	edi, eax
	ja	short sysvideo_7
	pop	edi ; ***
	pop	esi ; ****
	inc	bl
	inc	dl ; column count
	mov	bh, dl
	shl	dl, 1
	mov	eax, 80*2
	sub	al, dl ; (80 - columns) * 2
sysvideo_5:	
	mov	cl, bh
	add	[u.r0], edx
	rep	movsw
	add	esi, eax ; next row
	add	edi, eax ; next row
	dec	bl
	jnz	short sysvideo_5
	jmp	sysret

sysvideo_6:
	pop	ecx ; *
	pop	edx ; **
sysvideo_7:	
	pop	edi ; ***
	pop	esi ; ****
	jmp	sysret

sysvideo_9:  
	cmp	bl, 2
        ja      sysret

	push	esi ; ****
	push	edi ; ***
	push	edx ; **
	push	ecx ; *

	shr	ecx, 16 ; top row
	shr	edx, 16 ; bottom row
	cmp	ecx, 24 ; max. 25 rows
	ja	short sysvideo_6
	cmp	edx, 24 ; max. 25 rows
	ja	short sysvideo_6		
	sub	dl, cl
	jc	short sysvideo_6

	mov	ch, cl ; top row
	mov	cl, [ACTIVE_PAGE]
	mov	edi, 80*25*2
	shl	edi, cl 
	add	edi, 0B8000h - 80*25*2

	mov	bh, dl ; row count - 1
	mov	dl, ch ; top row
	mov	eax, 80*2
	mul	edx
	add	edi, eax

	pop	ecx ; *
	pop	edx ; **
	and	ecx, 0FFFFh
	and	edx, 0FFFFh
	cmp	ecx, 79 ; max. 80 columns
	ja	short sysvideo_7
	cmp	edx, 79 ; max. 80 columns
	ja	short sysvideo_7
	
	sub	dl, cl
	jna	short sysvideo_7
	
	movzx	eax, cl ; left column
	shl	al, 1  ; column * 2
	add	edi, eax

	inc	dl ; column count
	shl	dl, 1
	mov	cl, dl ; column count * 2
	mov	dl, 80*2
	pop	eax ; *** (swap address)
	pop	esi ; ****
	inc	bh
	
	;mov	edx, 80*2
	mov	dl, 80*2
	;
	cmp	bl, 1
	ja	short sysvideo_11

	; user to system video/display page window transfer (mode 0)	
	and	eax, eax ; swap address
	jz	short sysvideo_10 ; no window swap
	; save previous window content in user's buffer (swap address)
	push	esi ; user buffer
	push	edi ; beginning address of the window
	mov	esi, edi
	mov	edi, eax
	call	transfer_to_user_buffer ; fast transfer
	pop	edi
	pop	esi
	jc	sysret
sysvideo_10: 
	; user to system video/display page window transfer (mode 0)	
	; esi =	user buffer
	call	transfer_from_user_buffer ; fast transfer
	jc	sysret
	add	[u.r0], ecx
	add	edi, edx ; next row
	add	esi, ecx
	dec	bh
	jnz	short sysvideo_10
	jmp	sysret
	
sysvideo_11:
	; system to user video/display page window transfer (mode 0)
	xchg	edi, esi
sysvideo_12:
	; esi = beginning address of the window
	; edi =	user buffer	
	call	transfer_to_user_buffer ; fast transfer
	jc	sysret
	add	[u.r0], ecx
	add	esi, edx ; next row
	add	edi, ecx
	dec	bh
	jnz	short sysvideo_12
	jmp	sysret

sysvideo_13:
	cmp	bh, 1
        ja      sysvideo_38
	; BH = 1 = CGA Graphics (0B8000h) data transfers

	and	bl, bl
	jnz	short sysvideo_14

	; BL =	0 = Fill color (color in CL] (32K)

	mov	al, cl
	mov	ecx, 32768
	mov	[u.r0], cx
	mov	edi, 0B8000h
	rep	stosd
	jmp	sysret

sysvideo_14:
	cmp	bl, 1
	ja	short sysvideo_16

	mov	esi, ecx ; user buffer
	; BL = 1 = user to system video/display page transfer
sysvideo_15:	
	mov	edi, 0B8000h
	; edi = video page address
	mov	ecx, 32768
	call	transfer_from_user_buffer ; fast transfer
	jc	sysret ; [u.r0] = 0
	mov	[u.r0], cx		
	jmp	sysret

sysvideo_16:
	cmp	bl, 2	
	ja	short sysvideo_18

	mov	edi, ecx ; user buffer
	; BL = 2 = system to user video/display page transfer
sysvideo_17:	
	mov	esi, 0B8000h
	mov	ecx, 32768
	call	transfer_to_user_buffer ; fast transfer
	jc	sysret ; [u.r0] = 0
	mov	[u.r0], cx
	jmp	sysret 	

sysvideo_18:
	cmp	bl, 3
	ja	short sysvideo_23

	; BL = 3 = NOT bits in window (ECX, EDX)

	mov	edi, 0B8000h
	mov	esi, edi
	
	cmp	edx, ecx ; bottom-right > top-left ?
	ja	short sysvideo_20 ; window
	; full screen (update)
	mov	ecx, 32768
	mov	[u.r0], cx
sysvideo_19:
	not	byte [esi] ; NOT operation
	inc	esi
	loop	sysvideo_19
	jmp	sysret
sysvideo_20:
	movzx	eax, dx ; bottom right column
	sub	ax, cx  ; - top left column
        jb      sysret ; invalid
	inc	ax ; same column no == 1 column	 
	push	eax ; byte count per window row
	push	edx
	mov	ebx, 320 ; screen width
	mov	eax, ecx
	shr	eax, 16  ; top row
	mul	ebx
	mov	dx, cx ; top left column
	add	eax, edx
	add	esi, eax ; start address 
	pop	ecx ; edx
	mov	eax, ecx
	shr	eax, 16 ; bottom row
	mul	ebx
	mov	dx, cx ; bottom right column
	add	eax, edx
	add	edi, eax ; stop address (included)
	pop	edx ; byte count per window row
	cmp	edi, 0BFFFFh
	ja	sysret	
	push	esi
	dec	esi
sysvideo_21:
	mov	ecx, edx
sysvideo_22:
	inc	esi
	not	byte [esi]
	loop	sysvideo_22
	add	esi, ebx ; bytes per screen row
	;
	cmp	esi, edi ; stop address (included in loop)
	jna	short sysvideo_21
	pop	esi
	sub	edi, esi
	mov	[u.r0], di
	jmp	sysret

sysvideo_23:
	cmp	bl, 4
        ja      sysvideo_26

	; BL = 4 = window copy (system to system)

	mov	eax, 0B8000h
	cmp	esi, eax
	jb	sysret
	cmp	edi, eax
	jb	sysret
	add	ax, 7FFFh ; 32767
	cmp	esi, eax
	ja	sysret
	cmp	edi, eax
	ja	sysret
	
	cmp	edx, ecx ; bottom-right > top-left ?
	ja	short sysvideo_24 ; window
	; full screen copy
	mov	ecx, eax
	sub	ecx, edi
	inc	cx
	mov	[u.r0], cx
	rep	movsb
	jmp	sysret
sysvideo_24:
	movzx	eax, dx ; bottom right column
	sub	ax, cx  ; - top left column
        jb      sysret ; invalid
	inc	ax ; same column no == 1 column	 
	push	eax ; byte count per window row
	;
	push	edx
	mov	ebx, 320 ; screen width
	mov	eax, ecx
	shr	eax, 16	; top row
	mul	ebx
	mov	dx, cx ; top left column
	add	eax, edx
	add	edi, eax ; start address 
	add	esi, eax
	pop	ecx ; edx
	mov	eax, ecx
	shr	eax, 16 ; bottom row
	mul	ebx
	mov	dx, cx ; bottom right column
	add	eax, edx
	pop	edx ; byte count per window row
	add	eax, 0B8000h
	cmp	eax, 0BFFFFh
	ja	sysret
	push	edi ; start address 
	push	eax ; stop address (included)
sysvideo_25:
	mov	ecx, edx
	rep	movsb
	dec	edi
	dec	esi
	add	edi, ebx ; bytes per screen row
	add	esi, ebx
	;
	cmp	edi, [esp] ; stop addr(included in loop)
	jna	short sysvideo_25
	pop	ebx ; stop address
	pop	edi ; start address
	sub	ebx, edi
	inc	bx
	mov	[u.r0], bx	
	jmp	sysret

sysvideo_26:
	cmp	bl, 5
        ja      sysvideo_29

	; BL = 5 = window copy (user to system)

	mov	eax, 0B8000h
	cmp	edi, eax
	jb	sysret
	add	ax, 7FFFh ; 32767
	cmp	edi, eax
	ja	sysret

	; esi = user buffer (in user's memory space)
	cmp	edx, ecx ; bottom-right > top-left ?
        jna     sysvideo_15 ; full screen copy

	movzx	eax, dx ; bottom right column
	sub	ax, cx  ; - top left column
        jb      sysret ; invalid
	inc	ax ; same column no == 1 column	 
	push	eax ; byte count per window row

	push	edx
	mov	ebx, 320 ; screen width
	mov	eax, ecx
	shr	eax, 16	; top row
	mul	ebx
	mov	dx, cx ; top left column
	add	eax, edx
	add	edi, eax ; start address 
	pop	ecx ; edx
	mov	eax, ecx
	shr	eax, 16 ; bottom row
	mul	ebx
	mov	dx, cx ; bottom right column
	add	eax, edx
	pop	edx ; byte count per window row
	add	eax, 0B8000h
	cmp	eax, 0BFFFFh
	ja	sysret
	push	edi ; start address 
	push	eax ; stop address (included)
sysvideo_27:
	mov	ecx, edx ; byte count
	; user to system video/display page window transfer
	; esi =	user buffer
	call	transfer_from_user_buffer ; fast transfer
	jc	short sysvideo_28
	add	[u.r0], ecx
	add	edi, ebx ; next row
	add	esi, ecx
	cmp	edi, [esp] ; stop addr(included in loop)
	jna	short sysvideo_27
	pop	ebx ; stop address
	pop	edi ; start address
	sub	ebx, edi
	inc	bx
	mov	[u.r0], bx	
	jmp	sysret
sysvideo_28:
	pop	eax
	pop	edx
	jmp	sysret

sysvideo_29:
	cmp	bl, 6
        ja      sysvideo_32

	; BL = 6 = window copy (system to user)

	mov	edi, esi ; user buffer

	mov	eax, 0B8000h
	cmp	esi, eax
	jb	sysret
	add	ax, 7FFFh ; 32767
	cmp	esi, eax
	ja	sysret

	; edi = user buffer (in user's memory space)
	cmp	edx, ecx ; bottom-right > top-left ?
        jna     sysvideo_17 ; full screen copy

	movzx	eax, dx ; bottom right column
	sub	ax, cx  ; - top left column
        jb      sysret ; invalid
	inc	ax ; same column no == 1 column	 
	push	eax ; byte count per window row

	push	edx
	mov	ebx, 320 ; screen width
	mov	eax, ecx
	shr	eax, 16	; top row
	mul	ebx
	mov	dx, cx ; top left column
	add	eax, edx
	add	esi, eax ; start address 
	pop	ecx ; edx
	mov	eax, ecx
	shr	eax, 16 ; bottom row
	mul	ebx
	mov	dx, cx ; bottom right column
	add	eax, edx
	pop	edx ; byte count per window row
	add	eax, 0B8000h
	cmp	eax, 0BFFFFh
	ja	sysret
	push	esi ; start address 
	push	eax ; stop address (included)
sysvideo_30:
	mov	ecx, edx ; byte count
	; user to system video/display page window transfer
	; esi =	user buffer
	call	transfer_to_user_buffer ; fast transfer
	jc	short sysvideo_31
	add	[u.r0], ecx
	add	edi, ebx ; next row
	add	esi, ecx
	cmp	edi, [esp] ; stop addr(included in loop)
	jna	short sysvideo_30
	pop	ebx ; stop address
	pop	edi ; start address
	sub	ebx, edi
	inc	bx
	mov	[u.r0], bx	
	jmp	sysret
sysvideo_31:
	pop	eax
	pop	edx
	jmp	sysret

sysvideo_32:
	cmp	bl, 7
	ja	short sysvideo_34

	; BL = 7 = AND display page bytes with CL

	mov	esi, 0B8000h
	mov	ecx, 32768
sysvideo_33:
	and	byte [esi], cl
	inc	esi
	loop	sysvideo_33

sysvideo_34:
	cmp	bl, 8
	ja	short sysvideo_36

	; BL = 8 = OR display page bytes with CL

	mov	esi, 0B8000h
	mov	ecx, 32768
sysvideo_35:
	or	byte [esi], cl
	inc	esi
	loop	sysvideo_35	

sysvideo_36:
	cmp	bl, 9
        ja      sysret ; nothing to do

	; BL = 9 = XOR display page bytes with CL

	mov	esi, 0B8000h
	mov	ecx, 32768
sysvideo_37:
	xor	byte [esi], cl
	inc	esi
	loop	sysvideo_37

sysvideo_38:
	cmp	bh, 2
        ja      sysvideo_64
	; BH = 2 = VGA Graphics (0A0000h) data transfers

	mov	ah, bl
	and	bl, 0Fh	
	shr	ah, 4
	shl	ebx, 16
	mov	bx, 320 ; 320*200, 320*240
	and	ah, ah
	jz	short sysvideo_39
	shl	bx, 1 ; 640*200, 640 * 400, 640*480
	cmp	ah, 2
	jb	short sysvideo_39 		
	ja	sysret ; invalid
	; 800*600
	add	bx, 160 ; 800
sysvideo_39:
	ror	ebx, 16
		
 	and	bl, bl
	jnz	short sysvideo_40

	; BL =	0 = Fill color (color in CL] (64K)

	mov	al, cl
	mov	ecx, 65536
	mov	[u.r0], ecx
	mov	edi, 0A0000h
	rep	stosd
	jmp	sysret

sysvideo_40:
	cmp	bl, 1
	ja	short sysvideo_42

	mov	esi, ecx ; user buffer
	; BL = 1 = user to system video/display page transfer
sysvideo_41:	
	mov	edi, 0A0000h
	; edi = video page address
	mov	ecx, 65536
	call	transfer_from_user_buffer ; fast transfer
	jc	sysret ; [u.r0] = 0
	mov	[u.r0], ecx		
	jmp	sysret

sysvideo_42:
	cmp	bl, 2	
	ja	short sysvideo_44

	mov	edi, ecx ; user buffer
	; BL = 2 = system to user video/display page transfer
sysvideo_43:	
	mov	esi, 0A0000h
	mov	ecx, 65536
	call	transfer_to_user_buffer ; fast transfer
	jc	sysret ; [u.r0] = 0
	mov	[u.r0], ecx
	jmp	sysret 	

sysvideo_44:
	cmp	bl, 3
	ja	short sysvideo_49

	; BL = 3 = NOT bits in window (ECX, EDX)

	mov	edi, 0A0000h
	mov	esi, edi
	
	cmp	edx, ecx ; bottom-right > top-left ?
	ja	short sysvideo_45 ; window
	; full screen (update)
	mov	ecx, 65536
	mov	[u.r0], ecx
sysvideo_45:
	not	byte [esi] ; NOT operation
	inc	esi
	loop	sysvideo_45
	jmp	sysret
sysvideo_46:
	movzx	eax, dx ; bottom right column
	sub	ax, cx  ; - top left column
        jb      sysret ; invalid
	inc	ax ; same column no == 1 column	 
	push	eax ; byte count per window row
	push	edx
	shr	ebx, 16 ; 320,640,800 : screen width
	mov	eax, ecx
	shr	eax, 16  ; top row
	mul	ebx
	mov	dx, cx ; top left column
	add	eax, edx
	add	esi, eax ; start address 
	pop	ecx ; edx
	mov	eax, ecx
	shr	eax, 16 ; bottom row
	mul	ebx
	mov	dx, cx ; bottom right column
	add	eax, edx
	add	edi, eax ; stop address (included)
	pop	edx ; byte count per window row
	cmp	edi, 0AFFFFh
	ja	sysret	
	push	esi
	dec	esi
sysvideo_47:
	mov	ecx, edx
sysvideo_48:
	inc	esi
	not	byte [esi]
	loop	sysvideo_48
	add	esi, ebx ; bytes per screen row
	;
	cmp	esi, edi ; stop address (included in loop)
	jna	short sysvideo_47
	pop	esi
	sub	edi, esi
	mov	[u.r0], edi
	jmp	sysret

sysvideo_49:
	cmp	bl, 4
        ja      sysvideo_52

	; BL = 4 = window copy (system to system)

	mov	eax, 0A0000h
	cmp	esi, eax
	jb	sysret
	cmp	edi, eax
	jb	sysret
	add	ax, 0FFFFh ; 65535
	cmp	esi, eax
	ja	sysret
	cmp	edi, eax
	ja	sysret
	
	cmp	edx, ecx ; bottom-right > top-left ?
	ja	short sysvideo_50 ; window
	; full screen copy
	mov	ecx, eax
	sub	ecx, edi
	inc	ecx
	mov	[u.r0], ecx
	rep	movsb
	jmp	sysret
sysvideo_50:
	movzx	eax, dx ; bottom right column
	sub	ax, cx  ; - top left column
        jb      sysret ; invalid
	inc	ax ; same column no == 1 column	 
	push	eax ; byte count per window row
	;
	push	edx
	shr	ebx, 16 ; 320,640,800 : screen width
	mov	eax, ecx
	shr	eax, 16	; top row
	mul	ebx
	mov	dx, cx ; top left column
	add	eax, edx
	add	edi, eax ; start address 
	add	esi, eax
	pop	ecx ; edx
	mov	eax, ecx
	shr	eax, 16 ; bottom row
	mul	ebx
	mov	dx, cx ; bottom right column
	add	eax, edx
	pop	edx ; byte count per window row
	add	eax, 0A0000h
	cmp	eax, 0AFFFFh
	ja	sysret
	push	edi ; start address 
	push	eax ; stop address (included)
sysvideo_51:
	mov	ecx, edx
	rep	movsb
	dec	edi
	dec	esi
	add	edi, ebx ; bytes per screen row
	add	esi, ebx
	;
	cmp	edi, [esp] ; stop addr(included in loop)
	jna	short sysvideo_51
	pop	ebx ; stop address
	pop	edi ; start address
	sub	ebx, edi
	inc	ebx
	mov	[u.r0], ebx	
	jmp	sysret

sysvideo_52:
	cmp	bl, 5
        ja      sysvideo_55

	; BL = 5 = window copy (user to system)

	mov	eax, 0A0000h
	cmp	edi, eax
	jb	sysret
	add	ax, 0FFFFh ; 65535
	cmp	edi, eax
	ja	sysret

	; esi = user buffer (in user's memory space)
	cmp	edx, ecx ; bottom-right > top-left ?
        jna     sysvideo_41 ; full screen copy

	movzx	eax, dx ; bottom right column
	sub	ax, cx  ; - top left column
        jb      sysret ; invalid
	inc	ax ; same column no == 1 column	 
	push	eax ; byte count per window row

	push	edx
	shr	ebx, 16 ; 320,640,800 : screen width
	mov	eax, ecx
	shr	eax, 16	; top row
	mul	ebx
	mov	dx, cx ; top left column
	add	eax, edx
	add	edi, eax ; start address 
	pop	ecx ; edx
	mov	eax, ecx
	shr	eax, 16 ; bottom row
	mul	ebx
	mov	dx, cx ; bottom right column
	add	eax, edx
	pop	edx ; byte count per window row
	add	eax, 0A0000h
	cmp	eax, 0AFFFFh
	ja	sysret
	push	edi ; start address 
	push	eax ; stop address (included)
sysvideo_53:
	mov	ecx, edx ; byte count
	; user to system video/display page window transfer
	; esi =	user buffer
	call	transfer_from_user_buffer ; fast transfer
	jc	short sysvideo_54
	add	[u.r0], ecx
	add	edi, ebx ; next row
	add	esi, ecx
	cmp	edi, [esp] ; stop addr(included in loop)
	jna	short sysvideo_53
	pop	ebx ; stop address
	pop	edi ; start address
	sub	ebx, edi
	inc	ebx
	mov	[u.r0], ebx	
	jmp	sysret
sysvideo_54:
	pop	eax
	pop	edx
	jmp	sysret

sysvideo_55:
	cmp	bl, 6
        ja      sysvideo_58

	; BL = 6 = window copy (system to user)

	mov	edi, esi ; user buffer

	mov	eax, 0A0000h
	cmp	esi, eax
	jb	sysret
	add	ax, 0FFFFh ; 65535
	cmp	esi, eax
	ja	sysret

	; edi = user buffer (in user's memory space)
	cmp	edx, ecx ; bottom-right > top-left ?
        jna     sysvideo_17 ; full screen copy

	movzx	eax, dx ; bottom right column
	sub	ax, cx  ; - top left column
        jb      sysret ; invalid
	inc	ax ; same column no == 1 column	 
	push	eax ; byte count per window row

	push	edx
	shr	ebx, 16 ; 320, 640,800 ; screen width
	mov	eax, ecx
	shr	eax, 16	; top row
	mul	ebx
	mov	dx, cx ; top left column
	add	eax, edx
	add	esi, eax ; start address 
	pop	ecx ; edx
	mov	eax, ecx
	shr	eax, 16 ; bottom row
	mul	ebx
	mov	dx, cx ; bottom right column
	add	eax, edx
	pop	edx ; byte count per window row
	add	eax, 0A0000h
	cmp	eax, 0AFFFFh
	ja	sysret
	push	esi ; start address 
	push	eax ; stop address (included)
sysvideo_56:
	mov	ecx, edx ; byte count
	; user to system video/display page window transfer
	; esi =	user buffer
	call	transfer_to_user_buffer ; fast transfer
	jc	short sysvideo_57
	add	[u.r0], ecx
	add	edi, ebx ; next row
	add	esi, ecx
	cmp	edi, [esp] ; stop addr(included in loop)
	jna	short sysvideo_56
	pop	ebx ; stop address
	pop	edi ; start address
	sub	ebx, edi
	inc	ebx
	mov	[u.r0], ebx	
	jmp	sysret
sysvideo_57:
	pop	eax
	pop	edx
	jmp	sysret

sysvideo_58:
	cmp	bl, 7
	ja	short sysvideo_60

	; BL = 7 = AND display page bytes with CL

	mov	esi, 0A0000h
	mov	ecx, 65536
sysvideo_59:
	and	byte [esi], cl
	inc	esi
	loop	sysvideo_59

sysvideo_60:
	cmp	bl, 8
	ja	short sysvideo_62

	; BL = 8 = OR display page bytes with CL

	mov	esi, 0A0000h
	mov	ecx, 65536
sysvideo_61:
	or	byte [esi], cl
	inc	esi
	loop	sysvideo_61	

sysvideo_62:
	cmp	bl, 9
        ja      sysret ; nothing to do

	; BL = 9 = XOR display page bytes with CL

	mov	esi, 0A0000h
	mov	ecx, 65536
sysvideo_63:
	xor	byte [esi], cl
	inc	esi
	loop	sysvideo_63

sysvideo_64:
	cmp	bh, 3
	je	short sysvideo_68
	cmp	bh, 4
	ja	short sysvideo_65
	
	; BH = 4
	; Direct User Access for CGA video memory.
	; Setup user's page tables for direct access to 0B8000h.
	;
	; Permission checks are not implemented yet !
	; (11/07/2016)

	mov	eax, 0B8000h
	mov	ecx, 8 ; 8 pages (8*4K=32K)
	call	direct_memory_access	
	jc	sysret
	; eax = 0B8000h if there is not an error
	mov	[u.r0], eax
	jmp	sysret

sysvideo_65:
	cmp	bh, 5
	ja	short sysvideo_66

	; BH = 5
	; Direct User Access for VGA video memory.
	; Setup user's page tables for direct access to 0A0000h.
	;
	; Permission checks are not implemented yet !
	; (11/07/2016)

	mov	eax, 0A0000h
	mov	ecx, 16 ; 16 pages (16*4K=64K)
	call	direct_memory_access	
	jc	sysret
	; eax = 0A0000h if there is not an error
	mov	[u.r0], eax
	jmp	sysret

sysvideo_66:
	cmp	bh, 6
	ja	short sysvideo_67
	; BH = 6
	; Direct User Access for (Super VGA) Linear Frame Buffer.
	; Setup user's page tables for direct access to LFB.
	;
	; Not implemented yet !
	; (11/07/2016)
	jmp	sysret

sysvideo_67:
	cmp	bh, 7
	ja	sysret ; invalid !

	; BH = 7
	; Get (Super/Extended VGA) Linear Frame Buffer info.
	;
	; Not implemented yet !
	; (11/07/2016)
	jmp	sysret

sysvideo_68:
	; BH = 3
	; Super VGA, LINEAR FRAME BUFFER data transfers
	; Not implemented for yet ! (11/07/2016)
	jmp	sysret

sysaudio: ; AUDIO FUNCTIONS
	; 16/05/2016 - TRDOS 386 (TRDOS v2.0)
	;
	; Inputs:
	;	BH = 0 -> Beep (PC Speaker)
	;	     BL = Duration Counter (1 for 1/64 second)
	;	     CX = Frequency Divisor (1193180/Frequency)
	;		 (1331 for 886 Hz)			
	;
	; Outputs:
	;	none
	;
	and	bh, bh
	jnz	sysret
	call	beep
	jmp	sysret
 
syslink:
	; 23/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 19/06/2013 (Retro UNIX 8086 v1)
	;
	; 'syslink' is given two arguments, name 1 and name 2.
	; name 1 is a file that already exists. name 2 is the name
	; given to the entry that will go in the current directory.
	; name2 will then be a link to the name 1 file. The i-number
	; in the name 2 entry of current directory is the same
	; i-number for the name 1 file.
	;
	; Calling sequence:
	;	syslink; name 1; name 2
	; Arguments:
	;	name 1 - file name to which link will be created.
	;	name 2 - name of entry in current directory that
	;		 links to name 1.
	; Inputs: -
	; Outputs: -
	; ...............................................................
	;	
	; Retro UNIX 8086 v1 modification: 
	;       'syslink' system call has two arguments; so,
	;	* 1st argument, name 1 is pointed to by BX register
	;	* 2nd argument, name 2 is pointed to by CX register
	;
		; / name1, name2
		;jsr r0,arg2 / u.namep has 1st arg u.off has 2nd
	mov	[u.namep], ebx
	push	ecx
	call	namei
		; jsr r0,namei / find the i-number associated with
			     ; / the 1st path name
     	;;and	ax, ax
	;;jz	error ; File not found
	;jc	error 
		; br error9 / cannot be found
	jnc	short syslink0
	;pop 	ecx
	; 'file not found !' error
	mov	dword [u.error], ERR_FILE_NOT_FOUND ; 12
	jmp	error
syslink0:
	call	iget
		; jsr r0,iget / get the i-node into core
	pop	dword [u.namep] ; ecx
		; mov (sp)+,u.namep / u.namep points to 2nd name
	push	ax
		; mov r1,-(sp) / put i-number of name1 on the stack
			    ; / (a link to this file is to be created)
	push	word [cdev]
		; mov cdev,-(sp) / put i-nodes device on the stack
	call	isdir
		; jsr r0,isdir / is it a directory
	call	namei
		; jsr r0,namei / no, get i-number of name2
	;jnc	error
		; br .+4   / not found 
			 ; / so r1 = i-number of current directory
			 ; / ii = i-number of current directory
		; br error9 / file already exists., error
	jc	short syslink1
	; pop ax
	; pop ax
	; 'file exists !' error
	mov	dword [u.error], ERR_FILE_EXISTS ; 14
	jmp	error
syslink1:
	pop	cx
	;cmp	cx, [cdev]
	cmp	cl, [cdev]
	;jne	error
		; cmp (sp)+,cdev / u.dirp now points to 
			       ; / end of current directory
	        ; bne error9
	je	short syslink2
	; 'not same drive !' error
	mov	dword [u.error],  ERR_DRV_NOT_SAME ; 21
	jmp	error
syslink2:
	pop	ax
	push	ax
	mov	[u.dirbuf], ax
		; mov (sp),u.dirbuf / i-number of name1 into u.dirbuf
	call	mkdir
		; jsr r0,mkdir / make directory entry for name2 
		 	     ; / in current directory
	pop	ax
		; mov (sp)+,r1 / r1 has i-number of name1
	call	iget
		; jsr r0,iget / get i-node into core
	inc	byte [i.nlks]
		; incb i.nlks / add 1 to its number of links
	call	setimod
		; jsr r0,setimod / set the i-node modified flag
	jmp	sysret

isdir:
	; 22/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 04/05/2013 - 02/08/2013 (Retro UNIX 8086 v1)
	;
	; 'isdir' check to see if the i-node whose i-number is in r1
	;  is a directory. If it is, an error occurs, because 'isdir'
	;  called by syslink and sysunlink to make sure directories
	;  are not linked. If the user is the super user (u.uid=0),
	; 'isdir' does not bother checking. The current i-node
	;  is not disturbed.			
	;		
	; INPUTS ->
	;    r1 - contains the i-number whose i-node is being checked.
	;    u.uid - user id
	; OUTPUTS ->
	;    r1 - contains current i-number upon exit
	;    	 (current i-node back in core) 
	;	
	; ((AX = R1))
	;
        ; ((Modified registers: eAX, eDX, eBX, eCX, eSI, eDI, eBP))  
	;

	; / if the i-node whose i-number is in r1 is a directory 
	; / there is an error unless super user made the call
	
	cmp	byte [u.uid], 0 
		; tstb u.uid / super user
	jna	short isdir1
		; beq 1f / yes, don't care
	push	word [ii]
		; mov ii,-(sp) / put current i-number on stack
	call	iget
		; jsr r0,iget / get i-node into core (i-number in r1)
	test 	word [i.flgs], 4000h ; Bit 14 : Directory flag
		; bit $40000,i.flgs / is it a directory
	;jnz	error
		; bne error9 / yes, error
	jz	short isdir0
	mov 	dword [u.error], ERR_NOT_FILE  ; 11 ; ERR_DIR_ACCESS 
				; 'permission denied !' error
	; pop	ax
	jmp	error	
isdir0:	
	pop	ax
		; mov (sp)+,r1 / no, put current i-number in r1 (ii)
	call	iget
		; jsr r0,iget / get it back in
isdir1: ; 1:
	retn
		; rts r0

sysunlink:
	; 04/12/2015 (14 byte file names)
	; 23/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 19/06/2013 (Retro UNIX 8086 v1)
	;
	; 'sysunlink' removes the entry for the file pointed to by
	; name from its directory. If this entry was the last link
	; to the file, the contents of the file are freed and the
	; file is destroyed. If, however, the file was open in any
	; process, the actual destruction is delayed until it is 
	; closed, even though the directory entry has disappeared.
	; 
	; The error bit (e-bit) is set to indicate that the file	
	; does not exist or that its directory can not be written.
	; Write permission is not required on the file itself.
	; It is also illegal to unlink a directory (except for
	; the superuser).
	;
	; Calling sequence:
	;	sysunlink; name
	; Arguments:
	;	name - name of directory entry to be removed 
	; Inputs: -
	; Outputs: -
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification:
	;	 The user/application program puts address of the name
	;        in BX register as 'sysunlink' system call argument.

	; / name - remove link name
	mov	[u.namep], ebx
		;jsr r0,arg; u.namep / u.namep points to name
	call	namei
		; jsr r0,namei / find the i-number associated 
			     ; / with the path name
	;jc	error
		; br error9 / not found
	jnc	short sysunlink1
	; 'file not found !' error
	mov	dword [u.error], ERR_FILE_NOT_FOUND ; 12
	jmp	error
sysunlink1:
	push	ax
		; mov r1,-(sp) / put its i-number on the stack
	call	isdir
		; jsr r0,isdir / is it a directory
	xor 	ax, ax
	mov	[u.dirbuf], ax ; 0
		; clr u.dirbuf / no, clear the location that will
			   ; / get written into the i-number portion
			 ; / of the entry
	sub	dword [u.off], 16 ; 04/12/2015 (10 -> 16) 
		; sub $10.,u.off / move u.off back 1 directory entry
	call	wdir
		; jsr r0,wdir / free the directory entry
	pop	ax
		; mov (sp)+,r1 / get i-number back
	call	iget
		; jsr r0,iget / get i-node
	call	setimod
		; jsr r0,setimod / set modified flag
	dec	byte [i.nlks]
		; decb i.nlks / decrement the number of links
	jnz	sysret
		; bgt sysret9 / if this was not the last link
			    ; / to file return
	; AX = r1 = i-number
	call	anyi
		; jsr r0,anyi / if it was, see if anyone has it open.
			 ; / Then free contents of file and destroy it.
	jmp	sysret
		; br sysret9

mkdir:
	; 04/12/2015 (14 byte directory names)
	; 12/10/2015
	; 17/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 29/04/2013 - 01/08/2013 (Retro UNIX 8086 v1)
	;
	; 'mkdir' makes a directory entry from the name pointed to
	; by u.namep into the current directory.
	;
	; INPUTS ->
	;    u.namep - points to a file name 
	;	           that is about to be a directory entry.
	;    ii - current directory's i-number.	
	; OUTPUTS ->
	;    u.dirbuf+2 - u.dirbuf+10 - contains file name. 
	;    u.off - points to entry to be filled 
	;	     in the current directory		
	;    u.base - points to start of u.dirbuf.
	;    r1 - contains i-number of current directory 
	;	
	; ((AX = R1)) output
	;
	;    (Retro UNIX Prototype : 11/11/2012, UNIXCOPY.ASM)
        ;    ((Modified registers: eAX, eDX, eBX, eCX, eSI, eDI, eBP))  
	;

	; 17/06/2015 - 32 bit modifications (Retro UNIX 386 v1)
	xor 	eax, eax
	mov     edi, u.dirbuf+2
	mov	esi, edi
	stosd
	stosd
	; 04/12/2015 (14 byte directory names)
	stosd
	stosw
		; jsr r0,copyz; u.dirbuf+2; u.dirbuf+10. / clear this
	mov	edi, esi ; offset to u.dirbuf
	; 12/10/2015 ([u.namep] -> ebp)
	;mov 	ebp, [u.namep]
	call	trans_addr_nmbp ; convert virtual address to physical
		; esi = physical address (page start + offset)
		; ecx = byte count in the page (1 - 4096)
	; edi = offset to u.dirbuf (edi is not modified in trans_addr_nm)
		; mov u.namep,r2 / r2 points to name of directory entry
		; mov $u.dirbuf+2,r3 / r3 points to u.dirbuf+2
mkdir_1: ; 1: 
	inc	ebp ; 12/10/2015
	;
	; / put characters in the directory name in u.dirbuf+2 - u.dirbuf+10
	 ; 01/08/2013
	lodsb
		; movb (r2)+,r1 / move character in name to r1
	and 	al, al
	jz 	short mkdir_3 	  
		; beq 1f / if null, done
	cmp	al, '/'
		; cmp r1,$'/ / is it a "/"?
	je	short mkdir_err
	;je	error
		; beq error9 / yes, error
	; 12/10/2015
	dec	cx
	jnz	short mkdir_2
	; 12/10/2015 ([u.namep] -> ebp)
	call	trans_addr_nm ; convert virtual address to physical
		; esi = physical address (page start + offset)
		; ecx = byte count in the page
	; edi = offset to u.dirbuf (edi is not modified in trans_addr_nm)
mkdir_2:
	cmp     edi, u.dirbuf+16 ; ; 04/12/2015 (10 -> 16) 
		; cmp r3,$u.dirbuf+10. / have we reached the last slot for
				     ; / a char?
	je	short mkdir_1
		; beq 1b / yes, go back
	stosb
		; movb r1,(r3)+ / no, put the char in the u.dirbuf
	jmp 	short mkdir_1
		; br 1b / get next char
mkdir_err:
	; 17/06/2015
	mov	dword [u.error], ERR_NOT_DIR ; 'not a valid directory !'
	jmp	error

mkdir_3: ; 1:
	mov	eax, [u.dirp]
	mov	[u.off], eax
		; mov u.dirp,u.off / pointer to empty current directory
				 ; / slot to u.off
wdir: ; 29/04/2013
        mov     dword [u.base], u.dirbuf
		; mov $u.dirbuf,u.base / u.base points to created file name
        mov     dword [u.count], 16 ; 04/12/2015 (10 -> 16) 
		; mov $10.,u.count / u.count = 10
	mov	ax, [ii] 
		; mov ii,r1 / r1 has i-number of current directory
	mov	dl, 1 ; owner flag mask ; RETRO UNIX 8086 v1 modification !
	call 	access
		; jsr r0,access; 1 / get i-node and set its file up 
				 ; / for writing
	; AX = i-number of current directory
	; 01/08/2013
	inc     byte [u.kcall] ; the caller is 'mkdir' sign	
	call	writei
		; jsr r0,writei / write into directory
	retn	
		; rts r0

sysexec:
	; 24/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 23/06/2015 - 23/10/2015 (Retro UNIX 386 v1)
	; 03/06/2013 - 06/12/2013 (Retro UNIX 8086 v1)
	;
	; 'sysexec' initiates execution of a file whose path name if
	; pointed to by 'name' in the sysexec call. 
	; 'sysexec' performs the following operations:
	;    1. obtains i-number of file to be executed via 'namei'.
	;    2. obtains i-node of file to be exceuted via 'iget'.
	;    3. sets trap vectors to system routines.
	;    4. loads arguments to be passed to executing file into
	;	highest locations of user's core
	;    5. puts pointers to arguments in locations immediately
	;	following arguments.
	;    6.	saves number of arguments in next location.
	;    7. intializes user's stack area so that all registers
	;	will be zeroed and the PS is cleared and the PC set
	;	to core when 'sysret' restores registers 
	;	and does an rti.
	;    8. inializes u.r0 and u.sp
	;    9. zeros user's core down to u.r0
	;   10.	reads executable file from storage device into core
	;	starting at location 'core'.
	;   11.	sets u.break to point to end of user's code with
	;	data area appended.
	;   12.	calls 'sysret' which returns control at location
	;	'core' via 'rti' instruction. 		  		
	;
	; Calling sequence:
	;	sysexec; namep; argp
	; Arguments:
	;	namep - points to pathname of file to be executed
	;	argp  - address of table of argument pointers
	;	argp1... argpn - table of argument pointers
	;	argp1:<...0> ... argpn:<...0> - argument strings
	; Inputs: (arguments)
	; Outputs: -	
	; ...............................................................
	;
	; Retro UNIX 386 v1 modification: 
	;	User application runs in it's own virtual space 
	;	which is izolated from kernel memory (and other
	;	memory pages) via 80386	paging in ring 3 
	;	privilige mode. Virtual start address is always 0.
	;	User's core memory starts at linear address 400000h
	;	(the end of the 1st 4MB).
	;
	; Retro UNIX 8086 v1 modification: 
	;	user/application segment and system/kernel segment
	;	are different and sysenter/sysret/sysrele routines
	;	are different (user's registers are saved to 
	;	and then restored from system's stack.)
	;
	;	NOTE: Retro UNIX 8086 v1 'arg2' routine gets these
	;	      arguments which were in these registers;
	;	      but, it returns by putting the 1st argument
	;	      in 'u.namep' and the 2nd argument
	;	      on top of stack. (1st argument is offset of the
	;	      file/path name in the user's program segment.)		 	
	
	;call	arg2
	; * name - 'u.namep' points to address of file/path name
	;          in the user's program segment ('u.segmnt')
	;          with offset in BX register (as sysopen argument 1).
	; * argp - sysexec argument 2 is in CX register 
	;          which is on top of stack.
	;
		; jsr r0,arg2 / arg0 in u.namep,arg1 on top of stack

	; 23/06/2015 (32 bit modifications)

	mov	[u.namep], ebx ; argument 1
        ; 18/10/2015
	mov     [argv], ecx  ; * ; argument 2
	call	namei
		; jsr r0,namei / namei returns i-number of file 
			     ; / named in sysexec call in r1
	;jc	error
		; br error9
	jnc	short sysexec_0
	;
	; 'file not found !' error
	mov	dword [u.error], ERR_FILE_NOT_FOUND
	jmp	error 
sysexec_not_exf:
	; 'not executable file !' error
	mov	dword [u.error], ERR_NOT_EXECUTABLE
	jmp	error 
sysexec_0:
	call	iget
		; jsr r0,iget / get i-node for file to be executed
        test    word [i.flgs], 10h
		; bit $20,i.flgs / is file executable
	jz	short sysexec_not_exf
	;jz	error
		; beq error9
	;;
	call	iopen
		; jsr r0,iopen / gets i-node for file with i-number
			     ; / given in r1 (opens file)
	; AX = i-number of the file
	test	word [i.flgs], 20h
		; bit $40,i.flgs / test user id on execution bit
	jz	short sysexec_1
		; beq 1f
	cmp 	byte [u.uid], 0 ; 02/08/2013
		; tstb u.uid / test user id
	jna	short sysexec_1
		; beq 1f / super user
	mov	cl, [i.uid]
	mov	[u.uid], cl ; 02/08/2013
		; movb i.uid,u.uid / put user id of owner of file
				 ; / as process user id
sysexec_1:
	; 18/10/2215
	; 10/10/2015
	; 24/07/2015
	; 21/07/2015
	; 25/06/2015
	; 24/06/2015
        ; Moving arguments to the end of [u.upage]
	; (by regarding page borders in user's memory space)
	;
	; 10/10/2015
	; 21/07/2015
	mov	ebp, esp ; (**)
	; 18/10/2015
	mov 	edi, ebp
	mov 	ecx, MAX_ARG_LEN ; 256
	;sub	edi, MAX_ARG_LEN ; 256
	sub	edi, ecx
	mov	esp, edi
	xor	eax, eax
	mov 	[u.nread], eax ; 0
	dec	ecx ; 256 - 1
	mov 	[u.count], ecx ; MAX_ARG_LEN - 1 ; 255
	;mov 	dword [u.count], MAX_ARG_LEN - 1 ; 255
sysexec_2:
	mov	esi, [argv] ; 18/10/2015 
	call	get_argp
	mov	ecx, 4 ; mov ecx, 4
sysexec_3:
	and	eax, eax
        jz      sysexec_6
	; 18/10/2015
	add	[argv], ecx ; 4
	inc	word [argc]
	;
	mov	[u.base], eax
 	; 23/10/2015
	mov	word [u.pcount], 0
sysexec_4:
	call	cpass ; get a character from user's core memory
        jnz      short sysexec_5
		; (max. 255 chars + null)
	; 18/10/2015
	sub 	al, al
	stosb
	inc	dword [u.nread]
	jmp	sysexec_6 ; 24/04/2016
sysexec_5:
	stosb
	and 	al, al
	jnz	short sysexec_4
	mov	ecx, 4
	cmp	[ncount], ecx ; 4
	jb	short sysexec_2
	mov	esi, [nbase]
	add	[nbase], ecx ; 4	
	sub	[ncount], cx 
	mov	eax, [esi]
	jmp	short sysexec_3

get_argp:
	; 18/10/2015 (nbase, ncount)
	; 21/07/2015
	; 24/06/2015 (Retro UNIX 386 v1)
	; Get (virtual) address of argument from user's core memory
	;
	; INPUT:
	;	esi = virtual address of argument pointer
	; OUTPUT:
	;	eax = virtual address of argument
	;
	; Modified registers: EAX, EBX, ECX, EDX, ESI 
	;
 	cmp     dword [u.ppgdir], 0 ; /etc/init ?
				    ; (the caller is kernel)
        jna     short get_argpk 
	;
     	mov	ebx, esi
	call	get_physical_addr ; get physical address
        jc      get_argp_err
	mov 	[nbase], eax ; physical address	
	mov	[ncount], cx ; remain byte count in page (1-4096)
	mov	eax, 4 ; 21/07/2015
	cmp	cx, ax ; 4
	jnb	short get_argp2
	mov	ebx, esi
	add	ebx, ecx
	call	get_physical_addr ; get physical address
	jc	short get_argp_err
	;push	esi
	mov	esi, eax
	xchg	cx, [ncount]
	xchg	esi, [nbase]
	mov	ch, 4
	sub	ch, cl
get_argp0:
	lodsb
	push	ax
	dec	cl
        jnz     short get_argp0
	mov	esi, [nbase]
	; 21/07/2015
	movzx	eax, ch
	add	[nbase], eax
	sub	[ncount], ax
get_argp1:
	lodsb
	dec	ch
        jz      short get_argp3
        push	ax
	jmp     short get_argp1
get_argpk:
	; Argument is in kernel's memory space
	mov	word [ncount], PAGE_SIZE ; 4096
	mov	[nbase], esi
	add	dword [nbase], 4
	mov	eax, [esi] ; virtual addr. = physcal addr.
	retn
get_argp2:
	; 21/07/2015
	;mov	eax, 4
	mov 	edx, [nbase] ; 18/10/2015
	add	[nbase], eax
	sub	[ncount], ax
	;
	mov	eax, [edx]
	retn
get_argp_err:
	mov	[u.error], eax
	jmp	error
get_argp3:
	mov	cl, 3
get_argp4:
	shl	eax, 8
	pop	dx
	mov 	al, dl
        loop    get_argp4
	;pop	esi
	retn	

sysfstat: 
	; 23/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 19/06/2013 (Retro UNIX 8086 v1)
	;
	; 'sysfstat' is identical to 'sysstat' except that it operates
	; on open files instead of files given by name. It puts the
	; buffer address on the stack, gets the i-number and
	; checks to see if the file is open for reading or writing.
	; If the file is open for writing (i-number is negative)
	; the i-number is set positive and a branch into 'sysstat'
	; is made.	
	;
	; Calling sequence:
	;	sysfstat; buf
	; Arguments:
	;	buf - buffer address
	;
	; Inputs: *u.r0 - file descriptor
	; Outputs: buffer is loaded with file information
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification:
	;       'sysfstat' system call has two arguments; so,
	;	* 1st argument, file descriptor is in BX register
	;	* 2nd argument, buf is pointed to by CX register

	; / set status of open file
		; jsr r0,arg; u.off / put buffer address in u.off
	push	ecx
		; mov u.off,-(sp) / put buffer address on the stack
		; mov *u.r0,r1 / put file descriptor in r1
		; jsr r0,getf / get the files i-number
	; BX = file descriptor (file number)
	call	getf1
	and	ax, ax ; i-number of the file
		; tst	r1 / is it 0?
	;jz	error
		; beq error3 / yes, error
	jnz	short sysfstat1
	mov	dword [u.error], ERR_FILE_NOT_OPEN  ; 'file not open !'
	jmp	error
sysfstat1:
	cmp	ah, 80h
        jb      short sysstat1
		; bgt 1f / if i-number is negative (open for writing)
	neg	ax
		; neg r1 / make it positive, then branch
	jmp	short sysstat1
		; br 1f / to 1f
sysstat:
	; 18/10/2015
	; 07/10/2015
	; 02/09/2015
	; 23/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 19/06/2013 (Retro UNIX 8086 v1)
	;
	; 'sysstat' gets the status of a file. Its arguments are the
	; name of the file and buffer address. The buffer is 34 bytes
	; long and information about the file placed in it.	
	; sysstat calls 'namei' to get the i-number of the file.
	; Then 'iget' is called to get i-node in core. The buffer
	; is then loaded and the results are given in the UNIX
	; Programmers Manual sysstat (II).	
	;
	; Calling sequence:
	;	sysstat; name; buf
	; Arguments:
	;	name - points to the name of the file
	;	buf - address of a 34 bytes buffer
	; Inputs: -
	; Outputs: buffer is loaded with file information
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification: 
	;       'sysstat' system call has two arguments; so,
	;	Retro UNIX 8086 v1 argument transfer method 2 is used
	;	to get sysstat system call arguments from the user;
	;	* 1st argument, name is pointed to by BX register
	;	* 2nd argument, buf is pointed to by CX register
	;
	;	NOTE: Retro UNIX 8086 v1 'arg2' routine gets these
	;	      arguments which were in these registers;
	;	      but, it returns by putting the 1st argument
	;	      in 'u.namep' and the 2nd argument
	;	      on top of stack. (1st argument is offset of the
	;	      file/path name in the user's program segment.)		 	
	
	; / ; name of file; buffer - get files status
		; jsr r0,arg2 / get the 2 arguments
	mov	[u.namep], ebx
	push	ecx
	call	namei
		; jsr r0,namei / get the i-number for the file
	;jc	error
		; br error3 / no such file, error
	jnc	short sysstat1
	; pop 	ecx
sysstat_err0:
	; 'file not found !' error
	mov	dword [u.error], ERR_FILE_NOT_FOUND ; 12
	jmp	error

statx: db 0

sysstat1: ; 1:
	call	iget
		; jsr r0,iget / get the i-node into core
	; 07/10/2015 (ax = [ii], inode number)
	; 02/09/2015
	pop	dword [u.base]
		; mov (sp)+,r3 / move u.off to r3 (points to buffer)
	call	sysstat_gpa ; get physical address
	jnc 	short sysstat2
sysstat_err1:
	mov	dword [u.error], eax ; error code
	jmp	error
sysstat2:
	mov 	al, [ii] ; 07/10/2015 (result of 'iget' call, above)
	stosb
	inc 	dword [u.base]
	dec 	cx
	jnz	short sysstat3
	call	sysstat_gpa
	;jc	short sysstat_err1
sysstat3:
	mov 	al, [ii+1] ; 07/10/2015 (result of 'iget' call, above)
	stosb
		; mov r1,(r3)+ / put i-number in 1st word of buffer
	inc 	dword [u.base]
	;dec 	word [u.pcount]
	dec	cx
	jnz	short sysstat4
	call	sysstat_gpa
	;jc	short sysstat_err1	
sysstat4:
	mov	esi, inode
		; mov $inode,r2 / r2 points to i-node
sysstat5: ; 1:
	movsb
		; mov (r2)+,(r3)+ / move rest of i-node to buffer
	inc 	dword [u.base]
	;dec 	word [u.pcount]
	dec	cx
	jnz	short sysstat6
	call	sysstat_gpa
	;jc	short sysstat_err1
sysstat6:		
	cmp	esi, inode + 32
		; cmp r2,$inode+32 / done?
	jne	short sysstat5
		; bne 1b / no, go back
	jmp	sysret
		; br sysret3 / return through sysret
	;
sysstat_gpa: ; get physical address of file status buffer
	; 02/09/2015
	mov 	ebx, [u.base]
	; 07/10/2015
	call	get_physical_addr ; get physical address
	;jc	short sysstat_gpa1
	jc	short sysstat_err1
	; 18/10/2015
	mov	edi, eax ; physical address
	;mov	[u.pcount], cx ; remain bytes in page
;sysstat_gpa1:
	retn

fclose:
	; 18/06/2015 (Retro UNIX 386 v1 - Beginning)
	;            (32 bit offset pointer modification)
	; 19/04/2013 - 12/01/2014 (Retro UNIX 8086 v1)
	;
	; Given the file descriptor (index to the u.fp list)
	; 'fclose' first gets the i-number of the file via 'getf'.
	; If i-node is active (i-number > 0) the entry in 
	; u.fp list is cleared. If all the processes that opened
	; that file close it, then fsp etry is freed and the file
	; is closed. If not a return is taken. 
	; If the file has been deleted while open, 'anyi' is called
	; to see anyone else has it open, i.e., see if it is appears
	; in another entry in the fsp table. Upon return from 'anyi'
	; a check is made to see if the file is special.	
	;
	; INPUTS ->
	;    r1 - contains the file descriptor (value=0,1,2...)
	;    u.fp - list of entries in the fsp table
	;    fsp - table of entries (4 words/entry) of open files.	 
	; OUTPUTS ->
	;    r1 - contains the same file descriptor
	;    r2 - contains i-number
	;
	; ((AX = R1))
	; ((Modified registers: eDX, eBX, eCX, eSI, eDI, eBP))
	;
	; Retro UNIX 8086 v1 modification : CF = 1
	;              if i-number of the file is 0. (error)  	
	;
	movzx	edx, ax ; **
	push	ax ; ***
		; mov r1,-(sp) / put r1 on the stack (it contains 
			     ; / the index to u.fp list)
	call	getf
		; jsr r0,getf / r1 contains i-number, 
			    ; / cdev has device =, u.fofp 
			    ; / points to 3rd word of fsp entry
	cmp	ax, 1 ; r1
		; tst r1 / is i-number 0?
	jb	short fclose_2
		; beq 1f / yes, i-node not active so return
		; tst (r0)+ / no, jump over error return
	mov	ebx, edx ; **
	mov 	dx, ax ; *
		; mov r1,r2 / move i-number to r2 ;*
		; mov (sp),r1 / restore value of r1 from the stack
			    ; / which is index to u.fp ; **
	mov	byte [ebx+u.fp], 0
		; clrb u.fp(r1) / clear that entry in the u.fp list
	mov	ebx, [u.fofp]
		; mov u.fofp,r1 / r1 points to 3rd word in fsp entry
fclose_0:
	dec	byte [ebx+4] ; 18/06/2015
		; decb 2(r1) / decrement the number of processes 
			   ; / that have opened the file
	jns	short fclose_2 ; jump if not negative (jump if bit 7 is 0)	 
		; bge 1f / if all processes haven't closed the file, return
	;
	push	dx ;*
		; mov r2,-(sp) / put r2 on the stack (i-number)
	xor	ax, ax ; 0
	mov	[ebx-4], ax ; 0
		; clr -4(r1) / clear 1st word of fsp entry
	mov	al, [ebx+5] ; 18/06/2015
		; tstb	3(r1) / has this file been deleted
	and	al, al
	jz	short fclose_1
		; beq 2f / no, branch
	mov	ax, dx ; *
		; mov r2,r1 / yes, put i-number back into r1
	; AX = inode number
	call	anyi
		; jsr r0,anyi / free all blocks related to i-number
			    ; / check if file appears in fsp again
fclose_1: ; 2:
	pop	ax ; *
		; mov (sp)+,r1 / put i-number back into r1
	call	iclose ; close if it is special file 
		; jsr r0,iclose / check to see if its a special file
fclose_2: ; 1:
	pop	ax ; ***
		; mov (sp)+,r1 / put index to u.fp back into r1
	retn
		; rts r0

getf:	; / get the device number and the i-number of an open file
	; 13/05/2015
	; 11/05/2015 (Retro UNIX 386 v1 - Beginning)
	; 19/04/2013 - 18/11/2013 (Retro UNIX 8086 v1)
	;
	mov	ebx, eax
getf1: ;; Calling point from 'rw1' (23/05/2013)
	cmp	ebx, 10
		; cmp r1,$10. / user limited to 10 open files
        jnb	short getf2 ; 13/05/2015
	;jnb     error
		; bhis error3 / u.fp is table of users open files, 
			    ; / index in fsp table
	mov	bl, [ebx+u.fp]
		; movb	u.fp(r1),r1 / r1 contains number of entry 
		                  ; / in fsp table
	or	bl, bl
	jnz	short getf3
	;jz	short getf4
		; beq 1f / if its zero return
getf2:
	; 'File not open !' error (ax=0)
	sub	eax, eax
	retn
getf3:	
	; Retro UNIX 386 v1 modification ! (11/05/2015)
	;
	; 'fsp' table (10 bytes/entry)
	; bit 15				   bit 0
	; ---|-------------------------------------------
	; r/w|		i-number of open file
	; ---|-------------------------------------------
	;		   device number
	; -----------------------------------------------
	; offset pointer, r/w pointer to file (bit 0-15)
	; -----------------------------------------------
	; offset pointer, r/w pointer to file (bit 16-31)
	; ----------------------|------------------------
	;  flag that says file 	| number of processes
	;   has been deleted	| that have file open 
	; ----------------------|------------------------
	;
	mov	eax, 10
	mul	bl
	mov	ebx, fsp - 6 ; the 3rd word in the fsp entry
	add	ebx, eax
		; asl r1
		; asl r1 / multiply by 8 to get index into 
		       ; / fsp table entry
		; asl r1
		; add $fsp-4,r1 / r1 is pointing at the 3rd word 
			      ; / in the fsp entry
	mov	[u.fofp], ebx
		; mov r1,u.fofp / save address of 3rd word 
			      ; / in fsp entry in u.fofp
	dec	ebx
	dec	ebx
	mov	ax, [ebx]
	;mov	[cdev], al ; ;;Retro UNIX 8086 v1 ! 
	mov	[cdev], ax ; ;;in fact (!) 
			     ;;dev number is in 1 byte
		; mov -(r1),cdev / remove the device number  cdev
	dec	ebx
	dec	ebx
	mov	ax, [ebx]
		; mov -(r1),r1 / and the i-number  r1
getf4:	; 1:
	retn
		; rts r0

namei:
	; 04/12/2015 (14 byte file names)
	; 18/10/2015 (nbase, ncount)
	; 12/10/2015
	; 21/08/2015
	; 18/07/2015
	; 02/07/2015
	; 17/06/2015
	; 16/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 24/04/2013 - 31/07/2013 (Retro UNIX 8086 v1)
	;
	; 'namei' takes a file path name and returns i-number of
	; the file in the current directory or the root directory
	; (if the first character of the pathname is '/').	
	;
	; INPUTS ->
	;    u.namep - points to a file path name
	;    u.cdir - i-number of users directory
	;    u.cdev - device number on which user directory resides	
	; OUTPUTS ->
	;    r1 - i-number of file
	;    cdev
	;    u.dirbuf - points to directory entry where a match 
	;               occurs in the search for file path name.
	;	        If no match u.dirb points to the end of 
	;               the directory and r1 = i-number of the current
	;	        directory.	
	; ((AX = R1))
	;
	; (Retro UNIX Prototype : 07/10/2012 - 05/01/2013, UNIXCOPY.ASM)
        ; ((Modified registers: eDX, eBX, eCX, eSI, eDI, eBP))  
	;

	mov	ax, [u.cdir]
		; mov u.cdir,r1 / put the i-number of current directory
			      ; / in r1
	mov	dx, [u.cdrv]
	mov	[cdev], dx 	    ; NOTE: Retro UNIX 8086 v1 
				    ; device/drive number is in 1 byte, 
				    ; not in 1 word!
		; mov u.cdev,cdev / device number for users directory 
				; / into cdev
	; 12/10/2015
	; 16/06/2015 - 32 bit modifications (Retro UNIX 386 v1)
      	 ; convert virtual (pathname) addr to physical address
	call    trans_addr_nmbp ; 12/10/2015
		; esi = physical address of [u.namep]
		; ecx = byte count in the page
	cmp	byte [esi], '/'
		; cmpb *u.namep,$'/ / is first char in file name a /
	jne	short namei_1
		; bne 1f
	inc	dword [u.namep]
		; inc u.namep / go to next char
	dec	cx ; remain byte count in the page
	jnz	short namei_0
	; 12/10/2015
	call	trans_addr_nmbp ; convert virtual address to physical
		; esi = physical address (page start + offset)
		; ecx = byte count in the page
	dec	esi
namei_0:
	inc 	esi  ; go to next char
	mov	ax, [rootdir] ; 09/07/2013
		; mov rootdir,r1 / put i-number of rootdirectory in r1
	mov	byte [cdev], 0
		; clr cdev / clear device number
namei_1: ; 1:
	test	byte [esi], 0FFh
	jz	short getf4
	;jz      nig
		; tstb *u.namep / is the character in file name a nul
		; beq nig / yes, end of file name reached; 
			; / branch to "nig"
namei_2: ; 1:
	; 18/10/2015
	mov 	[nbase], esi
	mov 	[ncount], cx
	;
	;mov	dx, 2
	mov	dl, 2 ; user flag (read, non-owner)
	call	access
		; jsr r0,access; 2 / get i-node with i-number r1
	; 'access' will not return here if user has not "r" permission !
	test 	word [i.flgs], 4000h
		; bit $40000,i.flgs / directory i-node?
        jz      short namei_err
		; beq error3 / no, got an error
	; 16/06/2015 - 32 bit modifications (Retro UNIX 386 v1)
	xor	eax, eax
	mov	[u.off], eax ; 0
	mov	ax, [i.size]
	mov	[u.dirp], eax
		; mov i.size,u.dirp / put size of directory in u.dirp
		; clr u.off / u.off is file offset used by user
	mov	dword [u.fofp], u.off
		; mov $u.off,u.fofp / u.fofp is a pointer to 
				  ; / the offset portion of fsp entry
namei_3: ; 2:
	mov	dword [u.base], u.dirbuf
		; mov $u.dirbuf,u.base / u.dirbuf holds a file name 
				    ; / copied from a directory
	mov 	dword [u.count], 16 ; 04/12/2015 (10 -> 16) 	
 		; mov $10.,u.count / u.count is byte count 
				 ; / for reads and writes
	mov 	ax, [ii]
	; 31/07/2013 ('namei_r') - 16/06/2015 ('u.kcall')
 	inc     byte [u.kcall] ; the caller is 'namei' sign	
    	call	readi
		; jsr r0,readi / read 10. bytes of file 
		      ; with i-number (r1); i.e. read a directory entry
	mov 	ecx, [u.nread]
	or 	ecx, ecx
		; tst u.nread
	jz	short nib
		; ble nib / gives error return
	;
	mov 	bx, [u.dirbuf]
	and 	bx, bx       
		; tst u.dirbuf /
	jnz	short namei_4
		; bne 3f / branch when active directory entry 
		       ; / (i-node word in entry non zero)
	mov	eax, [u.off]
	sub	eax, 16 ; 04/12/2015 (10 -> 16) 
	mov	[u.dirp], eax
		; mov u.off,u.dirp
		; sub $10.,u.dirp
	jmp	short namei_3
		; br 2b

	; 18/07/2013
nib: 
	xor	eax, eax  ; xor ax, ax ; ax = 0 -> file not found 
	stc
nig:
	retn

namei_err:
	; 16/06/2015
	mov	dword [u.error], ERR_NOT_DIR ; 'not a directory !' error
	jmp	error

namei_4: ; 3:
	; 18/10/2015
	; 12/10/2015
	; 21/08/2015
	; 18/07/2015
	mov	ebp, [u.namep]
		; mov u.namep,r2 / u.namep points into a file name string
	mov 	edi, u.dirbuf + 2
		; mov $u.dirbuf+2,r3 / points to file name of directory entry
	; 18/10/2015
	mov	esi, [nbase]	
	mov	cx, [ncount]
	;
	and	cx, cx
	jnz	short namei_5	
	;
	call	trans_addr_nm ; convert virtual address to physical
		; esi = physical address (page start + offset)
		; ecx = byte count in the page
namei_5: ; 3:
	inc	ebp ; 18/07/2015
	lodsb   ; mov al, [esi] ; inc esi (al = r4)
		; movb (r2)+,r4 / move a character from u.namep string into r4
	or 	al, al
	jz 	short namei_7
		; beq 3f / if char is nul, then the last char in string
			; / has been moved
	cmp	al, '/'
		; cmp r4,$'/ / is char a </>
	je 	short namei_7
		; beq 3f	
	; 12/10/2015
	dec	cx ; remain byte count in the page
	jnz	short namei_6
	call	trans_addr_nm ; convert virtual address to physical
		; esi = physical address (page start + offset)
		; ecx = byte count in the page
namei_6:
        cmp     edi, u.dirbuf + 16 ; 04/12/2015 (10 -> 16) 
		; cmp r3,$u.dirbuf+10. / have I checked
				     ; / all 8 bytes of file name
	je	short namei_5
		; beq 3b
	scasb	
		; cmpb (r3)+,r4 / compare char in u.namep string to file name 
			      ; / char read from directory
	je 	short namei_5
		; beq 3b / branch if chars match

        jmp    namei_3 ; 2b
		; br 2b / file names do not match go to next directory entry
namei_7: ; 3:
	cmp	edi, u.dirbuf + 16 ; 04/12/2015 (10 -> 16) 
		; cmp r3,$u.dirbuf+10. / if equal all 8 bytes were matched
	je	short namei_8
		; beq 3f
	mov 	ah, [edi]
	;inc 	edi 
	and 	ah, ah
		; tstb (r3)+ /
        jnz     namei_3
		; bne 2b
namei_8: ; 3
	mov	[u.namep], ebp ; 18/07/2015
		; mov r2,u.namep / u.namep points to char 
			       ; / following a / or nul
	;mov	bx, [u.dirbuf]
		; mov u.dirbuf,r1 / move i-node number in directory 
				; / entry to r1
	and 	al, al
		; tst r4 / if r4 = 0 the end of file name reached,
		      ;  / if r4 = </> then go to next directory
	; mov	ax, bx
	mov 	ax, [u.dirbuf] ; 17/06/2015
        jnz     namei_2 
		; bne 1b
	; AX = i-number of the file
;;nig:
	retn
		; tst (r0)+ / gives non-error return
;;nib:
;;	xor	ax, ax ; Retro UNIX 8086 v1 modification !
		       ; ax = 0 -> file not found 
;;	stc	; 27/05/2013
;;	retn
		; rts r0

trans_addr_nmbp:
	; 18/10/2015
	; 12/10/2015
	mov 	ebp, [u.namep]
trans_addr_nm: 
	; Convert virtual (pathname) address to physical address
	; (Retro UNIX 386 v1 feature only !)
	; 18/10/2015
	; 12/10/2015 (u.pnbase & u.pncount has been removed from code)
	; 02/07/2015
	; 17/06/2015
	; 16/06/2015
	;
	; INPUTS: 
	;	ebp = pathname address (virtual) ; [u.namep]
	;	[u.pgdir] = user's page directory
	; OUTPUT:
	;       esi = physical address of the pathname
	;	ecx = remain byte count in the page
	;
	; (Modified registers: EAX, EBX, ECX, EDX, ESI)
	;
        cmp     dword [u.ppgdir], 0  ; /etc/init ? (sysexec)
	jna	short trans_addr_nmk ; the caller is os kernel;
				     ; it is already physical address
   	push	eax	
	mov	ebx, ebp ; [u.namep] ; pathname address (virtual)
       	call	get_physical_addr ; get physical address
	jc	short tr_addr_nm_err
	; 18/10/2015
	; eax = physical address 
	; cx = remain byte count in page (1-4096) 
		; 12/10/2015 (cx = [u.pncount])
	mov	esi, eax ; 12/10/2015 (esi=[u.pnbase])
	pop	eax 
	retn

tr_addr_nm_err:
	mov	[u.error], eax
	;pop 	eax
	jmp	error

trans_addr_nmk:
	; 12/10/2015
	; 02/07/2015
	mov	esi, [u.namep]  ; [u.pnbase]
	mov	cx, PAGE_SIZE ; 4096 ; [u.pncount]
	retn

syschdir:
	; / makes the directory specified in the argument
	; / the current directory
	;
	; 23/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 19/06/2013 (Retro UNIX 8086 v1)
	;
	; 'syschdir' makes the directory specified in its argument
	; the current working directory.
	;
	; Calling sequence:
	;	syschdir; name
	; Arguments:
	;	name - address of the path name of a directory
	;	       terminated by nul byte.	
	; Inputs: -
	; Outputs: -
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification:
	;	 The user/application program puts address of 
	;	 the path name in BX register as 'syschdir' 
	; 	 system call argument.

	mov	[u.namep], ebx
		;jsr r0,arg; u.namep / u.namep points to path name
	call	namei
		; jsr r0,namei / find its i-number
	;jc	error
		; br error3
	jnc	short syschdir0
	; 'directory not found !' error
	mov	dword [u.error], ERR_DIR_NOT_FOUND ; 12
	jmp	error
syschdir0:
	call	access
		; jsr r0,access; 2 / get i-node into core
	test	word [i.flgs], 4000h
		; bit $40000,i.flgs / is it a directory?
	;jz	error 
		; beq error3 / no error
	jnz	short syschdir1
	mov	dword [u.error], ERR_NOT_DIR ; 'not a valid directory !'
	jmp	error
syschdir1:
	mov	[u.cdir], ax
		; mov r1,u.cdir / move i-number to users 
			      ; / current directory
	mov	ax, [cdev]
	mov	[u.cdrv], ax
		; mov cdev,u.cdev / move its device to users 
			        ; / current device
	jmp	sysret
		; br sysret3
	
syschmod: ; < change mode of file >
	; 23/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 20/06/2013 - 07/07/2013 (Retro UNIX 8086 v1)
	;
	; 'syschmod' changes mode of the file whose name is given as
	; null terminated string pointed to by 'name' has it's mode 
	; changed to 'mode'.
	;
	; Calling sequence:
	;	syschmod; name; mode
	; Arguments:
	;	name - address of the file name
	;	       terminated by null byte.
	;	mode - (new) mode/flags < attributes >
	;	
	; Inputs: -
	; Outputs: -
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification: 
	;       'syschmod' system call has two arguments; so,
	;	* 1st argument, name is pointed to by BX register
	;	* 2nd argument, mode is in CX register
	;
	; Mode bits (Flags):
	;	bit 0 - write permission for non-owner (1)
	;	bit 1 - read permission for non-owner (2)
	;	bit 2 - write permission for owner (4)
	;	bit 3 - read permission for owner (8)
	;	bit 4 - executable flag (16) 	
	;	bit 5 - set user ID on execution flag (32) 
	;	bit 6,7,8,9,10,11 are not used (undefined)
	;	bit 12 - large file flag (4096)
	;	bit 13 - file has modified flag (always on) (8192)
	;	bit 14 - directory flag (16384)
	;	bit 15 - 'i-node is allocated' flag (32768)

	; / name; mode
	call	isown
		;jsr r0,isown / get the i-node and check user status
	test	word [i.flgs], 4000h
		; bit	$40000,i.flgs / directory?
	jz	short syschmod1
		; beq 2f / no
	; AL = (new) mode
	and	al, 0CFh ; 11001111b (clears bit 4 & 5)
		; bic $60,r2 / su & ex / yes, clear set user id and 
			   ; / executable modes
syschmod1: ; 2:
	mov	[i.flgs], al	
		; movb r2,i.flgs / move remaining mode to i.flgs
	jmp	short isown1
		; br 1f

isown:
	; 22/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 04/05/2013 - 07/07/2013 (Retro UNIX 8086 v1)
	;
	; 'isown' is given a file name (the 1st argument).
	;  It find the i-number of that file via 'namei' 
	;  then gets the i-node into core via 'iget'.
	;  It then tests to see if the user is super user. 
	;  If not, it cheks to see if the user is owner of 
	;  the file. If he is not an error occurs.
	;  If user is the owner 'setimod' is called to indicate
	;  the inode has been modificed and the 2nd argument of
	;  the call is put in r2.
	;
	; INPUTS ->
	;    arguments of syschmod and syschown calls
	; OUTPUTS ->
	;    u.uid - id of user
	;    imod - set to a 1
	;    r2 - contains second argument of the system call				 	
	;
	;   ((AX=R2) output as 2nd argument)
	;
        ; ((Modified registers: eAX, eDX, eBX, eCX, eSI, eDI, eBP))  
	;
		; jsr r0,arg2 / u.namep points to file name
	;; ! 2nd argument on top of stack !
	;; 22/06/2015 - 32 bit modifications
	;; 07/07/2013
	mov	[u.namep], ebx ;; 1st argument
	push 	ecx ;; 2nd argument
	;;
	call	namei
		; jsr r0,namei / get its i-number
       ; Retro UNIX 8086 v1 modification !
       ; ax = 0 -> file not found 
	;and	ax, ax
	;jz	error
	;jc	error ; 27/05/2013
		; br error3
	jnc	short isown0
	; 'file not found !' error
	mov	dword [u.error], ERR_FILE_NOT_FOUND ; 12
	jmp	error
isown0:
	call	iget
		; jsr r0,iget / get i-node into core
	mov	al, [u.uid] ; 02/08/2013
	or	al, al
		; tstb u.uid / super user?
	jz	short isown1
		; beq 1f / yes, branch
	cmp	al, [i.uid]
		; cmpb i.uid,u.uid / no, is this the owner of
				 ; / the file
	;jne	error
		; beq 1f / yes
		; jmp error3 / no, error
	je	short isown1

	mov	dword [u.error], ERR_NOT_OWNER  ; 11
			;  'permission denied !' error
	jmp	error
isown1: ; 1:
	call	setimod
		; jsr r0,setimod / indicates 
		;	       ; / i-node has been modified
	pop	eax ; 2nd argument
		; mov (sp)+,r2 / mode is put in r2 
		       ; / (u.off put on stack with 2nd arg)
	retn
		; rts r0

;;arg:  ; < get system call arguments >
	; 'arg' extracts an argument for a routine whose call is 
	; of form:
	;	sys 'routine' ; arg1
	;		or
	;	sys 'routine' ; arg1 ; arg2
	;		or
	;	sys 'routine' ; arg1;...;arg10 (sys exec) 
	;	
	; INPUTS ->
	;    u.sp+18 - contains a pointer to one of arg1..argn
	;	This pointers's value is actually the value of
	;	update pc at the the trap to sysent (unkni) is
	;	made to process the sys instruction
	;    r0 - contains the return address for the routine
	;	that called arg. The data in the word pointer 
	;	to by the return address is used as address
	;	in which the extracted argument is stored   		
	;    	
	; OUTPUTS ->
	;    'address' - contains the extracted argument 
	;    u.sp+18 - is incremented by 2 
	;    r1 - contains the extracted argument
	;    r0 - points to the next instruction to be
	;	 executed in the calling routine.
	;
  
	; mov u.sp,r1
	; mov *18.(r1),*(r0)+ / put argument of system call
			; / into argument of arg2
	; add $2,18.(r1) / point pc on stack 
			      ; / to next system argument
	; rts r0

;;arg2: ; < get system calls arguments - with file name pointer>
	; 'arg2' takes first argument in system call
	;  (pointer to name of the file) and puts it in location
	;  u.namep; takes second argument and puts it in u.off
	;  and on top of the stack
	;	
	; INPUTS ->
	;    u.sp, r0
	;    	
	; OUTPUTS ->
	;    u.namep
	;    u.off 
	;    u.off pushed on stack
	;    r1
	;

	; jsr	r0,arg; u.namep / u.namep contains value of
				; / first arg in sys call
	; jsr r0,arg; u.off / u.off contains value of 
				; / second arg in sys call
	; mov r0,r1 / r0 points to calling routine
	; mov (sp),r0 / put operation code back in r0
	; mov u.off,(sp) / put pointer to second argument 
			; / on stack
	; jmp (r1) / return to calling routine

syschown: ; < change owner of file >
	; 23/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 20/06/2013 - 02/08/2013 (Retro UNIX 8086 v1)
	;
	; 'syschown' changes the owner of the file whose name is given
	; as null terminated string pointed to by 'name' has it's owner
	; changed to 'owner'
	;
	; Calling sequence:
	;	syschown; name; owner
	; Arguments:
	;	name - address of the file name
	;	       terminated by null byte.
	;	owner - (new) owner (number/ID)
	;	
	; Inputs: -
	; Outputs: -
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification: 
	;       'syschown' system call has two arguments; so,
	;	* 1st argument, name is pointed to by BX register
	;	* 2nd argument, owner number is in CX register
	;
	; / name; owner
	call	isown
		; jsr r0,isown / get the i-node and check user status
	cmp 	byte [u.uid], 0 ; 02/08/2013 
		; tstb u.uid / super user
	jz	short syschown1
		; beq 2f / yes, 2f
        test    byte [i.flgs], 20h ; 32
		; bit $40,i.flgs / no, set userid on execution?
	;jnz	error
		; bne 3f / yes error, could create Trojan Horses
	jz	short syschown1
	; 'permission denied !'
	mov	dword [u.error], ERR_FILE_ACCESS  ; 11
	jmp	error
syschown1: ; 2:
	; AL = owner (number/ID)
	mov	[i.uid], al ; 23/06/2015
		;  movb	r2,i.uid / no, put the new owners id 
			       ; / in the i-node
	jmp	sysret
	; 1: 
		; jmp sysret4
	; 3:
		; jmp	error

systime: ; / get time of year
	; 23/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 20/06/2013 (Retro UNIX 8086 v1)
	;
	; 20/06/2013
	; 'systime' gets the time of the year.
	; The present time is put on the stack.
	;
	; Calling sequence:
	;	systime
	; Arguments: -
	;	
	; Inputs: -
	; Outputs: sp+2, sp+4 - present time
	; ...............................................................
	;	
	; Retro UNIX 8086 v1 modification: 
	;       'systime' system call will return to the user
	;	with unix time (epoch) in DX:AX register pair
	;
	; 	!! Major modification on original Unix v1 'systime' 
	;	system call for PC compatibility !!		 	

	call 	epoch
	mov 	[u.r0], eax
		; mov s.time,4(sp)
		; mov s.time+2,2(sp) / put the present time 
				   ; / on the stack
		; br sysret4
	jmp	sysret 

sysstime: ; / set time
	; 23/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 20/06/2013 - 02/08/2013 (Retro UNIX 8086 v1)
	;
	; 'sysstime' sets the time. Only super user can use this call.
	;
	; Calling sequence:
	;	sysstime
	; Arguments: -
	;	
	; Inputs: sp+2, sp+4 - time system is to be set to.
	; Outputs: -
	; ...............................................................
	;	
	; Retro UNIX 8086 v1 modification: 
	;	the user calls 'sysstime' with unix (epoch) time
	;	(to be set) is in CX:BX register pair as two arguments.
	; 
	;	Retro UNIX 8086 v1 argument transfer method 2 is used
	;	to get sysstime system call arguments from the user;
	;	* 1st argument, lowword of unix time is in BX register
	;	* 2nd argument, highword of unix time is in CX register		 	
	;
	; 	!! Major modification on original Unix v1 'sysstime' 
	;	system call for PC compatibility !!	

	cmp	byte [u.uid], 0
		; tstb u.uid / is user the super user
	;ja	error
		; bne error4 / no, error
	jna	short systime1
	; 'permission denied !'
	mov	dword [u.error], ERR_NOT_SUPERUSER  ; 11 
	jmp	error
systime1:
	; 23/06/2015 (Retro UNIX 386 v1 - 32 bit version)
	; EBX = unix (epoch) time (from user)
	mov	eax, ebx
	call 	set_date_time
		; mov 4(sp),s.time
		; mov 2(sp),s.time+2 / set the system time
	jmp	sysret
		; br sysret4

sysbreak:
	; 18/10/2015
	; 07/10/2015
	; 23/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 20/06/2013 - 24/03/2014 (Retro UNIX 8086 v1)
	;
	; 'sysbreak' sets the programs break points. 
	; It checks the current break point (u.break) to see if it is
	; between "core" and the stack (sp). If it is, it is made an
	; even address (if it was odd) and the area between u.break
	; and the stack is cleared. The new breakpoint is then put
	; in u.break and control is passed to 'sysret'.
	;
	; Calling sequence:
	;	sysbreak; addr
	; Arguments: -
	;	
	; Inputs: u.break - current breakpoint
	; Outputs: u.break - new breakpoint 
	;	area between old u.break and the stack (sp) is cleared.
	; ...............................................................
	;	
	; Retro UNIX 8086 v1 modification:
	;	The user/application program puts breakpoint address
	;       in BX register as 'sysbreak' system call argument.
	; 	(argument transfer method 1)
	;
	;  NOTE: Beginning of core is 0 in Retro UNIX 8086 v1 !
	; 	((!'sysbreak' is not needed in Retro UNIX 8086 v1!))
	;  NOTE:
	; 	'sysbreak' clears extended part (beyond of previous
	;	'u.break' address) of user's memory for original unix's
	;	'bss' compatibility with Retro UNIX 8086 v1 (19/11/2013)

		; mov u.break,r1 / move users break point to r1
		; cmp r1,$core / is it the same or lower than core?
		; blos 1f / yes, 1f
	; 23/06/2015
	mov	ebp, [u.break] ; virtual address (offset)
	;and	ebp, ebp
	;jz	short sysbreak_3 
	; Retro UNIX 386 v1 NOTE: u.break points to virtual address !!!
	; (Even break point address is not needed for Retro UNIX 386 v1)
	mov	edx, [u.sp] ; kernel stack at the beginning of sys call
	add	edx, 12 ; EIP -4-> CS -4-> EFLAGS -4-> ESP (user) 
	; 07/10/2015
	mov	[u.break], ebx ; virtual address !!!
	;
	cmp	ebx, [edx] ; compare new break point with 
			   ; with top of user's stack (virtual!)
	jnb	short sysbreak_3
		; cmp r1,sp / is it the same or higher 
			  ; / than the stack?
		; bhis 1f / yes, 1f
	mov	esi, ebx
	sub	esi, ebp ; new break point - old break point
	jna	short sysbreak_3 
	;push	ebx
sysbreak_1:
	mov	ebx, ebp  
	call	get_physical_addr ; get physical address
	jc	tr_addr_nm_err
	; 18/10/2015
	mov	edi, eax 
	sub	eax, eax ; 0
		 ; ECX = remain byte count in page (1-4096)
	cmp	esi, ecx
	jnb	short sysbreak_2
	mov	ecx, esi
sysbreak_2:
	sub	esi, ecx
	add	ebp, ecx
	rep 	stosb
	or	esi, esi
	jnz	short sysbreak_1
	;
		; bit $1,r1 / is it an odd address
		; beq 2f / no, its even
		; clrb (r1)+ / yes, make it even
	; 2: / clear area between the break point and the stack
		; cmp r1,sp / is it higher or same than the stack
		; bhis 1f / yes, quit
		; clr (r1)+ / clear word
		; br 2b / go back
	;pop	ebx
sysbreak_3: ; 1:
	;mov	[u.break], ebx ; virtual address !!!
		; jsr r0,arg; u.break / put the "address" 
			; / in u.break (set new break point)
		; br sysret4 / br sysret
	jmp	sysret


maknod: 
	; 22/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 02/05/2013 - 02/08/2013 (Retro UNIX 8086 v1)
	;
	; 'maknod' creates an i-node and makes a directory entry
	; for this i-node in the current directory.
	;
	; INPUTS ->
	;    r1 - contains mode
	;    ii - current directory's i-number	
	;    	
	; OUTPUTS ->
	;    u.dirbuf - contains i-number of free i-node 
	;    i.flgs - flags in new i-node 
	;    i.uid - filled with u.uid
	;    i.nlks - 1 is put in the number of links
	;    i.ctim - creation time				
	;    i.ctim+2 - modification time
	;    imod - set via call to setimod
	;	
	; ((AX = R1)) input
	;
	; (Retro UNIX Prototype : 
	;	30/10/2012 - 01/03/2013, UNIXCOPY.ASM)
        ; ((Modified registers: eAX, eDX, eBX, eCX, eSI, eDI, eBP))  

	; / r1 contains the mode
	or 	ah, 80h  ; 10000000b
		; bis	$100000,r1 / allocate flag set
	push	ax
		; mov r1,-(sp) / put mode on stack
	; 31/07/2013
	mov	ax, [ii] ; move current i-number to AX/r1
		; mov ii,r1 / move current i-number to r1
	mov	dl, 1 ; owner flag mask
	call	access	
		; jsr r0,access; 1 / get its i-node into core
	push	ax
		; mov r1,-(sp) / put i-number on stack
	mov	ax, 40
		; mov $40.,r1 / r1 = 40
maknod1: ; 1: / scan for a free i-node (next 4 instructions)
	inc	ax
		; inc r1 / r1 = r1 + 1
	call	imap
		; jsr r0,imap / get byte address and bit position in 
			    ; /	inode map in r2 & m
          ; DX (MQ) has a 1 in the calculated bit position
          ; eBX (R2) has byte address of the byte with allocation bit
	; 22/06/2015 - NOTE for next Retro UNIX version: 
	;	       Inode count must be checked here
	; (Original UNIX v1 did not check inode count here !?) 	
	test	[ebx], dl
		; bitb mq,(r2) / is the i-node active
	jnz	short maknod1
		; bne 1b / yes, try the next one
	or	[ebx], dl
		; bisb mq,(r2) / no, make it active 
			     ; / (put a 1 in the bit map)
	call	iget
		; jsr r0,iget / get i-node into core
	test	word [i.flgs], 8000h 
		; tst i.flgs / is i-node already allocated
	jnz	short maknod1	
		; blt 1b / yes, look for another one
	mov	[u.dirbuf], ax
		; mov r1,u.dirbuf / no, put i-number in u.dirbuf
	pop	ax
		; mov (sp)+,r1 / get current i-number back
	call	iget
		; jsr r0,iget / get i-node in core
	call	mkdir
		; jsr r0,mkdir / make a directory entry 
			     ; / in current directory
	mov	ax, [u.dirbuf]
		; mov u.dirbuf,r1 / r1 = new inode number
	call	iget
		; jsr r0,iget / get it into core
		; jsr r0,copyz; inode; inode+32. / 0 it out
	mov	ecx, 8 
	xor	eax, eax ; 0
	mov	edi, inode 
	rep	stosd
	;
	pop	word [i.flgs]
		; mov (sp)+,i.flgs / fill flags
	mov 	cl, [u.uid] ; 02/08/2013
	mov 	[i.uid], cl
		; movb u.uid,i.uid / user id	
	mov     byte [i.nlks], 1
		; movb $1,i.nlks / 1 link
	;call	epoch ; Retro UNIX 8086 v1 modification !
	;mov	eax, [s.time]
	;mov 	[i.ctim], eax
	 	; mov s.time,i.ctim / time created
	 	; mov s.time+2,i.ctim+2 / time modified
	; Retro UNIX 8086 v1 modification !
	; i.ctime=0, i.ctime+2=0 and
        ; 'setimod' will set ctime of file via 'epoch'
	call setimod
		; jsr r0,setimod / set modified flag
	retn
		; rts r0 / return

sysseek: ; / moves read write pointer in an fsp entry
	; 22/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 07/07/2013 - 05/08/2013 (Retro UNIX 8086 v1)
	;
	; 'sysseek' changes the r/w pointer of (3rd word of in an
	; fsp entry) of an open file whose file descriptor is in u.r0.
	; The file descriptor refers to a file open for reading or
	; writing. The read (or write) pointer is set as follows:
	;	* if 'ptrname' is 0, the pointer is set to offset.
	;	* if 'ptrname' is 1, the pointer is set to its
	;	  current location plus offset.
	;	* if 'ptrname' is 2, the pointer is set to the
	;	  size of file plus offset.
	; The error bit (e-bit) is set for an undefined descriptor.
	;
	; Calling sequence:
	;	sysseek; offset; ptrname
	; Arguments:
	;	offset - number of bytes desired to move 
	;		 the r/w pointer
	;	ptrname - a switch indicated above
	;
	; Inputs: r0 - file descriptor 
	; Outputs: -
	; ...............................................................
	;	
	; Retro UNIX 8086 v1 modification: 
	;       'sysseek' system call has three arguments; so,
	;	* 1st argument, file descriptor is in BX (BL) register
	;	* 2nd argument, offset is in CX register
	;	* 3rd argument, ptrname/switch is in DX (DL) register	
	;	

	call	seektell
	; AX = u.count
	; BX = *u.fofp
		; jsr r0,seektell / get proper value in u.count
		; add u.base,u.count / add u.base to it
	add	eax, [u.base] ; add offset (u.base) to base
	mov	[ebx], eax
		; mov u.count,*u.fofp / put result into r/w pointer
	jmp	sysret
		; br sysret4

systell: ; / get the r/w pointer
	; 22/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 07/07/2013 - 05/08/2013 (Retro UNIX 8086 v1)
	;
	; Retro UNIX 8086 v1 modification:
	; ! 'systell' does not work in original UNIX v1,
	; 	    it returns with error !
	; Inputs: r0 - file descriptor 
	; Outputs: r0 - file r/w pointer

	;xor	ecx, ecx ; 0
	mov	edx, 1 ; 05/08/2013
	;call 	seektell
	call 	seektell0 ; 05/08/2013
	;mov	ebx, [u.fofp]
	mov	eax, [ebx]
	mov	[u.r0], eax
	jmp	sysret

; Original unix v1 'systell' system call:
		; jsr r0,seektell
		; br error4

seektell:
	; 03/01/2016
	; 22/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 07/07/2013 - 05/08/2013 (Retro UNIX 8086 v1)
	;
	; 'seektell' puts the arguments from sysseek and systell
	; call in u.base and u.count. It then gets the i-number of
	; the file from the file descriptor in u.r0 and by calling
	; getf. The i-node is brought into core and then u.count
	; is checked to see it is a 0, 1, or 2.
	; If it is 0 - u.count stays the same
	;          1 - u.count = offset (u.fofp)
	;	   2 - u.count = i.size (size of file)
	; 	 		
	; !! Retro UNIX 8086 v1 modification:
	;	Argument 1, file descriptor is in BX;
	;	Argument 2, offset is in CX;
	;	Argument 3, ptrname/switch is in DX register.	
	;
	; mov 	ax, 3 ; Argument transfer method 3 (three arguments)	
	; call 	arg
	;
	; ((Return -> ax = base for offset (position= base+offset))
	;
	mov 	[u.base], ecx ; offset
		; jsr r0,arg; u.base / puts offset in u.base
seektell0:
	mov 	[u.count], edx
		; jsr r0,arg; u.count / put ptr name in u.count
	; mov	ax, bx
		; mov *u.r0,r1 / file descriptor in r1 
			     ; / (index in u.fp list)
	; call	getf
		; jsr r0,getf / u.fofp points to 3rd word in fsp entry
	; BX = file descriptor (file number)
	call	getf1
	or	ax, ax ; i-number of the file
		; mov r1,-(sp) / r1 has i-number of file, 
		             ; / put it on the stack
	;jz	error
		; beq error4 / if i-number is 0, not active so error
	jnz	short seektell1
	mov	dword [u.error], ERR_FILE_NOT_OPEN  ; 'file not open !'
	jmp	error
seektell1:
	;push	eax
	cmp	ah, 80h
	jb	short seektell2
		; bgt .+4 / if its positive jump
	neg	ax
		; neg r1 / if not make it positive
seektell2:
	call	iget
		; jsr r0,iget / get its i-node into core
        mov     ebx, [u.fofp] ; 05/08/2013
	cmp	byte [u.count], 1
		; cmp u.count,$1 / is ptr name =1
	ja	short seektell3
		; blt 2f / no its zero
	je	short seektell_4
		; beq 1f / yes its 1
	xor	eax, eax
	;jmp	short seektell_5
	retn
seektell3:
	; 03/01/2016
	;movzx	eax, word [i.size]
        mov   	ax, [i.size]
                ; mov i.size,u.count /  put number of bytes 
                                   ; / in file in u.count
	;jmp	short seektell_5
		; br 2f
	retn
seektell_4: ; 1: / ptrname =1
	;mov	ebx, [u.fofp]
	mov	eax, [ebx]
		; mov *u.fofp,u.count / put offset in u.count
;seektell_5: ; 2: / ptrname =0
	;mov	[u.count], eax
	;pop	eax 
		; mov (sp)+,r1 / i-number on stack  r1
	retn
		; rts r0

sysintr: ; / set interrupt handling
	; 22/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 07/07/2013 (Retro UNIX 8086 v1)
	;
	; 'sysintr' sets the interrupt handling value. It puts
	; argument of its call in u.intr then branches into 'sysquit'
	; routine. u.tty is checked if to see if a control tty exists.
	; If one does the interrupt character in the tty buffer is
	; cleared and 'sysret'is called. If one does not exits
	; 'sysret' is just called.	
	;
	; Calling sequence:
	;	sysintr; arg
	; Argument:
	;	arg - if 0, interrupts (ASCII DELETE) are ignored.
	;	    - if 1, intterupts cause their normal result
	;		 i.e force an exit.
	;	    - if arg is a location within the program,
	;		control is passed to that location when
	;		an interrupt occurs.	
	; Inputs: -
	; Outputs: -
	; ...............................................................
	;	
	; Retro UNIX 8086 v1 modification: 
	;       'sysintr' system call sets u.intr to value of BX
	;	then branches into sysquit.
	;
	mov	[u.intr], bx
		; jsr r0,arg; u.intr / put the argument in u.intr
		; br 1f / go into quit routine
	jmp	sysret

sysquit:
	; 22/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 07/07/2013 (Retro UNIX 8086 v1)
	;
	; 'sysquit' turns off the quit signal. it puts the argument of
	; the call in u.quit. u.tty is checked if to see if a control 
	; tty exists. If one does the interrupt character in the tty
	; buffer is cleared and 'sysret'is called. If one does not exits
	; 'sysret' is just called.	
	;
	; Calling sequence:
	;	sysquit; arg
	; Argument:
	;	arg - if 0, this call diables quit signals from the
	;		typewriter (ASCII FS)
	;	    - if 1, quits are re-enabled and cause execution to
	;		cease and a core image to be produced.
	;		 i.e force an exit.
	;	    - if arg is an addres in the program,
	;		a quit causes control to sent to that
	;		location.	
	; Inputs: -
	; Outputs: -
	; ...............................................................
	;	
	; Retro UNIX 8086 v1 modification: 
	;       'sysquit' system call sets u.quit to value of BX
	;	then branches into 'sysret'.
	;
	mov	[u.quit], bx
	jmp	sysret
		; jsr r0,arg; u.quit / put argument in u.quit
	;1:
		; mov u.ttyp,r1 / move pointer to control tty buffer
			      ; / to r1
		; beq sysret4 / return to user
		; clrb 6(r1) / clear the interrupt character 
			   ; / in the tty buffer
		; br sysret4 / return to user

syssetuid: ; / set process id
	; 22/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 07/07/2013 - 02/08/2013 (Retro UNIX 8086 v1)
	;
	; 'syssetuid' sets the user id (u.uid) of the current process
	; to the process id in (u.r0). Both the effective user and 
	; u.uid and the real user u.ruid are set to this. 
	; Only the super user can make this call.	
	;
	; Calling sequence:
	;	syssetuid
	; Arguments: -
	;
	; Inputs: (u.r0) - contains the process id.
	; Outputs: -
	; ...............................................................
	;	
	; Retro UNIX 8086 v1 modification: 
	;       BL contains the (new) user ID of the current process

		; movb *u.r0,r1 / move process id (number) to r1
	cmp	bl, [u.ruid] 
		; cmpb r1,u.ruid / is it equal to the real user 
			       ; / id number
	je	short setuid1
		; beq 1f / yes
	cmp	byte [u.uid], 0 ; 02/08/2013
		; tstb u.uid / no, is current user the super user?
	;ja	error
		; bne error4 / no, error
	jna	short setuid0
	mov	dword [u.error], ERR_NOT_SUPERUSER  ; 11
				;  'permission denied !' error
	jmp	error
setuid0:
	mov	[u.ruid], bl
setuid1: ; 1:
	mov	[u.uid], bl ; 02/08/2013
		; movb r1,u.uid / put process id in u.uid
		; movb r1,u.ruid / put process id in u.ruid
	jmp	sysret
		; br sysret4 / system return

sysgetuid: ; < get user id >
	; 22/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 07/07/2013 (Retro UNIX 8086 v1)
	;
	; 'sysgetuid' returns the real user ID of the current process.
	; The real user ID identifies the person who is logged in,
	; in contradistinction to the effective user ID, which
	; determines his access permission at each moment. It is thus
	; useful to programs which operate using the 'set user ID'
	; mode, to find out who invoked them.	
	;
	; Calling sequence:
	;	syssetuid
	; Arguments: -
	;
	; Inputs: -
	; Outputs: (u.r0) - contains the real user's id.
	; ...............................................................
	;	
	; Retro UNIX 8086 v1 modification: 
	;       AL contains the real user ID at return.
	;
	movzx 	eax, byte [u.ruid]
	mov	[u.r0], eax
		; movb	u.ruid,*u.r0 / move the real user id to (u.r0)
	jmp	sysret
		; br sysret4 / systerm return, sysret

anyi: 
	; 22/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 25/04/2013 (Retro UNIX 8086 v1)
	;
	; 'anyi' is called if a file deleted while open.
	; "anyi" checks to see if someone else has opened this file.
	;
	; INPUTS ->
	;    r1 - contains an i-number
	;    fsp - start of table containing open files
	;
	; OUTPUTS ->
	;    "deleted" flag set in fsp entry of another occurrence of
	;	   this file and r2 points 1st word of this fsp entry.
	;    if file not found - bit in i-node map is cleared
	;    			 (i-node is freed)
	;               all blocks related to i-node are freed
	;	        all flags in i-node are cleared
	; ((AX = R1)) input
	;
	;    (Retro UNIX Prototype : 02/12/2012, UNIXCOPY.ASM)
        ;    ((Modified registers: eDX, eCX, eBX, eSI, eDI, eBP))  
	;
		; / r1 contains an i-number
	mov	ebx, fsp
		; mov $fsp,r2 / move start of fsp table to r2
anyi_1: ; 1:
	cmp	ax, [ebx]
		; cmp r1,(r2) / do i-numbers match?
	je	short anyi_3
		; beq 1f / yes, 1f
	neg	ax
		; neg r1 / no complement r1
	cmp	ax, [ebx]
		; cmp r1,(r2) / do they match now?
	je	short anyi_3
		; beq 1f / yes, transfer
		; / i-numbers do not match
	add	ebx, 10 ; fsp table size is 10 bytes
			; in Retro UNIX 386 v1 (22/06/2015)
		; add $8,r2 / no, bump to next entry in fsp table
	cmp	ebx, fsp + (nfiles*10) ; 22/06/2015 
		; cmp r2,$fsp+[nfiles*8] 
				; / are we at last entry in the table
	jb	short anyi_1
		; blt 1b / no, check next entries i-number
	;cmp	ax, 32768
	cmp	ah, 80h ; negative number check
		; tst r1 / yes, no match
		; bge .+4
	jb	short anyi_2
	neg	ax
		; neg r1 / make i-number positive
anyi_2:	
	call	imap
		; jsr r0,imap / get address of allocation bit 
			    ; / in the i-map in r2
	;; DL/DX (MQ) has a 1 in the calculated bit position
        ;; eBX (R2) has address of the byte with allocation bit
 	; not	dx
	not 	dl ;; 0 at calculated bit position, other bits are 1
        ;and	[ebx], dx
	and 	[ebx], dl 
		; bicb mq,(r2) / clear bit for i-node in the imap
	call	itrunc
		; jsr r0,itrunc / free all blocks related to i-node
 	mov 	word [i.flgs], 0
		; clr i.flgs / clear all flags in the i-node
	retn
		;rts	r0 / return
anyi_3: ; 1: / i-numbers match
	inc 	byte [ebx+9] ; 22/06/2015
		;incb 7(r2) / increment upper byte of the 4th word
		   ; / in that fsp entry (deleted flag of fsp entry)
	retn
		; rts r0

; Retro UNIX 386 v1 Kernel (v0.2) - u7.s
; Last Modification: 14/11/2015

sysmount: ; / mount file system; args special; name
	; 14/11/2015
	; 24/10/2015
	; 13/10/2015
	; 10/07/2015
	; 16/05/2015 (Retro UNIX 386 v1 - Beginning)
	; 09/07/2013 - 04/11/2013 (Retro UNIX 8086 v1)
	;
	; 'sysmount' anounces to the system that a removable 
	; file system has been mounted on a special file.
	; The device number of the special file is obtained via
	; a call to 'getspl'. It is put in the I/O queue entry for
	; dismountable file system (sb1) and the I/O queue entry is
	; set up to read (bit 10 is set). 'ppoke' is then called to
	; to read file system into core, i.e. the first block on the
	; mountable file system is read in. This block is super block
	; for the file system. This call is super user restricted.	
	;
	; Calling sequence:
	;	sysmount; special; name
	; Arguments:
	;	special - pointer to name of special file (device)
	;	name -  pointer to name of the root directory of the
	;		newly mounted file system. 'name' should 
	;		always be a directory.
	; Inputs: - 
	; Outputs: -
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification: 
	;       'sysmount' system call has two arguments; so,
	;	* 1st argument, special is pointed to by BX register
	;	* 2nd argument, name is in CX register
	;
	;	NOTE: Device numbers, names and related procedures are 
	;	       already modified for IBM PC compatibility and 
	;	       Retro UNIX 8086 v1 device configuration.	
	
	;call	arg2
		; jsr r0,arg2 / get arguments special and name
	mov	[u.namep], ebx
	push	ecx ; directory name
	cmp	word [mnti], 0
		; tst mnti / is the i-number of the cross device file
			 ; / zero?
	;ja	error
        	; bne errora / no, error
	ja	sysmnt_err0
	;
	call	getspl
		; jsr r0,getspl / get special files device number in r1
	; 13/10/2015
	movzx	ebx, ax ; ; Retro UNIX 8086 v1 device number (0 to 5)
        test    byte [ebx+drv.status], 80h ; 24/10/2015 
	jnz	short sysmnt_1
sysmnt_err1:
        mov     dword [u.error], ERR_DRV_NOT_RDY ; drive not ready !
	jmp	error
sysmnt_1:
	pop	dword [u.namep]
        	; mov (sp)+,u.namep / put the name of file to be placed
				  ; / on the device
	; 14/11/2015
	push	ebx ; 13/10/2015
		; mov r1,-(sp) / save the device number
        ;
	call	namei
	;or	ax, ax ; Retro UNIX 8086 v1 modification !
		       ; ax = 0 -> file not found 	
	;jz	error
	;jc	error
		; jsr r0,namei / get the i-number of the file
               	; br errora
	jnc	short sysmnt_2
sysmnt_err2:
        mov     dword [u.error], ERR_FILE_NOT_FOUND ; drive not ready !
	jmp	error
sysmnt_2:	
	mov	[mnti], ax
        	; mov r1,mnti / put it in mnti
;	mov	ebx, sb1 ; super block buffer (of mounted disk)
sysmnt_3: ;1:
        ;cmp	byte [ebx+1], 0
		; tstb sb1+1 / is 15th bit of I/O queue entry for
			   ; / dismountable device set?
        ;jna	short sysmnt_4		
		; bne 1b / (inhibit bit) yes, skip writing
	;call	idle 	; (wait for hardware interrupt)
	;jmp	short sysmnt_3
sysmnt_4:   
	pop	eax ; Retro UNIX 8086 v1 device number/ID (0 to 5)     
	mov	[mdev], al
		; mov  (sp),mntd / no, put the device number in mntd
	mov	[ebx], al
        	; movb (sp),sb1 / put the device number in the lower byte
			      ; / of the I/O queue entry
	;mov	byte [cdev], 1 ; mounted device/drive
        	; mov (sp)+,cdev / put device number in cdev
        or	word [ebx], 400h ; Bit 10, 'read' flag/bit
		; bis $2000,sb1 / set the read bit
	; Retro UNIX 386 v1 modification : 
	;	32 bit block number at buffer header offset 4
	mov	dword [ebx+4], 1 ; physical block number = 1
	call 	diskio
	jnc	short sysmnt_5
	xor 	eax, eax
	mov	[mnti], ax ; 0
	mov	[mdev], al ; 0
	;mov	[cdev], al ; 0
sysmnt_invd:
	; 14/11/2015
	dec 	al
	mov	[ebx], eax ; 000000FFh
	inc	al
	dec	eax
	mov	[ebx+4], eax ; 0FFFFFFFFh
	jmp	error
sysmnt_5:
	; 14/11/2015 (Retro UNIX 386 v1 modification)
	; (Following check is needed to prevent mounting an
	; in valid valid file system (in valid super block).
	; 
	movzx	eax, byte [ebx] ; device number
	shl	al, 2 ; 4*index
	mov	ecx, [eax+drv.size] ; volume (fs) size
	shl 	ecx, 3
	movzx	edx, word [sb1+4] ; the 1st data word
	cmp	ecx, edx ; compare free map bits and volume size
			 ; (in sectors), if they are not equal
			 ; the disk to be mounted is an...	
	jne	short sysmnt_invd ; invalid disk !
			 ; (which has not got a valid super block)
	;
	mov	byte [ebx+1], 0
	       	; jsr r0,ppoke / read in entire file system
;sysmnt_6: ;1:
	;;cmp	byte [sb1+1], 0
		; tstb   sb1+1 / done reading?
   	;;jna	sysret
	;;call	idle ; (wait for hardware interrupt)
	;;jmp	short sysmnt_6
		;bne 1b / no, wait
        	;br sysreta / yes
	jmp	sysret

sysumount: ; / special dismount file system
	; 16/05/2015 (Retro UNIX 386 v1 - Beginning)
	; 09/07/2013 - 04/11/2013 (Retro UNIX 8086 v1)
	;
	; 04/11/2013
	; 09/07/2013
	; 'sysumount' anounces to the system that the special file, 
	; indicated as an argument is no longer contain a removable
	; file system. 'getspl' gets the device number of the special
	; file. If no file system was mounted on that device an error
	; occurs. 'mntd' and 'mnti' are cleared and control is passed
	; to 'sysret'.
	;
	; Calling sequence:
	;	sysmount; special
	; Arguments:
	;	special - special file to dismount (device)
	;
	; Inputs: - 
	; Outputs: -
	; ...............................................................
	;				
	; Retro UNIX 8086 v1 modification: 
	;       'sysumount' system call has one argument; so,
	;	* Single argument, special is pointed to by BX register
	;
	
	;mov 	ax, 1 ; one/single argument, put argument in BX	
	;call	arg
		; jsr r0,arg; u.namep / point u.namep to special
        mov	[u.namep], ebx
	call	getspl
		; jsr r0,getspl / get the device number in r1
	cmp	al, [mdev]
		; cmp r1,mntd / is it equal to the last device mounted?
	jne	short sysmnt_err0 ; 'permission denied !' error
	;jne	error
        	; bne errora / no error
	xor	al, al ; ah = 0
sysumnt_0: ;1:
     	cmp 	[sb1+1], al ; 0
		; tstb sb1+1 / yes, is the device still doing I/O 
			   ; / (inhibit bit set)?
	jna	short sysumnt_1		
		; bne 1b / yes, wait
	call	idle ; (wait for hardware interrupt)
	jmp	short sysumnt_0
sysumnt_1:        
	mov	[mdev], al
	     	; clr mntd / no, clear these
   	mov	[mnti], ax
        	; clr mnti
        jmp	sysret
		; br sysreta / return

getspl: ; / get device number from a special file name
	call	namei
	;or	ax, ax ; Retro UNIX 8086 v1 modification !
		       ; ax = 0 -> file not found 	
        jc      sysmnt_err2 ; 'file not found !' error
	;jz	error
	;jc	error
		; jsr r0,namei / get the i-number of the special file
                ; br errora / no such file
        sub	ax, 3 ; Retro UNIX 8086 v1 modification !
		      ;	i-number-3, 0 = fd0, 5 = hd3 
		; sub $4,r1 / i-number-4 rk=1,tap=2+n
        jc	short sysmnt_err0 ; 'permission denied !' error
	;jc	error
		; ble errora / less than 0?  yes, error
        cmp	ax, 5 ;
		; cmp  r1,$9. / greater than 9  tap 7
	ja	short sysmnt_err0 ; 'permission denied !' error
	;ja	error
        	; bgt errora / yes, error
        ; AX = Retro UNIX 8086 v1 Device Number (0 to 5)
iopen_retn:
	retn
		; rts    r0 / return with device number in r1
sysmnt_err0:
	mov	dword [u.error], ERR_FILE_ACCESS ; permission denied !
	jmp	error

; Retro UNIX 386 v1 Kernel (v0.2) - SYS9.INC
; Last Modification: 09/12/2015

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 -> 
	;   BH = video page number	 
	;
	; OUTPUT ->
	;   none
	; ((Modified registers: eAX, BH, eCX, eDX, eSI, eDI))
	;
	; 04/12/2013
	sub	al, al
	; al = 0 (clear video page)
	; bh = video page ; 13/05/2016
	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
	; bh = video page
	xor	dx, dx ; 0 (cursor position) 
	jmp 	_set_cpos

sysmsg:
	; 13/05/2016
	; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 01/07/2015 - 11/11/2015 (Retro UNIX 386 v1)
	; 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 ; ASCIIZ
	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: 
	; 13/05/2016 - TRDOS 386 (TRDOS v2.0)
	; 01/07/2015 (Retro UNIX 386 v1)
	;
	; 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)
	
	mov	bh, [u.ttyn]
	;mov	bh, ah

	lodsb
pcmsg1:
	push 	esi
	mov	bl, [ccolor]
	;mov	bh, [u.ttyn]
	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

load_and_run_file:
	; 06/06/2016
	; 06/05/2016
	; 03/05/2016
	; 02/05/2016
	; 24/04/2016
	; 23/04/2016 (TRDOS 386 = TRDOS v2.0)
	; 23/10/2015 (Retro UNIX 386 v1, 'sysexec')
	; 23/06/2015 (Retro UNIX 386 v1 - Beginning)
	; 03/06/2013 - 06/12/2013 (Retro UNIX 8086 v1)
	; EAX = First Cluster number
	; EDX = File Size
	; ESI = Argument list address
	; [argc] = argument count
	; [u.nread] = argument list length
	; [esp] = return address to the caller (*)
	;
	mov	[argv], esi
	mov	[i.size], edx
	mov	[ii], eax

	; 06/05/2016
	; Set 'sysexit' return order to MainProg
	;
	pop	eax ; * 'loc_load_and_run_file_8:' address
	mov	esp, [tss.esp0]
	;
	; 'loc_load_run_file_8' address has 
	; 'jmp loc_file_rw_restore_retn' instruction
	; 'loc_file_rw_restore_retn:' will return to
	; [mainprog_return_addr] 
	; just after 'call command_interpreter'
	;
	push	_end_of_mainprog ; we must not return to here !
	push	dword [mainprog_return_addr]
	mov	ebp, esp ; **
	;	
	pushfd  ; EFLAGS      ; IRETD
	push	KCODE ; cs    ; IRETD
	push	eax ; * (eip) ; IRETD
	mov	[u.sp], esp
	push	ds
	push	es
	push	fs
	push	gs	
	pushad
	push	sysret
	mov	[u.usp], esp
	;
	call	wswap ; Save MainProg (process 1) 'u' structure
		      ; and registers for return (from program)	
	mov	esp, ebp ; **
	push	eax  ; * 'loc_load_and_run_file_8:' address
	;
	;;; 02/05/2016
	;;; Create a new process (parent: MainProg)	
	xor 	esi, esi
cnpm_1: ; search p.stat table for unused process number
	inc	esi
	cmp	byte [esi+p.stat-1], 0 ; SFREE
				; is process active, unused, dead
	jna	short cnpm_2	; it's unused so branch
	cmp	si, nproc 	; all processes checked
	jb	short cnpm_1    ; no, branch back
	jmp	panic 
cnpm_2:
	mov	eax, [u.pgdir] ; page directory of MainProg
	mov	[u.ppgdir], eax ; parent's page directory
	call	allocate_page
	jc	panic
	; EAX = UPAGE (user structure page) address
	mov	[u.upage], eax ; memory page for 'user' struct (child)
	mov	edi, esi
	shl	di, 2
	mov	[edi+p.upage-4], eax ; memory page for 'user' struct
	call	clear_page ; 03/05/2016
	;movzx	eax, byte [p.ttyc] ; console tty (for MainProg)
	sub	ax, ax ; 0
	mov     [esi+p.ttyc-1], ax ; al - set child's console tty
				   ; ah - reset child's wait channel	
	mov	eax, esi
	mov	[u.uno], al ; child process number
        inc     byte [esi+p.stat-1] ; 1, SRUN
	shl	si, 1 ; multiply si by 2 to get index into p.pid table
	inc	word [mpid] ; increment m.pid; get a new process name
	mov	ax, [mpid]
	mov	[esi+p.pid-2], ax ; put new process name 
				  ; in child process' name slot
	;mov	ax, [p.pid]  ; get process name of MainProg
	mov	ax, 1
	mov	[esi+p.ppid-2], ax ; put parent process name 
			           ; in parent process slot for child
	dec	ax ; 0
	mov 	[u.ttyp], ax ; 0
	;;;
	mov 	eax,  [ii]
	; Retro UNIX 386 v1, 'sysexec' (u2.s)
	call	iopen
	; 06/06/2016
	mov	byte [u.pri], 1 ; normal priority
	;
	jmp	short sysexec_7 ; 02/05/2016

sysexec_6:
	;;02/05/2016
	; 23/04/2016
	; 18/10/2015 ('sysexec_6')
	; 23/06/2015
	;;mov	eax, [u.pgdir] ; parent's page directory
	;;cmp 	eax, [k_page_dir] ; TRDOS MainProg ? 
	;;je	short sysexec_7
	mov	eax, [u.pgdir] ; physical address of page directory
	call	deallocate_page_dir
sysexec_7:
	call	make_page_dir
	jc	panic  ; allocation error 
		       ; after a deallocation would be nonsence !?
	; 24/07/2015
	; map kernel pages (1st 4MB) to PDE 0
	;     of the user's page directory
	;     (It is needed for interrupts!)
	; 18/10/2015
	mov	edx, [k_page_dir] ; Kernel's page directory
	mov	eax, [edx] ; physical address of
			   ; kernel's first page table (1st 4 MB)
			   ; (PDE 0 of kernel's page directory)
	mov 	edx, [u.pgdir]
	mov	[edx], eax ; PDE 0 (1st 4MB)
	;
	; 20/07/2015
	mov	ebx, CORE ; start address = 0 (virtual) + CORE
	; 18/10/2015
	mov	esi, pcore ; physical start address
sysexec_8:	
	mov	ecx, PDE_A_USER + PDE_A_WRITE + PDE_A_PRESENT
	call	make_page_table
	jc	panic
	;mov	ecx, PTE_A_USER + PTE_A_WRITE + PTE_A_PRESENT
	call	make_page ; make new page, clear and set the pte 
	jc	panic
	;
	mov	[esi], eax ; 24/06/2015
	; ebx = virtual address (24/07/2015)
	call 	add_to_swap_queue
	; 18/10/2015
	cmp	esi, ecore ; user's stack (last) page ?
	je	short sysexec_9 ; yes
	mov	esi, ecore  ; physical address of the last page 
	; 20/07/2015
	mov	ebx, (ECORE - PAGE_SIZE) + CORE
	; ebx = virtual end address + segment base address - 4K
        jmp     short sysexec_8
sysexec_9:
	; 24/04/2016
	; 18/10/2015
	; 26/08/2015
	; 25/06/2015
	; move arguments from kernel stack to [ecore]
	; (argument list/line will be copied from kernel stack
	; frame to the last (stack) page of user's core memory)
	; 18/10/2015
	mov	edi, [ecore]
	add	edi, PAGE_SIZE
	movzx	eax, word [argc]
	or	eax, eax
	jnz	short sysexec_10
	mov 	ebx, edi
	sub	ebx, 4 
	mov	[ebx], eax ; 0
	jmp 	short sysexec_13
sysexec_10:
	mov	ecx, [u.nread]
	;mov	esi, TextBuffer
	mov	esi, [argv] ; 24/04/2016 (TRDOS 386  = TRDOS v2.0)
	sub	edi, ecx ; page end address - argument list length
	mov	edx, eax
	inc	dl ; argument count + 1 for argc value  
	shl 	dl, 2  ; 4 * (argument count + 1)
	mov	ebx, edi
	and	bl, 0FCh ; 32 bit (dword) alignment
	sub 	ebx, edx
	mov	edx, edi
	rep	movsb
	mov 	esi, edx
	mov 	edi, ebx
	mov	edx, ECORE - PAGE_SIZE ; virtual addr. of the last page
	sub 	edx, [ecore] ; difference (virtual - physical) 
	stosd	; eax = argument count	
sysexec_11:
	mov	eax, esi
	add	eax, edx
	stosd  ; eax = virtual address
	dec	byte [argc]
	jz	short sysexec_13
sysexec_12:
	lodsb
	and	al, al
	jnz	short sysexec_12
	jmp	short sysexec_11
sysexec_13:
	; 24/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 23/06/2015 - 19/10/2015 (Retro UNIX 386 v1, 'sysexec_13')
	;
	; moving arguments to [ecore] is OK here..
	;
	; ebx = beginning addres of argument list pointers
		;	in user's stack
	sub 	ebx, [ecore]
	add     ebx, (ECORE - PAGE_SIZE)
			; end of core - 4096 (last page)
			; (virtual address)
	mov	[argv], ebx
	mov	[u.break], ebx ; available user memory
	;
	sub	eax, eax
	mov	dword [u.count], 32 ; Executable file header size
	mov	dword [u.fofp], u.off
	mov	[u.off], eax ; 0
	mov	[u.base], eax ; 0, start of user's core (virtual)
	mov	eax, [ii] ; Fist Cluster of the Program (PRG) file
	; EAX = First cluster of the executable file
	call	readi

	mov	ecx, [u.break] ; top of user's stack (physical addr.)
	mov	[u.count], ecx ; save for overrun check
	;
	mov	ecx, [u.nread]
	mov	[u.break], ecx ; virtual address (offset from start)
	cmp	cl, 32
        jne     short sysexec_15
	;:
	; Retro UNIX 386 v1 (32 bit) executable file header format
	mov	esi, [pcore] ; start address of user's core memory 
		             ; (phys. start addr. of the exec. file)
	lodsd
	cmp	ax, 1EEBh ; EBH, 1Eh -> jump to +32
	jne	short sysexec_15
	lodsd
	mov	ecx, eax ; text (code) section size
	lodsd
	add	ecx, eax ; + data section size (initialized data)
	mov	ebx, ecx
	lodsd	
	add	ebx, eax ; + bss section size (for overrun checking)
	cmp	ebx, [u.count]
	ja	short sysexec_14  ; program overruns stack !
	;
	; add bss section size to [u.break]
	add 	[u.break], eax
	;
	sub	ecx, 32  ; header size (already loaded)
	;cmp	ecx, [u.count]
	;jnb	short sysexec_16
	mov	[u.count], ecx ; required read count
	jmp	short sysexec_16
sysexec_14:
	; insufficient (out of) memory
	mov	dword [u.error], ERR_MINOR_IM ; 1
	jmp	error
sysexec_15:
        mov	edx, [i.size] ; file size
	sub	edx, ecx ; file size - loaded bytes
	jna	short sysexec_17 ; no need to next read
	add	ecx, edx ; [i.size]
	cmp	ecx, [u.count] ; overrun check (!)
	ja	short sysexec_14
	mov	[u.count], edx
sysexec_16:
	mov	eax, [ii] ; first cluster
	call	readi
	mov	ecx, [u.nread]
	add	[u.break], ecx
sysexec_17:
	mov	eax, [ii] ; first cluster
	call	iclose
	xor     eax, eax
	inc	al
	mov	[u.intr], ax ; 1 (interrupt/time-out is enabled)
	mov	[u.quit], ax ; 1 ('crtl+brk' signal is enabled) 
        cmp	dword [u.ppgdir], 0  ; is the caller MainProg (kernel) ?
	ja	short sysexec_18 ; no, the caller is user process
	; If the caller is kernel (MainProg), 'sysexec' will come here
	mov	edx, [k_page_dir] ; kernel's page directory
	mov	[u.ppgdir], edx ; next time 'sysexec' must not come here 
sysexec_18:
	; 02/05/2016
	; 24/04/2016 (TRDOS 386 = TRDOS v2.0)
	; 18/10/2015 (Retro UNIX 386 v1)
	; 05/08/2015
	; 29/07/2015
	mov	ebp, [argv] ; user's stack pointer must point to argument
			    ; list pointers (argument count)
	cli
        mov     esp, [tss.esp0]  ; ring 0 (kernel) stack pointer
	;mov   	esp, [u.sp] ; Restore Kernel stack
			    ; for this process	 
	;add	esp, 20 ; --> EIP, CS, EFLAGS, ESP, SS
	;xor	eax, eax ; 0
	dec	al ; eax = 0
	mov	dx, UDATA
	push	dx  ; user's stack segment
	push	ebp ; user's stack pointer
		    ; (points to number of arguments)
	sti
	pushfd	; EFLAGS
		; Set IF for enabling interrupts in user mode	
	;or	dword [esp], 200h 
	;
	;mov	bx, UCODE
	;push	bx ; user's code segment
	push	UCODE
	;push	0
	push	eax ; EIP (=0) - start address -	
	mov	[u.sp], esp ; 29/07/2015
	; 05/08/2015
	; Remedy of a General Protection Fault during 'iretd' is here !
	; ('push dx' would cause to general protection fault, 
	; after 'pop ds' etc.)
	;
	;; push dx ; ds (UDATA)
	;; push dx ; es (UDATA)
	;; push dx ; fs (UDATA)
	;; push dx ; gs (UDATA)
	;
	; This is a trick to prevent general protection fault
	; during 'iretd' intruction at the end of 'sysrele' (in u1.s):
	mov 	es, dx ; UDATA
	push 	es ; ds (UDATA)
	push 	es ; es (UDATA)
	push 	es ; fs (UDATA)
	push	es ; gs (UDATA)
	mov	dx, KDATA
	mov	es, dx
	;
	;; pushad simulation
	mov	ebp, esp ; esp before pushad
	push	eax ; eax (0)
	push	eax ; ecx (0)
	push	eax ; edx (0)
	push	eax ; ebx (0)
	push	ebp ; esp before pushad
	push	eax ; ebp (0)
	push	eax ; esi (0)		
	push	eax ; edi (0)	
	;
	mov	[u.r0], eax ; eax = 0
	mov	[u.usp], esp

	; 02/05/2016
	inc	byte [sysflg] ; 0FFh -> 0
	movzx	ebx, byte [u.uno]	
	cmp	word [ebx+p.ppid-2], 1 ; MainProg
	ja	sysret0 ; 03/05/2016
	push	sysret ; * 
	mov	[u.usp], esp
	call	wswap ; save child process 'u' structure and
		      ; registers
	add	dword [u.usp], 4 ; 03/05/2016 
sysexec_19: ; 02/05/2016
	retn ; * 'sysret' ; byte [sysflg] -> 0FFh

readi:
	; 01/05/2016
	; 25/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 20/05/2015 - Retro UNIX 386 v1
	; 11/03/2013 - 31/07/2013 (Retro UNIX 8086 v1)
	;
	; Reads from a file whose the first cluster number in EAX
	; 
	; INPUTS ->
	;    EAX - First cluster number of the file
	;    u.count - byte count user desires
	;    u.base - points to user buffer
	;    u.fofp - points to dword with current file offset
	;    i.size - file size
	; OUTPUTS ->
	;    u.count - cleared
	;    u.nread - accumulates total bytes passed back
	;
	; ((EAX)) input/output
	; (Retro UNIX Prototype : 01/03/2013 - 14/12/2012, UNIXCOPY.ASM)
        ; ((Modified registers: edx, ebx, ecx, esi, esi, ebp))  

	xor	edx, edx ; 0
	mov 	[u.nread], edx ; 0
	mov	[u.pcount], dx ; 19/05/2015
	cmp 	[u.count], edx ; 0
	ja 	short readi_1
	retn
readi_1:
dskr:
	; 01/05/2016
	; 25/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 24/05/2015 - 12/10/2015 (Retro UNIX 386 v1)
	; 26/04/2013 - 03/08/2013 (Retro UNIX 8086 v1)
dskr_0:
        mov	edx, [i.size]
	mov	ebx, [u.fofp]
	sub	edx, [ebx]
	jna	short dskr_4
	;
	push	eax ; 01/05/2016
	cmp     edx, [u.count] 
	jnb	short dskr_1
	mov	[u.count], edx
dskr_1:
	; EAX = First Cluster
	; [Current_Drv] = Physical drive number 
	call	mget_r
	; NOTE: in 'mget_r', relevant sector will be read in buffer
	; if it not already in buffer !
	mov	ebx, readi_buffer
	cmp	byte [u.kcall], 0 ; the caller is 'namei' sign (=1)
	ja	short dskr_3	  ; zf=0 -> the caller is 'namei'
	cmp	word [u.pcount], 0
	ja	short dskr_3
dskr_2:
	; [u.base] = virtual address to transfer (as destination address)
	call	trans_addr_w ; translate virtual address to physical (w)
dskr_3:
	; EBX (r5) = system (I/O) buffer address -physical-
	call	sioreg
	xchg	esi, edi
	; EDI = file (user data) offset
	; ESI = sector (I/O) buffer offset
	; ECX = byte count
	rep	movsb
	; eax = remain bytes in buffer
        ;       (check if remain bytes in the buffer > [u.pcount])
	or	eax, eax
	jnz	short dskr_2 ; (page end before system buffer end!)		
	pop	eax  ; (first cluster number)
	cmp	[u.count], ecx ; 0
	ja	short dskr_0
dskr_4:
	mov	byte [u.kcall], 0
	retn

mget_r:
	; 29/04/2016
	; 25/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 03/06/2015 (Retro UNIX 386 v1, 'mget', u.5s)
	; 22/03/2013 - 31/07/2013 (Retro UNIX 8086 v1)
	;
	; Get existing or (allocate) a new disk block for file
	; 
	; INPUTS ->
	;    u.fofp = file offset pointer
	;    EAX = First Cluster
	;    (u.off = file offset)
	; OUTPUTS ->
	;    EAX = logical sector number
	;    ESI = Logical Dos Drive Description Table address	
	;
	; Modified registers: EDX, EBX, ECX, ESI, EDI, EBP  

	mov     esi, [u.fofp]
	mov	ebx, [esi] ; u.off

	sub	ecx, ecx
	mov	ch, [Current_Drv]

	mov	esi, Logical_DOSDisks
	add	esi, ecx

	cmp	[readi.valid], cl ; 0
	jna	short mget_r_0
	
	cmp	ch, [readi.drv]
	jne	short mget_r_0

	cmp	eax, [readi.fclust]
	jne	short mget_r_3
	
	mov	cx, [readi.bpc]
	inc	ecx ; <= 65536
	sub	edx, edx
	div	ecx

	mov	edi, [readi.c_index] ; cluster index

	cmp	eax, edi
        jne     mget_r_5  ; (*)

	; edx = byte offset in cluster (<= 65535)
	mov	[readi.offset], dx
	shr	dx, 9 ; / 512
	mov	[readi.s_index], dl ; sector index in cluster (0 to spc -1)
	
	mov	eax, [readi.cluster]
	mov	edx, [readi.fs_index]
        jmp     mget_r_8
	
mget_r_0:
	mov	[readi.drv], ch ; physical drive number
	cmp	byte [esi+LD_FATType], 0
	ja	short  mget_r_1
	mov	cl, [esi+LD_FS_BytesPerSec+1]
	shr	cl, 1 ;  ; 1 for 512 bytes, 4 for 2048 bytes
	jmp	short mget_r_2	
mget_r_1:
	mov	cl, [esi+LD_BPB+BPB_SecPerClust]
mget_r_2:
	mov	[readi.spc], cl  ; sectors per cluster
	; NOTE: readi bytes per sector value is always 512 ! 
	shl	cx, 9 ; * 512
	dec	cx ; bytes per cluster - 1
	mov	[readi.bpc], cx
	sub	cx, cx
mget_r_3:
	mov	[readi.fclust], eax ; first cluster (or FDT address)
	mov	[readi.valid], cl ; 0 
	mov	[readi.s_index], cl ; 0
	mov	[readi.offset], cx ; 0
	mov	[readi.c_index], ecx ; 0
	mov	[readi.cluster], ecx ; 0
	mov	[readi.sector], ecx ; 0
mget_r_4:	
	mov	eax, ebx ; file offset
	mov	cx, [readi.bpc]
	inc	ecx ; <= 65536
	sub	edx, edx
	div	ecx
	mov	edi, [readi.c_index] ; previous cluster index
mget_r_5:
	mov	[readi.c_index], eax ; cluster index
	; edx = byte offset in cluster (<= 65535)
	mov	[readi.offset], dx
	shr	dx, 9 ; / 512
	mov	[readi.s_index], dl ; sector index in cluster (0 to spc -1)

	mov	ecx, eax ; current cluster index
	mov	eax, [readi.fclust]
	or	ecx, ecx ; cluster index
	jz	short mget_r_7

	cmp	edi, ecx
	ja	short mget_r_6 ; old cluster index is higher
	mov	edx, [readi.cluster]
	and	edx, edx
	jz	short mget_r_6
	; valid 'readi' parameters (*)
	mov	eax, edx
	sub	ecx, edi
	jz	short mget_r_8
mget_r_6:
	; EAX = Beginning cluster
	; EDX = Sector index in disk/file section
	;	(Only for SINGLIX file system!)
	; ECX = Cluster sequence number after the beginning cluster
	; ESI = Logical DOS Drive Description Table address
	call	get_cluster_by_index
	jc	error
	; EAX = Cluster number		
mget_r_7:
	mov	[readi.cluster], eax ; FDT number for Singlix File System
mget_r_8:
	cmp	byte [esi+LD_FATType], 0
	jna	short  mget_r_13

	sub	eax, 2
	movzx	edx, byte [readi.spc]
	mul	edx

	add	eax, [esi+LD_DATABegin]
	mov	dl, [readi.s_index]
	add	eax, edx
msget_r_9:
	; eax = logical sector number
	cmp	byte [readi.valid], 0
	jna	short mget_r_10
	cmp	eax, [readi.sector]
	je	short mget_r_12 ; sector is already in 'readi' buffer
mget_r_10:
	mov	[readi.sector], eax
	mov	ebx, readi_buffer ; buffer address
	mov	ecx, 1
	; 29/04/2016
	;xor	dl, dl

	; EAX = Logical sector number
	; ECX = Sector count
	; EBX = Buffer address
	; (EDX = 0)
	; ESI = Logical DOS drive description table address	

	call	disk_read
	jnc	short mget_r_11

	mov	eax, 15h ; Drive not ready or read error !
	jmp	error
mget_r_11:
	mov	eax, [readi.sector]
mget_r_12:
	retn
mget_r_13:
	; EAX = FDT number
	; EDX = Sector index from FDT sector (0,1,2,3,4...)
	inc	eax ; the first data sector in FS disk section	
	mov	[readi.fs_index], edx
	add	eax, edx
	jmp	short msget_r_9

trans_addr_r:
	; 02/05/2016 - TRDOS 386 (TRDOS v2.0)
	; Translate virtual address to physical address 
	; for reading from user's memory space
	; 04/06/2015 - 18/10/2015 (Retro UNIX 386 v1)

	xor	edx, edx ; 0 (read access sign)
	jmp 	short trans_addr_rw

trans_addr_w:
	; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
	; Translate virtual address to physical address 
	; for writing to user's memory space
	; 04/06/2015 - 18/10/2015 (Retro UNIX 386 v1)
	
	sub	edx, edx
	inc	dl ; 1 (write access sign)
trans_addr_rw:
	push	eax
	push	ebx
	push 	edx ; r/w sign (in DL)
	;
	mov	ebx, [u.base]
	call	get_physical_addr ; get physical address
	jnc	short passc_0
	mov	[u.error], eax
	;pop	edx
	;pop 	ebx
	;pop	eax
	jmp	error
passc_0:
	test	dl, PTE_A_WRITE ; writable page
	pop	edx
	jnz	short passc_1
	
	and 	dl, dl
	jz	short passc_1
	; read only (duplicated) page -must be copied to a new page-
	; EBX = linear address
	push 	ecx
	call 	copy_page
	pop	ecx
	jc	short passc_2
	push	eax ; physical address of the new/allocated page
	call	add_to_swap_queue	
	pop	eax
	and 	ebx, PAGE_OFF ; 0FFFh
	;mov 	ecx, PAGE_SIZE
	;sub	ecx, ebx 
	add	eax, ebx  
passc_1: 
	mov 	[u.pbase], eax ; physical address	
	mov	[u.pcount], cx ; remain byte count in page (1-4096)
	pop	ebx
	pop	eax
	retn
passc_2:
	mov	dword [u.error], ERR_MINOR_IM ; "Insufficient memory !" error
	;pop 	ebx
	;pop	eax
	jmp	error

sioreg: 
	; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 19/05/2015 - 25/07/2015 (Retro UNIX 386 v1)
	; 12/03/2013 - 22/07/2013 (Retro UNIX 8086 v1)
	; INPUTS -> 
	;     EBX = system buffer (data) address (r5)
	;     [u.fofp] = pointer to file offset pointer
	;     [u.base] = virtual address of the user buffer
	;     [u.pbase] = physical address of the user buffer
	;     [u.count] = byte count
	;     [u.pcount] = byte count within page frame 			
	; OUTPUTS -> 
	;     ESI = user data offset (r1)
	;     EDI = system (I/O) buffer offset (r2)
	;     ECX = byte count (r3)
	;     EAX = remain bytes after byte count within page frame
	;	(If EAX > 0, transfer will continue from the next page)
        ;
	; ((Modified registers:  EDX))
 
        mov     esi, [u.fofp]
        mov     edi, [esi]
	mov	ecx, edi
	or	ecx, 0FFFFFE00h
	and	edi, 1FFh
	add	edi, ebx ; EBX = system buffer (data) address
	neg	ecx
	cmp	ecx, [u.count]
	jna	short sioreg_0
	mov	ecx, [u.count]
sioreg_0:
	cmp	byte [u.kcall], 0 
	jna	short sioreg_1
	 ; the caller is 'mkdir' or 'namei'
	mov	eax, [u.base]
	mov 	[u.pbase], eax ; physical address = virtual address
	mov	word [u.pcount], cx ; remain bytes in buffer (1 sector)
	jmp	short sioreg_2
sioreg_1:
	movzx	edx, word [u.pcount]
	cmp	ecx, edx	
	ja	short sioreg_4 ; transfer count > [u.pcount]
sioreg_2: ; 2:
	xor 	eax, eax
sioreg_3:
	add 	[u.nread], ecx
	sub 	[u.count], ecx
	add 	[u.base], ecx
        add 	[esi], ecx 
	mov	esi, [u.pbase]
	sub	[u.pcount], cx
	add	[u.pbase], ecx
        retn
sioreg_4:
	; transfer count > [u.pcount] 
	; (ecx > edx)
	mov	eax, ecx
	sub	eax, edx ; remain bytes for 1 sector (block) transfer 
	mov	ecx, edx ; current transfer count = [u.pcount]
	jmp	short sioreg_3

tswitch: ; Retro UNIX 386 v1
tswap:
	; 21/05/2016 - TRDOS 386 (TRDOS v2.0)
	; 10/05/2015 - 01/09/2015 (Retro UNIX 386 v1)
	; 14/04/2013 - 14/02/2014 (Retro UNIX 8086 v1)
	; time out swap, called when a user times out.
	; the user is put on the low priority queue.
	; This is done by making a link from the last user
	; on the low priority queue to him via a call to 'putlu'.
	; then he is swapped out.

	; TRDOS 386 (TRDOS v2.0) modification ->  ** 21/05/2016 **
	;     * when a high priority (event) process will be stopped
	;	(swapped out, swithched out/off), 'tswap/tswitch' will
	;	not add it to a run queue. 
	;	/// What for: Process may be already in a run queue, 
	;	it is unspeficied state because process might be started
	;	by a timer event which does not regard previous priority
	;	level and run queue of the process (for fast executing!).
	;	After the 'run for event', process will be sequenced
	;	to run by it's actual run queue. ///
	;
	; Retro UNIX 386 v1 modification ->
	;       swap (software task switch) is performed by changing
	;	user's page directory (u.pgdir) instead of segment change
	;	as in Retro UNIX 8086 v1.
	;
	; RETRO UNIX 8086 v1 modification ->
	;       'swap to disk' is replaced with 'change running segment'
	;	according to 8086 cpu (x86 real mode) architecture.
	;	pdp-11 was using 64KB uniform memory while IBM PC
	;	compatibles was using 1MB segmented memory 
	;	in 8086/8088 times.
	;
	; INPUTS ->
	;    u.uno - users process number
	;    runq+4 - lowest priority queue
	; OUTPUTS ->
	;    r0 - users process number
	;    r2 - lowest priority queue address
	;
	; ((AX = R0, BX = R2)) output
	; ((Modified registers: EDX, EBX, ECX, ESI, EDI))  	
	;

	NOTE:
	;* [u.pri] priority level is specified by run queue which is process 
	;  comes to run from.
	;* Initial [u.pri] is 1 ('normal/regular') for programs 
	;  (which are launched by MainProg or 'sysexec'), it is changed
	;  to 2 ('high') by timer event, if program uses 'systimer' system call.
	;* Program (Process) also can change it's running priority 
	;  from 1 to 0 or up to 2 by using 'syspri' system call; but,
	;  if program selects priority level 2 (high) for running, next time 
	;  it is reduced to 1 (normal/regular) because 'syspri' adds this
	;  program to 'run for normal' queue while running duration is a bit
	;  protected from swap/switch out immediate, behalf of other high 
	;  priority process in sequence. Program (with high priority) will not 
	;  be swapped/switched out (by timer event) before it's time quantum 
	;  will be elapsed, but, this will be temporary if program is not using 
	;  timer event function.	  			 	

	;For example:
	;If a process frequently gets a timer event, it runs at high priority
	;level but when it returns from running it returns to actual run queue,
	;not to 'run for event' queue again.
	;'tswap' will not change the sequence at return/stop(swap out) stage.
	;But if priority level not high (=2, 'run for event'), 'tswap/tswitch'
	;will add the stopping process to relevant run queue according to
	;[u.pri] priority level.

	; 21/05/2016
	cmp	byte [u.pri], 2	; high priority (run for event) ?
	jnb	short swap      ; yes, do not add process to the run queue   
	
	mov	ebx, runq+2 	; 'runq_normal' ; normal/regular priority
	cmp	byte [u.pri], 0
	ja	short tswap_1	; normal priority

	inc	ebx
	inc	ebx		; runq+4, 'runq_background', low priority
tswap_1:
	mov 	al, [u.uno]
	       	; movb u.uno,r1 / move users process number to r1
		; mov  $runq+4,r2 
			; / move lowest priority queue address to r2
      	; ebx = run queue
	call 	putlu
		; jsr r0,putlu / create link from last user on Q to 
		             ; / u.uno's user

switch: ; Retro UNIX 386 v1
swap:
	; 21/05/2016
	; 20/05/2016
	; 02/05/2016
	; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 10/05/2015 - 02/09/2015 (Retro UNIX 386 v1)
	; 14/04/2013 - 08/03/2014 (Retro UNIX 8086 v1)
	;
	; 'swap' is routine that controls the swapping of processes
	; in and out of core.
	;
	; TRDOS 386 (TRDOS v2.0) modification ->  ** 20/05/2016 **
	;     * 3 different priority level is applied 
	;	(just as original unix v1)
	;	1) high priority (event) run queue, 'runq_event'
	;	2) normal priority (regular) run queue, 'runq_normal'
	;	3) low priority (background) run queue, 'runq_backgroud'
	;	'swap' code will run a process which has max. priority
        ;       (for earliest event at first)
	;
	; Retro UNIX 386 v1 modification ->
	;       swap (software task switch) is performed by changing
	;	user's page directory (u.pgdir) instead of segment change
	;	as in Retro UNIX 8086 v1.
	;
	; RETRO UNIX 8086 v1 modification ->
	;       'swap to disk' is replaced with 'change running segment'
	;	according to 8086 cpu (x86 real mode) architecture.
	;	pdp-11 was using 64KB uniform memory while IBM PC
	;	compatibles was using 1MB segmented memory 
	;	in 8086/8088 times.
	;
	; INPUTS ->
	;    runq table - contains processes to run.
	;    p.link - contains next process in line to be run.
	;    u.uno - process number of process in core	
	;    s.stack - swap stack used as an internal stack for swapping.	
	; OUTPUTS ->
	;    (original unix v1 -> present process to its disk block)
	;    (original unix v1 -> new process into core -> 
	;	   Retro Unix 8086 v1 -> segment registers changed 
	;	   for new process)
	;    u.quant = 3 (Time quantum for a process)
	; 	((INT 1Ch count down speed -> 18.2 times per second)	 	
	;    RETRO UNIX 8086 v1 will use INT 1Ch (18.2 times per second)
	;	 for now, it will swap the process if there is not
	;	 a keyboard event (keystroke) (Int 15h, function 4Fh)
	;	 or will count down from 3 to 0 even if there is a
	;        keyboard event locking due to repetitive key strokes.
	;	 u.quant will be reset to 3 for RETRO UNIX 8086 v1.
	;
	; ((Modified registers: EAX, EDX, EBX, ECX, ESI, EDI))  	

	;NOTE:
	;High priority queue is the first for selecting a process to run.
	;If there is not a process in high priority level run queue,
	;a process in normal priority run queue will be selected
	;or a proces in low priority run queue will be selected if normal
	;priority level run queue is empty.
	
	; 21/05/2016 -(3 priority levels, 3 run queues)
	mov	esi, runq ; 'runq_event' ; high priority, 'run for event'
	mov	byte [priority], 3 ; high priority + 1
swap_0: ; 1: / search runq table for highest priority process
	lodsw  ; mov ax, [esi], add esi+2
	xor	ebx, ebx ; 02/05/2016
	and 	ax, ax ; are there any processes to run in this Q entry
	jnz	short swap_2 
	; 21/05/2026
	; runq_normal = runq+2, runq_background = runq+4
	dec	byte [priority] ; 3 -> 3, 2 -> 1, 1-> 0
	jnz	short swap_0	
	;cmp	esi, runq+6  ; if zero compare address to end of table
	;jb	short swap_0 ; if not at end, go back
swap_1:
	; 02/05/2016
	; 29/04/2016 (TRDOS 386 = TRDOS v2.0)
	; No user process to run...
	; Run the kernel process... MainProg: Internal Command Interpreter  
	inc	al ; mov al, 1  ; process number of MainProg
	inc	bl ; mov bl, al ; 1
	jmp	short swap_4
swap_2:
	; 21/05/2016
	dec	byte [priority] ; priority level of present user/process
			        ; 0, 1, 2	   
	dec	esi
        dec     esi
	;
	mov	bl, al
	cmp	al, ah ; is there only 1 process in the queue to be run
	je	short swap_3 ; yes
	mov	ah, [ebx+p.link-1] 
       	mov	[esi], ah ; move next process in line into run queue
	jmp	short swap_4
swap_3: 
	xor	dx, dx
	mov	[esi], dx ; zero the entry; no processes on the Q
swap_4: 
	mov 	ah, [u.uno]
	cmp	ah, al ;is this process the same as the process in core?
       	je	short swap_8 ; yes, don't have to swap
	or	ah, ah  ; is the process # = 0
       	jz	short swap_6 ; 'sysexit'
	mov	[u.usp], esp ; return  address for 'syswait' & 'sleep'
	call	wswap   ; write out core to disk
	jmp 	short swap_7
swap_6:
	; Deallocate memory pages belong to the process
	; which is being terminated.
	; (Retro UNIX 386 v1 modification !)
	;
	push	ebx
	mov 	eax, [u.pgdir]  ; page directory of the process
	mov	ebx, [u.ppgdir] ; page directory of the parent process
	call	deallocate_page_dir
	mov	eax, [u.upage] ; 'user' structure page of the process
	call	deallocate_page
	pop	ebx
swap_7: 
	shl	bl, 2 ; * 4 
	mov	eax, [ebx+p.upage-4] ; the 'u' page of the new process
	call	rswap ; read new process into core
swap_8: 
	; Retro UNIX  8086 v1 modification !
	mov	byte [u.quant], time_count 
	retn

wswap:  ; < swap out, swap to disk >
	; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 09/05/2015 (Retro UNIX 386 v1)
	; 26/05/2013 - 08/03/2014 (Retro UNIX 8086 v1)
	; 'wswap' writes out the process that is in core onto its 
	; appropriate disk area.
	;
	; Retro UNIX 386 v1 modification ->
	;       User (u) structure content and the user's register content
	;	will be copied to the process's/user's UPAGE (a page for
	;	saving 'u' structure and user registers for task switching).
	;	u.usp - points to kernel stack address which contains
	;		user's registers while entering system call.  
	;	u.sp  - points to kernel stack address 
	;		to return from system call -for IRET-.
	;	[u.usp]+32+16 = [u.sp] 
	;	[u.usp] -> edi, esi, ebp, esp (= [u.usp]+32), ebx, 
	;		edx, ecx, eax, gs, fs, es, ds, -> [u.sp].
	;
	; Retro UNIX 8086 v1 modification ->
	;       'swap to disk' is replaced with 'change running segment'
	;	according to 8086 cpu (x86 real mode) architecture.
	;	pdp-11 was using 64KB uniform memory while IBM PC
	;	compatibles was using 1MB segmented memory 
	;	in 8086/8088 times.
	;
	; INPUTS ->
	;    u.break - points to end of program
	;    u.usp - stack pointer at the moment of swap
	;    core - beginning of process program		
	;    ecore - end of core 	
	;    user - start of user parameter area		
	;    u.uno - user process number	
	;    p.dska - holds block number of process	
	; OUTPUTS ->
	;    swp I/O queue
	;    p.break - negative word count of process 
	;    r1 - process disk address	
	;    r2 - negative word count
	;
	; RETRO UNIX 8086 v1 input/output:
	;
	; INPUTS ->
	;    u.uno - process number (to be swapped out)
	; OUTPUTS ->
	;    none
	;
	;   ((Modified registers: ECX, ESI, EDI))  
	;
	mov	edi, [u.upage] ; process's user (u) structure page addr
	mov	ecx, (U_SIZE + 3) / 4
	mov	esi, user ; active user (u) structure	
	rep	movsd
	;
	mov	esi, [u.usp] ; esp (system stack pointer, 
			     ;      points to user registers)
	mov	ecx, [u.sp]  ; return address from the system call
			     ; (for IRET)
			     ; [u.sp] -> EIP (user)
			     ; [u.sp+4]-> CS (user)
			     ; [u.sp+8] -> EFLAGS (user)
			     ; [u.sp+12] -> ESP (user)
			     ; [u.sp+16] -> SS (user)	
	sub	ecx, esi     ; required space for user registers
	add	ecx, 20	     ; +5 dwords to return from system call
			     ; (for IRET) 	
	shr	ecx, 2	     		
	rep	movsd
	retn

rswap:  ; < swap in, swap from disk >
	; 21/05/2016
	; 03/05/2016
	; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 09/05/2015 - 15/09/2015 (Retro UNIX 386 v1)
	; 26/05/2013 - 08/03/2014 (Retro UNIX 8086 v1)
	; 'rswap' reads a process whose number is in r1, 
	; from disk into core.
	;
	; Retro UNIX 386 v1 modification ->
	;       User (u) structure content and the user's register content
	;	will be restored from process's/user's UPAGE (a page for
	;	saving 'u' structure and user registers for task switching).
	;	u.usp - points to kernel stack address which contains
	;		user's registers while entering system call.  
	;	u.sp  - points to kernel stack address 
	;		to return from system call -for IRET-.
	;	[u.usp]+32+16 = [u.sp] 
	;	[u.usp] -> edi, esi, ebp, esp (= [u.usp]+32), ebx, 
	;		edx, ecx, eax, gs, fs, es, ds, -> [u.sp].
	;
	; RETRO UNIX 8086 v1 modification ->
	;       'swap to disk' is replaced with 'change running segment'
	;	according to 8086 cpu (x86 real mode) architecture.
	;	pdp-11 was using 64KB uniform memory while IBM PC
	;	compatibles was using 1MB segmented memory 
	;	in 8086/8088 times.
	;
	; INPUTS ->
	;    r1 - process number of process to be read in
	;    p.break - negative of word count of process 
	;    p.dska - disk address of the process		
	;    u.emt - determines handling of emt's 	
	;    u.ilgins - determines handling of illegal instructions		
	; OUTPUTS ->
	;    8 = (u.ilgins)
	;    24 = (u.emt)
	;    swp - bit 10 is set to indicate read 
	;		(bit 15=0 when reading is done)	
	;    swp+2 - disk block address
	;    swp+4 - negative word count 	
	;      ((swp+6 - address of user structure)) 
	;
	; RETRO UNIX 8086 v1 input/output:
	;
	; INPUTS ->
	;    AL	- new process number (to be swapped in)	 
	; OUTPUTS ->
	;    none
	;
	;   ((Modified registers: EAX, ECX, ESI, EDI, ESP)) 
	;
	; Retro UNIX 386 v1 - modification ! 14/05/2015
	mov	esi, eax  ; process's user (u) structure page addr
	mov	ecx, (U_SIZE + 3) / 4
	mov	edi, user ; active user (u) structure	
	rep	movsd
	pop	eax	; 'rswap' return address
	; 
	;; 21/05/2016
	mov	cl, [priority]
	mov	[u.pri], cl  ; running priority level 
			     ; (specifies run queue which is 
			     ; process comes from)
	;cli
	mov	edi, [u.usp] ; esp (system stack pointer, 
			     ;     points to user registers)
	mov	ecx, [u.sp]  ; return address from the system call
			     ; (for IRET)
			     ; [u.sp] -> EIP (user)
			     ; [u.sp+4]-> CS (user)
			     ; [u.sp+8] -> EFLAGS (user)
			     ; [u.sp+12] -> ESP (user)
			     ; [u.sp+16] -> SS (user)		
	sub	ecx, edi     ; required space for user registers
	add	ecx, 20	     ; +5 dwords to return from system call
			     ; (for IRET) 	
	shr	ecx, 2	       		
	rep	movsd
	mov	esp, [u.usp] ; 15/09/2015
	;sti
	push	eax	; 'rswap' return address
	retn

putlu: 
	; 20/05/2016
	; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
	; 10/05/2015 - 12/09/2015 (Retro UNIX 386 v1)
	; 15/04/2013 - 23/02/2014 (Retro UNIX 8086 v1)
	; 'putlu' is called with a process number in r1 and a pointer
	; to lowest priority Q (runq+4) in r2. A link is created from
	; the last process on the queue to process in r1 by putting
	; the process number in r1 into the last process's link.
	;
	; INPUTS ->
	;    r1 - user process number
	;    r2 - points to lowest priority queue 
	;    p.dska - disk address of the process		
	;    u.emt - determines handling of emt's 	
	;    u.ilgins - determines handling of illegal instructions		
	; OUTPUTS ->
	;    r3 - process number of last process on the queue upon
	;	  entering putlu
	;    p.link-1 + r3 - process number in r1
	;    r2 - points to lowest priority queue
	;
	; ((Modified registers: EDX, EBX)) 
	;
	; / r1 = user process no.; r2 points to lowest priority queue

	; EBX = r2
	; EAX = r1 (AL=r1b)

	; 20/05/2016
	; AL = process number (1 to 16) // Retro UNIX 8086, 386 v1 // 
	;     (max. 16 processes available for current kernel version) 
	; EBX = run queue address ; 20/05/2016 (TRDOS 386)
		; which is one of following addresses:
		;  1) 'runq_event' high priority run queue	 	
		;  2) 'runq_normal' normal/regular priority run queue
		;  3) 'runq_background' low priority run queue

	;mov	ebx, runq
	movzx  	edx, byte [ebx]
	inc	ebx
	and	dl, dl
		; tstb (r2)+ / is queue empty?
       	jz	short putlu_1
		; beq 1f / yes, branch
	mov 	dl, [ebx] ; 12/09/2015
		; movb (r2),r3 / no, save the "last user" process number
			     ; / in r3
       	mov	[edx+p.link-1], al
		; movb r1,p.link-1(r3) / put pointer to user on 
			     ; / "last users" link
	jmp	short putlu_2
		; br 2f /
putlu_1: ; 1:
	mov	[ebx-1], al
       		; movb r1,-1(r2) / user is only user; 
			    ; / put process no. at beginning and at end
putlu_2: ; 2: 
	mov	[ebx], al
       		; movb r1,(r2) / user process in r1 is now the last entry
			     ; / on the queue
	mov	dl, al
        mov     [edx+p.link-1], dh ; 0
		; dec r2 / restore r2
        retn
		; rts r0
sysver:
	; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
	mov	dword [u.r0], 200h ; AH = major version, AL = minor version 
	jmp	sysret

sysreserved1:
	; 19/05/2016 - TRDOS 386 (TRDOS v2.0)
	; // name and contect will be changed later //
	mov	dword [u.r0], 2016
	jmp	sysret

syspri: ; change running priority (of the process)
	; 21/05/2016
	; 20/05/2026 - TRDOS 386 (TRDOS v2.0)
	; INPUT ->
	;	BL = priority level
	;	   0 = low running priority (running on background)
	;	   1 = normal/regular priority (running as regular)
	;	   2 = high/event priority (running for event)
	;	   >2 = invalid, it will accepted as 2 (event)
	;	   0FFh = get/return current running priority only	
	; OUTPUT -> 	
	;	* if current [u.pri] < 2
	;	  if BL input < 0FFh ->
	;	     [u.pri] is updated as in BL input (0,1,2)
	;	  if BL input = 0FFh -> AL = [u.pri] (current)
	;	      
	;	* if current [u.pri] = 2
	;	  if BL input < 0FFh -> cf = 1 & AL = 2
	;	  if BL input = 0FFh -> cf = 0 & AL = 2  
	;	
	;	NOTE: 	
	;	If [u.pri] = 2, it can not be changed to 1 or 0;
	;	because, run queue of the running process is unspecified
	;	at this	stage. Process might be started by a timer event
	;	or priority might be changed to high by previous
	;	'syspri' system	call. In both cases, the process is in
	;	'runq_normal' or 'runq_background' queue.
	;	As result of this fact, when the [u.quant] time quantum
	;	of the process is elapsed or 'sysrele' system call is
	;	instructed by the process, 'tswap' ('tswitch') procedure
	;	will be called (to 'swap' or 'switch' out the procedure)
	;	and it will not call 'putlu' to add the (stopping)
	;	process to relevant run queue when [u.pri] = 2.
	;	(Otherwise, it would be possible to add process to
	;	a run queue while it is already in a run queue, wrongly.)
  	;
	;	If [u.pri]< 2, 'tswap/tswitch' procedure will call 
	;	'putlu' to add process to relevant run queue
	;	according to [u.pri] value. ('runq_normal' for 1, 
	;	'runq_background' for 0).
	;
	;	If BL input >= 2 and < 0FFh while [u.pri] < 2,
	;	process will be added to 'runq_normal' queue and
	;	[u.pri] will be set to 2. (in 'syspri' system call)
	;

	sub	eax, eax ; 0
	mov	[u.error], eax

	mov	al, [u.pri]
	mov	[u.r0], eax

	inc	bl
	jz	sysret ; 0FFh -> 0, get priority level
	
	cmp	al, 2
	jnb	error ; CF = 1 & AL = 2 (& last error = 0)

	dec	bl
	cmp	bl, 2
	jna	short syspri_1
	mov	bl, 2
syspri_1:
	mov	[u.pri], bl
	cmp	bl, 2
        jb      sysret

	; here...
	; Priority of current process has been changed to high
	; ('run for event') but current process will be added to
	; 'run as normal' queue. ('run for event' high priority
	; queue is under control of timer -& RTC- interrupt only!) 
	;
	; (Otherwise, process can fall into black hole!
	; e.g. if it is not in waiting list and it has not got 
	; a timer event and it is not in a run queue!
	; Because, when [u.pri] is 2, 'tswap/tswitch' will not 
	; add the stopping process to a run queue.)

	mov	al, [u.uno]
	mov	ebx, runq_normal ; normal priority !
				 ; [u.pri] is set to high
				 ; but 'runq_event' queue is set
				 ; only by the kernel's timer
				 ; event function (timer interrupt). 
	call	putlu
	jmp	sysret

cpass: ; / get next character from user area of core and put it in AL (r1)
	; 02/05/2016 - TRDOS 386 (TRDOS v2.0)
	; 19/05/2015 - 18/10/2015 (Retro UNIX 386 v1)
	; 14/08/2013 - 20/09/2013 (Retro UNIX 8086 v1)
	; INPUTS -> 
	;     [u.base] = virtual address in user area
	;     [u.count] = byte count (max.)
	;     [u.pcount] = byte count in page (0 = reset)		
	; OUTPUTS -> 
	;     AL = the character which is pointed by [u.base]
	;     zf = 1 -> transfer count has been completed	
        ;
	; ((Modified registers:  EAX, EDX, ECX))
	;
	cmp 	dword [u.count], 0  ; have all the characters been transferred
			    	    ; i.e., u.count, # of chars. left
	jna	short cpass_3	    ; to be transferred = 0?) yes, branch
	dec	dword [u.count]	    ; no, decrement u.count
        ; 19/05/2015 
	;(Retro UNIX 386 v1 - translation from user's virtual address
	;		      to physical address
	cmp	word [u.pcount], 0 ; byte count in page = 0 (initial value)
			     ; 1-4095 --> use previous physical base address
			     ; in [u.pbase]
	ja	short cpass_1
	cmp     dword [u.ppgdir], 0  ; is the caller os kernel
        je      short cpass_k       ; (sysexec, '/etc/init') ?  (MainProg)
	call	trans_addr_r
cpass_1:
	dec	word [u.pcount]
cpass_2: 
	mov	edx, [u.pbase]
	mov	al, [edx]	; take the character pointed to 
				; by u.base and put it in r1
	inc	dword [u.nread] ; increment no. of bytes transferred
	inc	dword [u.base]  ; increment the buffer address to point to the
			        ; next byte
	inc	dword [u.pbase]
cpass_3:
	retn
cpass_k:
	; 02/07/2015
	; The caller is os kernel 
	; (get sysexec arguments from kernel's memory space)
	mov	ebx, [u.base]
        mov     word [u.pcount], PAGE_SIZE ; 4096
	mov	[u.pbase], ebx
	jmp	short cpass_2

transfer_to_user_buffer: ; fast transfer
	; 27/05/2016
	; 16/05/2016 - TRDOS 386 (TRDOS v2.0)
	;
	; INPUT ->
	;	ESI = source address in system space
	;	EDI = user's buffer address
	;	ECX = transfer (byte) count
	;	[u.pgdir] = user's page directory
	; OUTPUT ->
	;	ecx = actual transfer count
	;	cf = 1 -> error
	;	[u.count] = remain byte count
	;
	; Modified registers: eax, ecx
	;

	and	ecx, ecx
	jz	short ttub_4

	mov	[u.count], ecx
	
	push	edi
	push	esi
	push	ebx
	push	edx
	push	ecx

	mov	ebx, edi
	add	ebx, CORE ; 27/05/2016
ttub_1:
	; ebx = virtual (linear) address
	; [u.pgdir] = user's page directory
       	call	get_physical_addr_x ; get physical address
	jc	short ttub_5
	; eax = physical address 
	; ecx = remain byte count in page (1-4096)
	mov	edi, eax
	mov	eax, [u.count]
	cmp	ecx, eax
	jna	short ttub_2
	mov	ecx, eax
ttub_2:	
	sub	eax, ecx
	add	ebx, ecx
	rep	movsb
	mov	[u.count], eax
	or	eax, eax
	jnz	short ttub_1
ttub_retn:
tfub_retn:
	pop	ecx ; transfer count = actual transfer count
ttub_3:
	pop	edx
	pop	ebx
	pop	esi
	pop	edi
ttub_4:
	retn
ttub_5:
	pop	ecx
	sub	ecx, [u.count] ; actual transfer count
	stc
	jmp	short ttub_3

transfer_from_user_buffer: ; fast transfer
	; 27/05/2016
	; 16/05/2016 - TRDOS 386 (TRDOS v2.0)
	;
	; INPUT ->
	;	ESI = user's buffer address
	;	EDI = destination address in system space
	;	ECX = transfer (byte) count
	;	[u.pgdir] = user's page directory
	; OUTPUT ->
	;	ecx = actual transfer count
	;	cf = 1 -> error
	;	[u.count] = remain byte count
	;
	; Modified registers: eax, ecx
	;

	and	ecx, ecx
	;jz	short tfub_4
	jz	short ttub_4

	mov	[u.count], ecx
	
	push	edi
	push	esi
	push	ebx
	push	edx
	push	ecx

	mov	ebx, esi
	add	ebx, CORE ; 27/05/2016
tfub_1:
	; ebx = virtual (linear) address
	; [u.pgdir] = user's page directory
       	call	get_physical_addr_x ; get physical address
	;jc	short tfub_5
	jc	short ttub_5
	; eax = physical address 
	; ecx = remain byte count in page (1-4096)
	mov	esi, eax
	mov	eax, [u.count]
	cmp	ecx, eax
	jna	short tfub_2
	mov	ecx, eax
tfub_2:	
	sub	eax, ecx
	add	ebx, ecx
	rep	movsb
	mov	[u.count], eax
	or	eax, eax
	jnz	short tfub_1

	jmp	short tfub_retn

;tfub_retn:
;	pop	ecx ; transfer count = actual transfer count
;tfub_3:
;	pop	edx
;	pop	ebx
;	pop	esi
;	pop	edi
;tfub_4:
;	retn
;tfub_5:
;	pop	ecx
;	sub	ecx, [u.count] ; actual transfer count
;	stc
;	jmp	short tfub_3
	
; temporary - 24/01/2016

iget:
	retn
poke:
	retn
isintr:
	retn
writei:
	retn
iopen:
	retn
iclose:
	retn
itrunc:
	retn
setimod:
	retn
ottyp:
	retn
cttyp:
	retn
sndc:
	retn
access:
	retn
passc:
	retn
epoch:
	retn
set_date_time:
	retn
imap:
	retn
diskio:
	retn
idle:
	retn
sleep:
	retn
