; ****************************************************************************
; TRDOS386.ASM (TRDOS 386 Kernel - v2.0.3) - MAIN PROGRAM : trdosk6.s
; ----------------------------------------------------------------------------
; Last Update: 23/11/2020
; ----------------------------------------------------------------------------
; 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)
; ****************************************************************************
; Derived from TRDOS Operating System v1.0 (8086) source code by Erdogan Tan
; TRDOS2.ASM (09/11/2011)
; ----------------------------------------------------------------------------
; INT_21H.ASM (c) 2009-2011 Erdogan TAN  [14/11/2009] Last Update: 08/11/2011

sysent: ; < enter to system call >
	; 17/03/2017
	; 03/03/2017
	; 19/02/2017
	; 13/01/2017
	; 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:
	; 17/03/2017
	and	byte [esp+ESPACE+8], ~1 ; clear carry flag

	; 16/04/2015
	mov	[u.r0], eax
	mov	[u.usp], esp ; kernel stack points to user's registers

	; 13/01/2017 (TRDOS 386 Feaure only !)
	cmp	byte [u.t_lock], 0 ; timer interrupt lock ?
	ja	sysrele		   ; yes, sys release only !!!

		; 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:
	; 31/12/2017
	; 28/02/2017
	; 20/02/2017
	; 19/02/2017
	; 15/10/2016
	; 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 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 sysrename	; 9  ; TRDOS 386, Rename File (31/12/2017)
	dd sysdelete	; 10 ; TRDOS 386, Delete File (29/12/2017)
	dd sysexec 	; 11
	dd syschdir 	; 12
	dd systime 	; 13 ; TRDOS 386, Get Sys Date&Time (30/12/2017)
	dd sysmkdir 	; 14
	dd syschmod 	; 15 ; TRDOS 386, Change Attributes (30/12/2017) 
	dd sysrmdir 	; 16 ; TRDOS 386, Remove Directory (29/12/2017)
	dd sysbreak 	; 17
	dd sysdrive 	; 18 ; TRDOS 386, Get/Set Current Drv (30/12/2017) 
	dd sysseek 	; 19
	dd systell 	; 20
	dd sysmem 	; 21 ; TRDOS 386, Get Total&Free Mem (31/12/2017)
	dd sysprompt 	; 22 ; TRDOS 386, Change Cmd Prompt (31/12/2017)
	dd syspath 	; 23 ; TRDOS 386, Get/Set Run Path (31/12/2017)
	dd sysenv 	; 24 ; TRDOS 386, Get/Set Env Vars (31/12/2017)
	dd sysstime 	; 25 ; TRDOS 386, Set Sys Date&Time (30/12/2017)
	dd sysquit 	; 26
	dd sysintr 	; 27
	dd sysdir 	; 28 ; TRDOS 386, Get Curr Drive&Dir (30/12/2017) 
	dd sysemt 	; 29
	dd sysldrvt	; 30 ; TRDOS 386, Get Logical DOS DDT (30/12/2017)
	dd sysvideo 	; 31 ; TRDOS 386 Video Functions (16/05/2016)
	dd sysaudio 	; 32 ; TRDOS 386 Audio Functions (16/05/2016)
	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 sysfpstat	; 37 ; TRDOS 386 FPU state option (28/02/2017)
	dd syspri 	; 38 ; change priority - TRDOS 386 (20/05/2016)
	dd sysrele	; 39 ; TRDOS 386 (19/05/2016) (0 -> 39)
	dd sysfff	; 40 ; Find First File - TRDOS 386 (15/10/2016)
	dd sysfnf	; 41 ; Find Next File - TRDOS 386 (15/10/2016)
	dd sysalloc	; 42 ; Allocate contiguous memory block/pages
			     ; TRDOS 386 (19/02/2017) DMA buff fuctions		
	dd sysdalloc	; 43 ; Deallocate contiguous memory block/pages
			     ; TRDOS 386 (19/02/2017) DMA buff fuctions
	dd syscalbac	; 44 ; IRQ Callback and Signal Response Byte
			     ; service setup - TRDOS 386 (20/02/2017)
			     ; 28/08/2017 (20/08/2017)	
	dd sysdma	; 45 ; TRDOS 386 - (ISA) DMA service
	
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>
	; 01/03/2017
	; 28/02/2017
	; 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	eax, eax ; 28/02/2017
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
	; 10/01/2017
	; 09/01/2017
;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
sysrel2:
	; 28/02/2017
	; Check if there is a (delayed) callback for current user/process
	mov	al, [u.irqwait]
	and	al, 0Fh ; is there a waiting IRQ callback service ?
	jz	short sysrel8 ; no

	; Set return to IRQ callback service and return from the service
	movzx	ebx, al
	mov 	[u.irqwait], bh ; 0 ; reset
	mov	bl, [ebx+IRQenum] ; (available) IRQ index +1 (1 to 9)
	; 01/03/2017
	dec	bl ; IRQ index number, 0 to 8
	js	short sysrel8 ; 0 -> FFh (not in use!?) 
	;
	mov 	al, [u.uno] ; current process (user) number 
	cmp	[ebx+IRQ.owner], al
	jne	short sysrel8 ; it is not the current user/process !?
	test	byte [ebx+IRQ.method], 1 ; callback ?
	jz	short sysrel8 ; not a callback method !?

	mov	edx, [ebx+IRQ.addr] ; IRQ callback service address (virtual)
	mov	byte [u.r_lock], 1 ; IRQ callback service in progress flag

	call	wswap ; save user's registers & status 
		      ;	(for return from IRQ callback service)

	mov	ebp, [u.sp]; kernel's stack, points to EIP (user)
	mov	[ebp], edx ; IRQ call back service address
sysrel8:
	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
	;or	word [esp+8], 200h ; 22/01/2017 ; force enabling interrupts
	iretd	
		; rti / no, return from interrupt

sysrele:
	; 24/03/2017
	; 28/02/2017
	; 27/02/2017
	; 29/01/2017
	; 14/01/2017
	; 13/01/2017
	; 09/01/2017, 10/01/2017, 12/01/2017
	; Major modification for TRDOS 386 (CallBack return)
	;
	; 'sysrele' system call restores previously saved
	; registers and addresses of the process
	; (Main purpose -in TRDOS 386- is to return from
	; timer callback service routine in ring 3 -user mode-.)
	;
	; check if the process is in timer callback phase
	cmp	byte [u.t_lock], 0 ; TIMER INT LOCK
	;je	short sysrel0 ; classic (Retro UNIX 386 type) sysrele
	ja	short sysrel3
	; 27/02/2017
	cmp	byte [u.r_lock], 0 ; IRQ callback lock	
	jna	sysrel0 ; classic sysrele ; 24/03/2017
	call	sysrel7
	cmp	byte [u.r_lock], 0 ; IRQ callback service lock
	jna	short sysrel4
	mov	byte [u.r_lock], 0 ; reset
	;mov	byte [u.irqwait], 0 ; reset ; 28/02/2017
	mov	al, [u.r_mode]
	or	al, al
	jnz	short sysrel4
	dec	al
	mov	[u.r_mode], al ; 0FFh ; not necessary !?
	jmp	short sysrel6		
sysrel3:
	; 27/02/2017
	call	sysrel7
	; 14/01/2017
	sub	al, al
	cmp	[u.t_lock], al ; 0 ; TIMER INT LOCK
	ja	short sysrel5 ; yes
sysrel4:
	; 29/01/2017
	mov	eax, [esp+28] ; eax
	mov	[u.r0], eax
	jmp	sysrel2
sysrel5:
	mov	[u.t_lock], al ; 0 ; reset
	mov	al, [u.t_mode]
	and	al, al
	;jnz	short sysrel2 ; 0FFh ; user mode
	jnz	short sysrel4 ; 29/01/2017
	dec	al
	mov	[u.t_mode], al ; 0FFh ; not necessary !?
sysrel6:
	; cpu will continue from the interrupted sytem call addr
	popad		; edi, esi, ebp, esp, ebx, edx, ecx, eax
	add	esp, 16	; pass segment segisters: ds, es, fs, gs		
	iretd		; eip, cs, eflags

sysrel7:
	movzx	ebx, byte [u.uno] ; current process number
	shl	bx, 2
	;cmp	[ebx+p.tcb-4], eax ; 0 ; is there callback address ?
	;jna	short sysrel0 
	; yes, reset callback address then restore process registers 
	;mov	[ebx+p.tcb-4], eax ; 0 ; reset
	mov     eax, [ebx+p.upage-4] ; UPAGE address
	cli	; disable interrupts till 'iretd'
	jmp	rswap ; restore process 'u' structure
 
badsys:
	; 25/12/2016
	; 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	word [int_num_str], SYSCALL_INT_NUM ; 25/12/2016

	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>
	; 14/11/2017
	; 27/05/2017
	; 10/04/2017
	; 26/02/2017, 28/02/2017
	; 02/01/2017, 23/01/2017
	; 06/06/2016, 10/06/2016
	; 19/05/2016, 23/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
sysexit_0:
	; 23/01/2017
	; 02/01/2017
	; 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.

	; 02/01/2017 
	cli	; disable interrupts
	; 23/01/2017 - reset timer frequency (to 18.2Hz)
	mov	al, 00110110b ; 36h
 	out	43h, al
	sub	al, al ; 0
	out	40h, al ; LB
	out	40h, al ; HB
 	; 
	movzx	ebx, byte [u.uno]
	;mov	bl, [u.uno] ; process number of dying process
	cmp	byte [ebx+p.timer-1], al ; 0
	jna	short sysexit_12 ; no timer events for this process
	mov	byte [ebx+p.timer-1], al ; 0 ; reset
	;mov	al, [timer_events]
	;or	al, al
 	;jz	short sysexit_12 ; no timer events
	;mov	cl, al
	mov	cl, [timer_events] ; 14/11/2017
	;cli	; disable interrupts 
	mov	ah, 16 ; number of available timer events
	mov	esi, timer_set ; beginning address of timer events
sysexit_7:
	mov	al, [esi] ; process number (of timer event)
	cmp	al, bl ; 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	ah
	jz	short sysexit_12
	add	esi, 16
	jmp	short sysexit_7

sysexit_10:
	;mov	byte [esi], 0
	mov	word [esi], 0
	;mov	dword [esi+12], 0
	;
	dec	byte [timer_events] ; 02/01/2017
	;
	jmp	short sysexit_8

sysexit_11:
	sub	ax, ax ; 0 ; 26/02/2017
sysexit_12:
	; 26/02/2017 (Unlink IRQ callbacks belong to the user)
	cmp	byte [u.irqc], 0 ; Count of IRQ callbacks
	jng	short sysexit_16 ; zero or invalid
	; 28/02/2017
	; clear IRQ callback flags (for 'sysrele' and 'sysret')
	mov	[u.irqwait], al ; 0 ; force to clear waiting flag
	mov	[u.r_lock], al ; 0 ; force to clear busy flag
	mov	esi, IRQ.owner
sysexit_13:	
	lodsb
	cmp	al, [u.uno] ; owner  = current user ?
	jne	short sysexit_14
	mov	byte [esi-1], 0 ; owner = 0 : Free
	dec	byte [u.irqc]
	jz	short sysexit_15
sysexit_14:
	cmp	esi, IRQ.owner + 8 ; the last IRQ index number ?
	jna	short sysexit_13 ; no
sysexit_15:
	xor	al, al ; 0
sysexit_16: ; 2:
	sti	; enable interrupts 
	;
	; 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]
	mov	bl, [u.uno] ; 02/01/2017
		; 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
	; 10/04/2017
	cmp	[audio_user], bl
	jne	short sysexit_17
	; reset audio device (current) owner and 'initializated' flag
	mov	[audio_user], bh ; 0
	; 27/05/2017
	mov	ecx,  [audio_buffer]
	or	ecx, ecx
	jz	short sysexit_17
	; 'deallocate_user_pages' is not necessary in sysexit !!!
	;push	ebx
	;mov	ebx, ecx
	;mov	ecx, [audio_buff_size]
	;call	deallocate_user_pages
	;; (Modified Registers -> EAX, EDX, ESI, EDI, EBX, ECX, EBP)
	sub	ecx, ecx
	mov	[audio_buffer], ecx ; 0
	;pop	ebx
sysexit_17:
	;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
	mov	ebx, runq+2 ; normal run queue ; 02/01/2017
	call	putlu
		; jsr r0, putlu
sysexit_6: 
		; / 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 >
	; 02/01/2017 (TRDOS 386 modification)
	; 04/09/2015, 18/05/2015
	; 28/08/2015, 01/09/2015, 02/09/2015
	; 09/05/2015, 10/05/2015, 14/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
	mov	ebx, runq+2 ; normal run queue ; 02/01/2017  
	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

syscreat: ; < create file >
	; 13/11/2017
	; 27/10/2016
	; 25/10/2016, 26/10/2016
	; 15/10/2016, 16/10/2016, 17/10/2016
	; 10/10/2016 (TRDOS 386 = TRDOS v2.0) 
	;	     -derived from INT_21H.ASM-
	;            ("loc_INT21h_create_file")
        ; 	10/07/2011 (12/03/2011)
        ;	INT 21h Function AH = 3Ch
        ;	Create File
        ;	INPUT
        ;	   CX = Attributes
        ;          DS:DX= Address of zero terminaned path name
        ;
	; 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.
	;
	; TRDOS 386 (10/10/2016)
	;	
        ; INPUT ->
        ;	   CL = File Attributes
	;     	      bit 0 (1) - Read only file (R)
	;             bit 1 (1) - Hidden file (H)
        ;             bit 2 (1) - System file (R)
	;             bit 3 (1) - Volume label/name (V)
        ;             bit 4 (1) - Subdirectory (D)
	;	      bit 5 (1) - File has been archived (A)	 	
        ;          EBX = Pointer to filename (ASCIIZ) -path-
	;	
	; OUTPUT ->
	;          eax = File/Device Handle/Number (index) (AL)
	;          cf = 1 -> Error code in AL
	;
	; Modified Registers: EAX (at the return of system call)
	;  
	; Note: If the file is existing and it has not any one
	;	of S,H,R,V,D attributes, it will be truncated 
	;	to zero length; otherwise, access error will be 
	;	returned. 

sysmkdir_0:
	test	cl, 08h ; Volume name 
	jz	short syscreat_0

	; Volume name or long name creation
	; is not permitted (in TRDOS 386)!
	mov	eax, ERR_FILE_ACCESS  ; 11 ; 'permission denied !'
        jmp	sysopen_dev_err

syscreat_0:
        ;mov	[u.namep], ebx
	push	ecx
	mov	esi, ebx
	; file name is forced, change directory as temporary
	;mov	ax, 1
	;mov	[FFF_Valid], ah ; 0 ; reset ; 17/10/2016
	;call	set_working_path 
	call	set_working_path_x ; 17/10/2016	
	jc	syscreat_err

	; 16/10/2016
	cmp	byte [SWP_inv_fname], 0
	ja	short  syscreat_inv_fname ; invalid file name !

	; Here, we have a valid path and also a valid file name
	; (Working dir has been changed if the path
	;  -file name string- had contained a dir name.)

	xor	ax, ax 
	;mov	esi, FindFile_Name
	call	find_first_file
	pop	ecx
		; ESI = Directory Entry (FindFile_DirEntry) Location
		; EDI = Directory Buffer Directory Entry Location
		; EAX = File Size
		;  BL = Attributes of The File/Directory
		;  BH = Long Name Yes/No Status (>0 is YES)
		;  DX > 0 : Ambiguous filename chars are used
	jc	short syscreat_1 ; file not found (the good!) 
				 ; or another error (the bad') 

	; (& the uggly!) truncate file to zero length before open

	;'*' and '?' already checked at 'set_working_path' stage
	;and	dx, dx
	;jnz	short sysmkdir_err ; permission denied 
				   ; invalid filename chars

	;test	cl, 10h ; subdirectory ?
	;jnz	short sysmkdir_err	

	; BL = File Attributes:	
	;     	      bit 0 (1) - Read only file (R)
	;             bit 1 (1) - Hidden file (H)
        ;             bit 2 (1) - System file (R)
	;             bit 3 (1) - Volume label/name (V)
        ;             bit 4 (1) - Subdirectory (D)
	;	      bit 5 (1) - File has been archived	 	

	; * existing directory must not be truncated
	;   (we don't know it is empty or not, at this stage) 
	; * existing volume name (or a long name) can not be
	;   re-created or truncated by 'syscreat' 	
	; * A file with S, H, R attributes must not be truncated
	;   (change attributes to normal, if you need truncate it)

	test	bl, 00011111b  ; check attributes of existing file
	jnz	short sysmkdir_err

	;; normal file, OK to continue...

	; ESI = FindFile_DirEntry
	mov	ax, [esi+DirEntry_FstClusHI] ; 20
	shl	eax, 16 ; 13/11/2017
	mov	ax, [esi+DirEntry_FstClusLO] ; 26 
	; EAX = First cluster to be truncated/unlinked
	push	edi
	push	ecx
	mov	esi, Logical_DOSDisks
	sub	ecx, ecx
	mov	ch, [Current_Drv]
	add	esi, ecx
	; ESI = Logical dos drive description table address
	call	truncate_cluster_chain
	pop	ecx
	pop	edi
	jc	short syscreate_truncate_err

	; 26/10/2016
	; EDI = Directory entry address in directory buffer
	; Update directory entry
	call	convert_current_date_time
	; OUTPUT -> DX = Date in dos dir entry format
        ; 	    AX = Time in dos dir entry format	
	mov	[edi+DirEntry_WrtTime], ax
	mov	[edi+DirEntry_WrtDate], dx	
	mov	[edi+DirEntry_LastAccDate], dx
	xor	eax, eax ; file size = 0 
	mov	[edi+DirEntry_FileSize], eax ; 0
	mov	byte [DirBuff_ValidData], 2 ; data changed sign	
	mov	esi, FindFile_DirEntry
	mov	dl, 1 ; open file for writing
	jmp	sysopen_2

sysmkdir_err:
	; 1 = write, 2 = read & write, >2 = invalid
        mov	eax, ERR_FILE_ACCESS  ; 11 ; 'permission denied !'
        jmp	short sysopen_err

syscreate_truncate_err:
	mov	eax, ERR_DRV_WRITE ; 18 ; 'disk write error !'
        jmp	short sysopen_err

syscreat_inv_fname:  ; invalid file name chars 
	; 16/10/2016
	mov	eax, ERR_INV_FILE_NAME  ; 26 ; invalid file name chars 
	pop	ecx
	jmp	sysopen_err

syscreat_1:
	; Error code in EAX
        cmp	al, 02h ; 'File not found' error
        jne	sysopen_err

	test	cl, 10h ; Directory
	jnz	sysmkdir_2

syscreat_2:
	mov	esi, FindFile_Name 
        ;xor	edx, edx
        xor	eax, eax ; File Size  = 0
	xor	ebx, ebx
	dec 	ebx ; FFFFFFFFh -> create empty file 
	            ;              (only for FAT fs) 
	; CL = File Attributes
	call	create_file
	jc	sysopen_err
		; EAX = New file's first cluster
		; ESI = Logical Dos Drv Descr. Table Addr.
		; EBX = offset CreateFile_Size
		; ECX = Sectors per cluster (<256) 
		; EDX = Directory entry index/number (<65536)
	; 26/10/2016
	;mov	esi, Directory_Buffer
	;shl	dx, 5 ; *32
	;add	esi, edx
	;; esi = directory entry address in directory buffer

	; Here, directory entry has been created but last
	; modification date & time of the parent dir has not
	; been updated, yet! 
	; (Note: Directory and FAT buffers have been updated...)
 	
	call	update_parent_dir_lmdt ; now, it is OK too!

	; 25/10/2016
	mov	ax, 1800h
	mov	esi, FindFile_Name
	call	find_first_file
	jc	short sysopen_err

	; Only possible error after here is 
	; "too many open files !" error.
	;
	; If "syscreat" will return with that error,
	; (the file has been created but it could not be opened)
	; the user must retry to open this file again
	; or must close another file before using 
	; "sysopen" system call.

	mov	dl, 1 ; open file for writing
	; ESI = Directory Entry (FindFile_DirEntry) Location
	; EAX = File Size (= 0)
	jmp	short sysopen_2

sysopen: ;<open file>
	; 26/10/2016
	; 24/10/2016
	; 17/10/2016
	; 15/10/2016
	; 06/10/2016, 07/10/2016, 08/10/2016
	; 05/10/2016 (TRDOS 386 = TRDOS v2.0) 
	;	     -derived from INT_21H.ASM-
	;            ("loc_INT21h_open_file")
        ; 	26/02/2011 
        ;	INT 21h Function AH = 3Dh
        ;	Open File
        ;	INPUT
        ;	   AL= File Access Value
	;     	     0- Open for reading
	;            1- Open for writing
        ;            2- Open for reading and writing
        ;          DS:DX= Pointer to filename (ASCIIZ)
        ;
	; 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')
	;
	; TRDOS 386 (05/10/2016)
	;	
        ; INPUT ->
        ;	   CL = File Access Value (Open Mode)
	;     	      0 - Open file for reading
	;             1 - Open file for writing
        ;             2 - Open device for reading
	;	      3 - Open device for writing
        ;          EBX = Pointer to filename/devicename (ASCIIZ)
	; OUTPUT ->
	;          eax = File/Device Handle/Number (index) (AL)
	;          cf = 1 -> Error code in AL
	;
	; Modified Registers: EAX (at the return of system call)
	;  

	cmp	cl, 1 ; read file (0), write file (1)
	jna	short sysopen_0

	cmp	cl, 3
	jna	sysopen_device

	; Invalid access code
	mov	eax, ERR_INV_PARAMETER
	ja	sysopen_dev_err

sysopen_0:
	;mov	[u.namep], ebx
	push	ecx
	mov	esi, ebx
	; file name is forced, change directory as temporary
	;mov	ax, 1
	;mov	[FFF_Valid], ah ; 0 ; reset ; 17/10/2016
	;call	set_working_path 
	call	set_working_path_x ; 17/10/2016	
	jnc	short sysopen_1

syscreat_err: ; ecx = file attributes (for 'syscreat')
	pop	ecx ; open mode  
	and	eax, eax  ; 0 -> Bad Path!  
	jnz	short sysopen_err
	; eax = 0
	mov	eax, ERR_DIR_NOT_FOUND ; Directory not found !
sysopen_err:
	mov	[u.r0], eax
	mov	[u.error], eax
	call 	reset_working_path
	jmp	error

sysopen_1:
	;mov	esi, FindFile_Name
        mov	ax, 1800h ; Only files 
	call	find_first_file
	pop	edx
	jc	short sysopen_err ; eax = 2 (File not found !)

	; check_open_file_attr_access_code

        test	bl, 7  ; system, hidden, readonly 
        jz	short sysopen_2

	and	dl, dl ; 0 = read mode
	jz	short sysopen_2

	; 1 = write, 2 = read & write, >2 = invalid
        mov	eax, ERR_FILE_ACCESS  ; 11 = 'permission denied !'
        jmp	short sysopen_err

sysopen_2:
	; esi = Directory Entry (FindFile_DirEntry) Location
	mov	ebx, esi
        xor     esi, esi ; 0
        xor     edi, edi ; 0
sysopen_3: ; scan the list of entries in fsp table
        cmp     byte [esi+u.fp], 0
        jna     short sysopen_4 ; empty slot
        inc     si
        cmp     si, 10
	jb	short sysopen_3
toomanyf:
	mov	eax, ERR_TOO_MANY_FILES ; too many open files !
	jmp	short sysopen_err

sysopen_4: 
        cmp     byte [edi+OF_MODE], 0 ; Scan open files table 
	jna     short sysopen_5
	inc	di
	cmp     di, OPENFILES ; max. number of open files (=10)
	jb	short sysopen_4
	jmp	short toomanyf

sysopen_5:
	inc	dl
        mov     [edi+OF_MODE], dl
	mov	dl, [FindFile_Drv]
        mov     [edi+OF_DRIVE], dl ; Logical DOS drive number
	shl	di, 2 ; *4 (dword offset)

	mov	[edi+OF_SIZE], eax ; File size in bytes

        mov 	ax, [ebx+DirEntry_FstClusHI]
	shl	eax, 16
	mov 	ax, [ebx+DirEntry_FstClusLO]
	mov     [edi+OF_FCLUSTER], eax ; First cluster
	mov     [edi+OF_CCLUSTER], eax ; Current cluster

        xor	ebx, ebx
        mov     [edi+OF_POINTER], ebx ; offset pointer (0)
        mov     [edi+OF_CCINDEX], ebx ; cluster index (0)

	mov	eax, [FindFile_DirFirstCluster]
	mov	[edi+OF_DIRFCLUSTER], eax

	mov	eax, [FindFile_DirCluster]
	mov	[edi+OF_DIRCLUSTER], eax

	; Get (& Save) Volume ID 
	; Important for files of removable drives
	; (In order to check the drive has same volume/disk)
	mov	bh, dl
        add	ebx, Logical_DOSDisks
        mov	al, [ebx+LD_FATType]
        cmp	al, 1
        jb	short sysopen_6_fs
        cmp	al, 2
        ja	short sysopen_6_fat32
sysopen_6_fat:
        mov	eax, [ebx+LD_BPB+VolumeID]
        jmp	short sysopen_7
sysopen_6_fs:
        mov	eax, [ebx+LD_FS_VolumeSerial]
        jmp	short sysopen_7
sysopen_6_fat32:
        mov	eax, [ebx+LD_BPB+FAT32_VolID]
sysopen_7:
        mov	[Current_VolSerial], eax

	mov	[edi+OF_VOLUMEID], eax

	; 24/10/2016
	shr	di, 1 ; 4/2, word offset
	mov	bx, [FindFile_DirEntryNumber]
	mov	[edi+OF_DIRENTRY], bx

	xor	edx, edx
	;shr	di, 2 ; /4 (byte offset)
	shr	di, 1 ; 2/2, byte offset
	mov	byte [edi+OF_OPENCOUNT], dl ; 0
	mov	byte [edi+OF_STATUS], dl ; 0

	mov	ebx, edi
	inc	bl

        mov     [esi+u.fp], bl ; Open File Entry Number
	mov     [u.r0], esi ; move index to u.fp list 
			    ; into eax on stack

        call 	reset_working_path
        
	jmp	sysret

	; (Retro UNIX 386 v1.0)
	; '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 
	; ----------------------|------------------------

sysopen_device:
	; 15/10/2016
	; 08/10/2016
	; 07/10/2016 (TRDOS 386 = TRDOS v2.0)
	push	ecx ; open mode
	mov	ebp, esp
	mov	ecx, 16 ; transfer length = 16 bytes 
	sub	esp, ecx
	mov	edi, esp ; destination address 
	mov 	esi, ebx ; dev name in user's memory space
	call	transfer_from_user_buffer
	jnc	short sysopen_dev_0
	; eax = ERR_OUT_OF_MEMORY = 4 = ERR_MINOR_IM
	pop	ecx
sysopen_dev_err:
	mov	[u.r0], eax
	mov	[u.error], eax
	jmp	error
sysopen_dev_0:
	mov	esi, edi ; Device name addr (max. 16 bytes, ASCIIZ) 
			 ; for example: "tty, TTY, /dev/tty"
	call	get_device_number
	mov	esp, ebp
	pop	ecx
	jnc	short sysopen_dev_1
	mov	eax, ERR_INV_DEV_NAME ; 24 ; 'invalid device name !'
	jmp	short sysopen_dev_err
sysopen_dev_1:
	; eax = Device Number (AL)
	;  cl = Open mode (2 = device read, 3 = device write)
        xor     ebx, ebx ; 0
sysopen_dev_2: ; scan the list of entries
        cmp     [ebx+u.fp], bl ; 0
        jna     short sysopen_dev_3 ; empty slot
        inc     bl
        cmp     bl, 10
	jb	short sysopen_dev_2
	;
	mov	eax, ERR_TOO_MANY_FILES ; too many open files !
	jmp	short sysopen_dev_err
sysopen_dev_3:
	mov 	[u.r0], ebx ; File/Device index/handle/descriptor
	; eax = device number (entry offset)
	mov	ch, [eax+DEV_ACCESS] ; bit 0 = accessable by users
				     ; bit 1 = read access perm
				     ; bit 2 = write access perm
				     ; bit 3 = IOCTL permit to users
				     ; bit 4 = block device if set
				     ; bit 5 = 16 bit or 1024 byte
				     ; bit 6 = 32 bit or 2048 byte
				     ; bit 7 = installable device drv
	test 	ch, 1 ; accessable by normal users (except root)
	jnz	short sysopen_dev_4 ; yes, permission has been given
	cmp	byte [u.uid], 0 ; root?
	jna	short sysopen_dev_4 ; superuser can open all devices
sysopen_dev_perm_err:
	mov	eax, ERR_DEV_ACCESS  ; 11 = 'permission denied !'
	jmp	short sysopen_dev_err
sysopen_dev_4:
	shr	ch, 1 ; result: 1 = read, 2 = write, 3 = r & w 
	dec	cl  ; result: 1 = read, 2 = write
	test	cl, ch
	jz	short sysopen_dev_perm_err 

	shl	ch, 1 ; bit 0 = 0
	; eax = device number (entry offset)
	call	device_open
	jc	short sysopen_dev_perm_err 

	; eax = device number (entry offset)
	or	al, 80h ; set device bit (set bit 7 to 1)
	mov	ebx, [u.r0]
	mov	[ebx+u.fp], al	; bit 7 (=1) points to device	
	
	jmp	sysret

sysmkdir: ; < make directory >
	; 15/10/2016
	; 10/10/2016 (TRDOS 386 = TRDOS v2.0) 
	;	     -derived from INT_21H.ASM-
	;            ("loc_INT21h_create_file")
        ; 	10/07/2011 (12/03/2011)
        ;	INT 21h Function AH = 3Ch
        ;	Create File
        ;	INPUT
        ;	   CX = Attributes
        ;          DS:DX= Address of zero terminaned path name
        ;
        ;
	; 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
	;
	; TRDOS 386 (10/10/2016)
	;	
        ; INPUT ->
        ;	   CL = Directory Attributes
	;     	      bit 0 (1) - Read only file/dir (R)
	;             bit 1 (1) - Hidden file/dir (H)
        ;             bit 2 (1) - System file/dir (R)
	;             bit 3 (1) - Volume label/name (V)
        ;             bit 4 (1) - Subdirectory (D)
	;	      bit 5 (1) - File/Dir has been archived (A)
	;	   CX = 0 -> create normal directory	 	
        ;          EBX = Pointer to directory name (ASCIIZ) -path-
	;	
	; OUTPUT ->
	;          eax = First cluster of the new directory
	;          cf = 1 -> Error code in AL
	;
	; Modified Registers: EAX (at the return of system call)
	;  
	; Note: If the file or directory is existing
	;	an access error will be returned. 

	and	cx, cx ; if cx = 0 -> create a normal subdir
	jz	short sysmkdir_1

	test	cl, 10h ; if dir flags set, also use other flags
	jnz	sysmkdir_0 ; jump to head of 'syscreat'

	; CX has wrong flags
	mov 	eax, ERR_INV_FLAGS
	jmp	sysopen_dev_err

sysmkdir_1:
	mov	cl, 10h ; set subdir flag and reset other flags
	jmp	sysmkdir_0 ; jump to head of 'syscreat'
sysmkdir_2: 
	; jump from 'syscreat' ; from 'syscreat_1'
	;  CL = Directory attributes/flags  
	mov	esi, FindFile_Name 
	call	make_sub_directory
	jc	sysopen_err       ; NOTE: Old type (TRDOS 8086)
				  ; error codes must be modified
				  ; for next TRDOS 386 versions
				  ; (10/10/2016)
				  ; Old (MSDOS type)
				  ; error codes (2011):
				  ;  2 = file not found
				  ;  3 = directory not found
				  ;  5 = access denied
				  ; 12 = no more files
			          ; 19 = disk write protected   
				  ; 39 = insufficient disk space
				  ; 'sysdefs.s' ; 10/10/2016  	
	
	mov	[u.r0], eax ; New sub dir's first cluster

        call 	reset_working_path

	jmp	sysret	

sysclose: ;<close file>
	; 06/10/2016 (TRDOS 386 = TRDOS v2.0) 
	;
	; 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)

	; TRDOS 386 (06/10/2016)
	;	
        ; INPUT ->
        ;	   EBX = File Handle/Number (file index) (AL)
	; OUTPUT ->
	;          cf = 0 -> EAX = 0
	;          cf = 1 -> Error code in EAX (ERR_FILE_NOT_OPEN)
	;
	; Modified Registers: EAX (at the return of system call)
	;  

	mov 	eax, ebx
	xor	ebx, ebx	
	mov	[u.r0], ebx ; 0  ; return value of EAX
	call 	fclose
	jnc	sysret
	mov	eax, ERR_FILE_NOT_OPEN ; file not open !
	mov	[u.error], eax ;
	mov	[u.r0], eax ; ! invalid handle !
	jmp	error

sysread: ; < read from file >
	; 11/10/2016 (TRDOS 386 = TRDOS v2.0) 
	;	     -derived from INT_21H.ASM-
	;            ("loc_INT21h_read_file")
        ; 	13/03/2011 (05/03/2011)
        ;	INT 21h Function AH = 3Fh
        ;	Read from a File
        ;	INPUT
	;	   BX = File Handle
        ;	   CX = Number of bytes to read
        ;          DS:DX= Buffer address
        ;
	; Note: TRDOS 386 'sysread' has been derived from 
	;	Retro UNIX 386 v1 'sysread', except a few 
	;	code modifications.
	;
	; 13/05/2015 (Retro UNIX 386 v1)
	; 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. 
	;
	; TRDOS 386 (05/10/2016)
	;	
        ; INPUT ->
        ;	   EBX = File handle (descriptor/index)
	;	   ECX = Buffer address		
        ;          EDX = Number of bytes
	; OUTPUT ->
	;          EAX = Number of bytes have been read
	;          cf = 1 -> Error code in AL
	;
	; Modified Registers: EAX (at the return of system call)
	; 

	; EBX = File descriptor
	call	getf1 
	jc	short device_read ; read data from device
	; EAX = First cluster of the file

	call	rw1
	jnc	short sysread_0

	mov	[u.r0], eax ; error code
	jmp	error
	 
sysread_0:
	call	readi
	jmp	short rw0

syswrite: ; < write to file >
	; 23/10/2016
	; 11/10/2016 (TRDOS 386 = TRDOS v2.0) 
	;	     -derived from INT_21H.ASM-
	;            ("loc_INT21h_write_file")
        ; 	13/03/2011 (05/03/2011)
        ;	INT 21h Function AH = 40h
        ;	Write to a File
        ;	INPUT
	;	   BX = File Handle
        ;	   CX = Number of bytes to write
        ;          DS:DX= Buffer address
        ;
	; Note: TRDOS 386 'sysrwrite' has been derived from 
	;	Retro UNIX 386 v1 'syswrite', except a few 
	;	code modifications.
	;

	; 13/05/2015 (Retro UNIX 386 v1)
	; 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. 
	;
	; INPUT ->
        ;	   EBX = File handle (descriptor/index)
	;	   ECX = Buffer address		
        ;          EDX = Number of bytes
	; OUTPUT ->
	;          EAX = Number of bytes have been written
	;          cf = 1 -> Error code in AL
	;
	; Modified Registers: EAX (at the return of system call)
	;  

	; EBX = File descriptor
	call	getf1 
	jc	short device_write ; write data to device
	; EAX = First cluster of the file
	; EBX = File number  (Open file number) ; 23/10/2016

	call	rw1
	jnc	short syswrite_0
	mov	[u.r0], eax ; error code
	jmp	error
	 
syswrite_0:
	call	writei
rw0: ; 1:
        mov	eax, [u.nread]
	mov	[u.r0], eax
	jmp	sysret

rw1:	
	; 11/10/2016 (TRDOS 386 = TRDOS v2.0) 
	; 14/05/2015 (Retro UNIX 386 v1)
	; 11/05/2015 (Retro UNIX 386 v1 - Beginning)
	; 23/05/2013 - 24/05/2013 (Retro UNIX 8086 v1)
	; System call registers: ebx, ecx, edx (through 'sysenter')
	;
	; EBX = File descriptor
	;call	getf1 ; calling point in 'getf' from 'rw1'
	;jc	short device_rw ; read/write data from/to device
	; EAX = First cluster of the file

	cmp 	eax, 2
	jb	short rw2
	;
	mov	[u.base], ecx 	; buffer address/offset 
				;(in the user's virtual memory space)
	mov	[u.count], edx 

        mov     dword [u.error], 0 ; reset the last error code
	retn

rw2:
	mov	eax, ERR_FILE_NOT_OPEN ; file not open !
	mov	dword [u.error], eax
	retn
rw3: 
	mov	eax, ERR_FILE_ACCESS ; permission denied !
	mov	dword [u.error], eax
	stc
	retn

device_read:
	; 11/10/2016 (TRDOS 386 = TRDOS v2.0)
	; cl = DEV_OPENMODE ; open mode
	; ch = DEV_ACCESS   ; access flags   
	; al = DEV_DRIVER   ; device number (eax)

	test	cl, 1 ; 1 = read, 2 = write, 3 = read&write
	jz	short rw3

	mov	ebx, eax
	shl	bx, 2 ; *4

	test	ch, 80h ; bit 7, installable device driver flag
	jz	short d_read_2 ; Kernel device
	; installable device
d_read_1:
        jmp	dword [ebx+IDEV_RADDR-4]
d_read_2:
	jmp	dword [ebx+KDEV_RADDR-4]

device_write:
	; 11/10/2016 (TRDOS 386 = TRDOS v2.0)
	; cl = DEV_OPENMODE ; open mode
	; ch = DEV_ACCESS   ; access flags   
	; al = DEV_DRIVER   ; device number (eax)

	test	cl, 2 ; 1 = read, 2 = write, 3 = read&write
	jz	short rw3	

	mov	ebx, eax
	shl	bx, 2 ; *4

	test	ch, 80h ; bit 7, installable device driver flag
	jz	short d_write_2 ; Kernel device
	; installable device
d_write_1:
        jmp	dword [ebx+IDEV_WADDR-4]
d_write_2:
	jmp	dword [ebx+KDEV_WADDR-4]


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:
	; 02/01/2017
	; 21/12/2016
	; 19/12/2016
	; 10/12/2016 (callback)
	; 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 = time/tick count in current time count unit
	;	    // 10/12/2016			
	;	    80h = Stop timer event (callback method)
	;	    81h = 18.2 ticks per second, callback method
	;	    82h = 10 milliseconds, callback method 	
	;	    83h = 1 second (for RTC int), callback method 	
	;	    84h = current time count unit, callback method
	;
	;	    Note: Only 03h or 83h will set real time clock
	;		  (RTC) events (Others are for PIT events)! 	
	;	
	;	NOTE: If callback (user service) method is used,
	;	    EDX will point to the return address (of service
	;	    procedure) in user's space instead of signal 
	;	    response byte address. (TRDOS 386 kernel will
	;	    direct the cpu to that address -in user's space-
	;	    at the return of system call or interrupt 
	;	    just after the adjusted count/time is elapsed.)
	;	    User's sevice routine must be ended with a
	;	    'iret'. Normal return addresses from system 
	;	    calls or and interrupts will be kept same except
	;	    the timer returns. 			
     	;
	;	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)
	;	Calback:	resb 1 ; 1 = callback, 0 = response byte
	;	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
	;

	; 19/12/2016 (timer callback)
	mov	byte [tcallback], 0 
	mov	byte [trtc], 0
	mov	dword [u.tcb], 0 ; this is not necessary...

	cmp	bh, 80h
	jb	short systimer_cb2
	ja	short systimer_cb0

	xor	edx, edx ; 0, reset callback address
	jmp	short systimer_cb1

systimer_cb0:
	cmp	bh, 84h	
	ja	short systimer_5 ;  undefined, error

	;mov	byte [tcallback], 1 ; 19/12/2016
	inc	byte [tcallback]

systimer_cb1:
	movzx	esi, byte [u.uno] ; process number
	shl	si, 2	
	mov	[esi+p.tcb-4], edx ; set process timer callback address
				   ; (overwrite prev value if it is set!)
	and	bh, 7Fh

systimer_cb2:
	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)

systimer_19:
	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, 4
        ja      short systimer_5  ; undefined time count unit
	;jb	short systimer_16	

	;mov	al, 1	; default (use current timer unit)
			; countdown value is in ECX ! 
			; max. value of ecx = 4294967296/10
        ;jmp    short systimer_0
	;jmp	short systimer_19	
	je	short systimer_19

systimer_16:
	; bh = 3
	; timer event via real time clock interrupt
	; interrupt/update frequency: 1 Hz (1 tick per second)
	
	mov	al, 182 ; (*) ; 18.2 * 10
	inc	byte [trtc] ; timer event via real time clock
        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
	mov	al, [tcallback] ; timer callback flag
	stosb 	; 1= callback method, 0= signal response byte method
	mov	al, [trtc] ; timer interrupt type
	stosb	; 1= real time clock, 0= programmable interval timer
	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

	; 19/12/2016
	cmp	byte [tcallback], 0 ; timer callback method ?
	jna	short systimer_17 ; no	
	mov	eax, ebx ; virtual address for callback routine 
	jmp	short systimer_18

systimer_17: ; signal response byte method
	; ebx = virtual address
	; [u.pgdir] = page directory's physical address
	; 20/02/2017
	inc	 byte [no_page_swap] ; 1 
			; Do not add this page to swap queue
			; and remove it from swap queue if it is
			; on the queue.
	call	get_physical_addr
	jc	short systimer_8 ; 07/06/2016
	; eax = physical address of the virtual address in user's space
systimer_18:
	stosd	; response addr (physical) or callback addr (virtual)
	inc	byte [timer_events] ; 07/06/201
	; 02/01/2017
	movzx	eax, byte [u.uno]
	inc	byte [eax+p.timer-1]	
	;
	sti 	; enable interrupts
	jmp	sysret

systimer_8:
	; 10/06/2016
	; 07/06/2016
	sub	al, al ; 0
	mov	[edi-12], al ; clear process number (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 number (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 ; 02/01/2017
	;mov	byte [edi], 0 
	mov	word [edi], 0 ; clear
	;mov	dword [edi+12], 0 ; clear
	dec	byte [timer_events]
	jmp	short systimer_12

sysvideo: ; VIDEO DATA TRANSFER FUNCTIONS
	; 23/11/2020
	; 22/11/2020
	; 21/11/2020 (TRDOS 386 v2.0.3)
	; 12/05/2017
	; 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 (undefined)
	;		Bits 2&3, Transfer Type
	;			0 - Display page transfer	
	;	     		1 - Display page window transfer
	;	     		2 - Frame/Viewport/Window address transfer
	;			3 - Window handle transfer (undefined)
	;		; 23/11/2020
	;		Bits 4..7 - Reserved, undefined (must be 0)
	;
	;	     /// 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 address
	;		 DL = Video page
	;	     /// BL = 5&6 -> user to system, system to user transfer 
	;		(system window is in current/active display page)	 	 		
	;		 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
	;		; 22/11/2020
	;	     /// BL = 9&10 -> user to system, system to user transfer 
	;		(system window is in current/active display page)	 	 		
	;		 ESI = User's buffer address
	;		  CX = offset in video page (char position in 80*25)
	;		  DX = transfer count (character count, max. 80*25)
        ;                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 = 8 -> system to system transfer
	;		 ESI = System's source buffer (video page) address
	;		  CX = offset in video page (char position in 80*25)
	;		  DX = transfer count (character count, max. 80*25)
	;		 EDX High 16 bits = Bottom row (Y2 position)
	;		 EDI = System's destination buffer (video page) address
	;
	;		; 23/11/2020 (major modification)
	;		; 22/11/2020 (bugfixes and extensions)
	;	BH = 1 = 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
	;		; 22/11/2020 (TRDOS v2.0.3)
	;	        xAh = INC display page bytes
	;	        xBh = DEC display page bytes
	;	        xCh = NEG display page bytes
	;	        xDh = NOT display page bytes
	;
	;		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, 
	;		 If CH > 0, color in CL will be mixed with pixel color  
	;	     /// 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 = 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
	;		; 22/11/2020 (TRDOS v2.0.3)
	;	       10 = INC display page bytes (color mask in CL) 
	;	       11 = DEC display page bytes (color mask in CL)
	;	       12 = NEG display page bytes (color mask in CL)
	;	       13 = NOT display page bytes (color mask in CL)
	;
	;	     /// BL = 0 -> Fill color (all screen pixels)
	;		 CL = Color value, 
	;		 If CH > 0, color in CL will be mixed with pixel color 
	;	     /// 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)
	;
	;		Example: (21/11/2020)
	;			esi = 0B0000h (window start value)
	;			ecx = 00000000h (start at row 0, column 0) 
	;			edx = 0018004Fh (end at row 24,column 79)
	;			row count= 25, column count = 80
	;			(end) byte offset = (80*2)*24+(79*2) = 3998	
	;			end value of esi = 0B8F9Eh (last word of page)
	;			(! end address is included to transfer !)
	;			((just as line start and line end coordinates)) 
	;
	; 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.
	;
	; PIXEL READ/WRITE
	;
	;	BH = 3 = Read/Write pixel(s) -for all graphics modes-
	;	     BL = 
	;	     bits 0&1&2
	;		0 = Read one pixel
	;		1 = Write one pixel
	;	     	2 = Read two pixels
	;		3 = Write two pixels
	;	     	4 = Read four pixels
	;		5 = Write four pixels
	;		6 - read one nibble (4 bits)
	;		7 - write one nibble (4 bits)
	;	     bit 3 - color mix option (1 = mix, 0 = overwrite)
	;	     bit 4 - true color (dword color number) option
	;		   (linear frame buffer address will be used)
	;	     bit 5 - alpha byte will be written if bit 5 is 1			 	
	;	     bit 6 - use CL if bit 6 is set (do not use ECX or CX)
	;	     bit 7 - force/use linear frame buffer address
	;		    (if bit 7&4 are 0, 0A0000h address will be used)		
	;		
	;	     CL = color for writing pixel(s) or
 	;	     ECX = color for writing pixel(s) in true color modes
	;		  or ecx is used as separate color bytes for reading
	;		  or writing 2 or 4 pixels in byte order
	;		 (24 bit will be written for 24 bit true color modes)
	;		 (alpha byte will be regarded for 32 bit true colors)
	;		 (but alpha byte will not written if BL bit 5 is 0)
	;
	;	     EDX = Offset from start of video memory (0A0000h)
	;		   or start of linear frame buffer
	;		  (edx contains nibble offset if BL bits 1&2 are 1)
	;		  (nibble read/write is used with low 4 bits of CL) 
	;
	; Outputs:
	;	EAX = pixel color (according to BL and CL input)
	;	
	;
	; 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 permission 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 permission to
	;		access locked display memory if the owner is
	;		it's parent.)
	;	
	;	    ; 23/11/2020		
	;	    Screen width options = 320, 640, 800 
	;			
	; Outputs:
	;	EAX = Display memory 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 permission to
	;		access locked display memory if the owner is
	;		it's parent.)
	;			
	;		Return: EAX = Linear Frame Buffer address
	;			EDX = Frame Buffer Size in bytes	
	;
	; 	23/11/2020
	; ***	GET VIDEO MODE & LINEAR FRAME BUFFER INFO   
	;	(This function -7- also is used for VGA and CGA modes)
	; 	
	;	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
	;		 BX = Current Video Mode (VGA bios modes)
	;		      If BH = 1 ->
	;			 BX = VESA VBE2 extended video mode
	;		      If BH = 0FFh (if BL input is 0FFh)
	;			 BL = 0 - 256 virtual colors (of 16M)	
	;			      1 - 16 colors
	;		     	      2 - 256 colors
	;		     	      3 - 65536 colors
	;			      4 - 24 bit true colors (16M colors)
	;		     	      5 - 32 bit true colors (16M colors)
	;		     	      6 - mono color (black-white)
	;		     	      7 - 4 colors
	;		Note:	
	;		Alpha byte will be used as virtual color index.
	;		(32 bit pixel colors.. byte 0,1,2 rgb and 3 alpha)
	;
	;		ECX = Pixel resolution
	;		      CX = Width (320, 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
	;
	; 	23/11/2020
	; SET VIDEO MODE & COLOR INDEXES (not implemented yet)
	;	
	;	BH = 8 = Set Video Mode & Color Indexes (256 colors)
	;		
	;		BL = Requested Video Mode (method)
	;
	;		bit 0&1 -
	;		     0 - VGA modes
	;		     1 - VESA (VBE2) modes
	;		     2 - TRD0S 386 mode index
	;		     3 - Resolution in ECX
	;		bit 2&3&4
	;		     0 - 256 virtual colors (of 16M colors)
	;		     1 - 16 colors
	;		     2 - 256 colors
	;		     3 - 65536 colors
	;		     4 - 24 bit true colors (16M colors)
	;		     5 - 32 bit	true colors (16M colors)
	;		     6 - mono color (black-white)
	;		     7 - 4 colors
	;		bit 5 - color palette (index) option
	;		bit 6 - force VESA VBE 3 protected mode interface
	;		     (set equivalent VBE mode by using 'PMID' info)
	;		bit 7 - do not set mode, but check if available  	
	;
	;		ECX = Pixel resolution (if bit 0&1 of BL are 1)
	;		      CX = Width (320, 640, 800, 1024, 1366, 1920)
	;		      High 16 bits of ECX = Height
	;		     (BL bit 2,3,4 is used for color option) 	
	;		
	;		ECX = Video mode (if bit 1 of BL is 0) 
	;
	;		EDX = user's true color palette buffer for
	;		      mono, 4 colors, 16 colors, 256 colors only   
	;		     (edx will be used if bit 5 of BL is set)
	;		     ((True colors mode will be set but 256 virtual
	;		     colors will be used if BL bits 2&3&4 is 0.))
	;		Note: 
	;		Alpha byte will be used as virtual color index.
	;		(32 bit pixel colors.. byte 0,1,2 rgb and 3 alpha)	 	
	;
	;		TRDOS 386 v2 mode index (BL, bit 2%3&4)
	;		  0 - 80x25 text, 16 colors
	;		  1 - 320x200 graphics, 256 colors
	;		  2 - 640x480 graphics, 256 colors		 		 	  								 	 					
	;		  3 - 800x600 graphics, 256 colors	
	;		  4 - 1024x768 graphics, 256 colors
	;		  5 - 1024x768 graphics, 16M colors (24+8 bits)
	;		  6 - 1366x768 graphics, 16M colors (24+8 bits)
	;		  7 - 1920x1080 graphics, 16M colors (24+8 bits)
	;		(256 colors modes can be used with color palette)
	;
	; Outputs:
	;	EAX = Requested (Proper) video mode number + 1
	;	      ("dec eax" by user will give requested video mode)	
	;
	;	EAX = 0 -> Error
	;

	; 16/05/2016
	xor	eax, eax
	mov	[u.r0], eax
	and	bh, bh
	jnz	sysvideo_13 ; 11/07/2016

	;; 21/11/2020 (TRDOS 386 v2.0.3)
	;; tty/text mode transfers are only for video mode 3

	; 22/11/2020
	;cmp	byte [CRT_MODE], 3 ; 80x25 text, 16 colors
	;jne	sysret ; invalid (nothing to do), [u.r0] = 0	
	
	; 23/11/2020
	; bit 7,6,5,4 of BL are reserved and it must be 0
	;	 	 for current 'sysvideo' version
	test	bl, 0F0h
	jnz	sysret ; invalid (undefined) ! 

	; Video mode 0, 80*25 text mode, CGA 16 colors  ; [CRT_MODE] = 3
	mov	bh, bl
	shr	bh, 2 ; 4..7 -> 1, 8..11 -> 2, 12..15 -> 3
	; 21/11/2020
	;and	bh, bh
        jnz	sysvideo_4  ; Display page window transfer etc.

	; Display page (complete) transfer

	mov	edi, 0B8000h
	; dl = display page number, destination
	mov	ax, 4096 ; 21/11/2020
	and	dl, dl
	jz	short sysvideo_1
	cmp	dl, 7
	ja	sysret ; invalid (nothing to do), [u.r0] = 0
sysvideo_0:
	; page lenght = 4096 bytes (but page content is 80*25*2 bytes)
	add	edi, 4096 ; 21//11/2020 ([CRT_LEN] = 1000h for mode 3)
	dec	dl
	jnz	short sysvideo_0	
sysvideo_1:	
	and	bl, 3
	jnz	short sysvideo_2 ; user to system display page transfer
	; cl = display page number, source
	cmp	cl, 7
	ja	sysret ; invalid (nothing to do), [u.r0] = 0
	; system to system video/display page transfer (mode 0)
	; 21/11/2020
	;mov	esi, 0B8000h
	;movzx	eax, cl
	;mov	edx, 4096 ; [CRT_LEN] = 1000h for video mode 3
	;mov	ecx, edx
	;mul	edx
	;add	esi, eax
	movzx	esi, cl
	shl	si, 12 ; * 4096
	add	esi, 0B8000h 

	; 21/11/2020
	;mov	ecx, 4096
	mov	ecx, eax ; 4096
	;mov	[u.r0], ecx ; 4096 bytes
	mov	[u.r0], cx

	shr	cx, 2 ; / 4
	rep	movsd
	jmp	sysret	
sysvideo_2:  
	cmp	bl, 2
        ja      sysret; invalid (user to user), [u.r0] = 0
	jb	short sysvideo_3 ; user to system
	; system to user video/display page transfer (mode 0)
	mov	esi, edi
	mov	edi, ecx ; user buffer
	; 21/11/2020
	mov	ecx, eax ; 4096
	call	transfer_to_user_buffer ; fast transfer
	jc	sysret ; [u.r0] = 0
	; 21/11/2020
	;mov	[u.r0], ecx
	mov	[u.r0], cx
	jmp	sysret	
sysvideo_3: 
	; user to system video/display page transfer (mode 0)	
	mov	esi, ecx ; user buffer
	; edi = video page address
	; 21/11/2020
	mov	ecx, eax ; 4096
	call	transfer_from_user_buffer ; fast transfer
	jc	sysret ; [u.r0] = 0
	; 21/11/2020
	;mov	[u.r0], ecx
	mov	[u.r0], cx
	jmp	sysret

sysvideo_4:
	; 23/11/2020 (TRDOS 386 v2.0.3)

	; Display page window transfer etc.
	and	bl, 3
        jnz     sysvideo_9 ; user to system or system to user
	; 21/11/2020
	; system to system video/display page window transfer (mode 0)
	cmp	esi, 0B8000h	; source buffer address (system)
	jb	sysret
	cmp	esi, 0B8000h+(4096*8)
	jnb	sysret
	cmp	edi, 0B8000h	; destination buffer address (system)	
	jb	sysret
        cmp     edi, 0B8000h+(4096*8)
	jnb	sysret
	;
	push	ecx ; X1 and Y1 position - top left column, row
	movzx	eax, cx ; top left column
	; 21/11/2020
	shr	ecx, 16 ; top row
	jz	short sysvideo_4_0 ; bypass following code
	push	edx ; X2 and Y2 position - bottom right column, row
	push	eax
	mov	ax, 80*2 ; 80 colums, 160 bytes per row
	mul	ecx
		; eax = offset for start row number 
	add	esi, eax
	add	edi, eax
	pop	eax
	pop	edx
sysvideo_4_0:
	shl	ax, 1 ; * 2 ; convert start column number to offset
	jz	short sysvideo_4_1	
	add	esi, eax
	add	edi, eax
		; esi = source page window start offset
		; edi = destination page window start offset
sysvideo_4_1:
	pop	ecx
	;mov	eax, 0B8000h+(80*25*2*8)
	; 21/11/2020
	mov	eax, 0B8000h+(4096*8)
	cmp	esi, eax
	jnb	sysret ; out of video page
	cmp	esi, eax
	jnb	sysret  ;out of video page	

	push	esi ; ****
	push	edi ; ***
	push	edx ; **
	push	ecx ; *
	shr	ecx, 16 ; top row
	shr	edx, 16 ; bottom row
	; 21/11/2020
	;cmp	ecx, 24 ; max. 25 rows
	cmp	cx, 24
	ja	short sysvideo_6 ; invalid, [u.r0] = 0
	;cmp	edx, 24 ; max. 25 rows
	cmp	dx, 24
	ja	short sysvideo_6 ; invalid, [u.r0] = 0		
	sub	dl, cl ; end >= start
	jc	short sysvideo_6 ; invalid, [u.r0] = 0
	; 21/11/2020
	mov	ebx, edx ; row count - 1
	jz	short sysvideo_4_2
	push	eax ; *****
	mov	eax, 80*2 ; bytes per row
	mul	ebx ; 21/11/2020
		; eax = window end offset 
		; (for the last row, before adding column bytes)
	add	esi, eax
	add	edi, eax
	pop	eax ; ***** ; mode 3 video memory end (0C000h)
	cmp	esi, eax
	;ja	short sysvideo_6 ; invalid, [u.r0] = 0
	jnb	short sysvideo_6 ; 21/11/2020
	cmp	edi, eax
	;ja	short sysvideo_6 ; invalid, [u.r0] = 0
	jnb	short sysvideo_6 ; 21/11/2020
sysvideo_4_2:
	pop	ecx ; *
	pop	edx ; **
	and	ecx, 0FFFFh
	and	edx, 0FFFFh
	; 21/11/2020
	;cmp	ecx, 79 ; max. 80 columns
	cmp	cx, 79
	ja	short sysvideo_7 ; invalid, [u.r0] = 0
	;cmp	edx, 79 ; max. 80 columns
	cmp	dx, 79 
	ja	short sysvideo_7 ; invalid, [u.r0] = 0
	sub	dl, cl
	jna	short sysvideo_7 ; invalid, [u.r0] = 0
	; 21/11/2020
	jz	short sysvideo_4_3 
	; edx = column count (width) - 1
	shl	dl, 1 ; * 2 ; byte offset (in end row)
	add	esi, edx
	add	edi, edx
		; esi = source page window end offset
		; edi = destination page window end offset
	cmp	esi, eax ; video memory end
	;ja	short sysvideo_7
	jnb	short sysvideo_7 ; 21/11/2020
	cmp	edi, eax ; video memory end
	;ja	short sysvideo_7
	jnb	short sysvideo_7 ; 21/11/2020
sysvideo_4_3:
	pop	edi ; ***
	pop	esi ; ****
	inc	bl ; row count - 1 -> row count	
	inc	dl ; column count
	mov	bh, dl
	shl	dl, 1 ; convert column count to byte offset
	; 21/11/2020
	; esi = source page window start offset
	; edi = destination page window start offset
	mov	eax, 80*2 ; bytes per row
	; Note: 160 bytes per row (even if move count < 160)
sysvideo_5:	
	mov	cl, bh ; move/transfer -word- count per row
	add	[u.r0], edx ; transfer count in bytes
	rep	movsw
	add	esi, eax ; + 160 bytes to next row
	add	edi, eax ; + 160 bytes to next row
	dec	bl ; remain count of rows
	jnz	short sysvideo_5
	jmp	sysret

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

sysvideo_9:
	; user to system or system to user window transfer
	cmp	bl, 2
        ja      sysret	; user to user transfer is invalid
			; [u.r0] = 0

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

	shr	ecx, 16 ; top row
	shr	edx, 16 ; bottom row
	
	; 21/11/2020
	;cmp	ecx, 24 ; max. 25 rows
	cmp	cx, 24
	ja	short sysvideo_6 ; invalid, [u.r0] = 0
	;cmp	edx, 24 ; max. 25 rows
	cmp	dx, 24
	ja	short sysvideo_6 ; invalid, [u.r0] = 0	
	sub	dl, cl
	jc	short sysvideo_6 ; invalid, [u.r0] = 0

	;mov	ch, cl ; top row
	;mov	cl, [ACTIVE_PAGE]

	;mov	edi, 80*25*2 ; 4000
	; 21/11/2020
	;mov	edi, 4096 ; [CRT_LEN = 4096 for video mode 3 
	;shl	edi, cl  ; ! wrong for page 2 to page 7 !
	;;add	edi, 0B8000h - 80*25*2
	;add	edi, 0B8000h - 1000h ; - 4096
	
	; 21/11/2020
	;xor	eax, eax
	;mov	edi, 0B8000h
	;and	cl, cl ; is video page = 0 ?
	;jz	short sysvideo_9_1 ; yes
	; eax = 0

;sysvideo_9_0:
	;add	ax, 4096
	;dec	cl
	;jnz	short sysvideo_9_0
	;add	edi, eax
	;	; edi = video page start address

	; 21/11/2020
	mov	edi, 0B8000h
	cmp	byte [ACTIVE_PAGE], 0
	jna	short sysvideo_9_1
stsvideo_9_0:
	mov	al, 16 ; 4096/256
	mul	byte [ACTIVE_PAGE] 
	xchg	ah, al ; * 256
	add	edi, eax
		; edi = video page start address
sysvideo_9_1:
	; bl = transfer direction 
	;     ( 1 = from user, 2 = to user)
	mov	bh, dl ; row count - 1
	;mov	dl, ch ; top row
	; 21/11/2020
	or	cl, cl ; top row number
	jz	short sysvideo_9_2

	;mov	eax, 80*2
	mov	ax, 80*2 ; 160, bytes per row
	mul	ecx ; 22/11/2020
	add	edi, eax
		; edi = window start address for top row
sysvideo_9_2:
	pop	ecx ; *
	pop	edx ; **
	and	ecx, 0FFFFh
	and	edx, 0FFFFh
	; 21/11/2020
	;cmp	ecx, 79 ; max. 80 columns
	cmp	cx, 79
	ja	short sysvideo_7 ; invalid, [u.r0] = 0
	;cmp	edx, 79 ; max. 80 columns
	cmp	dx, 79
	ja	short sysvideo_7 ; invalid, [u.r0] = 0
	
	sub	dl, cl
	jc	short sysvideo_7 ; invalid, [u.r0] = 0
	
	or	cl, cl ; left column 
	jz	short sysvideo_9_3 ; 0

	; 21/11/2020
	shl	cl, 1  ; column * 2
	add	edi, ecx
		; edi = window start addr for top left column
sysvideo_9_3:
	mov	cl, dl
	inc	cl ; column count
	shl	cl, 1 ; column count * 2
		; ecx = tranfer count per row

	pop	eax ; *** (swap address)
	pop	esi ; ****

	inc	bh  ; row count	
	
	;mov	edx, 80*2
	mov	dl, 80*2  ; bytes per row
	;
	cmp	bl, 1 ; transfer direction
	ja	short sysvideo_11 ; system to user transfer

	; 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
	; 21/11/2020
	push	ebx ; save bh
	mov	esi, edi
	mov	edi, eax
sysvideo_9_4:
	call	transfer_to_user_buffer ; fast transfer
	jc	short sysvideo_9_5
	; ecx = actual transfer count (must be same with input)
	add	esi, edx ; next row address of (video page) window 
	add	edi, ecx ; next row address of user's window
		; Note: ecx may be less than row length of video page
		; user's window uses offset according to window width
	dec	bh
	jnz	short sysvideo_9_4 ; repeat for next row
sysvideo_9_5:
	pop	ebx ; restore bh
	pop	edi
	pop	esi
	jnc	short sysvideo_10
	jmp	sysret ; [u.r0] = 0
sysvideo_10: 
	; user to system video/display page window transfer (mode 0)	
	; esi =	user buffer
	call	transfer_from_user_buffer ; fast transfer
	jc	sysret
	; ecx = actual transfer count (must be same with input)
	add	[u.r0], ecx ; actual transfer count
	add	edi, edx ; next row address of (ivdeo page) window 
	add	esi, ecx ; next row address of user's window
		; Note: ecx may be less than row length of video page
		; user's window uses offset according to window width
	dec	bh
	jnz	short sysvideo_10 ; repeat for next row
	jmp	sysret
	
sysvideo_11:
	; system to user video/display page window transfer (mode 0)
	xchg	edi, esi
sysvideo_12:
	; esi = beginning addr of the (screen, video page) window
	; edi =	user's buffer	
	call	transfer_to_user_buffer ; fast transfer
	jc	sysret
	; ecx = actual transfer count (must be same with input)
	add	[u.r0], ecx
	add	esi, edx ; next row (edx = 160)
	add	edi, ecx ; next row of the user's window 
			 ; (ecx <= 160)
	dec	bh
	jnz	short sysvideo_12
	jmp	sysret

sysvideo_13:
	cmp	bh, 1
        ja      sysvideo_38 ; 23/11/2020

	; 23/11/2020 (TRDOS 386 v2.0.3)
	; (major modification, from mode 13h to all VGA modes,
	;  except super VGA modes and liner frame buffer method)

	; BH = 1 = VGA Graphics mode (0A0000h) data transfers

	; 23/11/2020
	; screen width will be saved into EBP register

	mov	ah, bl
	;and	bl, 0Fh	
	shr	ah, 4
	mov	ebp, 320 ; 320*200, 320*240
	and	ah, ah
	jz	short sysvideo_13_0
	shl	bp, 1 ; 640*200, 640*400, 640*480
	cmp	ah, 2
	jb	short sysvideo_13_0		
	ja	sysret ; invalid
	; 800*600
	add	bp, 160 ; 800

sysvideo_13_0:

	;; 21/11/2020 (TRDOS 386 v2.0.3)
	;; VGA graphics mode transfers are only for video mode 13h

	; 23/11/2020
	;cmp	byte [CRT_MODE], 13h ; 320x200 graphics, 256 colors
	;jne	sysret ; invalid (nothing to do), [u.r0] = 0 

	;and	bl, bl
	and	bl, 0Fh
	jnz	short sysvideo_14

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

	; 22/11/2020
	;mov	eax, 65536 ; in fact 320*200 pixel bytes (32000)
	;mov	[u.r0], eax
	mov	byte [u.r0+2], 1 ; 65536
	mov	edi, 0A0000h

	or	ch, ch ; mix ?
	jz	short sysvideo_13_2 ; no, overwrite
	
	; Mix current pixel colors with mixing (CL input) color
	mov	esi, edi
	mov	bl, cl ; mixing color 
	xor	ah, ah
	mov	ecx, 65536
sysvideo_13_1:
	lodsb
	add	al, bl
	shr	ax, 1 ; / 2
	stosb
	loop	sysvideo_13_1
	jmp	sysret	

	; Overwrite pixel colors
sysvideo_13_2:
	mov	ch, cl ; color
	mov	ax, cx
	shl	eax, 16
	mov	ax, cx
	mov	ecx, 16834
	rep	stosd
	jmp	sysret

sysvideo_16:
	cmp	bl, 2	
	ja	short sysvideo_18
	; 23/11/2020
	jb	short sysvideo_14

	mov	edi, ecx ; user's buffer
	; BL = 2 = system to user video/display page transfer
sysvideo_17:
	; 22/11/2020
	mov	esi, 0A0000h
	mov	ecx, 65536
	call	transfer_to_user_buffer ; fast transfer
	;jc	sysret ; [u.r0] = 0
	;mov	[u.r0], ecx ; actual transfer count (32768)
	;jmp	sysret
	; 23/11/2020
	jmp	short sysvideo_15_0 	

sysvideo_14:
	; 23/11/2020
	;cmp	bl, 1
	;ja	short sysvideo_16

	mov	esi, ecx ; user's buffer
	; BL = 1 = user to system video/display page transfer
sysvideo_15:
	; 22/11/2020
	mov	edi, 0A0000h
	; edi = video page address
	mov	ecx, 65536
	call	transfer_from_user_buffer ; fast transfer
sysvideo_15_0:
	jc	sysret ; [u.r0] = 0
	mov	[u.r0], ecx ; actual transfer count (32768)		
	jmp	sysret

sysvideo_18:
	cmp	bl, 3
	ja	sysvideo_23

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

	; 22/11/2020
	mov	edi, 0A0000h
	mov	esi, edi
	
	; 21/11/2020
	cmp	edx, ecx ; bottom-right > top-left ?
	jnb	short sysvideo_20 ; window

	or	edx, edx
	jnz	sysret  ; invalid !

	; full screen (update) ; edx = 0, ecx > 0
	;mov	ecx, 66536
	; 21/11/2020
	mov	ecx, 16384  ; dword 'NOT'
	mov	[u.r0], cx
sysvideo_19:
	;not	byte [esi] ; NOT operation
	;inc	esi
	not	dword [esi]
	lodsd	; add esi, 4
	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
	; 23/11/2020
	mov	ebx, ebp ; 320,600,800
	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 ; 22/11/2020
	ja	sysret
	; 21/11/2020
	sub	ebx, edx ; screen width - window width
	dec	esi 
sysvideo_21:
	;push	esi
	mov	ecx, edx ; window width (byte count)
sysvideo_22:
	inc	esi
	not	byte [esi]
	;inc	esi
	loop	sysvideo_22
	; 21/11/2020
	add	[u.r0], edx
	;pop	esi ; 21/11/2020
	;add	esi, ebx ; bytes per screen row (320)
	add	esi, ebx ; add difference for next row
	;
	cmp	esi, edi ; stop address (included in loop)
	;jna	short sysvideo_21
	jb	short sysvideo_21
	jmp	sysret

sysvideo_23:
	cmp	bl, 4
        ja      sysvideo_26

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

	; 22/11/2020
	mov	eax, 0A0000h
	cmp	esi, eax
	jb	sysret
	cmp	edi, eax
	jb	sysret
	;add	ax, 0FFFFh
	mov	ax, 0FFFFh ; 65535
	cmp	esi, eax
	ja	sysret
	cmp	edi, eax
	ja	sysret
	
	cmp	edx, ecx ; bottom-right > top-left ?
	jnb	short sysvideo_24 ; window

	or	edx, edx
	jnz	sysret  ; invalid !

	; full screen copy ; edx = 0, ecx > 0
	mov	ecx, 65536
	mov	[u.r0], ecx
	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
	; 23/11/2020
	mov	ebx, ebp ; 320,600,800
	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)
	; 21/11/2020
	sub	ebx, edx ; screen width - window width
sysvideo_25:
	mov	ecx, edx
	rep	movsb
	; 21/11/2020
	add	[u.r0], edx ; window width (byte count)
	;or	ebx, ebx
	;jz	short sysvideo_25_0
	add	edi, ebx ; add difference (for next row) 
	add	esi, ebx
;sysvideo_25_0:
	cmp	edi, [esp] ; stop addr (included in loop)
	jna	short sysvideo_25
	; 21/11/2020
	jmp	sysvideo_28
	;pop	ebx ; stop address
	;pop	edi ; start address
	;jmp	sysret

sysvideo_26:
	cmp	bl, 5
        ja      sysvideo_29

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

	; 22/11/2020
	mov	eax, 0A0000h
	cmp	edi, eax
	jb	sysret
	;add	ax, 0FFFFh ; 65535
	mov	ax, 0FFFFh
	cmp	edi, eax
	ja	sysret

	; 21/11/2020
	; esi = user buffer (in user's memory space)
	cmp	edx, ecx ; bottom-right > top-left ?
      	jnb	short sysvideo_26_0 ; window

	or	edx, edx
	jnz	sysret  ; invalid !

	; full screen copy ; edx = 0, ecx > 0
	jmp	sysvideo_15 ; full screen copy

sysvideo_26_0:
	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
	; 23/11/2020
	mov	ebx, ebp ; 320,600,800
	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_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
		; ecx = actual transfer count (= ecx input) 
	add	[u.r0], ecx
	add	edi, ebx ; next row in video memory window
	add	esi, ecx ; next window row in user's buffer
	cmp	edi, [esp] ; stop addr (included in loop)
	jna	short sysvideo_27
sysvideo_28:
	pop	ebx ; stop address
	pop	edi ; start address
	; 21/11/2020
	jmp	sysret

sysvideo_29:
	cmp	bl, 6
	;ja	sysvideo_32
	; 21/11/2020
	ja	sysvideo_31

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

	mov	edi, esi ; user buffer

	; 22/11/2020
	mov	eax, 0A0000h
	cmp	esi, eax
	jb	sysret
	;add	ax, 0FFFFh ; 65535
	mov	ax, 0FFFFh
	cmp	esi, eax
	ja	sysret

	; 21/11/2020
	; edi = user buffer (in user's memory space)
	cmp	edx, ecx ; bottom-right > top-left ?
        jnb     short sysvideo_29_0
	
	and	edx, edx
	jnz	sysret	; invalid !

	; full screen copy  ; edx = 0, ecx > 0
	jmp	sysvideo_17

sysvideo_29_0:
	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
	; 23/11/2020
	mov	ebx, ebp ; 320,600,800
	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_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
	jc	sysvideo_28  ; 21/11/2020
	add	[u.r0], ecx
	add	edi, ebx ; next row
	add	esi, ecx
	cmp	edi, [esp] ; stop addr(included in loop)
	jna	short sysvideo_30
	; 21//11/2020
	jmp	sysvideo_28
;sysvideo_31:
	;pop	ebx ; stop address
	;pop	edi ; start address
	;jmp	sysret

sysvideo_31:
	; 21/11/2020 (TRDOS 386 v2.0.3)
	; bl = 7, 8, 9, 10, 11, 12
	; (bl > 12 is invalid for current kernel version)

	; 23/11/2020
	cmp	bl, 13
	ja	sysret

	; 22/11/2020
	mov	esi, 0A0000h
	mov	al, cl ; byte for and/or/xor 
	mov	ecx, 65536 ; byte count
	mov	[u.r0], ecx ; return with
			   ; byte count of display page
			   ; (eax > 0 -> OK)	
	cmp	bl, 10
	jb	short sysvideo_32  ; 7, 8, 9
	je	short sysvideo_31_inc ; 10
	cmp	bl, 12
	jb	short sysvideo_31_dec ; 11
	je	short sysvideo_31_neg ; 12
	; bl = 13
	mov	ecx, 16384
sysvideo_31_not:
	;not	byte [esi]
	;inc	esi
	not	dword [esi]
	lodsd
	loop	sysvideo_31_not
sysvideo_31_ok:
	jmp	sysret
sysvideo_31_inc:
	inc	byte [esi]
	inc	esi
	loop	sysvideo_31_inc
	jmp	short sysvideo_31_ok
sysvideo_31_dec:
	dec	byte [esi]
	inc	esi
	loop	sysvideo_31_dec
	jmp	short sysvideo_31_ok
sysvideo_31_neg:
	neg	byte [esi]
	inc	esi
	loop	sysvideo_31_neg
	jmp	short sysvideo_31_ok

sysvideo_32:
	; 22/11/2020
	; bl = 7, 8, 9
	; ecx = 65536 ; byte count
	mov	ecx, 16384 ; dword count
	mov	ah, al
	mov	dx, ax
	shl	edx, 16 
	mov	dx, ax

	;cmp	bl, 7
	;ja	short sysvideo_34

	; 21/11/2020
	cmp	bl, 8
	je	short sysvideo_34
	ja	short sysvideo_36

	; BL = 7 = AND display page bytes with CL

	;mov	esi, 0A0000h
	;mov	al, cl ; 21/11/2020
	;mov	ecx, 65536
sysvideo_33:
	;and	[esi], al
	;inc	esi
	and	esi, edx
	lodsd
	loop	sysvideo_33
	jmp	sysret 

sysvideo_34:
	; 21/11/2020
	;cmp	bl, 8
	;ja	short sysvideo_36

	; BL = 8 = OR display page bytes with CL

	;mov	esi, 0A0000h
	;mov	al, cl ; 21/11/2020
	;mov	ecx, 65536
sysvideo_35:
	;or	[esi], al
	;inc	esi
	or	esi, edx
	lodsd
	loop	sysvideo_35
	jmp	sysret	

sysvideo_36:
	; 21/11/2020
	;cmp	bl, 9
        ;ja	sysret ; nothing to do

	; BL = 9 = XOR display page bytes with CL

	;mov	esi, 0A0000h
	;mov	al, cl ; 21/11/2020
	;mov	ecx, 65536
sysvideo_37:
	;xor	[esi], al
	;inc	esi
	xor	esi, edx
	lodsd
	loop	sysvideo_37
	jmp	sysret

sysvideo_38:
	cmp	bh, 2
        ja      short sysvideo_40

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

;sysvideo_39:
	; 23/11/2020
	; BH = 3
	; PIXEL READ/WRITE
	; Not implemented for yet ! (23/11/2020)
;	jmp	sysret

sysvideo_40:
	; 23/11/2020
	cmp	bh, 4
	jb	short sysvideo_39 ; bh = 3, pixel r/w
	ja	short sysvideo_41
	
	; 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)
	mov	ebx, eax ; 12/05/2017 ; virtual = physical
	call	direct_memory_access	
	jc	sysret
	; eax = 0B8000h if there is not an error
	mov	[u.r0], eax
	jmp	sysret

sysvideo_41:
	; 23/11/2020
	cmp	bh, 6
	;ja	short sysvideo_44
	jb	short sysvideo_42

	; 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_42:
	; 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)
	mov	ebx, eax ; 12/05/2017 ; virtual = physical
	call	direct_memory_access	
	jc	sysret
	; eax = 0A0000h if there is not an error
	mov	[u.r0], eax
	jmp	sysret

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

;sysvideo_44:
	; 23/11/2020
;	cmp	bh, 8
;	jb	short sysvideo_43 ; video mode & lfb info
;	ja	sysret ; invalid !

	; BH = 8
	; Set (Super/Extended VGA) mode and color indexes
	;
	; Not implemented yet !
	; (23/11/2020)
;	jmp	sysret

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:
	; 18/11/2017
	; 14/11/2017
	; 13/11/2017
	; 24/10/2016, 04/01/2017
	; 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)

	;; 13/11/2017
	;;mov	[u.namep], ebx ; argument 1
        ; 18/10/2015
	mov     [argv], ecx  ; * ; argument 2

	; 13/11/2017
	mov	esi, ebx
	call	set_working_path_x
	jnc	short sysexec_0

	;; 'bad command or file name'
	;mov	eax, ERR_BAD_CMD_ARG ; 01h ; TRDOS 8086
	
	; 'file not found !' error
	mov	eax, ERR_NOT_FOUND ; 02h ; TRDOS 8086
sysexec_not_found_err:
sysexec_access_error:
sysexec_ext_error:
	mov	[u.r0], eax
	mov	[u.error], eax
	call 	reset_working_path
	jmp	error

sysexec_0:
	; 13/11/2017
	;mov	esi, FindFile_Name
        mov	ax, 1800h ; Only files 
	call	find_first_file
	jc	short sysexec_not_found_err ; eax = 2

	; check_ file attributes
	; (attribute bits = 00ADVSHR) ; 18h = Directory+Volume
	; BL = Attributes byte
	
        test	bl, 6  ; system file or hidden file (S+H) 
	;jz	short sysexec_0ext
	jz	short sysexec_1 ; yes

	; 13/11/2017 
	; /// TRDOS386 permission check for multiuser mode ///
	; SYSTEM file or HIDDEN file !!
	; (Only super user has permission to run this file.)
	
	; ([u.uid]=0 for super user or root in multiuser mode)  
	; ([u.uid]=0 for any users in singleuser mode) 
	cmp 	byte [u.uid], 0 ; Super User ([u.uid]=0) ?
	;jna	short sysexec_0ext
	jna	short sysexec_1 ; yes 

	; 'permission denied !' error  
        mov	eax, ERR_FILE_ACCESS  ; 11 = ERR_PERM_DENIED
        jmp	short sysexec_access_error

sysexec_not_exf:
	; 'not executable file !' error
	mov	eax, ERR_NOT_EXECUTABLE
	jmp	sysexec_ext_error

;sysexec_0ext:
sysexec_1:
	; 18/11/2017
	mov	esi, FindFile_Name
	; 13/11/2017
	; check program file name extension
	; ('.PRG' for current TRDOS version)
	call	check_prg_filename_ext
	jc	short sysexec_not_exf
	
	; 18/11/2017
	cmp	al, 'P'
	jne	short sysexec_not_exf	

	; '.PRG' extension is OK.
	; Only '.PRG' files are valid program files
	; for current TRDOS 386 version.

	mov	edx, [FindFile_DirEntry+DirEntry_FileSize]
	mov	ax, [FindFile_DirEntry+DirEntry_FstClusHI]
	shl	eax, 16
	mov	ax, [FindFile_DirEntry+DirEntry_FstClusLO]
	; EAX = First Cluster number
	; EDX = File Size

	mov	[ii], eax
	mov	[i.size], edx

;sysexec_1:
	; 13/11/2017 - TRDOS 386 (TRDOS v2.0)
	; 24/06/2015 - 23/10/2015 (Retro UNIX 386 v1)
        ; 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
	mov	[argc], ax ; 0 ; 13/11/2017
	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:
	; 14/11/2017 - TRDOS 386 (TRDOS v2.0)
	; 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
	; 14/11/2017
	mov	eax, ERR_BAD_CMD_ARG ; 01h ; TRDOS 8086
	mov	[u.r0], eax
	jmp	error
get_argp3:
	mov	cl, 3
get_argp4:
	shl	eax, 8
	pop	dx
	mov 	al, dl
        loop    get_argp4
	;pop	esi
	retn	

sysstat: 
	; 13/01/2017 - TRDOS 386 (TRDOS v2.0)
	; temporary !
	mov	eax, ERR_INV_FNUMBER ; 'invalid function number !'
        mov     [u.error], eax
        mov     [u.r0], eax 
	jmp	error

sysfstat: 
	; 13/01/2017 - TRDOS 386 (TRDOS v2.0)
	; temporary !
	mov	eax, ERR_INV_FNUMBER ; 'invalid function number !'
        mov     [u.error], eax
        mov     [u.r0], eax 
	jmp	error

fclose:
	; 06/10/2016 (TRDOS 386 = TRDOS v2.0)
	;
	; 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)  	
	;
	; TRDOS 386 (06/10/2016)
	; 
	; INPUT:
	;	EAX = File Handle (File Descriptor, File Index)
	;
	; OUTPUT:
	;	CF = 1 -> File not open !
	;	CF = 0 -> OK!
	;	     EBX = File Number (System)
	;	     [cdev] = Logical DOS Drive Number
	;	     EAX = File Handle/Number (user) 	
	;
	; Modified Registers: EBX

	push	eax ; File handle
	
	call	getf
	jc	device_close ; eax = device number

	cmp	byte [ebx+OF_MODE], 1 ; open mode ; 0 = empty entry
	jb	short fclose_1	      ; 1 = read, 2 = write
	
	cmp	eax, 1 ; is the first cluster number > 0
	jb	short fclose_1 ; no, this is empty entry

fclose_0:
	dec	byte [ebx+OF_OPENCOUNT] ; decrement the number of processes 
			                ; that have opened the file
	jns	short fclose_1 ; jump if not negative (jump if bit 7 is 0)	 
			; if all processes haven't closed the file, return
	;
	; eax ; First cluster
	xor	eax, eax ; 0
	mov	[ebx+OF_MODE], al ; 0 = empty entry
	;mov	[ebx+OF_STATUS], al ; 0 = empty entry
	shl	bx, 2 
	mov	[ebx+OF_FCLUSTER], eax ; 0
	mov	[ebx+OF_CCLUSTER], eax ; 0
	;mov	[ebx+OF_CCINDEX], eax ; 0
	mov	[u.fofp], eax ; 0
	shr	bx, 2
fclose_1: ; 1:
	pop	eax ; File handle (File Descriptor, File Index)
	mov	byte [eax+u.fp], 0 ; clear that entry in the u.fp list
	retn

getf:
	; 12/10/2016
	; 11/10/2016
	; 08/10/2016
	; 06/10/2016 (TRDOS 386 = TRDOS v2.0)
	; / 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: 
	cmp	ebx, 10
        jnb	short getf2
	mov	bl, [ebx+u.fp]
	or	bl, bl
	jnz	short getf3
getf2:
	; 'File not open !' error (ax=0)
	sub	eax, eax
	retn
getf3:	
	test	bl, 80h
	jnz	short getf5 ; device
	dec	bl ; 0 based
	mov	al, [ebx+OF_DRIVE]
	mov	[cdev], al
	shl	bl, 2 ; *4 (dword offset)
	mov	eax, [ebx+OF_SIZE]
	mov	[i.size], eax ; file size
	lea	eax, [ebx+OF_POINTER] ;12/10/2016
	mov	[u.fofp], eax
	mov	eax, [ebx+OF_FCLUSTER]
	shr	bl, 2 ; /4 (byte offset) 
getf4:
	retn
getf5: 
	; get device number
	and	bl, 7Fh ; 1 to 7Fh
	dec	bl ; 0 based (0 to 7Eh)
	mov	al, [ebx+DEV_DRIVER]
	mov	ch, [ebx+DEV_ACCESS]
	mov	cl, [ebx+DEV_OPENMODE]
	and	ch, 0FEh ; reset bit 0 ; dev_close
	stc ; cf = 1 
	retn	

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


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

sysseek: ; / moves read write pointer in an fsp entry
	; 06/11/2016 - TRDOS 386 (TRDOS v2.0)
	; 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
	; EAX = Current R/W pointer of the file
	; EBX = [u.fofp]
	; [u.base] = offset (ECX input)

	add	eax, [u.base]
	mov	[ebx], eax
	jmp	sysret

systell: ; / get the r/w pointer
	; 06/11/2016 - TRDOS 386 (TRDOS v2.0) - temporary !-
	; 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
	;; 06/11/2016
	;; mov	eax, [ebx]
	mov	[u.r0], eax
	jmp	sysret

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

seektell:
	; 06/11/2016 - TRDOS 386 (TRDOS v2.0)
	; 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.	
	;
	; ((Return -> eax = base for offset (position= base+offset))
	;
	mov 	[u.base], ecx ; offset
seektell0:
	mov 	[u.count], edx
	; EBX = file descriptor (file number)
	call	getf1
	; EAX = First cluster of the file
	; EBX = File number (Open file number)
	; [u.fofp] = Pointer to File pointer
	; [i.size] = File size

	or	eax, eax
	jnz	short seektell1

	mov	eax, ERR_FILE_NOT_OPEN
	mov	[u.r0], eax 
	mov	dword [u.error], eax ; 'file not open !'
	jmp	error

seektell1:
        mov     ebx, [u.fofp]
	cmp	byte [u.count], 1
	ja	short seektell2
	je	short seektell3
	xor	eax, eax
	retn

seektell2:
        mov   	eax, [i.size]
	retn

seektell3:
	mov	eax, [ebx]
	retn

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

anyi: 
	; 06/10/2016 (TRDOS 386 = TRDOS v2.0)
	; Major Modification!
	; TRDOS 386 does not permit to delete a file while it is open 
	; The role of 'anyi' procedure has beeen changed to ensure that.
	; 	
	; 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

	; TRDOS 386 (06/10/2016)
	; 
	; INPUT:
	;	EAX = First Cluster
	;	 DL = Logical DOS Drive Number
	;
	; OUTPUT:
	;	CF = 1 -> EBX = File Handle/Number/Index
	;	CF = 0 -> EBX = 0
	;
	; Modified Registers: EBX

	xor	ebx, ebx
anyi_0: 
	cmp	byte [ebx+OF_MODE], 0 ; 0 = empty entry
	ja	short anyi_2 ; 1 (r), 2 (w) or 3 (r&w)
anyi_1:
	inc	bl
	cmp	bl, OPENFILES ; max. count of open files
	jb	short anyi_0
	xor	eax, eax
	retn 
anyi_2:
	cmp	dl, [ebx+OF_DRIVE]
	jne	short anyi_1
	shl	bx, 2 ; *4 (dword offset)
	cmp	eax, [ebx+OF_FCLUSTER]
	je	short anyi_3
	shr	bx, 2 ; /4 (byte offset)
	jmp	short anyi_1 	
anyi_3:
	shr	bx, 2 ; /4 (bytes offset) (index)
	stc
	retn

; 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: 
	; 18/11/2017
	; 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, ah
	mov	bh, [u.ttyn]
	;mov	bl, [ccolor] ; *
pcmsg1:
	lodsb
	and 	al, al  ; 0
	jz 	short pcmsg2
	push 	esi
	mov	bl, [ccolor]  ; * (video.s 'u11'&'beep' change BL)
	;mov	bh, [u.ttyn]
	call 	_write_tty 
	pop	esi
	jmp	short pcmsg1
pcmsg2:
	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:
	; 18/11/2017
	; 22/01/2017
	; 04/01/2017, 07/01/2017
	; 24/10/2016
	; 24/04/2016, 02/05/2016, 03/05/2016, 06/05/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

	;sti	; 07/01/2017
	;mov	eax, [k_page_dir]
	;mov	[u.pgdir], eax
	xor 	eax, eax ; clc ; *** ; 04/01/2017
	;mov	[u.r0], eax ; 0 ; 07/01/2017

	; 06/05/2016
	; Set 'sysexit' return order to MainProg
	;
	pop	eax ; * 'loc_load_and_run_file_8:' address
	;; 22/01/2017
	;;cli ; 07/01/2017
	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
	;mov	byte [u.quant], time_count
	push	ds
	push	es
	push	fs
	push	gs	
	;mov	eax, [u.r0]
	sub	eax, eax
	pushad
	push	sysret
	;push	sysrel1 ; 07/01/2017
	mov	[u.usp], esp
	;
	call	wswap ; Save MainProg (process 1) 'u' structure
		      ; and registers for return (from program)	
	mov	esp, ebp ; **
	;;22/01/2017
	;;sti ; 07/01/2017
	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:
	; 19/11/2017
	; 18/11/2017
	; 14/11/2017
	; 13/11/2017
	mov	[argv], esp ; *!* ; start address of argument list 

	; 04/01/2017
	; 24/10/2016
	;;02/05/2016
	; 23/04/2016 (TRDOS 386)
	; 18/10/2015 ('sysexec_6')
	; 23/06/2015
	mov	eax, [u.pgdir] ; physical address of page directory
	;cmp 	eax, [k_page_dir] ; TRDOS MainProg ? 
	;je	short sysexec_7
	; 19/11/2017
	mov	ebx, [u.ppgdir] ; phy addr of the parent's page dir
	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:
	; 19/11/2017 
	; 24/04/2016 (TRDOS 386 = TRDOS v2.0)
	; 25/06/2015, 26/08/2015, 18/10/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
	; 19/11/2017
	sub	edi, 4
	mov	dword [edi], 0 
	mov	ebx, edi
	;
	movzx	eax, word [argc]
	or	eax, eax
	jz	short sysexec_13 ; 19/11/2017
	;jnz	short sysexec_10
	;mov 	ebx, edi
	;sub	ebx, 4 
	;mov	[ebx], eax ; 0
	;jmp 	short sysexec_13
sysexec_10:
	mov	ecx, [u.nread]
	; 13/11/2017
	;mov	esi, TextBuffer ; 'load_and_execute_file'
	;mov	esi, esp  	; 'sysexec'
	mov	esi, [argv] ; 24/04/2016 (TRDOS 386  = TRDOS v2.0)
	;sub	edi, ecx ; page end address - argument list length
	sub	ebx, ecx ; 19/11/2017
	mov	edx, eax
	inc	dl ; argument count + 1 for argc value  
	shl 	dl, 2  ; 4 * (argument count + 1)
	;mov	ebx, edi
	mov	edi, ebx ; 19//11/2017
	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]
	dec	word [argc] ; 14/11/2017
	jz	short sysexec_13
sysexec_12:
	lodsb
	and	al, al
	jnz	short sysexec_12
	jmp	short sysexec_11
sysexec_13:
	; 24/10/2016
	; 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)
	; 24/10/2016
	mov	al, [Current_Drv]
	mov	[cdev], al
	;
	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

;	; **** arguments list test start - 19/11/2017
;	mov	ebp, [argv]
;	sub	ebp, ECORE - 4096
;	add	ebp, [ecore]
;
;	mov	ebx, [ebp]
;	mov	[argc], bx
;	add	ebp, 4
;	mov	byte [ccolor], 1Fh
;_zx0:
;	cmp	word [argc], 0
;	jna	short _zx2	
;_zx1:
;	push	ebp
;	mov	esi, [ebp]
;
;	sub	esi, ECORE - 4096
;	add	esi, [ecore]
;
;	call	print_cmsg
;
;	dec	word [argc]
;	jz	short _zx2
;
;	mov	al, '.'
;	mov	bl, 07h
;	mov	bh, [u.ttyn]
;	call 	_write_tty 
;
;	pop	ebp
;	add	ebp, 4
;	jmp	short _zx1			
;_zx2:
;	pop	ebp
;	mov	byte [ccolor], 07h
;	mov	eax, 1
;	; **** arguments list test stop
;	Test result is OK! (there is not a wrong thing) - 19/11/2017

	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	edx, UDATA
	; 18/11/2017
	push	UDATA ; user's stack segment
	;push	edx
	push	ebp ; user's stack pointer
		    ; (points to number of arguments)
	
	; 04/01/2017
	; MainProg comes here while [sysflg]= 0FFh
	; (but sysexec comes here while [sysflg]= 0)
	mov	byte [sysflg], 0 ; 04/01/2017
				 ; (timer_int sysflg control)
	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	dx, UDATA ; 19/11/2017
	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

	; 14/11/2017
	jmp	sysret0

;	; 02/05/2016
;	;inc	byte [sysflg] ; 0FFh -> 0
;	;mov	byte [sysflg], 0 ; 04/01/2017
;	movzx	ebx, byte [u.uno]
;	shl	bl, 1 ; 13/11/2017 	
;	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
	;    cdev - logical dos drive number of the file
	; OUTPUTS ->
	;    u.count - cleared
	;    u.nread - accumulates total bytes passed back
	;
	; ((EAX)) input/output
	; (Retro UNIX Prototype : 14/12/2012 - 01/03/2013, UNIXCOPY.ASM)
        ; ((Modified registers: edx, ebx, ecx, esi, edi))  

	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 is 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:
	; 24/10/2016
	; 22/10/2016
	; 12/10/2016
	; 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
	;    [cdev] = Logical dos drive number 	  
	;    ([u.off] = file offset)
	; OUTPUTS ->
	;    EAX = logical sector number
	;    ESI = Logical Dos Drive Description Table address	
	;
	; Modified registers: EDX, EBX, ECX, ESI, EDI

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

	sub	ecx, ecx
	mov	ch, [cdev]

	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	eax, ebx ; file offset
	mov	cx, [readi.bpc]
	inc	ecx ; <= 65536
	sub	edx, edx
	div	ecx

	mov	edi, [readi.c_index] ; cluster index

	cmp	eax, edi
        jne     short mget_r_4  ; (*)

	; 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]  ; > 0 if [readi.valid] = 1
	mov	edx, [readi.fs_index]
        jmp     mget_r_7
	
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
	
	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
	sub	edi, edi
mget_r_4:
	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_6

	cmp	edi, ecx
	ja	short mget_r_5 ; old cluster index is higher
	mov	edx, [readi.cluster]
	and	edx, edx
	jz	short mget_r_5
	; valid 'readi' parameters (*)
	mov	eax, edx
	sub	ecx, edi
	jz	short mget_r_7
mget_r_5:
	; 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	short mget_r_err
	; EAX = Cluster number		
mget_r_6:
	mov	[readi.cluster], eax ; FDT number for Singlix File System
mget_r_7:
	cmp	byte [esi+LD_FATType], 0
	jna	short mget_r_12

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

	add	eax, [esi+LD_DATABegin]
	mov	dl, [readi.s_index]
	add	eax, edx
mget_r_8:
	; eax = logical sector number
	cmp	byte [readi.valid], 0
	jna	short mget_r_9
	cmp	eax, [readi.sector]
	je	short mget_r_11 ; sector is already in 'readi' buffer
mget_r_9:
	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_10

	; 22/10/2016 (15h -> 17)
	mov	eax, 17 ; Drive not ready or read error !
mget_r_err:
	mov	[u.error], eax
	; 12/10/2016
	mov	[u.r0], eax
	jmp	error
mget_r_10:
	mov	byte [readi.valid], 1 ; 24/10/2016
	mov	eax, [readi.sector]
mget_r_11:
	retn
mget_r_12:
	; 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 mget_r_8

trans_addr_r:
	; 12/10/2016
	; 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:
	; 12/10/2016
	; 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
	mov	[u.r0], eax ; 12/10/2016
	;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	eax, ERR_MINOR_IM ; "Insufficient memory !" error
	mov	[u.r0], eax ; 12/10/2016
	mov	dword [u.error], eax
	;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:
	; 16/01/2017
	; 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.

	; 16/01/2017
	mov	ebx, runq+2 	; 'runq_normal' ; normal/regular priority
	; 21/05/2016
	;cmp	byte [u.pri], 2	; high priority (run for event) ?
	;jnb	short swap
	; 16/01/2017
	; (Normal and also high/event priority processes will be added to
	; normal priority run queue for ensuring circular running sequence!)
	; (Timer interrupt or 'syspri' system call may change priority and run
	; queue to high/event level.)
	cmp	byte [u.pri], 0
	ja	short tswap_1	; normal priority run queue
	;
	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:
	; 02/01/2017
	; 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
	xor	ebx, ebx ; 02/01/2017
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'
	;cmp	ah, al ;is this process the same as the process in core?
       	;je	short swap_8 ; yes, don't have to swap
	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 >
	; 28/02/2017 (fnsave)
	; 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))  
	;

	; 28/02/2017
	;cmp	byte [multi_tasking], 0  ; Musti tasking mode ?
	;jna	short wswp
	cmp	byte [u.fpsave], 0 ; 28/02/2017
	jna	short wswp
	fnsave	[u.fpregs] ; save floating point registers (94 bytes)
wswp:
	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 >
	; 28/02/2017 (frstor)
	; 15/01/2017
	; 14/01/2017
	; 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
	; 
	;cli
	mov	edi, [u.usp] ; esp (system stack pointer, 
			     ;     points to user registers)
	mov	esp, edi     ; 14/01/2017
	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
	; 28/02/2017
	;cmp	byte [multi_tasking], 0  ; Musti tasking mode ?
	;jna	short rswp_retn
	cmp	byte [u.fpsave], 0
	jna	short rswp_retn
	frstor	[u.fpregs] ; restore floating point regs (94 bytes)
rswp_retn:
	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


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

sysfff: ; <Find First File>
	; 17/10/2016
	; 16/10/2016
	; 15/10/2016 TRDOS 386 (TRDOS v2.0) feature only ! 
	;           -derived from TRDOS v1.0, INT_21H.ASM-
	;            ("loc_INT21h_find_first_file")
	; TRDOS 8086 (v1.0)
        ; 	07/08/2011 
        ;	Find First File
	;	INPUT:
        ;	    CX= Attributes
        ;	    DS:DX= Pointer to filename
	;	MSDOS OUTPUT:
	;	    DTA: (Default address: PSP offset 80h)
	;	    Offset  Descrription
	;	    0	    Reserved for use find next file
	;	    21	    Attribute of file found
	;	    22	    Time stamp of file
	;	    24	    Date stamp of file
	;	    26	    File size in bytes
	;	    30	    Filename and extension (zero terminated)
	;	If cf = 1:
	;	    Error Codes: (in AX)
	;	    	2 - File not found
	;	       18 - No more files  			
        ;
	; TRDOS 386 (v2.0) 
	; 15/10/2016
	;	
        ; INPUT ->
        ;	   CL = File attributes
	;     	      bit 0 (1) - Read only file (R)
	;             bit 1 (1) - Hidden file (H)
        ;             bit 2 (1) - System file (R)
	;             bit 3 (1) - Volume label/name (V)
        ;             bit 4 (1) - Subdirectory (D)
	;	      bit 5 (1) - File has been archived (A)
	;	   CH = 0 -> Return basic parameters (24 bytes)
	;	   CH > 0 -> Return FindFile structure/table (128 bytes)
        ;          EBX = Pointer to filename (ASCIIZ) -path-
	;	   EDX = File parameters buffer address
	;		(buffer size = 24 bytes if CH input = 0)
	;		(buffer size = 128 bytes if CH input > 0)
	;		 
	; OUTPUT ->
	;	   EAX = 0 if CH input > 0
	;	   EAX = First cluster number of file if CH input = 0	
	;	   EDX = File parameters table/structure address
	;	   Basic Parameters:
	;		Offset  Description
	;		------	---------------
	;		0	File Attributes
	;		1	Ambiguous filename chars are used sign
	;			(0 = filename fits exactly with request)
	;			(>0 = ambiguous filename chars are used)
	;	      	2	Time stamp of file
	;		4	Date stamp of file
	;		6	File size in bytes
	;		10	Short Filename (ASCIIZ, max. 13 bytes)
	;		23	Longname Length (1-255) if existing 
	;  
	;          cf = 1 -> Error code in AL
	;
	; Modified Registers: EAX (at the return of system call)
	;  
	; TR-DOS FindFile (FFF) Structure (128 bytes):
	; 09/10/2011 (DIR.ASM) - 10/02/2016 (trdoskx.s)
	;	
	; Offset	Parameter		Size
	; ------	------------------	-------- 
	; 0		FindFile_Drv		1 byte
	; 1		FindFile_Directory	65 bytes
	; 66		FindFile_Name		13 bytes
	; 79		FindFile_LongNameEntryLength 1 byte
	;Above 80 bytes form
	;TR-DOS Source/Destination File FullName Format/Structure
	; 80		FindFile_AttributesMask 1 word
	; 82		FindFile_DirEntry	32 bytes (*)
	; 114		FindFile_DirFirstCluster 1 double word
	; 118		FindFile_DirCluster	1 double word
	; 122		FindFile_DirEntryNumber 1 word
	; 124		FindFile_MatchCounter	1 word
	; 126		FindFile_Reserved	1 word
   	; (*) MS-DOS, FAT 12-16-32 classic directory entry (32 bytes)

	;mov	[u.namep], ebx
	; 16/10/2016
	mov	[FFF_UBuffer], edx
	mov	[FFF_Attrib], cx ; [FFF_RType] = ch
		    ; Attributes in CL, return data type in CH
	mov	esi, ebx
	; file name is forced, change directory as temporary
	;mov	ax, 1
	;mov	[FFF_Valid], ah ; 0 ; reset ; 17/10/2016
	;call	set_working_path 
	call	set_working_path_x ; 17/10/2016	
	jnc	short sysfff_0
 
	and	eax, eax  ; 0 -> Bad Path!  
	jnz	short sysfff_err

	; eax = 0
	mov	eax, ERR_DIR_NOT_FOUND ; Directory not found !
sysfff_err:
	mov	[u.r0], eax
	mov	[u.error], eax
        call 	reset_working_path
	jmp	error

sysfff_0:
	;sub	ah, ah ; ah = 0
	mov	al, [esp]
	or	al, al
	jz	short sysfff_2
	mov	ah, 10h
	test	al, 08h
	jnz	short sysfff_1
	or	ah, 08h
sysfff_1:
	and	al, 10h ; Directory
	jz	short sysfff_2
	and	ah, 08h
	xor	al, al ; When a directory is searched,
		       ; filename will be returned even if
		       ; it is not a directory!
		       ; Because: (in order to prevent
		       ; creating a dir with existing file name)
		       ; Dir and file names must not be same!
		       ; (return attribute must be checked)  	  	 	
sysfff_2:
	; AX = Attributes mask 
		; AL = AND mask (result must be equal to AL)
		; AH = Negative AND mask (result must be ZERO)			
 	; ESI = FindFile_Name address

	call	find_first_file
	jc	short sysfff_err ; eax = 2 (File not found !)

	; ESI = Directory Entry (FindFile_DirEntry) Location
	; EDI = Directory Buffer Directory Entry Location
	; EAX = File Size
	;  BL = Attributes of The File/Directory
	;  BH = Long Name Yes/No Status (>0 is YES)
	;  DX > 0 : Ambiguous filename chars are used

sysfff_3:
	; 16/10/2016
	mov	cx, [FFF_Attrib]
	; Attribs in CL, return data type in CH

	;or	cl, cl
	;jz	short sysfff_4 ; 0 = No filter
	xor	cl, 0FFh
	and	cl, bl
	jz	short sysfff_4

	;mov	eax, 2 ; 'file not found !' error
	;jmp	short sysfff_err_1

	; 16/10/2016
	call	find_next_file
	jc	short sysfff_err ; eax = 12 (no more files !)
	jmp	short sysfff_3

sysfff_4:
	and	ch, ch ; [FFF_RType]
	jz	short sysfff_5
	mov	ecx, 128 ; ; transfer length
	mov	[FFF_Valid], cl
sysfnf_11:
	mov	esi, FindFile_Drv
	jmp	short sysfff_6	
sysfff_5:
	;mov	esi, FindFile_DirEntry
	mov	ecx, 24  ; transfer length
	mov	[FFF_Valid], cl
sysfnf_12:
	mov	edi, DTA ; FFF data transfer address
	;mov	al, [esi+DirEntry_Attr] ; 11
	mov	al, bl ; File/Dir Attributes
	mov	[edi+23], bh ; Longname length (0= none)
	stosb
	mov	al, dl ; DL is for '?'
	add	al, dh ; DH is for '*'
	; AL > 0 if ambiguous file name wildcards are used
	stosb
	mov	eax, [esi+DirEntry_WrtTime] ; 22	
        stosd	; DirEntry_WrtTime & DirEntry_WrtDate
        mov	eax, [esi+DirEntry_FileSize] ; 28
        stosd
	mov	ax, [esi+DirEntry_FstClusHI] ; 20
	shl	ax, 16
	mov	ax, [esi+DirEntry_FstClusLO] ; 26 
	mov	[u.r0], eax ; First Cluster

        ;mov	esi, FindFile_DirEntry
	call	get_file_name

	mov	cl, [FFF_Valid]
       	mov	esi, DTA ; FFF data transfer address
sysfff_6:
	mov	edi, [FFF_UBuffer] ; user's buffer address (edx)
	call	transfer_to_user_buffer

	mov	[u.r0], ecx ; actual transfer count
        call 	reset_working_path
	jmp	sysret

sysfnf: ; <Find Next File>
	; 16/10/2016 TRDOS 386 (TRDOS v2.0) feature only ! 
	;           -derived from TRDOS v1.0, INT_21H.ASM-
	;            ("loc_INT21h_find_next_file")
	; TRDOS 8086 (v1.0)
        ; 	07/08/2011 
        ;	Find First File
	;	INPUT:
        ;	    none
	;	MSDOS OUTPUT:
	;	    DTA: (Default address: PSP offset 80h)
	;	    Offset  Descrription
	;	    0	    Reserved for use find next file
	;	    21	    Attribute of file found
	;	    22	    Time stamp of file
	;	    24	    Date stamp of file
	;	    26	    File size in bytes
	;	    30	    Filename and extension (zero terminated)
	;	If cf = 1:
	;	    Error Codes: (in AX)
	;	       18 - No more files  			
        ;
	; TRDOS 386 (v2.0) 
	; 16/10/2016
	;	
        ; INPUT ->
       	; 	   none
	; OUTPUT ->
	;	   EAX = 0 if CH input of 'Find First File' > 0
	;	   EAX = First cluster number of file
	;		 if CH input of 'Find First File' = 0	
	;	   EDX = File parameters table/structure address
	;
	;          cf = 1 -> Error code in AL
	;
 	; Modified Registers: EAX (at the return of system call)	

	;	
	; Note: If byte [FFF_Valid] = 0
	;	'sysfnf' will return with 'no more files' error.
	;	If byte [FFF_Valid] = 24
	;	'sysfnf' will return with 32 bytes basic parameters
	;	at the address which is in EDX.
	;	If byte [FFF_Valid] = 128
	;	'sysfnf' will return with 128 bytes Find File 
	;	Structure/Table at the address which is in EDX.
	
	cmp	byte [FFF_Valid], 0
	ja	short stsfnf_0
 	; 'no more files !' error 
	mov	eax, ERR_NO_MORE_FILES ; 12
	mov	[u.r0], eax
	mov	[u.error], eax
	jmp	error
stsfnf_0:
	;cmp	byte [FFF_Valid], 128
	;je	short stsfnf_1
	;cmp	byte [FFF_Valid], 24
	;je	short stsfnf_1
	;mov	[FFF_Valid], 24 ; Default
stsfnf_1:
	movzx	ebx, byte [Current_Drv]
	mov	[SWP_DRV], bx
	mov	dl, [FindFile_Drv]
	cmp	dl, bl
	jne	short stsfnf_2
	xchg	bh, bl
	mov	esi, Logical_DOSDisks
	add	esi, ebx
	jmp	short sysfnf_3

stsfnf_2:
	inc	byte [SWP_DRV_chg]

	call	change_current_drive
	jc	short sysfnf_err_1 ; read error ! 
				   ; (do not stop, because
				   ; we don't have a
				   ; 'no more files'
				   ; -file not found- error,	
				   ; next sysfnf system call
				   ; may solve the problem,
				   ; after re-placing the disk)				
sysfnf_3:
	mov	eax, [FindFile_DirCluster]
	and	eax, eax
	jnz	short sysfnf_6
        
	cmp	byte [Current_FATType], 2
	ja	short sysfnf_err_0 ; invalid, we neeed to stop !?
	cmp	byte [Current_FATType], 1
	jb	short sysfnf_err_0 ; invalid, we neeed to stop !?

	cmp	byte [DirBuff_ValidData], al ; 0
	jna	short sysfnf_4 

	cmp	eax, [DirBuff_Cluster] ; 0 ?
	je	short sysfnf_9

	;cmp	byte [Current_Dir_Level], 0
        ;ja	short sysfnf_4  
        ;jna	short sysfnf_9 

sysfnf_4:
	inc	byte [SWP_DRV_chg]
	call 	load_FAT_root_directory
	jnc	short sysfnf_9
	; eax = error code (17, 'drv not ready or read error')
	jmp	short sysfnf_err_1 ; read error ! (no FNF stop)
				   ; (if you want, try again, 
				   ;  after re-placing the disk)
sysfnf_5:	
	cmp	al, 12 ; 'no more files' error
	jne	short sysfnf_err_1 ; (no FNF stop -sysfnf will try
				   ;  to read the directory again,
				   ;  if the user calls sysfnf
				   ;  just after this error return-)
	; (FNF stop -sysfnf will not try
	;  to read the directory again-)	

sysfnf_err_0:
	mov	byte [FFF_Valid], 0 ; FNF stop sign
sysfnf_err_1:
	mov	[u.r0], eax
	mov	[u.error], eax
	call	reset_working_path
	jmp	error
	  
sysfnf_6:
	cmp	byte [DirBuff_ValidData], 0
	jna	short sysfnf_7

	cmp	eax, [DirBuff_Cluster]
	je	short sysfnf_9

sysfnf_7:
	inc	byte [SWP_DRV_chg]
	cmp	byte [Current_FATType], 1
	jnb	short sysfnf_8

	; Singlix (TRFS) File System 
	; (access via compatibility buffer)
	call	load_FS_sub_directory
	jnc	short sysfnf_9

	jmp	short sysfnf_err_1 ; read error (no FNF stop)

sysfnf_8:
	call	load_FAT_sub_directory	 
	jc	short sysfnf_err_1 ; read error (no FNF stop)

sysfnf_9:
	call	find_next_file
	jc	short sysfnf_5

	mov	al, [FFF_Attrib]
	;or	al, al
	;jz	short sysfnf_10 ; 0 = No filter
	xor	al, 0FFh
	and	al, bl
	jnz	short sysfnf_9 ; search for next file until
			       ; an error return from
			       ; find_next_file procedure	
sysfnf_10:
        movzx	ecx, byte [FFF_Valid]
	cmp	cl, 128 ; complete FindFile structure/table 
	je	sysfnf_11
	;cmp	cl, 24  ; basic parameters
	;je	sysfnf_12       
	jmp	sysfnf_12

writei:
	; 26/10/2016
	; 25/10/2016
	; 23/10/2016
	; 22/10/2016
	; 19/10/2016 - TRDOS 386 (TRDOS v2.0)
	; 19/05/2015 - 20/05/2015 (Retro UNIX 386 v1)
	; 12/03/2013 - 31/07/2013 (Retro UNIX 8086 v1)
	;
	; Write data to file with first cluster number in EAX
	; 
	; INPUTS ->
	;    EAX - First cluster number of the file
	;    EBX - File number (Open file index number)
	;    u.count - byte count to be written
	;    u.base - points to user buffer
	;    u.fofp - points to dword with current file offset
	;    i.size - file size
	;    cdev - logical dos drive number of the file
	; OUTPUTS ->
	;    u.count - cleared
	;    u.nread - accumulates total bytes passed back
	;    i.size - new file size (if file byte offset overs file size)
	;    u.fofp - points to u.off (with new offset value)	
	;
	; (Retro UNIX Prototype : 11/11/2012 - 18/11/2012, UNIXCOPY.ASM)
	; ((Modified registers: eax, edx, ebx, ecx, esi, edi, ebp)) 	

	xor	ecx, ecx
	mov 	[u.nread], ecx  ; 0
	mov	[u.pcount], cx ; 19/05/2015
	cmp 	[u.count], ecx
	ja 	short writei_1 
	retn
writei_1:
	mov	[writei.ofn], bl ; Open file number
	mov	[setfmod], cl ; 0 ; reset 'update lm date&time' sign
dskw_0: 
	; 26/10/2016
	; 22/10/2016, 23/10/2016, 25/10/2016
	; 19/10/2016 - TRDOS 386 (TRDOS v2.0)
	; 31/05/2015 - 25/07/2015 (Retro UNIX 386 v1)
	; 26/04/2013 - 20/09/2013 (Retro UNIX 8086 v1)
	;
	; 01/08/2013 (mkdir_w check)
 	call	mget_w
	; eax = sector/block number

	mov     ebx, [u.fofp]
	mov	edx, [ebx]
	and	edx, 1FFh  ; / test the lower 9 bits of the file offset
	jnz	short dskw_1 ; / if its non-zero, branch
			     ; if zero, file offset = 0,
		       	     ; / 512, 1024,...(i.e., start of new block)
	cmp	dword [u.count], 512
				; / if zero, is there enough data to fill
				; / an entire block? (i.e., no. of
	jnb	short dskw_2 ; / bytes to be written greater than 512.? 
			     ; / Yes, branch. Don't have to read block
dskw_1: ; in as no past info. is to be saved 
	; (the entire block will be overwritten).
	; 23/10/2016

	mov	ebx, writei_buffer
	; esi = logical dos drive description table address
	; eax = sector number
	; ebx = buffer address (in kernel's memory space)
	; ecx = sector count
	mov	ecx, 1
	call	disk_read
	;call	dskrd 	; / no, must retain old info.. 
		       	; / Hence, read block 'r1' into an I/O buffer
	jnc	short dskw_2

	; disk read error
	mov	eax, 17 ; drive not ready or READ ERROR !
dskw_err: ; jump from disk write error
	mov	[u.r0], eax
	mov	[u.error], eax

	cmp	byte [setfmod], 0
	jna	error

	call	update_file_lmdt ; update last modif. date&time of the file
	;mov	byte [setfmod], 0	

	jmp	error

dskw_2: ; 3:
	; 23/10/2016
	mov	byte [writei.valid], 1 ; writei buffer contains valid data
	push	esi ; logical dos drive description table address
	; EAX (r1) = block/sector number
	;call	wslot
		; jsr r0,wslot / set write and inhibit bits in I/O queue, 
			   ; / proc. status=0, r5 points to 1st word of data
	cmp	byte [u.kcall], 0
	ja	short dskw_4 ; zf=0 -> the caller is 'mkdir'
	;
	cmp	word [u.pcount], 0
	ja	short dskw_4
dskw_3:
	; [u.base] = virtual address to transfer (as source address)
	call	trans_addr_r ; translate virtual address to physical (r)
dskw_4:
	mov	ebx, writei_buffer
	; EBX (r5) = system (I/O) buffer address
	call	sioreg
	; ESI = file (user data) offset
	; EDI = sector (I/O) buffer offset
	; ECX = byte count
	;
  	rep	movsb
	; 25/07/2015
	; eax = remain bytes in buffer
        ;       (check if remain bytes in the buffer > [u.pcount])
	or	eax, eax
	jnz	short dskw_3 ; (page end before system buffer end!)	

	; 23/10/2016
	mov	cl, 1
	pop	esi
	mov	eax, [writei.sector]
	; esi = logical dos drive description table address
	; eax = sector number
	; ebx = writei buffer address
	; ecx = sector count
	call	disk_write ; / yes, write the block
	jnc	short dskw_5

	mov	eax, 18 ; drive not ready or WRITE ERROR !
	jmp	short dskw_err

dskw_5:
	; 26/10/2016
	movzx	ebx, byte [writei.ofn] ; open file number
	shl	bl, 2 ; *4
	mov	eax, [ebx+OF_POINTER]
	cmp	eax, [ebx+OF_SIZE]
	jna	short dskw_6
	mov	[ebx+OF_SIZE], eax
dskw_6:
	;shr	bl, 2
        cmp     dword [u.count], 0 ; / any more data to write?
	jna	short dskw_7
	mov	eax, [writei.fclust]
	jmp	dskw_0 ; / yes, branch
dskw_7:
 	; update last modif. date&time of the file
	; (also updates file size as OF_SIZE)
	call	update_file_lmdt
	;mov	byte [setfmod], 0	

	; 03/08/2013
	mov	byte [u.kcall], 0
	; 23/10/2016
	;mov	eax, [writei.fclust]
	retn

mget_w:
	; 02/11/2016
	; 01/11/2016
	; 23/10/2016, 31/10/2016
	; 22/10/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
	;    [i.size] = file size
	;    [u.count] = byte count	 
	;    EAX = First cluster
	;    [cdev] = Logical dos drive number 	  
	;    [writei.ofn] = File Number
	;		   (Open file index, 0 based)
	;    ([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	ebp, [esi] ; u.off (or EBX*4+OF_POINTER)

	sub	ecx, ecx
	mov	ch, [cdev]

	mov	esi, Logical_DOSDisks
	add	esi, ecx

	; 31/10/2016
	mov	ebx, eax ; First Cluster or FDT address

	cmp	byte [esi+LD_FATType], 0
	jna	mget_w_14 ; Singlix FS

	movzx	eax, word [esi+LD_BPB+BytesPerSec]
	movzx	edx, byte [esi+LD_BPB+SecPerClust]
	mov	[writei.spc], dl  ; sectors per cluster
	mul	edx
	; edx = 0
	; eax = bytes per cluster (<= 65536)

	; 02/11/2016
	mov	ecx, eax
	dec	eax
	mov	[writei.bpc], ax	
	
	mov	eax, ebp
	add	eax, [u.count] ; next file position
	cmp	eax, [i.size] ; <= file size ?
	jna	mget_w_4 ; no

	div	ecx
	mov	[writei.c_index], eax ; cluster index
	; edx = byte offset in cluster (<= 65535)
	;mov	[writei.offset], dx
	;shr	dx, 9 ; / 512
	;mov	[writei.s_index], dl ; sector index in cluster (0 to spc -1)

	sub	edx, edx ; 01/11/2016
	mov 	[writei.sector], edx ; 0
	mov	[writei.offset], dx  ; byte offset in cluster 
	mov	[writei.s_index], dl ; sector index in cluster (0 to spc -1)

	mov	eax, ebx ; First Cluster

	; is this the 1st mget_w or a next mget_w call ? (by 'writei')
	cmp	byte [writei.valid], dl ; 0 
	jna	short mget_w_0

	mov 	byte [writei.valid], dl ; 0 ; reset ('writei' will set it) 

	cmp	eax, [writei.fclust]
	jne	short mget_w_0

	mov	cl, [cdev]
	cmp	cl, [writei.drv]
	jne	short mget_w_0
 	; [writei.l_clust] & [writei.l_index] are valid, 
	;  we don't need to get last cluster & last cluster index
	mov	ecx, [writei.l_index]
	jmp	short mget_w_2
mget_w_0:
	mov	[writei.fclust], eax ; first cluster
	; edx = 0
	mov	[writei.cluster], eax ; first cluster ; 01/11/2016
	mov 	[writei.fs_index], edx ; 0 ; curret cluster index

	; FAT file system (FAT12, FAT16, FAT32)
	call	get_last_cluster
	jc	mget_w_err ; eax = error code
		
	mov	[writei.lclust], eax ; last cluster

	mov	ecx, [glc_index] ; last cluster index
	mov	[writei.l_index], ecx

	mov	al, [writei.ofn]
	inc	al
	mov	[setfmod], al ; update lm date&time sign

mget_w_1:
	cmp	ecx, [writei.c_index]  ; last cluster index
	jnb	short mget_w_2 ; 01/11/2016

	mov	eax, [writei.lclust]
	; EAX = Last cluster
	call	add_new_cluster
	jc	mget_w_err ; eax = error code
	; edx = 0
	mov	[writei.lclust], eax ; (new) last cluster
	mov	ecx, [writei.l_index]
	inc	ecx ; add 1 to last cluster index
	mov	[writei.l_index], ecx ; current last cluster index

	jmp	short mget_w_1

mget_w_2:
	mov	ecx, ebp
	add	ecx, [u.count]
	mov	[i.size], ecx ; save new file size
	;sub	edx, edx ; 0

	mov	al, [cdev]
	mov	[writei.drv], al ; physical drive number
	; edx = 0
	mov	eax, ebp ; file offset
	movzx	ecx, word [writei.bpc] ; bytes per cluster - 1
	inc	ecx ; bytes per cluster
	div	ecx
	; edx = byte offset in cluster (<= 65535)
	; eax = cluster index
	mov	[writei.c_index], eax
	mov	[writei.offset], dx
	shr	dx, 9 ; / 512
	mov	[writei.s_index], dl ; sector index in cluster (0 to spc -1)

mget_w_3:
	cmp	eax, [writei.l_index] ; last cluster index
	jne	short mget_w_5

	mov	[writei.fs_index], eax ; cluster index (for next check)
	mov	eax, [writei.lclust] ; last cluster
	jmp	short mget_w_10

mget_w_4: ; 02/11/2016
	; eax = next file position
	sub	eax, [u.count] ; current file position
	; edx = 0
	; ecx = bytes per cluster
	div	ecx
	mov	[writei.c_index], eax ; cluster index
	mov	[writei.offset], dx
	shr	dx, 9 ; / 512
	mov	[writei.s_index], dl ; sector index in cluster (0 to spc -1)

mget_w_5:
	and	eax, eax ; 0 = First Cluster's index number
	jnz	short mget_w_6

	mov	[writei.fs_index], eax ; cluster index (for next check)
	mov	eax, [writei.fclust] ; first cluster
	jmp	short mget_w_10

mget_w_6:
	cmp	eax, [writei.fs_index] ; current cluster index (>0)
	jne	short mget_w_7
	mov	eax, [writei.cluster] ; current cluster 
	jmp	short mget_w_11

mget_w_7:
	mov	ecx, eax
	sub	ecx, [writei.fs_index]
	jnc	short mget_w_8
	; get cluster by index from the first cluster
	mov	eax, [writei.fclust]
	mov	ecx, [writei.c_index]
	jmp	short mget_w_9

mget_w_8:
	mov	eax, [writei.cluster] ; beginning cluster
	; ecx = cluster sequence number after the beginning cluster
	; sub	edx, edx ; 0

mget_w_9:
	; 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	short mget_w_err ; error code in EAX
	; EAX = Cluster number		
mget_w_10:
	mov	[writei.cluster], eax ; FDT number for Singlix File System

	cmp	byte [esi+LD_FATType], 0
	jna	short mget_w_13
	; 01/11/2016
	mov	edx, [writei.c_index]
	mov	[writei.fs_index], edx
mget_w_11:
	sub	eax, 2
	movzx	edx, byte [writei.spc]
	mul	edx

	add	eax, [esi+LD_DATABegin]
	mov	dl, [writei.s_index]
	add	eax, edx
mget_w_12:
	mov	[writei.sector], eax
	;; buffer validation must be done in writei
	;;mov	byte [writei.valid], 1 
	retn

mget_w_err:
	mov	[u.error], eax
	mov	[u.r0], eax
	jmp	error

mget_w_13:
	; EAX = FDT number (Current Section)
	; EDX = Sector index from the first section (0,1,2,3,4...)
	sub	edx, [writei.fs_index]	
	; EDX = Sector index from current section
	mov	[writei.fs_index], edx
	inc	eax ; the first data sector in FS disk section	
	add	eax, edx
	jmp	short mget_w_12

mget_w_14:
	mov	cl, [esi+LD_FS_BytesPerSec+1]
	shr	cl, 1 ;  ; 1 for 512 bytes, 4 for 2048 bytes
	mov	[writei.spc], cl  ; sectors per cluster
	; NOTE: writei bytes per sector value is always 512 ! 
	mov	word [writei.bpc], 512

	mov	ecx, ebp
	add	ecx, [u.count] ; next file position
	cmp	ecx, [i.size] ; <= file size ?
	jna	mget_w_19 ; no

	sub	edx, edx ; 0
	mov 	[writei.sector], edx ; 0
	mov	[writei.offset], dx  ; byte offset in cluster 
	mov	[writei.s_index], dl ; sector index in cluster (0 to spc -1)

	shr	ecx, 9 ; 1 cluster = 512 bytes
	mov	[writei.c_index], ecx ; section/cluster index
	
	mov	eax, ebx ; FDT number (First FDT address)

	; is this the 1st mget_w or a next mget_w call ? (by 'writei')
	cmp	byte [writei.valid], dl ; 0 
	jna	short mget_w_15

	mov 	byte [writei.valid], dl ; 0 ; reset ('writei' will set it) 

	cmp	eax, [writei.fclust]
	jne	short mget_w_15

	mov	cl, [cdev]
	cmp	cl, [writei.drv]
	jne	short mget_w_15
 	; [writei.l_clust] & [writei.l_index] are valid, 
	;  we don't need to get last cluster & last cluster index
	mov	ecx, [writei.l_index]
	jmp	short mget_w_17
mget_w_15:
	mov	[writei.fclust], eax ; first section (FDT number)
	; edx = 0
	mov	[writei.cluster], edx ; 0 ; current section
	mov 	[writei.fs_index], edx ; 0 ; curret section index

	; eax = FDT number (section 0 header address)
	call	get_last_section
	jc	mget_w_err ; eax = error code

	mov 	[writei.fs_index], edx ; sector index in last section
		
	mov	[writei.lclust], eax ; last section address

	mov	ecx, [glc_index] ; last section index
	mov	[writei.l_index], ecx

	mov	al, [writei.ofn]
	inc	al
	mov	[setfmod], al ; update lm date&time sign

mget_w_16:
	; edx = (existing) last section (sector) index
	mov	ecx, [writei.c_index] ; final section (sector) index
	sub	ecx, edx
	jna	short mget_w_19
	 ; ecx = sector count
mget_w_17:
	mov	eax, [writei.lclust]
	; ESI = Logical dos drv desc. table address
        ; EAX = Last section
        ; (ECX = 0 for directory) 
        ; ECX = sector count (except FDT)
	call	add_new_fs_section
	jnc	short mget_w_18

	; If error number = 27h (insufficient disk space)
	; it is needed to check free consequent sectors
	; (1 data sector at least and +1 section header sector) 

	cmp	eax, 27h
	jne	mget_w_err ; eax = error code

	; ecx = count of free consequent sectors
	; ecx must be > 1 (1 data + 1 header sector)
	dec	ecx
	jz	mget_w_err
	jmp	short mget_w_17

mget_w_18:
	mov	[writei.lclust], eax ; (new) last section
	; ecx = sector count (except section header)
	mov	edx, [writei.l_index]
	add	edx, ecx ; add sector count to index
	mov	[writei.l_index], edx
	jmp	short mget_w_16

mget_w_19:
	mov	ecx, ebp
	add	ecx, [u.count]
	mov	[i.size], ecx ; save new file size
	;sub	edx, edx ; 0

	mov	al, [cdev]
	mov	[writei.drv], al ; physical drive number
	; edx = 0
	mov	eax, ebp ; file offset
	mov	edx, eax
	; 1 cluster = 512 bytes (for Singlix FS)
	shr	eax, 9  ; / 512
	and	edx, 1FFh
	; edx = byte offset in cluster/sector (<= 511)
	; eax = section (sector/cluster) index
	mov	[writei.c_index], eax
	mov	[writei.offset], dx
	;mov	byte [writei.s_index], 0 ; sector index in cluster
	jmp	mget_w_3

update_file_lmdt: ; & update file size
	; 26/10/2016
	; 24/10/2016
	; 23/10/2016
	; 22/10/2016 - TRDOS 386 (TRDOS v2.0)
	;
	; Update last modification date&time of file
	; (call from syswrite -> writei)
	; ((also updates file size)) // 26/10/2016
	; 
	; INPUT:
	;      byte [setfmod] = open file number
	; OUTPUT:
	;      cf = 0 -> success !
	;      cf = 1 -> lmdt update has been failed! 	 
	;
	; Modified registers: eax, ebx, ecx, edx, esi, edi       				
	;

	;cmp	byte [setfmod], 0
	;jna	short uflmdt_2 ; nothing to do

	xor	eax, eax

	movzx	ebx, byte [setfmod]
	dec	bl ; open file index number (0 based)

	mov	ah, [ebx+OF_DRIVE]
	mov	esi, Logical_DOSDisks
	add	esi, eax
	shl	bl, 2 ; *4	
	mov	ecx, [ebx+OF_FCLUSTER] ; first cluster
	mov	edx, [ebx+OF_DIRCLUSTER] ; dir cluster

	shr	bl, 1 ; /2
	movzx	edi, word [ebx+OF_DIRENTRY]
	
	cmp	byte [DirBuff_ValidData], 1
	jb	short uflmdt_4

	mov	al, [DirBuff_DRV]
	sub	al, 'A'	
	cmp	al, ah
	jne	short uflmdt_4 ; different drive
	mov	al, [esi+LD_FATType] 
	cmp	al, [DirBuff_FATType]
	jne	short uflmdt_5 ; different FS type
	cmp	edx, [DirBuff_Cluster]
	jne	short uflmdt_5 ; different cluster

uflmdt_1:
	; Directory buffer is ready here!
	; OF_FCLUSTER must be compared/verified
	mov	esi, Directory_Buffer
	shl	di, 5 ; dir entry index * 32
	add	esi, edi ; offset
	;
	test	byte [esi+DirEntry_Attr], 18h ; Vol & Dir
	jnz	short uflmdt_2 ; not a valid file !
	mov	ax, [esi+DirEntry_FstClusHI]
	shl	eax, 16
	mov	ax, [esi+DirEntry_FstClusLO]
	cmp	eax, ecx ; same first cluster ?
	je	short uflmdt_3 ; yes, it is OK !!!	

uflmdt_2:	
	; save directory buffer if has modified/changed sign
	; (It is good to save dir buff even if the searched
	; directory entry is not found !?)
	call	save_directory_buffer
	stc	; update failed
	retn
	
uflmdt_3:
	; Update directory entry
	; 26/10/2016
	shl	bl, 1 ; *2 
	mov	eax, [ebx+OF_SIZE] ; file size
	mov	[esi+DirEntry_FileSize], eax
	;
	call	convert_current_date_time
	; OUTPUT -> DX = Date in dos dir entry format
        ; 	    AX = Time in dos dir entry format	
	mov	[esi+DirEntry_WrtTime], ax
	mov	[esi+DirEntry_WrtDate], dx	
	mov	[esi+DirEntry_LastAccDate], dx
	mov	byte [DirBuff_ValidData], 2
	call	save_directory_buffer
	retn

uflmdt_4:
	; Directory buffer sector read&write
	; 23/10/2016
	;
	mov	al, [esi+LD_FATType]
uflmdt_5:
	mov	ebx, rw_buffer ; Common r/w sector buffer addr
		
	and	al, al ; 0 = Singlix FS
	jz	uflmdt_11

	and	edx, edx
	jnz	short uflmdt_9

	cmp	al, 2  ; 3 = FAT32
	ja	short uflmdt_8

	mov	eax, edi ; directory entry index number
	shr	ax, 4 ; 16 entries per sector	 
	add	eax, [esi+LD_ROOTBegin]
	; eax = root directory sector
uflmdt_6:
	push	eax ; * ; disk sector address
	push	ecx ; first cluster	
	mov	ecx, 1
	; ecx = sector count
	call	disk_read
	pop	ecx
	jnc	short uflmdt_10
	pop	eax ; *
uflmdt_7:
	retn	

uflmdt_8:
	mov	edx, [esi+LD_BPB+FAT32_RootFClust]
uflmdt_9:
	cmp	edx, 2
	jb	short uflmdt_7 ; invalid, nothing to do

	sub	edx, 2
	mov	eax, edx
	movzx	edx, byte [esi+LD_BPB+SecPerClust]
	mul	edx
	add	eax, [esi+LD_DATABegin]
	; eax = sub directory (data) sector 
	jmp	short uflmdt_6

uflmdt_10:
	; Directory sector buffer is ready here!
	; OF_FCLUSTER must be compared/verified
	; edi = dir entry index number (<= 2047)
	and	di, 0Fh ; 16 entries per sector
	shl	di, 5 ; dir entry index * 32
	add	edi, rw_buffer
	;
	test	byte [edi+DirEntry_Attr], 18h ; Vol & Dir
	jnz	uflmdt_2 ; not a valid file !
	mov	dx, [edi+DirEntry_FstClusHI]
	shl	edx, 16
	mov	dx, [edi+DirEntry_FstClusLO]
	cmp	edx, ecx ; same first cluster ?
	jne	uflmdt_2 ; no !?

	; Update directory entry
	call	convert_current_date_time
	; OUTPUT -> DX = Date in dos dir entry format
        ; 	    AX = Time in dos dir entry format	
	mov	[edi+DirEntry_WrtTime], ax
	mov	[edi+DirEntry_WrtDate], dx	
	mov	[edi+DirEntry_LastAccDate], dx
	
	pop	eax ; *

	mov	ebx, rw_buffer ; Common r/w sector buffer addr
	mov	ecx, 1
	; esi = logical dos description table address
	; eax = disk sector number/address (LBA)
	; ecx = sector count
	; ebx = buffer address
	call	disk_write
	jc	uflmdt_2
	
	; save directory buffer if has modified/changed sign
	call	save_directory_buffer
	retn

uflmdt_11:
	; 24/10/2016
	; Update last modification date & time of a file
	; on a disk with Singlix File System.
	;
	; (Method: Read the FDT -File Description Table-
	; sector of the file and update the lmdt data fields,
	; then write FDT sector to the disk.
	; /// It is easy but there is compatibility buffer
	; method also for changing directory entry data and
	; also there are some programming issues for Singlix
	; file system (TRFS), which are not completed yet!)
	;
	; Not ready yet ! (24/10/2016)
	; /// Temporary code for error return ! ///
	xor	eax, eax
	stc
	retn

sysalloc:
	; 14/10/2017
	; 20/08/2017, 01/09/2017
	; 20/02/2017, 04/03/2017, 15/05/2017
	; 19/02/2017 - TRDOS 386 (TRDOS v2.0)
	; (TRDOS 386 feature only!)
	;
	; Allocate Contiguous Memory Block/Pages (for user)
	; (System call for DMA Buffer allocation etc.)	
	;
	; INPUT ->
	;	EBX = Virtual address (for user)
	;	     (Physical memory block/aperture
	;	     will be mapped to this virtual address) 
	;	ECX = Byte Count
	;	     (will be rounded up to page border)
	;	If ECX = 0
	;	    System call will return with an error (cf=1)
	;	    but ECX will contain maximum size of
	;	    available memory aperture and physical
	;	    (beginning) address of that aperture
	;	    (which have maximum size) will be in EAX.
	;	EDX = Upper limit of the requested physical memory
	;	      block/pages.
	;	     (The last byte address of the memory aperture 
	;	      must not be equal to or above this limit.)	
	;	If EDX = 0
	; 	   there is NOLIMIT !
	;	If EDX = 0FFFFFFFFh (-1) 
	;	   ESI = Lower Limit !
	;		(Beginning of the block must not be 'less'
	;		than this.) (Must be equal to or above...)
	;	   EDI = Upper Limit !
	;		(End of the block must be !less! than this)
	;		(The last byte addr of the memory aperture 
	;		must not be equal to or above this limit.)
	;
	; OUTPUT ->
	;	If CF = 0
	;	EAX = Physical address of the allocated memory block
	;	ECX = Allocated bytes (as rounded up to page borders)
	;	EBX = Virtual address (as rounded up)
	;	IF CF = 1
	;	    Requested (size of) Memory block could not be 
	;	    allocated to the user!	
	;	IF CF = 1 & EAX = 0 (Insufficient memory error!)
	;	   ECX = Total number of free bytes
	;	         (not size of available contiguous bytes!)		 	
	;	If CF = 1 & EAX > 0
	;	   there is not a memory aperture with requested size
	;	   but total free mem is not less than requested size.
	;	   EAX = Physical addr of available memory aperture
	;	   	 with max size 
	;	        (but it doesn't fit to the conditions!) 	
	;	   ECX = Size of available memory aperture in bytes.
	;	If CF = 1 -> EAX = 0FFFFFFFFh	
	;	   Conditions/Parameters are wrong !		 		
	;	   ECX is same with input value.
	;
	; Note:	Previously allocated pages will be deallocated if
	;       new allocation conditions are met.
	;
	; Note: u.break control may be included in future versions	
	;

	xor	eax, eax ; 0
	; 14/10/2017
	dec	edx ; is there a limit ?
	js	short sysalloc_1 ;  0 -> 0FFFFFFFFh -> NO LIMIT
	inc	edx ; > 0
	; Check upper address limit
	;(round up to page borders)
	add	ecx, PAGE_SIZE-1 ; 4095
	and	cx, ~PAGE_OFF ; not 4095
	cmp	edx, ecx ; upper limit - block size
	jb	short sysalloc_err
sysalloc_1:
	; EAX = Beginning address (physical)
	; EAX = 0 -> Allocate mem block from the 1st proper aperture	
	; ECX = Number of bytes to be allocated		
	call	allocate_memory_block
	jc	short sysalloc_err
	; 01/09/2017
	sub	edx, eax ; upper limit address - beginning address
	jna	short sysalloc_3 ; begin addr not less than the limit
	cmp	edx, ecx
	jb	short sysalloc_3 ; end address overs the limit
sysalloc_2:	
	; EAX = Beginning (physical) addr of the allocated mem block
	; ECX = Num of allocated bytes (rounded up to page borders) 
	push	eax ; * ; 04/03/2017	
	; Here, requested contiquous memory pages have been allocated
	; on Memory Allocation Table but user's page directory 
	; and page tables have not been updated yet!
	push	ecx ; **
	; ebx = virtual address (will be rounded up to page border)
	; ecx = number of bytes to be deallocated
	;	will be adjusted to ebx+ecx round down - ebx round up
	call	deallocate_user_pages 
	jnc	short sysalloc_4 ; EAX = Deallocated memory bytes
	pop	ecx ; **
	pop	eax ; *
sysalloc_3:
	; error !
	; restore Memory Allocation Table Content
	call	deallocate_memory_block
	xor	eax, eax ; 0
	dec	eax ; 0FFFFFFFFh ; 15/05/2017
	jmp	short sysalloc_wrong
sysalloc_err:
	mov	ebp, [u.usp]  ; ebp points to user's registers
	mov	[ebp+24], ecx ; return to user with ecx value 
sysalloc_wrong:
	; eax = 0FFFFFFFFh
	mov	[u.r0], eax
	jmp	error
sysalloc_4:
	mov	ebp, [u.usp]  ; ebp points to user's registers
	mov	[ebp+24], eax ; return to user with ecx value 
	mov	[ebp+16], ebx ; new value of ebx (rounded up)
	mov	ecx, eax ; byte count (from 'deallocate_user_pages')
	pop	edx ; ** ; discard (another) byte count
	pop	eax ; *
	mov	[u.r0], eax ; physical address
	
	push	ecx ; 20/08/2017
	;
	; Write newly allocated contiguous (physical) pages
	; on page dir and page tables of current user/process	
	; as PRESENT, USER, WRITABLE
	; (then clear allocated pages)
	call	allocate_user_pages
	;jnc	sysret ; OK! return to process with success...

	; 20/08/2017 ('sysdma' modification)
	pop	ecx
	mov	eax, [u.r0]   ; physical address (of the block)

	jc	short sysalloc_6

	cmp	dword [dma_addr], 0FFFFFFFFh ; -1	
	jb	sysret

	mov	[dma_addr], eax ; save dma address for sysdma
	mov	[dma_size], ecx ; save dma buff size for sysdma

	jmp	sysret
		
sysalloc_6:
	;
	; unexpected error ! insufficient memory !? conflict !?
	; (!!?there is not a free page for a new page table?!!)
	; We need to terminate process with error message !!!  
	;
	mov	ebp, [u.usp]  ; ebp points to user's registers
	mov	ecx, [ebp+24] ; byte count
	
	; 20/08/2017
	;mov	eax, [u.r0]   ; physical address (of the block)
	
	;
	; restore Memory Allocation Table Content
	call	deallocate_memory_block
	;
	cmp	byte [CRT_MODE], 3 ; 80x25 text mode?
	je	short sysalloc_7 ; yes
	; Current mode is VGA (or CGA graphics) mode,
	; We need to return to text mode for displaying
	; error message just before 'sysexit'.  
	mov	al, 3
	call	_set_mode
sysalloc_7:
	mov	esi, beep_Insufficient_Memory ; error message
	call	print_msg ; print/display the message
	mov	eax, 1 ; ax=1 is needed for 'sysexit' procedure
	jmp	sysexit ; and terminate the process !

sysdalloc:
	; 19/02/2017 - TRDOS 386 (TRDOS v2.0)
	; (TRDOS 386 feature only!)
	;
	; Deallocate Memory Block/Pages (for user)
	; (Complementary call for sysalloc.)	
	;
	; INPUT ->
	;	EBX = Virtual address (for user)
	;	      (will be rounded up to page border)
	;	ECX = Byte Count
	;	     (will be adjusted to page borders)
	;	If ICX = 0
	;	   nothing to do
	;	If EBX + ECX > User's ESP
	;	   nothing to do		
	; 
	; Note: u.break control may be included in future versions	
	;
	; OUTPUT ->
	;	If CF = 0
	;	   EAX = Deallocated memory bytes
	;	   EBX = Virtual address (as rounded up)	
	;	IF CF = 1
	;	   EAX = 0
	;
	; Note:	Main purpose of this call is to deallocate/release
	;	previously allocated (physically) contiguous memory
	;	pages but beginning (virtual) address may not be
	;	followed by physically contiguous pages. So, this
	;	system call will deallocate user's virtually
	;	contiguous memory pages. Also, there is not any
	;	objections to use this system call without sysalloc
	;	system call; only possible objection is to lost data
	;	within user's memory space, if the beginning address
	;	and size is not proper.
	;
	; Note: Empty page tables will not be deallocated!!!
	;       (they will be deallocated at process termination)
	;
	; Note: When the program terminates itself or when it is 
	;	terminated by operating system kernel, all allocated
	;	memory pages will be deallocated during termination
	;	stage. So, 'sysdalloc' is not necessary except 
	;	forgiving memory block to other programs/processes.
	;	
	mov	edx, [u.sp]
	mov	eax, [edx+12] ; user's stack pointer
	sub	eax, ecx ; esp - byte count
	and	al, 0FCh ; dword alignment
	cmp	eax, ebx
	jb	short sysdalloc_err ; deallocation overlaps with stack 	

	xor	eax, eax
	and	ecx, ecx
	jz	short sysdalloc_2
	
	call	deallocate_user_pages
	jc	short sysdalloc_err

sysdalloc_2:
	mov	[u.r0], eax
	mov	ebp, [u.usp]
	mov	[ebp+16], ebx ; new value of ebx
	jmp	sysret

sysdalloc_err:
	mov	[u.r0], eax ; 0
	jmp	error

syscalbac:
	; SYS CALLBACK
	; 03/08/2020
	; 16/04/2017
	; 14/04/2017
	; 13/04/2017
	; 28/02/2017
	; 26/02/2017
	; 24/02/2017
	; 21/02/2017 - TRDOS 386 (TRDOS v2.0)
	; (TRDOS 386 feature only!)
	;
	; Link or unlink IRQ callback service to/from user (ring 3)	
	;
	; INPUT ->
	;	BL = IRQ number (Hardware interrupt request number)
	;	     (0 t0 15 but IRQ 0,1,2,6,8,14,15 are prohibited)
	;	     IRQ numbers 3,4,5,7,9,10,11,12,13 are valid
	;	     (numbers >15 are invalid)
	;
	;	BH = 0 = Unlink IRQ (in BL) from user (ring 3) service
	;	     1 = Link IRQ by using Signal Response Byte method
	;	     2 = Link IRQ by using Callback service method 		
	;	     3 = Link IRQ by using Auto Increment S.R.B. method 	
	;	    >3 = invalid 
	;	
	;	CL = Signal Return/Response Byte value 
	;
	;	If BH = 3, kernel will put a counter value  ; 03/08/2020
	;	          (into the S.R.B. addr)
	;		  between 0 to 255. (start value = CL+1)
	;	
	;	NOTE: counter value, for example: even and odd numbers
	;	      may be used for -audio- DMA buffer switch 
	;	      within double buffer method, etc. 		
	;
	;	EDX = Signal return (Response) byte address
	;	       		  - or -
	;	      Interrupt/Callback service/routine address
	; 	
	;	      (virtual address in user's memory space)
	;
	; OUTPUT ->
	;	CF = 0 & EAX = 0 -> Successful setting
	;	CF = 1 & EAX > 0 -> IRQ is prohibited or locked 
	;			by another process
	;		 eax = ERR_PERM_DENIED -> prohibited or locked
	;		 eax = ERR_INV_PARAMETER -> 
	;		       invalid parameter/option or bad address
	;
	;	NOTE: Timer callbacks are set by using 'systimer'
	;	      system call (IRQ 0, PIT and IRQ 8, RTC)
	;
	;	      Direct keyboard access is performed by using
	;	      Keyboard Interrupt (INT 32h)	 	
	;	
	;	      It is prohibited here because:
	;		1) Signal Response Byte method has not advantage
	;		   against INT 32h, function AH = 1. Also,
	;		   keyboard service interrupt will return with
	;		   ascii and scan codes (AL, AH) while
	;		   SRB method has only 1 byte space for ascii code
	;		   or scan code. One byte signal response is used 
	;		   for ensuring very simple and very fast
	;		   virtual to physical memory address conversion
	;		   without any memory page crossover risk. 
	;		   (Otherwise double page conversion or word 
	;		   alignment would be needed.)
	;		2) Badly written user code (callback code)
	;		   can prevent keyboard and timesharing functions
	;		   of the operating system via continuous and long
	;		   keyboard event handling by callback service.
	;		   (It can cause to lose immediate keystroke 
	;		   response from hardware to user.)
	;		3) If user will check any keyboard events, 'getkey'
	;		   (or 'getchar') must have more priority than other
	;		   (video etc.) events because only control ability
	;		   on a procedural infinite loop is a keyboard or
	;		   mouse event. So user can use keyboard function
	;		   at the end or at the beginning of a loop.
	;		   In this case, INT 32h is used for that purpose
	;		   and timer interrupt etc. callbacks can be used
	;		   for dynamic and synchronized data refresh/transfer
	;		   while cpu is in a static loop (without polling).
	;		   Keyboard Int callback is not more useful because 
	;		   already a manual check (a key is pressed or not)
	;		   can be performed (via INT 32h, AH = 1) efficiently
	;		   in a loop to prevent a locked infinitive loop.
	;
	;	    Disk IRQs (6,14,15) have been phohibited from ring 3 
	;	    callback because, disk operations (file system services
	;	    etc.) are independent from user program, for fast disk r/w.
	;	    They are not more useful at ring 3 while they are in use
	;	    by standard diskio functions which are mandatory part of 
	;	    (monolithic) OS kernel and mainprog command interpreter.
	;	    INT 33h diskio functions are enough for user level disk
	;	    r/w.				
	;
	; TRDOS 386 - IRQ CALLBACK structures (parameters):
	;	
	;	   [u.irqlock] = 1 word, IRQ flags (0-15) that indicates
	;			which IRQs are locked by (that) user.
	;		        Lock and unlock (by user) will change
	;			these flags or 'terminate process' (sysexit)
	;			will clear these flags and unlock those IRQs.
	;			               
	;		   	Bit 0 is for IRQ 0 and Bit 15 is for IRQ 15
	;
	;	   IRQ(x).owner	 : 1 byte, user, [u.uno], 0 = free (unlocked)	
	;
	;	   IRQ(x).method : 1 byte for callback method & status
	;			   0 = Signal Response Byte method
	;			   1 = Callback service method
	;			   >1 = invalid for current 'syscalback'.
	;			or(+) 80h = IRQ is in use by system (ring 0)
	;			            function (audio etc.) or
	;			   	    a device driver.	   	
	;			(system function will ignore the lock/owner)
	;
	;	   IRQ(x).srb	: 1 byte, Signal Return/Response byte value
	;			  (a fixed value by user or a counter value
	;			 from 0 to 255, which is increased by every
	;			 interrupt just before putting it into 
	;			 the Signal Response byte address
	;			 (This is not used in callback serv method)
	;	    	  
	;	   IRQ(x).addr	: 1 dword
	;			  Signal Response Byte address (physical)
	;			  		-or-
	;			  Callback service address (virtual)
	;
	;	   IRQ(x).dev	: 1 byte
	;			  0 = Default device or kernel function
	;			  		-or-
	;			  1-255 = Assigned device driver number
	;
	;	   (x) = 3,4,5,7,9,10,11,12,13
	;
	;	
	;	NOTE: If user's process/program calls the kernel (INT 40h)
	;	      while it is already running in a (ring 3) callback
	;	      service, kernel will force (convert) system call to
	;	      'sysrele' (sys release). So, this feature provides
	;	      easy and simple usage of callback services without
	;	      falling into deepless <please 'callback me' then 
	;	      let me 'callback you'> cycles! (User must return
	;	      from callback service by using 'sysrele' system
	;	      call, without a significant delay. Otherwise user
	;	      process/program may be late to catch the next event
	;	      within same callback purpose.	    		 	
	;

	xor	al, al ; the caller is 'syscalbac' sign/flag
 	call	set_irq_callback_service
	; 16/04/2017
	mov	[u.r0], eax
	jnc	sysret
	mov	dword [u.error], eax
	jmp	error

sysfpstat:
	; 28/02/2017 - TRDOS 386 (TRDOS v2.0)
	; (TRDOS 386 feature only!)
	;
	; Set or reset FPU registers save/restore option (for user)
	;	       (during software task switching, wswap-rswap)
	;
	; INPUT ->
	;	BL = 0 -> reset
	;	BL = 1 -> set (FPU register will be saved and restored)
	;	
	; OUTPUT ->
	;	cf = 0 -> no error, FPU is ready... 
	;		  (EAX = 0)
	;	Cf = 1 -> error, 80387 FPU is not ready !
	;		  (EAX = 0FFFFFFFFh)

	xor	eax, eax
	cmp	byte [fpready], 0
	jna	short sysfpstat_err	

	and	bl, 1 ; use BIT 0 only !
	mov	[u.fpsave], bl
	mov	[u.r0], eax ; 0
	jmp	sysret	 	

sysfpstat_err:
	dec 	eax ; 0FFFFFFFFh
	mov 	[u.r0], eax ; -1
	jmp 	error

sysdelete: ; Delete (Remove, Unlink) File
	; 29/12/2017 (TRDOS 386 = TRDOS v2.0) 
	;	
        ; INPUT ->
        ;          EBX = File name (ASCIIZ string) address
	; OUTPUT ->
	;          cf = 0 -> eax = 0
	;          cf = 1 -> Error code in AL
	;
	; Modified Registers: EAX (at the return of system call)
	;   

	mov	esi, ebx
	; file name is forced, change directory as temporary
	;mov	ax, 1
	;mov	[FFF_Valid], ah ; 0 ; reset
	;call	set_working_path 
	call	set_working_path_x
	jnc	short sysdelete_1

	and	eax, eax  ; 0 -> Bad Path!  
	jnz	short sysdelete_err
	; eax = 0
sysdelete_path_err:
	mov	eax, ERR_INV_PATH_NAME ; 'bad path name !'
sysdelete_err:
	mov	[u.r0], eax
	mov	[u.error], eax
	call 	reset_working_path
	jmp	error
sysdelete_1:
	;mov	esi, FindFile_Name
	mov	ax, 1800h ; Only files
	call	find_first_file
	jc	short sysdelete_err
sysdelete_2:
	; check file attributes

	;test	bl, 17 ; system, hidden, readonly, directory
        test	bl, 7 ; system, hidden, readonly 
        jz	short sysdelete_3

        mov	eax, ERR_FILE_ACCESS ; 11 = 'permission denied !'
        jmp	short sysdelete_err
sysdelete_3:
	and	dx, dx ; Ambiguous filename chars used sign (DX>0)
	jz	short sysdelete_4
	mov	eax, ERR_INV_FILE_NAME ; 26 = 'invalid file name !'
        jmp	short sysdelete_err
sysdelete_4:
	;mov	bh, [LongName_EntryLength]
	mov	[DelFile_LNEL], bh ; Long name entry length (if > 0)
	; edi = Directory Entry Offset (DirBuff)
	; esi = Directory Entry (FFF Structure)
	call	remove_file
	jc	short sysdelete_err
sysrmdir_5:
	xor	eax, eax ; 0
	mov	[u.r0], eax
	;mov	[u.error], eax
	call 	reset_working_path
	jmp	sysret


sysrmdir: ; Remove (Unlink) Directory
	; 29/12/2017 (TRDOS 386 = TRDOS v2.0) 
	;	
        ; INPUT ->
        ;          EBX = Pointer to directory name
	; OUTPUT ->
	;          cf = 0 -> eax = 0
	;          cf = 1 -> Error code in AL
	;
	; Modified Registers: EAX (at the return of system call)
	;  

	cmp	byte [u.uno], 0  ; root (super user) ?
	jna	short sysrmdir_0

	;mov	dword [u.r0], ERR_PERM_DENIED
	mov	eax, ERR_PERM_DENIED ; ERR_NOT_SUPERUSER
	mov	[u.r0], eax
	mov	[u.error], eax
	jmp	error

sysrmdir_0:
	mov	esi, ebx
	; file name is forced, change directory as temporary
	;mov	ax, 1
	;mov	[FFF_Valid], ah ; 0 ; reset
	;call	set_working_path 
	call	set_working_path_x
	jnc	short sysrmdir_1

	and	eax, eax  ; 0 -> Bad Path!  
	jnz	short sysrmdir_err
	; eax = 0
sysrmdir_not_found:
	mov	eax, ERR_DIR_NOT_FOUND ; Directory not found !
sysrmdir_err:
	mov	[u.r0], eax
	mov	[u.error], eax
	call 	reset_working_path
	jmp	error
sysrmdir_1:
	;mov	esi, FindFile_Name
	mov	ax, 0810h ; Only directories
	call	find_first_file
	jnc	short sysrmdir_2

	; eax = 2 (File not found !)
	cmp	al, 2 ; ERR_NOT_FOUND
	je	short sysrmdir_not_found
	jmp	short sysrmdir_err
sysrmdir_2:
	; check directory attributes

        test	bl, 7 ; system, hidden, readonly 
        jz	short sysrmdir_3

        mov	eax, ERR_DIR_ACCESS ; 11 = 'permission denied !'
        jmp	short sysrmdir_err
sysrmdir_3:
	and	dx, dx ; Ambiguous filename chars used sign (DX>0)
	jz	short sysrmdir_4
	;mov	eax, ERR_NOT_DIR ; 'not a valid directory !'
	mov	eax, ERR_INV_PATH_NAME ; 'bad path name !'
        jmp	short sysrmdir_err
sysrmdir_4:
	;mov	bh, [LongName_EntryLength]
	mov	[DelFile_LNEL], bh ; Long name entry length (if > 0)
	; edi = Directory Entry Offset (DirBuff)
	; esi = Directory Entry (FFF Structure)
	call	delete_sub_directory
	jnc	sysrmdir_5
;	jc	short sysrmdir_6
;
;	xor	eax, eax ; 0
;sysrmdir_5:
;	mov	[u.r0], eax
;	;mov	[u.error], eax
;	call 	reset_working_path
;	jmp	sysret
sysrmdir_6:
	mov	[u.r0], eax
	mov	[u.error], eax

	or	eax, eax ; EAX = 0 -> Directory not empty!
	jz	short sysrmdir_9

	; EAX > 0 -> Error code in AL (or AX or EAX)

	cmp	dword [FAT_ClusterCounter], 1
	jb	short sysrmdir_8
sysrmdir_7:
	; ESI = Logical DOS Drive Description Table address	
	mov	bx, 0FF00h ; BH = FFh -> use ESI for Drive parameters
	           ; BL = 0 -> Recalculate free cluster count
	call	calculate_fat_freespace	
sysrmdir_8:
	call 	reset_working_path
	jmp	error

sysrmdir_9:
	mov	eax, [FAT_ClusterCounter]
	or	eax, eax ; 0 ?
	jz	sysrmdir_err
	; ESI = Logical DOS Drive Description Table address	
	mov	bx, 0FF01h ; BH = FFh -> use ESI for Drive parameters
	           ; BL = 1 -> add free clusters
	call	calculate_fat_freespace
	or	ecx, ecx
        jz	short sysrmdir_8 ; ecx = 0 -> OK
	; ecx > 0 -> Error (Recalculation is needed)
	jmp	short sysrmdir_7


syschdir: ; Change Current (Working) Drive & Directory (for user)
	; 30/12/2017 (TRDOS 386 = TRDOS v2.0) 
	;	
        ; INPUT ->
        ;          EBX = Directory name (ASCIIZ string) address
	; OUTPUT ->
	;          cf = 0 -> eax = 0
	;          cf = 1 -> Error code in AL
	;
	; Modified Registers: EAX (at the return of system call)
	;
	; NOTE: If drive name is not included, only the working
	; directory (for user, not for drive/OS) will be chanded.
	; If there is a drive name (as A:, B:, C:, D: etc.)
	; at the beginning of the ASCIIZ (directory) string,
	; working drive and working directory (for user) 
	; will be changed together.
	; (When the program is terminated, MainProg -internal 
	; shell- will reset working directory to the previous
	; -current- logical drive's current directory again.) 	
  
	mov	esi, ebx
	; file name is not forced, change directory as temporary
	xor	eax, eax
	;mov	[FFF_Valid], ah ; 0 ; reset
	;call	set_working_path 
	call	set_working_path_xx
	jnc	short syschdir_ok
	and	eax, eax  ; 0 -> Bad Path!  
	jnz	short syschdir_err
	; eax = 0
syschdir_not_found:
	mov	eax, ERR_DIR_NOT_FOUND ; Directory not found !
syschdir_err:
	mov	[u.r0], eax
	mov	[u.error], eax
	call 	reset_working_path
	jmp	error
syschdir_ok:
	xor	eax, eax ; 0
	mov	[u.r0], eax
	;mov	[u.error], eax
	jmp	sysret


syschmod: ; Get & Change File (or Directory) Attributes
	; 30/12/2017 (TRDOS 386 = TRDOS v2.0) 
	;	
        ; INPUT ->
        ;          EBX = File/Directory (ASCIIZ) name address
	;	    CL = New attributes (if CL < 40h)
	;	    CL >= 40h -> Get File Attributes		
	; OUTPUT ->
	;          cf = 0 -> EAX = File attributes (in AL)
	;          cf = 1 -> Error code in AL
	;
	; Modified Registers: EAX (at the return of system call)
	;  
	; MSDOS File Attributes:    (bit value of attrib byte)
	;	ATTR_READ_ONLY	=	01h  (bit 0, 'R')
	;	ATTR_HIDDEN	=	02h  (bit 1, 'H')
	;	ATTR_SYSTEM	=	04h  (bit 2, 'S')
	;	ATTR_VOLUME_ID	=	08h  (bit 3)
	;	ATTR_DIRECTORY	=	10h  (bit 4)
	;	ATTR_ARCHIVE	=	20h  (bit 5, 'A')
	;	ATTR_LONG_NAME	=	ATTR_READONLY |
	;				ATTR_HIDDEN |
	;				ATTR_SYSTEM |
	;				ATTR_VOLUME_ID				
	;	The upper two bits of attributes must be 0.

	; Note:	* If ATTR_DIRECTORY is set, only directory names
	;	  will be searched (and S,H,R,A attributeds of 
	;	  the directory will be changed.)
	;	* If ATTR_VOLUME_ID is set, 'syschmod' system call
	;	  will return with 'permission denied' error.
	;	* If ATTR_DIRECTORY is not set, only file names
	;	  will be searched (and S,H,R,A attributes of the
	;	  file will be changed.)
	;
	; (Ony Super User can change S,H,R attributes.) 

	cmp	cl, 40h	
	jnb	short syschmod_0

	test	cl, 08h ; ATTR_VOLUME_ID
	jnz	short syschmod_perm_err

	cmp	byte [u.uno], 0  ; root (super user) ?
	jna	short syschmod_0

	; Not super user..
	test	cl, 07h	; S,H,R attributes
	jz	short syschmod_0

syschmod_perm_err:
	;mov	dword [u.r0], ERR_PERM_DENIED
	mov	eax, ERR_PERM_DENIED ; 'permission denied !'
	mov	[u.r0], eax
	mov	[u.error], eax
	jmp	error

syschmod_0:
	mov	[Attributes], cl
	mov	esi, ebx
	; file name is forced, change directory as temporary
	;mov	ax, 1
	;mov	[FFF_Valid], ah ; 0 ; reset
	;call	set_working_path 
	call	set_working_path_x
	jnc	short syschmod_1
	and	eax, eax  ; 0 -> Bad Path!  
	jnz	short syschmod_err
	; eax = 0
syschmod_path_not_found:
	mov	eax, ERR_INV_PATH_NAME ; 'Bad path name !'
syschmod_err:
	mov	[u.r0], eax
	mov	[u.error], eax
	call 	reset_working_path
	jmp	error
syschmod_1:
	mov	al, 08h ; Except volume labels (& long names)
	mov	al, [Attributes]
	and	al, 10h ;
	;mov	esi, FindFile_Name
	;mov	ax, 1800h ; Only files
	;mov	ax, 0810h ; Only directories
	call	find_first_file
	;jnc	short syschmod_2
	jc	short syschmod_err

	;; eax = 2 (File not found !)
	;cmp	al, 2 ; ERR_NOT_FOUND
	;jne	short syschmod_err

	;and	byte [Attributes], 10h
	;jz	short syschmod_err

	;; Directory not found !
	;mov	al, 3 ; ERR_PATH_NOT_FOUND
	;jmp	short syschmod_err

syschmod_2:
	and	dx, dx ; Ambiguous filename chars used sign (DX>0)
	jz	short syschmod_3
	mov	eax, ERR_INV_FILE_NAME ; 'invalid file name !'
        jmp	short syschmod_err
syschmod_3:
	; EDI = Directory buffer entry offset/address
	; BL = File (or Directory) Attributes 
	; mov	bl, [EDI+0Bh]

	; check directory attributes
	mov	bh, [Attributes] ; new attributes
	cmp	bh, 40h  ;>=40 -> get file/directory attributes
	jnb	short syschmod_6

	; set file/directory attributes
	test	bl, 7 ; system, hidden, readonly 
        jz	short syschmod_4

	cmp	byte [u.uno], 0  ; root (super user) ?
	ja	short syschmod_perm_err
syschmod_4:
	cmp	word [edi+DirEntry_NTRes], 01A1h ; Singlix FS
	je	short syschmod_7

	mov	[edi+0Bh], bh    ; Attributes (New!)

	mov	byte [DirBuff_ValidData], 2 ; modified sign
					    ; to force write	
	call 	save_directory_buffer
	jc	short syschmod_err

syschmod_5:
	mov	bl, [Attributes]
syschmod_6:
	movzx	eax, bl
	mov	[u.r0], eax
	;mov	dword [u.error], 0
	jmp	sysret

syschmod_7:
	sub	eax, eax
        mov     ah, [DirBuff_DRV]
	mov	esi, Logical_DOSDisks
        add     esi, eax
        cmp     byte [esi+LD_FSType], 0A1h
	jnc	short syschmod_8
	mov	al, ERR_INV_DATA ; 29 = Invalid Data
	jmp	syschmod_err

syschmod_8:
	; BH = New MS-DOS File Attributes
	mov	al, bh ; File/Directory Attributes
	xor	ah, ah ; Attributes in MS-DOS format sign	  
	call	change_fs_file_attributes
	jc	syschmod_err
	jmp	short syschmod_5


sysdrive: ; Get/Set Current (Working) Drive (for user)
	; 30/12/2017 (TRDOS 386 = TRDOS v2.0) 
	;	
        ; INPUT ->
        ;          BL = Logical DOS Drive number (0=A: ... 2=C:)
	;	   If BL = 0FFh -> Get Current Drive 	
	; OUTPUT ->
	;          cf = 0 -> 
	;		   AL = Current Drive number	
	;		   AH = The Last Logical DOS Drive no.
	;          cf = 1 -> Error code in AL
	;
	; Modified Registers: EAX (at the return of system call)
	;
	; NOTE: If the requested logical dos drive is ready, 
	;	it's current current directory will be the user's
	;	(program's) current directory.
	;	(When the program is terminated, MainProg -internal 
	;	shell- will reset the previous -current- logical drive
	;       as current drive again).	
  
	cmp	bl, 0FFh
	je	short sysdrive_ok
	cmp	bl, [Last_DOS_DiskNo]
	ja	short sysdrive_err	

	; Save current drive and reset mode
	; for 'reset_working_path' procedure (for MainProg)
	xor	al, al
	mov	[SWP_Mode], ax ; ah = 0
	mov	al, [Current_Drv]
	inc	ah ; mov ah, 1
	mov	[SWP_DRV], ax 

	mov	dl, bl
	call	change_current_drive
	jnc	short sysdrive_ok
sysdrive_err:
	mov	dword [u.r0], ERR_DRV_NOT_RDY ; 'drive not ready !'
	jmp	error
sysdrive_ok:
	mov	al, [Current_Drv]
	mov	ah, [Last_DOS_DiskNo]
	mov	[u.r0], eax
	jmp	sysret


sysdir: ; Get Current (Working) Drive & Directory (for user)
	; 30/12/2017 (TRDOS 386 = TRDOS v2.0) 
	;	
        ; INPUT ->
        ;          EBX = Current directory name buffer address
	;		(Buffer length = 92 bytes)
	; OUTPUT ->
	;          AL = Current drive (0=A: .. 2=C:)
	;	   If CF = 1 -> AL = error code
	;
	; Modified Registers: EAX (at the return of system call)
	;
	; Note: Required directory name buffer length may be 
	;	<= 92 bytes for current TRDOS 386 version.
	;	(7*12 name chars + 7 slash + 0)

	mov	ebp, esp
	sub	esp, 96
	push	ebx ; User's buffer address
	xor	dl, dl ; 0 = current drive
  	call	get_current_directory
	jc	short sysdrive_err ; 'drive not ready !' error
	mov	esi, esp ; System's buffer address
	pop	edi  ; User's buffer address
	; ecx = transfer (byte) count (<=92)
	call	transfer_to_user_buffer
	mov	esp, ebp
	jnc	short sysdir_ok
sysdir_err:
	mov	dword [u.r0], ERR_BUFFER  ; 'buffer error !'
	jmp	error
sysdir_ok:	
	mov	cl, [Current_Drv]
	mov	[u.r0], ecx
	jmp	sysret


sysldrvt: ; Get copy of Logical DOS Drive Description Table
	; 30/12/2017 (TRDOS 386 = TRDOS v2.0) 
	;	
        ; INPUT ->
	;	    BL = Logical DOS drive number (zero based)	
        ;          ECX = Logical DOS drv desc table buffer addr
	;		(Buffer length = 256 bytes)
	; OUTPUT ->
	;          cf = 0 -> 
	;		   AL = Current Drive number	
	;		   AH = The Last Logical DOS Drive no.
	;          cf = 1 -> Error code in AL
	;		   AH = The Last Logical DOS Drive no.
	;
	; Modified Registers: EAX (at the return of system call)
	;
	; Note: Required description table buffer length is
	;	256 bytes for current TRDOS 386 version.
	
	mov	edi, ecx ; Destination address (user space)
	mov	ah, bl
	xor	al, al
	mov	esi, Logical_DOSDisks
	add	esi, eax ; Source address (system space)	 
	mov	ecx, 256 ; Byte count 
			 ; Logical Dos Drv Desc Table size
	call	transfer_to_user_buffer
	jc	short sysdir_err
	mov	ch, [Last_DOS_DiskNo]
	jmp	short sysdir_ok


systime: ; Get System Date&Time
	; 30/12/2017 (TRDOS 386 = TRDOS v2.0) 
	;	
        ; INPUT -> BL =
	;	    0 = Get Date&Time in Unix/Epoch format
	;	    1 = Get Time in MSDOS format
	;	    2 = Get Date in MSDOS format
	;	    3 = Get Date&Time in MSDOS format
	;	    4 & other values =
	;		System timer ticks will be returned
	;		in EAX and Carry Flag will be set.
	;		(CF will not be set if BL = 4)	 	
	; OUTPUT ->
	;	For BL input = 3
	;          EAX = Current Time (RTC)
	;		AL = Second (DL in MSDOS)
	;		AH = Minute (CL in MSDOS)
	;	   	HW of EAX = Hour (CH in MSDOS)	
	;	   EDX = Current System Date (RTC)
	;		DL = Day (DL in MSDOS)
	;		DH = Month (DH in MSDOS)
	;		HW of EDX = Year (CX in MSDOS)	
	;
	;	For BL input = 2
	;	   EAX = Current System Date (RTC)
	;		DL = Day (DL in MSDOS)
	;		DH = Month (DH in MSDOS)
	;		HW of EDX = Year (CX in MSDOS)
	;
	;	For BL input = 1
	;          EAX = Current Time (RTC)
	;		AL = Second (DL in MSDOS)
	;		AH = Minute (CL in MSDOS)
	;	   	HW of EAX = Hour (CH in MSDOS)	
	;
	;	For BL input = 0
	;          EAX = Unix (Epoch) Time Ticks/Seconds
	;
	;	For BL input  = 4
	;	   EAX = System timer ticks
	;
	;	If CF = 1 (for other values of BL input)
	;	   EAX = System timer ticks (no error code!)	
	;		
	; Modified Registers: EAX, (EDX)
	;		 (at the return of system call)
	;

	and	bl, bl
	jnz	short systime_1
	call	epoch
systime_0:
	mov	[u.r0], eax
	jmp	sysret
systime_1:
	cmp	bl, 4
	jb	short systime_2
	mov	eax, [TIMER_LH] ; 18.2 Hz timer ticks
				; Note: [TIMER_LH] may be set
				; to wrong timer value due to
				; program functions.
				; (This value must not be
				; accepted as [TIMER_LH]/18.2
				; seconds since the midnight.)
	jna	short systime_0
	mov	[u.r0], eax	
	jmp	error ; cf = 1 & [u.r0] = eax = timer ticks 

systime_2:
	;push	ebx
	call	get_rtc_date_time
	;pop	ebx
	test	bl, 1
	jz	short systime_4
	xor	ah, ah
	mov	al, [hour]
	mov	dl, al
	shl	eax, 16
	mov	al, [second]
	mov	ah, [minute]
	test	bl, 2
	jz	short systime_0
	; Check time & date match risk 
	; (23:59:59 may cause to wrong
	; date -new day with previous date-...)
	cmp	dl, 23
	jb	short systime_3
	cmp	ax, (59*256)+59 ; if hour is 23:59:59
	jnb	short systime_2 ; wait for 1 second
systime_3:
	; eax = time
	mov	esi, eax
systime_4:	
	mov	ax, [year]
	shl	eax, 16
	mov	al, [day]
	mov	ah, [month]
	; eax = date
	and	bl, 1
	jz	short systime_0
	xchg	esi, eax
	; eax = time, esi = date
	mov	ebp, [u.usp]  ; EBP points to user's registers
	; (user) edx <-- (system) esi
	mov	[ebp+20], esi ; return to user with EDX value 
	jmp	short systime_0


sysstime: ; Set System Date&Time
	; 31/12/2017
	; 30/12/2017 (TRDOS 386 = TRDOS v2.0) 
	;	
        ; INPUT -> BL =
	;	    0 = Set Date&Time in Unix/Epoch format
	;	    1 = Set Time in MSDOS format
	;	    2 = Set Date in MSDOS format
	;	    3 = Set Date&Time in MSDOS format
	;	    4 = Set System Timer (Ticks)
	;	    5 = Convert/Save current time to/as
	;		18.2 Hz system timer ticks
	;	    6 = Convert MSDOS Date&Time to UNIX format
	;		without setting system date&time ; (test)
	;	    7 = Convert UNIX Date&Time to MSDOS format
	;		without setting system date&time ; (test)
	;	   8-0FFh = invalid !
	;	  ECX = Time (or Timer) value in selected format  	
	;	  EDX = Date value in MSDOS format if BL=2,3,6	
	; 	
	; OUTPUT ->
	;	If CF = 0 ->
	;          EAX = Set value
	;	If CF = 1 -> (invalid BL input)
	;	   EAX = Ticks count [TIMER_LH]	
	;

	and	bl, bl ; 0
	jnz	short sysstime_0
	mov	eax, ecx
	call	convert_from_epoch
	call	set_rtc_date_time
	jmp	sysret
sysstime_0:
	cmp	bl, 8
	jb	short sysstime_1
	; invalid input (>7)
	mov	eax, [TIMER_LH] ; 18.2 Hz timer ticks
				; Note: [TIMER_LH] may be set
				; to wrong timer value due to
				; program functions.
				; (This value must not be
				; accepted as [TIMER_LH]/18.2
				; seconds since the midnight.)
	mov	[u.r0], eax	
	jmp	error ; cf = 1 & [u.r0] = eax = timer ticks 

sysstime_8:	
	; BL = 7
	mov	eax, ecx ; seconds since 1/1/1970 00:00:00
	call	convert_from_epoch
	xor	ah, ah
	mov	al, [hour]
	shl	eax, 16
	mov	al, [second]
	mov	ah, [minute]
	jmp	short systime_3

sysstime_1:
	cmp	bl, 4
	je	short sysstime_2 ; set system timer ticks	
	cmp	bl, 5
	jne	short sysstime_4
	; convert current time to system timer ticks (18.2Hz)
	call	get_rtc_date_time
	movzx	ecx, byte [hour]
	mov	eax, 60*60 ; 1 hour = 3600 seconds
	mul	ecx
	mov	ebx, eax	
	mov	cl, 60  ; 1 minute = 60 seconds
	movzx	eax, byte [minute]
	mul	ecx
	add	eax, ebx  
	mov	cl, [second]
	add	eax, ecx
	mov	cl, 182
	mul	ecx
	add	eax, 9
	adc	edx, 0
	mov	cl, 10
	div	ecx
	; eax = ((182*seconds)+9)/10
	mov	ecx, eax
sysstime_2:
	mov	[TIMER_LH], ecx ; 18.2 * seconds
sysstime_3:
	mov	[u.r0], ecx
	jmp	sysret
sysstime_4:
	cmp	bl, 6
	ja	short sysstime_8

	mov	[u.r0], ecx

	mov	[second], cl
	mov	[minute], ch
	shr	ecx, 16
	mov	[hour], cl
	; BL = 1,2,3,6
	cmp	bl, 1
	jna	short sysstime_5
	; BL = 2,3,6
	mov	[day], dl
	mov	[month], dh
	shr	edx, 16
	mov	[year], dx
	and	bl, 3
	jz	short sysstime_7 ; 6
	; BL = 2,3
	test	bl, 1
	jz	short sysstime_6 ; 2
	; BL = 3
	call	set_rtc_date_time
	jmp	sysret
sysstime_5:
	; BL = 1
	call	set_time_bcd
	call	set_rtc_time
	jmp	sysret	
sysstime_6:
	; BL = 2
	call	set_date_bcd
	call	set_rtc_date
	jmp	sysret
sysstime_7:
	; BL = 6
	; [year], [month], [day], 
	; [hour], [minute], [second]
	call	convert_to_epoch
	mov	ecx, eax ; seconds since 1/1/1970 00:00:00
	jmp	sysstime_3

sysrename: ; Rename File (or Directory)
	; 31/12/2017 (TRDOS 386 = TRDOS v2.0) 
	;	
        ; INPUT ->
        ;          EBX = File/Directory (ASCIIZ) name address
	;	   ECX = New name (in same dir, no path name)			
	; OUTPUT ->
	;          cf = 0 -> EAX = 0
	;          cf = 1 -> Error code in AL

	cmp	byte [u.uno], 0  ; root (super user) ?
	jna	short sysrename_0

sysrename_perm_err:
	;mov	dword [u.r0], ERR_PERM_DENIED
	mov	eax, ERR_PERM_DENIED ; 'permission denied !'
	mov	[u.r0], eax
	mov	[u.error], eax
	jmp	error

sysrename_0:
	push	ecx ; new file name address (in user space)
	mov	esi, ebx
	; file name is forced, change directory as temporary
	;mov	ax, 1
	;mov	[FFF_Valid], ah ; 0 ; reset
	;call	set_working_path 
	call	set_working_path_x
	jnc	short sysrename_1
	and	eax, eax  ; 0 -> Bad Path!  
	jnz	short sysrename_err
	; eax = 0
sysrename_path_not_found:
	mov	eax, ERR_INV_PATH_NAME ; 'Bad path name !'
sysrename_err:
	pop	ecx ; new file name address (in user space)
sysrename_error:
	mov	[u.r0], eax
	mov	[u.error], eax
	call 	reset_working_path
	jmp	error
sysrename_1:
	mov	al, 08h ; Except volume labels (& long names)
	mov	al, [Attributes]
	and	al, 10h ;
	;mov	esi, FindFile_Name
	;mov	ax, 1800h ; Only files
	;mov	ax, 0810h ; Only directories
	mov	ax, 0800h ; Find File or Directory
	call	find_first_file
	;jnc	short sysrename_2
	jc	short sysrename_err
sysrename_2:
	; ESI = Directory Entry (FindFile_DirEntry) Location
	; EDI = Directory Buffer Directory Entry Location
	; EAX = File Size
	;  BL = Attributes of The File/Directory
	;  BH = Long Name Yes/No Status (>0 is YES)
	;  DX > 0 : Ambiguous filename chars are used

	and	dx, dx ; Ambiguous filename chars used sign (DX>0)
	jz	short sysrename_3
	mov	eax, ERR_INV_FILE_NAME ; 'invalid file name !'
        jmp	short sysrename_err
sysrename_3:
	; EDI = Directory buffer entry offset/address
	; BL = File (or Directory) Attributes 
	; mov	bl, [EDI+0Bh]

	pop	edx ; new file name address (in user space)

	; check file/directory attributes
	test	bl, 7 ; system, hidden, readonly 
        jnz	short sysrename_perm_err
sysrename_4:
	cmp	word [edi+DirEntry_NTRes], 01A1h ; Singlix FS
	je	short sysrename_perm_err ; -temporary!-

	; save old file name & file info (FFF structure)
	mov	esi, FindFile_Drv
	mov	edi, SourceFile_Drv
	mov	ecx, 128/4
	rep	movsd

	mov	esi, edx ; new file name address (in user space)
	mov	edi, DestinationFile_Drv
	call	parse_path_name
	jc	short sysrename_error ; eax = 1 (Bad file name)

	; same drive ? 
	mov	al, [FindFile_Drv]
	cmp	al, [DestinationFile_Drv]
	;jne	short sysrename_perm_err ; Permission denied
	jne	short sysrename_5 ; Bad file name
 
	; no path name !? (rename file in same directory)
	cmp	byte [DestinationFile_Directory], 20h
	jna	short sysrename_6
sysrename_5:
	mov	eax, ERR_BAD_CMD_ARG ; 1 = Bad file name 
				     ; (Bad argument)
	jmp	short sysrename_error
sysrename_6:
	cmp	byte [DestinationFile_Name], 20h
	jna	short sysrename_5

	mov	esi, DestinationFile_Name
	call	check_filename ; is it a valid msdos file name?
	jc	sysrename_error ; 26 = ERR_INV_FILE_NAME 
	
	;mov	esi, DestinationFile_Name
	mov	ax, 0800h ; Find File or Directory
	call	find_first_file
	jc	short sysrename_7
	
	mov	eax, ERR_FILE_EXISTS  ; file already exists !
	jmp	sysrename_error	
sysrename_7:
	; eax = 2 (File not found !)
	cmp	al, 2 ; ERR_NOT_FOUND
	jne	sysrename_error

	; 31/12/2017
	; Following code is also part of 'rename_file' in
	; 'trdosk3.s' (MainProg's 'rename' command) ; 13/11/2017
	mov	esi, DestinationFile_Name ; (Rename_NewName)
	mov	cx, [SourceFile_DirEntryNumber] 
	mov	ax, [SourceFile_DirEntry+20] ; First Cluster, HW 
	shl	eax, 16
	mov	ax, [SourceFile_DirEntry+26] ; First Cluster, LW 
  	movzx	ebx, byte [SourceFile_LongNameEntryLength]  
   	call	rename_directory_entry
	jc	sysrename_error
	;xor	eax, eax
	mov	[u.r0], eax ; 0
	;mov	[u.error], eax
	call 	reset_working_path
	jmp	sysret

sysmem: ; Get Total&Free Memory amount 
	; 31/12/2017 (TRDOS 386 = TRDOS v2.0) 
	;	
        ; INPUT -> 
	;	none	
	; OUTPUT ->
	;	EAX = Total memory count (in bytes)
	;	EBX = Virtually available memory amount (in bytes)
	;	      = 4GB - CORE (4MB)	
	;	ECX = Free memory count (in bytes)
	;	EDX = Calculated free memory count (in bytes)
	
	mov	eax, [memory_size] ; in pages
	shl	eax, 12		   ; in bytes
	mov	[u.r0], eax
	call	calc_free_mem
	; edx = calculated free pages
	; ecx = 0
	mov	ebp, [u.usp]  ; EBP points to user's registers
	mov	dword [ebp+16], ECORE ; EBX (for user)
				; 0FFC00000h ; 4GB - 4MB
	shl	edx, 12
	mov	[ebp+20], edx ; EDX (for user)
	mov 	ecx, [free_pages]
	shl	ecx, 12	; free bytes
	mov	[ebp+24], ecx ; ECX (for user)
	;mov	[free_pages], edx	
	jmp	sysret

sysprompt:
	; Set TRDOS 386 Command Interpreter (MainProg) prompt 
	; 31/12/2017 (TRDOS 386 = TRDOS v2.0) 
	;	
        ; INPUT -> 
	;	EBX = 0 -> use default prompt
	;	EBX > 0 -> prompt string (ASCIIZ) address
	;		  (Max. 11 characters except ZERO tail)	
	; OUTPUT ->
	;	(EAX = 0)
	;	CF = 0 -> Successful
	;	CF = 1 -> Failed

	and	ebx, ebx
	jnz	short sysprompt_0

	call	default_command_prompt ; '['+'TRDOS'+']'
	jmp	sysret

sysprompt_0:
	xor	eax, eax
	mov	[u.r0], eax 
	mov	esi, ebx
	mov	ecx, 12
	mov	ebp, esp
	sub	esp, ecx
	dec	ecx ; 11
	mov	edi, esp
	call	transfer_from_user_buffer
	jc	short sysprompt_err
	cmp	byte [esi], 20h
	jna	short sysprompt_err
	call	set_command_prompt
	mov	esp, ebp
	jmp	sysret
sysprompt_err:
syspath_err:
	mov	esp, ebp
	jmp	error

syspath:
	; Get/Set Run Path
	; 31/12/2017 (TRDOS 386 = TRDOS v2.0) 
	;	
        ; INPUT -> 
	;	EBX = 0 -> get path (to buffer address in ECX)
	;	EBX > 0 -> set path
	;	    EBX = Path string buffer address (ASCIIZ)
	;	    	  (Path description except 'PATH=')
	;	ECX = Buffer address (if EBX = 0)
	;	      (ECX will not be used if EBX > 0)	
	;	DL = Buffer size (0 = 256 byte)	
	;
	; OUTPUT ->
	;	CF = 0 -> Successful (EAX = String length)
	;	CF = 1 -> Failed (EAX = 0)
	;
	; NOTE: 'PATH=' or 'PATH' must be excluded
	;  (It must not be at the beginning of the string.)

	mov	ebp, esp
	sub	esp, 256
	mov	edi, esp

	xor	eax, eax
	mov	[u.r0], eax 
	
	and	ebx, ebx
	jnz	short syspath_0

	; EBX = 0 -> get run path
	mov	ebx, ecx ; buffer addr (in user's mem space)
	mov	esi, Cmd_Path  ; 'PATH' address
	movzx	ecx, dl
	sub	cl, 1 ; 0 -> 255, 1 -> 0
	adc	cx, 1 ; 255 -> 256, 0 -> 1
	; EDI = Output buffer 
	; CX = Buffer length
	; AL = 0 -> use ASCIIZ word in [ESI]
	; ESI = 'PATH' address (with zero tail)
	call	get_environment_string
	jc	short syspath_err
	mov	edi, ebx ; User's buffer address
	mov	esi, esp 
	; EDI = User's buffer address
	; ECX = transfer (byte) count
	call	transfer_to_user_buffer
	jc	short syspath_err
	mov	[u.r0], ecx 
	jmp	sysret

syspath_0:
	mov	esi, ebx
	movzx	ecx, dl
	sub	cl, 1 ; 0 -> 255, 1 -> 0
	adc	cx, 1 ; 255 -> 256, 0 -> 1
	call	transfer_from_user_buffer
	jc	short syspath_err
	;(*) 'PATH=' will be added to 
	;         the head of the string
	sub	esp, 8 ;(*)
	mov	esi, edi ;(*)
	call	set_path_x ;(*)
	jc	short syspath_err
	mov	[u.r0], edx ; run path string length
	jmp	sysret	

sysenv:
	; Get/Set Environment Variables
	; 31/12/2017 (TRDOS 386 = TRDOS v2.0) 
	;	
        ; INPUT -> 
	;	EBX = 0 -> get (all) environment variables
	;	      (Required Buffer length = 512 bytes)
	;	EBX > 0 -> set (one) environment variable
	;	      (If there is not a '=' after
	;	      the environment variable name, it will
	;	      accepted as 'get environment variable'.) 
	;	       EBX = Buffer address
	;	ECX = Buffer address (if EBX = 0)
	;	      (ECX will not be used if EBX > 0)	
	;	      (Note: Buffer size is 512 bytes.)	
	;	DL = Buffer size (0 = 256 byte)
	;	     (For one envrionment variable)		
	;
	; OUTPUT ->
	;	(EAX = 0)
	;	CF = 0 -> Successful (EAX = String length)
	;	CF = 1 -> Failed (EAX = 0)
	;
	; Note: Environment variable name, for example,
	;	'PATH=' must be included at the beginning
	;	of the environment string. If the variable
	;	name is as 'PATH' but it is not as 'PATH='
	;	the variable string (row) will be returned.
	;	If variable name is as 'PATH=' but there is
	;	not a following text after the variable name,
	;	the environment variable will be reset/deleted.

	mov	ebp, esp
	sub	esp, 512
	mov	edi, esp

	xor	eax, eax
	mov	[u.r0], eax 
	
	and	ebx, ebx
	jnz	short sysenv_0

	; EBX = 0 -> get (all) environment variables
	mov	esp, ebp
	mov	esi, Env_Page  ; Environment page
	mov	edi, ecx ; buffer addr (in user's mem space)
	mov	ecx, 512
	call	transfer_to_user_buffer
	jc	error
	mov	[u.r0], ecx 
	jmp	sysret

sysenv_0:
	mov	esi, ebx ; * ; user's buffer address
	movzx	ecx, dl
	sub	cl, 1 ; 0 -> 255, 1 -> 0
	adc	cx, 1 ; 255 -> 256, 0 -> 1
	call	transfer_from_user_buffer
	jc	short sysenv_err
	mov	esi, edi
	mov	al, [esi]
	cmp	al, 20h
	jna	short sysenv_err
	cmp	al, '='
	je	short sysenv_err
	push	esi
sysenv_1:
	inc	esi
	cmp	byte [esi], '='
	je	short sysenv_3
	cmp	byte [esi], 20h
	jnb	short sysenv_1
	mov	byte [esi], 0 
	pop	esi
	; EDI = Output buffer 
	; CX = Buffer length
	xor	al, al
	; AL = 0 -> use ASCIIZ word in [ESI]
	; ESI = Environment variable name address
	call	get_environment_string
	jc	short sysenv_err
	mov	edi, ebx ; * ; user's buffer address
	mov	ecx, eax ; String length
	mov	esi, esp 
	; ESI = system buffer address
	; EDI = User's buffer address
	; ECX = transfer (byte) count
	call	transfer_to_user_buffer
	jc	short sysenv_err
	mov	[u.r0], ecx ; transfer (byte) count
sysenv_2:
	mov	esp, ebp
	jmp	sysret
sysenv_err:
	mov	esp, ebp
	jmp	error
sysenv_3:
	inc	esi
	cmp	byte [esi], 20h
	jnb	short sysenv_3
	mov	byte [esi], 0	
	pop	esi
	call	set_environment_string
	jc	short sysenv_err
	mov	[u.r0], edx
	jmp	short sysenv_2


; temporary - 24/01/2016

iget:
	retn
isintr:
	retn
iopen:
	retn
iclose:
	retn
sndc:
	retn
access:
	retn
sleep:
	retn