; ****************************************************************************
; sh386.s (sh7.s) - Retro Unix 386 v2 Shell - /bin/sh
; ----------------------------------------------------------------------------
;
; RETRO UNIX 386 (Retro Unix == Turkish Rational Unix)
; Operating System Project (v0.3) by ERDOGAN TAN (Beginning: 24/12/2013)
;
; [ Last Modification: 19/11/2015 ] !!! not completed !!!
;
; Derived from UNIX Operating System v7 - osh.c
; Source Code by Ken Thompson (Bell Laboratories, 1979)
;
; ****************************************************************************
; <sh - command interpreter>
; ****************************************************************************

; sh7.s (Retro UNIX 386 v2, Kernel v0.3)

; Assembler: NASM 2.11

; 05/11/2015

; UNIX v1 system calls
_rele 	equ 0
_exit 	equ 1
_fork 	equ 2
_read 	equ 3
_write	equ 4
_open	equ 5
_close 	equ 6
_wait 	equ 7
_creat 	equ 8
_link 	equ 9
_unlink	equ 10
_exec	equ 11
_chdir	equ 12
_time 	equ 13
_mkdir 	equ 14
_chmod	equ 15
_chown	equ 16
_break	equ 17
_stat	equ 18
_seek	equ 19
_tell 	equ 20
_mount	equ 21
_umount	equ 22
_setuid	equ 23
_getuid	equ 24
_stime	equ 25
_quit	equ 26	
_intr	equ 27
_fstat	equ 28
_emt 	equ 29
_mdate 	equ 30
_stty 	equ 31
_gtty	equ 32
_ilgins	equ 33
_sleep	equ 34 ; Retro UNIX 8086 v1 feature only !
_msg    equ 35 ; Retro UNIX 386 v1 feature only !

%macro sys 1-4
    ; 05/11/2015
    ; 03/09/2015	
    ; 13/04/2015
    ; Retro UNIX 386 v1 (& v2) system call method.		
    %if %0 >= 2   
        mov ebx, %2
        %if %0 >= 3    
            mov ecx, %3
            %if %0 = 4
               mov edx, %4   
            %endif
        %endif
    %endif
    mov eax, %1
    int 30h	   
%endmacro

INTR 	equ 2
QUIT 	equ 3
LINSIZ 	equ 1000
ARGSIZ 	equ 50
TRESIZ 	equ 100

QUOTE	equ 80h


; Retro UNIX 386 v1 system call format:
; sys systemcall (eax) <arg1 (ebx)>, <arg2 (ecx)>, <arg3 (edx)>

[BITS 32] ; 32-bit intructions

[ORG 0] 

START_CODE:
	; 05/11/2015 - Retro UNIX 386 v2
	; Assembly source code by Erdogan Tan
	;
	; Source code has been derived from original UNIX v7, osh.c (1979)
	; (Thompson Shell)
	;

main:	; mainc (c, av)
		; esp points to argc
	;mov	ebx, 3  ; file descriptor (UNIX v7 - osh.c)
	mov	ebx, 2 ; UNIX v6 - sh.c
_1:
	sys 	_close	; close file
	cmp 	ebx, 15
	jnb	short _2
	inc	ebx
	jmp	short _1
_2:
	;mov 	ebx, 1	; file descriptor (stdout)
		; 0 = stdin, 1 = stdout, 2 = stderr
	sys	_dup, 1	; duplicate file ; UNIX v6 - sh.c
	cmp	eax, 2  ; stderr
	je	short _3
	;mov	ebx, eax
	sys	_close, eax ; close file
_3:	
	sys	_getpid	; get process id
		; eax = process id
	mov	ebx, pidp + 3 ; 4 digits
	mov	cx, 10
_4:
	sub	edx, edx
	div	cx  ; (dx:ax)/cx
	add	dl, '0'
	mov	ebx, dl ; '0'-'9'
	cmp	ebx, pidp
	jna	short _5
	dec	ebx
	jmp	short _4
_5:
	mov	word [promp], ' %'
	sys	_getuid ; get user id
		; eax = uid
	and	eax, eax
	jnz	short _6
	mov	byte [promp], '#'
_6:
	pop	ecx ; argument count = [esp]
	cmp	cx, 1
	jna	short _10
	pop	eax ; argument 0 addr. (file name)
	mov	ebx, [esp] ; argument 1 address
	cmp	word [ebx], 'e-' ; "-e"
	jne	short _7
	inc	byte [stoperr] 	
		; move arg 0 addr to arg 1 pointer
	mov	[esp], eax
	dec	ecx ; argc = argc - 1
	push	ecx
	mov	ebp, esp ; points to argument count
_7:
	cmp	cx, 1
	jna	short _10
	mov 	esi, esp ; points to argc 
	lodsd
	lodsd
	mov	edx, eax ; argument 0 address
	lodsd
	mov	ebx, eax ; argument 1 address
	mov	word [promp], 0
	mov	al, '-'
	cmp	[ebx], al ; '-' ; argument 1
	jne	short _9
	sub 	ah, ah ; 0
	mov	[edx], ax ; db '-', 0 ; argument 0
	inc	ebx
	cmp	byte [ebx], 'c'
	jne	short _8
	cmp	ecx, 2
	jna	short _8
	lodsd
	mov	[arginp], eax ; argument 2 address
	jmp 	short  _10
_8:
	cmp	byte [ebx], 't'
	jne	short _10
	mov	byte [onelflg], 2
	jmp	short _10		
_9:
	mov	edx, ebx ; new input file name addr.
	sub	ebx, ebx ; file descriptor = 0 
			 ; (standard input)
	sys	_close ; close(0)
	sys	_open, edx, 0 ; open for read
	jnc	short _10
	mov	esi, edx
	call	prs
	mov 	esi, open_err ; ": cannot open"
	mov	eax, 255 ; exit (error) number
	call	err
_10:
	mov	ebx, [edi] ; argument 0 address
	cmp	byte [ebx], '-' ; argument 0 (file name)
	sys	_signal, QUIT, 1
	sys	_signal, INTR, 1
		; eax = return value
	sub	edx, edx ; 0
	cmp	dword [arginp], edx ; 0
	jna	short _11
	cmp	byte [onelflg], dl ; 0
	je	short _12
_11:
	test	al, 1
	jnz	short _13
_12:
	inc 	byte [setintr]
_13: 	
	mov	eax, [esp+8] ; pointer to argument 1
	mov	[dolv], eax ; dolv = v+1
	mov	eax, [esp]   ; argument count
	dec	eax ; dolc = c-1
	mov	[dolc], eax
_loop:
	mov	esi, promp
	cmp	word [esi], 0	
	jna	short _14
	call	prs
_14:
	call	getc
	mov	[peekc], al
	call	main1
	jmp	short _loop

main1:	; main1()
	mov	dword [argp], args
	mov	dword [eargp], args+ARGSIZ-5
	mov	dword [linep], line
	mov	dword [elinep], line+LINSIZ-5
	xor	al, al
	mov	[error], al ; 0
	mov	[gflg], al ; 0
_15:
	mov	esi, [linep]
	call	_word ; word()	
	lodsb
	cmp	al, 0Dh ; ENTER key (\n)
	jne	short _15
	mov	esi, trebuf
	mov	[treep], esi
	add	esi, 4*TRESIZ
	mov	[treeend], esi
	sub	al, al
	cmp	byte [gflg], al ; 0
	ja	short _19
	cmp	byte [error], al ; 0
	ja	short _17
		; Set error return address to _err_retn
	; setjmp(jmpbuf) 
	call	setexit ; UNIX v6 (on 286) - SH.C
_err_retn:
	cmp	byte [error], 0
	ja	short _19
	call	syntax
_16:
	cmp	byte [error], 0
	jna	short _18
_17
	mov 	esi, syntax_err ; "syntax error"
	mov	eax, 255 ; exit (error) number
	call	err
	retn	
_18:
	call	execute
_19:
	retn

_word:	; word()
	mov	edi, [argp]  ; current arg pointer address
	mov	eax, [linep] ; current arg line address
	stosd		     ; argument address in edi 	
	mov	[argp], edi  ; pointer to argument pointer
__loop:
	call	getc
		; switch-case ' \t\'"'
	cmp	al, ' ' ; space
	je	short __loop
	cmp	al, 09h ; \t (tab key)	
	je	short __loop
	cmp	al, "'" ; single quote
	je	short _20
	cmp	al, '"' ; double quote
	jne	short _23
_20:
	mov	ah, al
_21:
	call 	readc
	cmp	al, ah
	je	short pack
	cmp	al, 0Dh ; \n (Carriage Return)
	jne	short _22
	inc	byte [error]
	mov	[peekc], al
	retn
_22:
	mov 	edi, [linep]
	or	al, QUOTE
	stosb
	mov	[linep], edi
	jmp	short _21
_23:
	call	anyc1	; switch-case "&;<>()|^\n"
	jne	short _25
_24:
	mov	edi, [linep] ; argument line address
	stosb
	mov	[linep], edi
	mov	byte [edi], 0 ; \0 (string terminator)
	retn	
_25:
	mov	[peekc], al

pack: 
	call	getc  ; c = getc()
	call	anyc0 ; any(c, " '\"\t;&<>()|^\n")
	jne	short _26
	mov	[peekc], al
			; any(c, "\"'") 
	cmp 	al, '"'	; Double quote
	je	short __loop
	cmp 	al, "'"	; Single quote
	je	short __loop
	mov	byte [linep], 0 ; \0
	inc	byte [linep]
	retn	
_26:
	mov 	edi, [linep]
	stosb
	mov	[linep], edi
	jmp	short pack

anyc0:
	cmp 	al, ' '	; Space
	je	short anyc2
	cmp 	al, "'"	; Single quote
	je	short anyc2
	cmp 	al, '"'	; Double quote
	je	short anyc2
	cmp 	al, 09h ; \t (Tab)
	je	short anyc2
anyc1:
	cmp 	al, '&'
	je	short anyc2
	cmp 	al, ';'
	je	short anyc2
	cmp 	al, '<'
	je	short anyc2
	cmp 	al, '>'
	je	short anyc2
	cmp 	al, '('
	je	short anyc2
	cmp 	al, ')'
	je	short anyc2
	cmp 	al, '|'
	je	short anyc2
	cmp 	al, '^'
	je	short anyc2
	cmp 	al, 0Dh ; \n (Carriage Return, ENTER key)
anyc2:
	retn

tree:	; tree(n)
	add	eax, [treep] ; t
	cmp	eax, [treeend]
	jna	short _27
	mov	esi, cmdl_overflow ; "Command line overflow\n"
	call	prs
	inc	byte [error]
	; longjmp(jmpbuf, 1)
	jmp	short reset ; UNIX v6 (on 286) - SH.C	
_27:
	retn	; return(t)

getc:	; getc()
	
	cmp 	byte [peekc], 0
	jna 	short _28
	sub	al, al ; 0
	xchg	al, [peekc]
	retn
_28:
	mov	ebx, [argp]
	cmp	ebx, [eargp]
	jna	short _30
	sub 	dword [argp], 10
_29:		; while((c=getc()) != '\n')
	call	getc
	cmp	al, 0Dh ; \n
	jne	short _29
	add 	dword [argp], 10
	mov 	esi, many_args_err
	mov	eax, 255 ; exit (error) number
	call	err ; err("Too many args",255)
	inc	byte [gflg]
	retn	; return(c)

_30:
	mov	edx, [linep]
	cmp	edx, [elinep]
	jna	short getd
	sub 	dword [linep], 10
_31:		; while((c=getc()) != '\n')
	call	getc
	cmp	al, 0Dh ; \n
	jne	short _29
	add 	dword [linep], 10
	mov 	esi, many_chars_err
	mov	eax, 255
	call	err ; err("Too many characters",255)
	inc	byte [gflg]
	retn	
getd:
	mov	ebx, [dolp]
	and	ebx, ebx
	jz	short _33
	mov	al, [ebx]
	or	al, al
	jz	short _32
	inc	byte [dolp]
	retn	; return(c)
_32:	
	sub	ebx, ebx ; 0
	mov	[dolp], ebx
	retn
_33:
	call	readc ; c = readc() 
	cmp	al, '\'
	jne	short _35
	call	readc
	cmp	al, 0Dh ; \n
	jne	short _34
	mov	al, ' ' ; 20h, space
	retn
_34:
	or	al, QUOTE
	retn
_35:	
	cmp	al, '$'
	jne	short _37
	call	readc
	cmp	al, '0' ; '$0'
	jb	short _36
	cmp	al, '9' ; '$9'
	ja	short _36
	sub	al, '0'
	cmp	al, dolc
	jnb	short getd
	movzx	ebx, al
	shl	bl, 2 ; 4 * al (0 to 9)
	mov	edx, [ebx+dolv]
	mov	[dolp], edx
	jmp	short getd
_36:
	cmp	al, '$'
	jne	short _37
	mov	edx, pidp
	mov	[dolp], edx
	jmp	short getd
_37:
	and	al, 7Fh
	retn

readc:	; readc()
	mov	ebx, [arginp]
	and	ebx, ebx ; 0 ?
	jz	short _42
	dec	ebx      ; 1 ?
	jnz	short _40
_38:
	mov	bl, [errval]
_39:
	; ebx = return code (error code)
	sys	_exit, ebx
	; xxx !!!
_40:
	inc 	ebx
	mov	al, [ebx] 	
	or	al, al
	jnz	short _41
	inc	al
	mov	[arginp], al ; 1
	mov	al, 0Dh ; \n (Carriage Return)
	retn	
_41:
	inc 	ebx
	mov	[arginp], ebx
	retn
_42:
	mov 	edx, ebx ; 0
	inc	dl ; 1
	cmp	[onelflg], dl ; 1 ?
	jne	short _44
_43:
	dec	bl ; 255
	; ebx = return code (error code)
	jmp	short _39
_44:
	mov	ecx, cc
	;sys	_read, 0, cc, edx
	sys	_read  ; read 1 byte from stdin
	jc	short _43
	cmp	eax, edx ; 1 byte ?
	jb	short _38 ; end of file
	mov	al, [cc]
	cmp	al, 0Dh  ; \n	
	jne	short _45
	cmp	[onelflg], dl ; 1 ?
	jb	short _45
	dec	byte [onelflg]
_45:
	retn
_46:
	dec	bl	; 255 ; error code
	jmp	short 39


err:	; err(s, exitno)
	
	; esi = error message address
	; eax = error code (number)
	
	push	eax
	call	prs
	mov	esi, nl ; next line
	call	prs
	cmp	word [promp], 0
	ja	short _47
	sys 	_seek, 0, 0, 2 ; move file ptr to EOF
	pop	ebx ; error code
	sys	_exit
	; xxx !!!
_47:
	retn

prs:	; prs(as)

	; esi = ASCIIZ string (text) address

	lodsb
	jz	short _48
	call	putc
	jmp	short prs
_48:
	retn

putc:	; putc(c)

	mov 	[cc], al
	sys	_write, 2, cc, 1 ; write char to stderr
	retn


prn:	; prn(n)

	mov	ecx, 10
_49:	
	xor	edx, edx ; 0
	div	ecx 	 ; (edx:eax) / ecx
			 ; eax = quotient, edx = remainder
	xchg 	eax, edx ; quotient in EDX (n/10)
			 ; remainder in EAX (n%10)
	add	al, '0'
	call	putc
	mov	eax, edx
	or	eax, eax
	jnz	short _49
	retn
				


setexit:
	; set return address after an error
	;
	; Derived from UNIX v6 (on 286) - SH.C
	;	& RESET.ASM  (_setexit)
	pop	eax
	push	eax
	mov	[_setex], eax
	retn

reset:
	; return after an error (to 'setexit' return address)
	;
	; Derived from UNIX v6 (on 286) - SH.C
	;	& RESET.ASM  (_reset)

	pop	eax ; return address
	mov	eax, [_setex]
	push	eax
	retn


_setex: ; UNIX v6 (on 286) - SH.C (& RESET.ASM)
	dd 0	

dolp:	
	dd 0
dolc:
	dd 0
dolv:
	dd 0
pidp:	
	dd 0
	db 0
errval:
	db 0	
acname: 
	db "<none>", 0
cc:
	db 0
promp:
	db "% ", 0
stoperr:
	db 0

arginp:
	dd 0
onelflg:
	db 0
setintr:
	db 0
peekc:
	db 0

error:
	db 0
gflg:
	db 0

align 4

argp:
	dd 0
eargp:
	dd 0
linep:
	dd 0
elinep:
	dd 0
treep:
	dd 0
treeend:
	dd 0
arginp:
	dd 0

line:
	times LINSIZ db 0 ; char	
args:	
	times ARGSIZ db 0 ; char
trebuf:
	times TRESIZ dd 0 ; int

open_err:
	db ": cannot open"," !", 0

syntax_err:
	db "syntax error"," !", 0

cmdl_overflow:
	db "Command line overflow"," !"

nl:	; next line
	db 0Dh, 0Ah, 0

many_args_err:
	db "Too many args", " !", 0 

many_chars_err:
	db "Too many characters", " !", 0
	

;/ sh -- command interpreter
	;mov 	byte [_echo], 1 ; 06/12/2013
	mov	ebp, esp
		; mov sp,r5
	mov	[shellarg], ebp
		; mov r5,shellarg / save orig sp in shellarg
	mov	ebx, [ebp+4]
	cmp	byte [ebx], '-'
		 ; cmpb	*2(r5),$'- / was this sh called by init or loginx~
	jne	short s1
		; bne 2f / no
	sys	_intr, 0
		; sys intr; 0 / yes, turn off interrupts
	sys	_quit, 0
		; sys quit; 0
	sys	_write, 1, msg_unix_sh, msgsh_size
s1: ;2:
	sys	_getuid
		; sys	getuid / who is user
	;and	eax, eax
	and	al, al
		; tst r0 / is it superuser
	jnz	short s2
		; bne 2f / no
	mov	byte [_at], '#'
		; movb $'#,at / yes, set new prompt symbol
s2: ;2:
	cmp	dword [ebp], 1
		; cmp (r5),$1 / tty input?
	jna	short newline
		; ble newline / yes, call with '-' (or with no command
		      ; / file name)
	xor	ebx, ebx
		; clr r0 / no, set tty
	sys	_close
		; sys close / close it
	mov	ebx, [ebp+8] ; arg 1
		; mov 4(r5),0f / get new file name
	xor	ecx, ecx ; arg 2
	sys	_open
		; sys open; 0:..; 0 / open it
	jnc	short s3
		; bec 1f / branch if no error
	mov	esi, msgNotFound
	call	error
		; jsr r5,error / error in file name
		; <Input not found\n\0>; .even
	sys	_exit
		; sys exit
s3: ;1:
	mov	byte [_at], 0
		; clr at / clear prompt character, if reading non-tty
		   ; / input file
	jmp	short newcom
newline:
	cmp	byte [_at], 0
		; tst at / is there a prompt symbol
	jna	short newcom
		; beq newcom / no
		; mov $1,r0 / yes
nl:
	sys	_write, 1, prompt, p_size
	;sys	_write, 1, _at, 2
		; sys write; at; 2. / print prompt
newcom:
	mov	esp, [shellarg]
		; mov shellarg,sp /
	mov	esi, parbuf 
		; mov $parbuf,r3 / initialize command list area
	mov	edi, parp
		; mov $parp,r4 / initialize command list pointers
	xor	eax, eax
	mov	[infile], eax ; 0	
		; clr infile / initialize alternate input
	mov	[outfile], eax ; 0
		; clr outfile / initialize alternate output
	mov	[glflag], al ; 0
	;mov	[glflag], ax ; 0
		; clr glflag / initialize global flag
newarg:
	call	blank
		; jsr pc,blank / squeeze out leading blanks
	call	delim
	je	short nch4 ; '\n', ';', '&'
		; jsr r5,delim / is new character a ; \n or &
		;     br 2f / yes
	push	esi
		; mov r3,-(sp) / no, push arg pointer onto stack
	cmp	al, '<'
		; cmp r0,$'< / new input file?
	jne	short na1
		; bne 1f / no
	mov	[infile], esi
		; mov (sp),infile / yes, save arg pointer
	jmp	short na2
	;mov	dword [esp], 0
		; clr (sp) / clear pointer
	;jmp	short nch1
		; br 3f
na1: ;1:
	cmp	al, '>'
		; cmp r0,$'> / new output file?
	jne	short nch0
	;jne	short newchar
		; bne newchar / no
	mov	[outfile], esi
		; mov (sp),outfile / yes, save arg pointer
na2:	
	mov	dword [esp], 0
		; clr (sp) / clear pointer
	jmp	short nch1
		; br 3f
newchar:
	cmp	al, 20h
		; cmp $' ,r0 / is character a blank
	je	short nch2
		; beq 1f / branch if it is (blank as arg separator)
        cmp     al, 8Dh ; 128 + 13
	je	short nch2
		; cmp $'\n+200,r0 / treat \n preceded by \
		; beq 1f / as blank
nch0:
	call	putc
		; jsr pc,putc / put this character in parbuf list
nch1: ;3:
	call	getc
		; jsr pc,getc / get next character
	call	delim
	jne	short newchar
	;jz	short nch2 ; '\n', ';', '&'
		; jsr	r5,delim / is char a ; \n or &,
		;	br 1f / yes
	;jmp	short newchar
		; br newchar / no, start new character tests
nch2: ;1:
	mov	byte [esi], 0
	inc	esi
		; clrb (r3)+ / end name with \0 when read blank, 
			  ; or delim
	pop	ebx
	mov	[edi], ebx
		; mov (sp)+,(r4)+ / move arg ptr to parp location
	or	ebx, ebx
	jz	short nch3
	;jnz	short nch3
		; bne 1f / if (sp)=0, in file or out file points
				; to arg
	add	edi, 4
		; tst -(r4) / so ignore dummy (0), in pointer list
nch3: ;1:
	call	delim
	jne	short newarg
	;jz	short nch4 ; '\n', ';', '&'
		; jsr r5,delim / is char a ; \n or &.
		;     br 2f / yes
	;jmp	short newarg
		; br newarg / no, start newarg processing
nch4: ;2:
	mov	dword [edi], 0
		; clr (r4) / \n, &, or ; takes to here 
			 ; / (end of arg list) after 'delim' call
	push	eax
		; mov r0,-(sp) / save delimiter in stack
	call	docom
		; jsr pc,docom / go to exec command in parbuf
	cmp	byte [esp], '&'
		; cmpb (sp),$'& / get a new command without wait?
        je      newcom
		; beq newcom / yes
	and	edx, edx
		; tst r1 / was chdir just executed or line ended
			; / with ampersand?
	jz	short nch6
		; beq 2f / yes
nch5: ;1:
	sys	_wait
		; sys wait / no, wait for new process to terminate
                        ; / command executed)
	jc	short nch6
		; bcs 2f / no, children not previously waited for
	cmp	eax, edx
		; cmp r0,r1 / is this my child
	jne	short nch5
		; bne 1b
nch6: ;2:
	cmp	byte [esp], 0Dh
	;cmp	byte [esp], 0Ah
		; cmp (sp),$'\n / was delimiter a new line
        je      newline
		; beq newline / yes
        jmp     newcom
		; br newcom / no, pick up next command
docom:
	sub	edi, parp
		; sub $parp,r4 / out arg count in r4
	jne	short dcom1
		; bne 1f / any arguments?
dcom0:
	sub 	edx, edx ; 0
		; clr r1 / no, line ended with ampersand
	retn
		; rts pc / return from call
dcom1: ;1:
	mov	ebx, edi
	; 06/12/2013
	mov	esi, qecho 
	call	chcom
	jnz	short dcom7
	cmp	bl, 8 ; 8 = arg count x 4 (24/08/2015)
	jne	short dcom8
	mov	ebx, [parp+4]
	cmp	byte [ebx], 'o'
	jne	short dcom9
	inc	ebx
	cmp	byte [ebx], 'n'
	jne	short dcom10
	inc	ebx
	cmp	byte [ebx], 0
	ja	short dcom9
	mov	byte [_echo], 1
	jmp	short dcom0
dcom10: ; 06/12/2013	
	cmp	word [ebx], 'ff'
	jne	short dcom9
	inc	ebx
	inc	ebx
	cmp	byte [ebx], 0
	ja	short dcom9
	mov	byte [_echo], 0
	jmp	short dcom0		
dcom9: ; 06/12/2013
	mov	esi, msgNoCmd
	call	error
	jmp	short dcom0
dcom7:
	mov	esi, qchdir
	call	chcom
	jnz	short dcom4
		; jsr r5,chcom; qchdir / is command chdir?
		;     br 2f / command not chdir
dcom12:
	cmp	bl, 8 ; 8 = arg count x 4 (24/08/2015)
	;cmp	bx, 8
		; cmp r4,$4 / prepare to exec chdir, 
			  ; 4 = arg count x 2
	je	short dcom2
		; beq 3f
dcom8:
	mov	esi, msgArgCount
	call	error
		; jsr r5,error / go to print error
		;  	<Arg count\n\0>; .even
	;jmp	short dcom3
		; br 4f
	jmp	short dcom0
dcom2: ;3:
	mov	ebx, [parp+4]
		;mov parp+2,0f / move directory name to sys call
	sys	_chdir
		; sys chdir; 0:0 / exec chdir
	jnc	short dcom3
		; bec 4f / no error exit
	mov	esi, msgBadDir
	call	error
		; jsr r5,error / go to print error
		; 	<Bad directory\n\0>; .even
				; / this diagnostic
dcom3: ;4:
	xor	edx, edx ; 0
		; clr r1 / set r1 to zero to skip wait
	retn
		; rts pc / and return
dcom4: ;2:
	; 06/12/2013
	mov	esi, qcd
	call	chcom
	jz	short dcom12
dcom11:
	mov	esi, glogin
	call	chcom
	jnz	short dcom5
		; jsr r5,chcom; glogin / is command login?
		;     br 2f / not loqin, go to fork
	sys	_exec, parbuf, parp
		; sys exec; parbuf; parp / exec login
	sys	_exec, binpb, parp
		; sys exec; binpb; parp / or /bin/login
dcom5: ;2: / no error return??
	mov	ebx, newproc
	; child process will return to 'newproc' address
	sys	_fork
		; sys fork / generate sh child process
			 ; for command
                ;     br newproc / exec command with 
			 ; new process
	; parent process will return here
	jnc	short dcom6
		; bec 1f / no error exit, old process
	mov	esi, msgTryAgain
	call	error
		; jsr r5,error / go to print error
		;     <Try again\n\0>; .even / this diagnostic
        jmp     newline
		; jmp newline / and return for next try
dcom6: ;1:
	mov	edx, eax ; child process ID
		; mov r0,r1 / save id of child sh
	retn
		; rts pc / return to "jsr pc, docom" call
			; in parent sh
error:
	sys	_write, 1, nextline, 2
s4:
	lodsb
	mov	[och], al
                ; movb (r5)+,och / pick up diagnostic character
	and	al, al
	jz	short s5
		; beq 1f / 0 is end of line
	sys	_write, 1, och, 1
		; mov $1,r0 / set for tty output
		; sys write; och; 1 / print it
	jmp	short s4
	;jmp	short error
		; br error / continue to get characters
s5: ;1:
	;inc	esi
		; inc r5 / inc r5 to point to return
	;;and	si, 0FFFEh
	;shr	esi, 1
	;shl	esi, 1
		; bic $1,r5 / make it even
	sys	_seek, 0, 0, 2	
		; clr r0 / set for input
		; sys seek; 0; 2 / exit from runcom. skip to
			       ; / end of input file
	retn
		;; ((/ rts r5)) 
		;; (not in original unix v1 'sh.s')

chcom: ; / has no effect if tty input
		; mov (r5)+,r1 / glogin gchdir r1, bump r5
	mov	edi, parbuf
		; mov $parbuf,r2 / command address  r2 'login'
s6: ;1:
	lodsb
		; movb (r1)+,r0 / is this command 'chdir'
	scasb
                ; cmpb (r2)+,r0 / compare command name byte
                              ; / with 'login' or 'chdir'
	jne	short s7
		; bne 1f / doesn't compare
	or	al, al
		; tst r0 / is this
	jnz	short s6
		; bne 1b / end of names
		; tst (r5)+ / yes, bump r5 again to execute 
			; / login or chdir
s7: ;1:
	retn
		; rts r5 / no, return to exec command

putc:
	cmp	al, 27h ; '
		; cmp r0,$'' / single quote?
	je	short pch1	
		; beq 1f / yes
	cmp	al, 22h ; "
		; cmp r0,$'" / double quote
	je	short pch1
		; beq 1f / yes
	and	al, 7Fh
		; bic $!177,r0 / no, remove 200, if present
	mov	[esi], al
	inc	esi
		; movb r0,(r3)+ / store character in parbuf
	retn
		; rts pc
pch1: ;1:
	push	eax
		; mov r0,-(sp) / push quote mark onto stack
pch2: ;1:
	call	getc
		; jsr pc,getc / get a quoted character
	cmp	al, 0Dh
	;cmp	al, 0Ah ; \n
		; cmp r0,$'\n / is it end or line
	jne	short pch3
		; bne 2f / no
	mov	esi, msgImbalance
	call	error
		; jsr r5,error / yes, indicate missing
			      ; quote mark
		;     <"' imbalance\n\0>; .even
        jmp     newline
		; jmp newline / ask for new line
pch3: ;2:
	cmp	[esp], al
		; cmp r0,(sp) / is this closing quote mark
	je	short pch4
		; beq 1f / yes
	and	al, 7Fh
		; bic $!177,r0 / no, strip off 200
			      ; if present
	mov	[esi], al
	inc	esi
		; movb r0,(r3)+ / store quoted character
			      ; in parbuf
	jmp	short pch2
		; br 1b / continue
pch4: ;1:
	pop	eax
		; tst (sp)+ / pop quote mark off stack
	retn
		; rts pc / return

; / thp`e new process

newproc:
	mov	esi, [infile]
	or	esi, esi
	jz	short np2
		; mov infile,0f / move pointer to new file name
		; beq 1f / branch if no alternate read file given
	cmp 	byte [esi], 0
		; tstb *0f
	jna	short np1
		; beq 3f / branch if no file name given
	sys	_close, 0
		; clr r0 / set tty input file name
		; sys close / close it
	sys	_open, esi, 0  
		; sys open; 0:..; 0 / open new input file 
				  ; for reading
	jnc	short np2
		; bcc 1f / branch if input file ok
np1: ;3:
	mov	esi, msgInputFile
	call	error
		; jsr r5,error / file not ok, print error
		;     <Input file\n\0>; .even / this diagnostic
	sys	_exit
		; sys exit / terminate this process 
			  ; and make parent sh
np2: ;1:
	mov	esi, [outfile]
		; mov outfile,r2 / more pointer to new file name
	and	esi, esi
	jz	short np6
		; beq 1f / branch if no alternate write file
	cmp	byte [esi], '>'
		; cmpb (r2),$'> / is > at beqinning of file name?
	jne	short np3
		; bne 4f / branch if it isn't
	inc	esi
		; inc r2 / yes, increment pointer
	sys	_open, esi, 1
		; mov r2,0f
		; sys open; 0:..; 1 / open file for writing
	jnc	short np5
		; bec 3f / if no error
np3: ;4:
	sys	_creat, esi, 15 ; Decimal 15 = Octal 17
		; mov r2,0f
		; sys creat; 0:..; 17 / create new file 
				    ; with this name
	jnc	short np5
		; bec 3f / branch if no error
np4: ;2:
	mov	esi, msgOutputFile
	call	error
		; jsr r5,error
		;     <Output file\n\0>; .even
	sys	_exit
		; sys exit
np5: ;3:
	sys	_close, eax
		; sys close / close the new write file
		; mov	r2,0f / move new name to open
	sys	_close, 1
		; mov $1,r0 / set tty file name
		; sys close / close it
	sys	_open, esi, 1
		; sys open; 0:..; 1 / open new output file,
			      ; /it now has file descriptor 1
	sys	_seek, eax, 0, 2
		; sys seek; 0; 2 / set pointer to 
				; current end of file
np6: ;1:
	cmp	byte [glflag], 0
		; tst glflag / was *, ? or [ encountered?
	ja	short np9
		; bne 1f / yes
	sys	_exec, parbuf, parp
		; sys exec; parbuf; parp / no, execute
					;  this command
	sys	_exec, binpb, parp
		; sys exec; binpb; parp / or /bin/this command
np7: ;2:
	sys	_stat, binpb, inbuf
		; sys stat; binpb; inbuf / if can't execute
					; / does it exist?
	jc	short np8
		; bes 2f / branch if it doesn't
	mov	esi, parp-4
	mov	dword [esi], shell
		; mov $shell,parp-2 / does exist,
				   ;  not executable
	mov	eax, binpb
	mov	[parp], eax
		; mov $binpb,parp / so it must be
	sys	_exec, shell, esi 
		; sys exec; shell; parp-2 / a command file,
		; / get it with sh /bin/x (if x name of file)
np8: ;2:
	mov	esi, msgNoCmd
	call	error
		; jsr r5,error / a return for exec 
			       ; is the diagnostic
		;     <No command\n\0>; .even
	mov	esp, [shellarg]
	sys 	_exit
		; sys exit
np9: ;1:
	mov	esi, parp-4
	mov	dword [esi], glob
		; mov $glob,parp-2 / prepare to process *,?
	sys	_exec, glob, esi
		; sys exec; glob; parp-2
			    ; / execute modified command
	jmp	short np8
		; br 2b

delim:
        cmp     al, 0Dh ; carriage return
	je	short dlim2
	;cmp	al, 0Ah
		; cmp r0,$'\n / is character a newline
	;je	short dlim2
		; beq 1f
	cmp	al, '&'
		;cmp r0,$'& / is it &
	je	short dlim2
		; beq 1f / yes
	cmp	al, ';'
		; cmp r0,$'; / is it ;
	je	short dlim2
		; beq 1f / yes
	cmp	al, '?'
		; cmp r0,$'? / is it ?
	je	short dlim1
		; beq 3f
	cmp	al, '['
		; cmp r0,$'[ / is it beginning of character string
		      ; / (for glob)
	jne	short dlim2
		; bne 2f
dlim1: ;3:
	inc	byte [glflag]
		; inc glflag / ? or * or [ set flag
;2:
	;tst	(r5)+ / bump to process all except \n,;,&
dlim2: ;1:
	; zf = 1 if the char is '\n' or ';' or '&'
	retn
		; rts r5

blank:
	call	getc
		; jsr pc,getc / get next character
	cmp	al, 20h
		; cmp $' ,r0 / leading blanks
	je	short blank
		; beq blank / yes, 'squeeze out'
	cmp	al, 8Dh ; 80h + 0Dh
        ;cmp     al, 8Ah ; 80h + 0Ah
	je	short blank
	        ; cmp r0,$200+'\n / new-line preceded by \
				 ;  is translated
		; beq blank / into blank
	retn
		; rts pc
getc:
	cmp	dword [param], 0
		; tst param / are we substituting for $n
	ja	short gch3
		; bne 2f/ yes
gch0:
        mov     ebx, [inbufp]
		; mov inbufp,r1 / no, move normal input pointer to r1
s8:
	cmp	ebx, [einbuf]
		; cmp r1,einbuf / end of input line?
	jb	short gch1
		; bne 1f / no
	call	getbuf	
		; jsr pc,getbuf / yes, put next console line
				;  in buffer
	jmp	short gch0
		; br getc
gch1: ;1:
	mov	al, [ebx]
	inc	ebx
		; movb (r1)+,r0 / move byte from input buffer to r0
	mov	[inbufp], ebx
		; mov r1,inbufp / increment routine
	or	al, [escap]
	;or	ax, escap
		; bis escap,r0 / if last character was \ this adds
		        	; / 200 to current character
	;mov	byte [escap], 0
	;mov	word [escap], 0
		; clr escap / clear, so escap normally zero
	cmp	al, '\'
		; cmp r0,$'\\ / note that \\ is equal \ in as
	je	short gch2
		; beq 1f
	mov	byte [escap], 0
	cmp	al, '$'
		; cmp r0,$'$ / is it $
	je	short gch5
		; beq 3f / yes
	retn
		; rts pc / no
gch2: ;1:
        mov     byte [escap], 80h
	;mov	word [escap], 128
		; mov $200,escap / mark presence of \ in command line
	jmp	short s8
	;jmp	short gch0
		; br getc / get next character
gch3: ;2:
	mov	ebx, [param]
	mov	al, [ebx]
		; movb *param,r0 / pick up substitution character
				; / put in r0
	or	al, al
	jz	short gch4
		; beq	1f / if end of substitution arg, branch
	inc	dword [param]
		; inc param / if not end, set for next character
	retn
		; rts pc / return as though character in ro is normal
		       ; / input
gch4: ;1:
	mov	dword [param], 0
		; clr param / unset substitution pointer
	jmp	short gch0
		; br getc / get next char in normal input
gch5: ;3:
	call	gch0
	;call	getc
		; jsr pc,getc / get digit after $
	sub	al, '0'
		; sub $'0,r0 / strip off zone bits
	cmp	al, 9
		; cmp r0,$9. / compare with digit 9
	jna	short gch6 
		; blos 1f / less than or equal 9
	mov	al, 9
		; mov $9.,r0 / if larger than 9, force 9
gch6: ;1:
	mov	ebx, [shellarg]
		; mov shellarg,r1 / get pointer to stack for
		           ; / this call of shell
	movzx 	eax, al	; al->eax
	inc	al
	;inc	eax
		; inc r0 / digit +1
	cmp	eax, [ebx]
		; cmp r0,(r1) / is it less than # of args 
			      ;	in this call
	jnb	short gch0
		; bge getc / no, ignore it. so this $n is not replaced
	shl	al, 2 ; multiply by 4 (24/08/2015)
	;shl	al, 1
	;;shl	ax, 1
		; asl r0 / yes, multiply by 2 (to skip words)
	add	ebx, eax
		; add r1,r0 / form pointer to arg pointer (-2)
	mov	eax, [ebx+4]
	mov	[param], eax
		; mov 2(r0),param / move arg pointer to param
        jmp     getc
		; br getc / go to get substitution arg for $n
getbuf:
	mov	ecx, inbuf
		; mov $inbuf,r0 / move input buffer address
        mov     [inbufp], ecx
		; mov r0,inbufp / to input buffer pointer
	mov	[einbuf], ecx
		; mov r0,einbuf / and initialize pointer to end of
		         ; / character string
	dec	ecx
		; dec r0 / decrement pointer so can utilize normal
		       ; / 100p starting at 1f
		; mov r0,0f / initialize address for reading 1st char
	mov	edx, 1
gbuf0: ;1:
	inc	ecx
		; inc 0f / this routine filles inbuf with line from
		       ; / console - if there is cnc
	push	ecx
	; edx = 1
	sys	_read, 0, och
	pop	ecx
	;xor	ebx, ebx ; 0
	;sys	_read ; sys _read, ebx, ecx, edx ; ebx = 0, edx = 1
		; sys read; 0:0; 1 / read next char into inbuf
        jc      xit1
		; bcs xit1 / error exit
	and	eax, eax
		; tst r0 / a zero input is end of file
        jz      xit1
		; beq xit1 / exit
	inc	dword [einbuf]  ; 08/04/2014 (24/08/2015, 32 bit)
	mov	al, [och]
	cmp	byte [_at], 0
	jna	short gbuf1
	cmp	al, 8 ; backspace
	je	short gbuf3
	cmp	al, 127 ; delete
	je	short gbuf6 ; 06/12/2013
gbuf1:
	;mov	ebx, ecx
	;inc	dword [einbuf]
		; inc einbuf / eventually einbuf points to \n
		       ; / (+1) of this line
	cmp	ecx, inbuf + 256
		; cmp 0b,$inbuf+256. / have we exceeded 
				   ; input buffer size
        jnb     xit1
		; bhis xit1 / if so, exit assume some sort of binary
	; 08/04/2014
	cmp	al, 0Dh
	jne	short gbuf8
	mov	ebx, [einbuf]
	dec	ebx
	mov	[ebx], al
	retn
gbuf8:
	mov	ebx, ecx
	mov	[ebx], al
	;cmp	al, 0Ah ; \n
		; cmpb	*0b,$'\n / end of line?
	;je	short  gbuf5
	;jne	short gbuf1
		; bne 1b / no, go to get next char
	;cmp	al, 0Dh ; ENTER
	;je	short gbuf5
	cmp	byte [_at], 0 ; at > 0 --> tty input
	jna	short gbuf0
	cmp	al, 1Bh	; ESC
	jne	short gbuf2
	mov	eax, inbuf
	mov	[inbufp], eax
	mov	[einbuf], eax
        jmp     nl  ; cancel current command, new line
gbuf2:
	 ; 06/12/2013
	cmp	byte [_at], 0
	ja	short gbuf7
	cmp 	byte [_echo], 0
        jna     gbuf0
gbuf7:
	push	ecx
	;mov	[och], al
	; edx = 1
	sys	_write, 1, och
	;sys	_write, 1, och, 1  ; echo (write char on tty)
	pop	ecx
        jmp     gbuf0
gbuf6: ; DELETE key -> BACKSPACE key
	; mov 	al, 8
	mov	byte [och], 8 ; 06/12/2013
gbuf3:
	; 08/04/2014
	dec	dword [einbuf] ; (24/08/2015, 32 bit code)
	; 12/12/2013
	dec	ecx
	cmp	ecx, inbuf
	jb	short gbuf4
	dec 	ecx
	; 08/04/2014
	;jmp	short gbuf2
	jmp 	short gbuf7
gbuf4:
	;mov 	al, 7
	mov	byte [och], 07h ; beep
	; 08/04/2014
	;jmp	short gbuf2
	jmp 	short gbuf7
;gbuf5:
;	retn
		; rts pc / yes, return

xit1:
	sys	_exit
		; sys exit

;quest:
	;db '?', 0Dh, 0Ah
	;<?\n>

prompt:
	db 0Dh, 0Ah
_at:
	db '@ '
	;<@ >
p_size  equ $ - prompt

; 06/12/2013
_echo:	db 1
qecho: 	db 'echo', 0
;
qcd:	db 'cd', 0
;
;	

qchdir:
	db 'chdir', 0
	;<chdir\0>
glogin:
	db 'login', 0
	;<login\0>
shell:
	db '/bin/sh', 0
	;</bin/sh\0>
glob:
	db '/etc/glob', 0
	;</etc/glob\0>
binpb:
	db '/bin/'
	;</bin/>
parbuf:
        times 1000 db 0
 	; .=.+1000.
align 2
	;.even
param:
	dd 0 ; 24/08/2015
	;dw 0
	;.=.+2
glflag:
	db 0
	db 0
	;.=.+2
infile:
	dd 0 ; 24/08/2015
	;dw 0
	; .=.+2 
outfile:
	dd 0 ; 24/08/2015
	;dw 0
	;.=.+2
; parp-4
	dd 0 ; 24/08/2015
	;dw 0
	;.=.+2 / room for glob
parp:
        times 200 db 0
	;.=.+200.
inbuf:
        times 256 db 0
	;.=.+256.
;escap:
	;dw 0
	;.=.+2
inbufp:
	dd 0 ; 24/08/2015
	;dw 0
	;.=.+2
einbuf:
	dd 0 ; 24/08/2015
	;dw 0
	;.=.+2
och:
	db 0 ; 24/08/2015
	db 0
	;dw 0
	;.=.+2
shellarg:
	dd 0 ; 24/08/2015
	;dw 0
	;.=.+2
escap:
	db 0
	;
	db 0

;-----------------------------------------------------------------
;  messages
;-----------------------------------------------------------------

msg_unix_sh:	db 0Dh, 0Ah
		db 'Retro Unix 386 v1 - shell'
		;db 0Dh, 0Ah
msgsh_size equ  $ - msg_unix_sh
		;db 0
		db '03/09/2015'
nextline:	db 0Dh, 0Ah, 0
;Error messages:
msgNotFound: 	db 'Input not found', 0
msgArgCount: 	db 'Arg count',  0
msgBadDir: 	db 'Bad directory', 0
msgTryAgain: 	db 'Try again', 0
msgImbalance:   db 22h, 27h, 20h, 'imbalance',  0
msgInputFile: 	db 'Input file', 0
msgOutputFile: 	db 'Output file', 0
msgNoCmd: 	db 'No command',  0
