; -----------------------------------------------
; PShell - a runtime environment for    
; sharp pocket computers                 
; file     : pshell.asm                   
; author   : puehringer edgar           
; date     : 29.02.2004					
; version  : 1.0.0                      
; assembler: as61860                  
;                                       
; Compile with as61860 -los pshell.asm
; Link with aslink -f pshell.lnk
; Convert to BASIC loader with 
;   ihx2bas -o pshell.bas pshell.ihx
;
; Emulation with sharptool:
;   Start the sharptool
;   Type 'powerup'
;   Press F5 and enter the path to the .bas file
;   Type 'POKE 28418,40'
;   and 'POKE 28420,40' to move the basic
;   pointers, then 'NEW' in PRO mode
;   Type OPEN, LOAD and RUN
;   Type 'CALL 8240' to run
; Topics:
;   - raw keyboard read (blocking/non blocking)
;   - keyboard read with SHIFT/SML evaluation (blocking)
;   - character/string write to 
;     display/CE-126P/RS232
;   - character/string write to display with
;   - CR, LF, FF, TAB, BS and BEL evaluation
;   - character/string read from keyboard/RS232
;   - short 2/4 kHz beep
;   - soft power off
;   - relative to absolute address calculation
;   - byte to hexstring conversion
;   - word to string conversion
;   - hexstring to byte/word conversion
;   - (G)CURSOR to display address conversion
;   - GPRINT function for single/multiple bytes
;   - CALL parameter read function
;   - parameter forwarding to pshell applications
;   - Possibility for error codes on return
;   - dynamic far call to other applications
;     e.g. for implementing a bitmap viewer
;   - execution shell function
; Topics implemented as pshell program:
;   - pshell program loader/saver (RS232)
;   - basic program loader/saver (RS232)
;   - shutdown
;   - phell program delete
;   - airplane and other games
;   - petools port for pshell
;   - renew/reclear
;   - help function like on pc1262
;   - audio record/playback
;   - licence plate application
;   - vu meter applikation
;   - greek letters applikation
;   - memory manager (FDISK)
;   - terminal program 
; -----------------------------------------------

.area pshell (REL)
        
.include "pshell.h"
.include "internal.h"

	
	;; -----------------------------------------------
	;; START of the main call interface 
	;; -----------------------------------------------
	
.org MEM_BAS_START

	;; -----------------------------------------------
	;; POKE XXXX-4 call interface parameter (byte or
	;; word)
	;; -----------------------------------------------
	
ps_ci_param::
	.dw 0x0000

	
	;; -----------------------------------------------
	;; the call address of the looked up program is
	;; stored here
	;; -----------------------------------------------
	
ps_progaddr:
	.dw 0x0000

		
	;; -----------------------------------------------
	;; CALL XXXX normal call (main menue)
	;; -----------------------------------------------

ci_main:
	JP  ps_init
	.db 0x00


	;; -----------------------------------------------
	;; CALL XXXX+4 remembers the parameter of the 
	;; WAIT command (for later restore)
	;; FIXXME implement this
	;; -----------------------------------------------
	
	RTN
	.db 0x00
	.db 0x00
	.db 0x00

		
	;; -----------------------------------------------
	;; CALL XXXX+8 calls the program looked up with
	;; CALL XXXX+12. A parameter may be given with the 
	;; WAIT command (the WAIT command is restored to 
	;; the value stored with CALL XXXX+4 after this)
	;; -----------------------------------------------
	
	JP ps_execpr
	.db 0x00
	
	;; -----------------------------------------------
	;; CALL XXXX+12 lookup a program with the name 
	;; given in S$ and perform CALL XXXX+4 inplicit
	;; (throws an ERROR 9 if not found)
	;; -----------------------------------------------

	JP  ps_findpr
	.db 0x00

	
	;; -----------------------------------------------
	;; CALL XXXX+16 returns next free address for 
	;; pshell programs in std. var. S$
	;; -----------------------------------------------

	JP  ps_nfree
	.db 0x00
	
		
	;; -----------------------------------------------
	;; CALL XXXX+20 returns the start of the BASIC
	;; RAM in std. var. S$
	;; -----------------------------------------------
	
	JP  ps_basp

	
	;; -----------------------------------------------
	;; END of the main call interface 
	;; -----------------------------------------------
	

	;; -----------------------------------------------
	;; Called when using the normal call (main menu)
	;; or whe calling the find program function.
	;; Stores bankswitch location on some modells,
	;; saves the X register and prepares inner RAM
	;;  pos. 95/port C with a useful default value
	;; -----------------------------------------------
	
ps_epilog:
.if __PC_IS_BANKSWITCHED__
         LIDP MEM_BANKSWITCH	; save the bankswitch location
         LDD
         PUSH
.endif

	LIDP ps_orig_x		; save the X register
	LP   REG_XL
	EXBD

	LIP  95			; prepare inner RAM pos. 95/port C
	ANIM PORT_C_DISPLAY
	OUTC

	RTN


	;; -----------------------------------------------
	;; To this label is given control after the
	;; normal call (main menu)
	;; -----------------------------------------------
	
ps_init:
        LIDP ps_orig_sp		; save stack ptr for fast exit w/o RTNs
        LDR
        STD

	CALL ps_epilog		; psm_main returns by jumping to ps_exit,
	JP   psm_main		; so we can save one bye by skipping RTN
	

	;; -----------------------------------------------
	;; ps_basp:
	;;   Writes the start of the BASIC RAM in std.
	;;   var. S$
	;; ps_nfree:
	;;   Writes the next free address for pshell
	;;   programs in std. var. S$
	;; -----------------------------------------------
		
ps_basp:	
	CALL ps_y2bass
	LIA BASVAR_STR_ESC
	IYS

	LIDP BASIC__START_L
	LP  REG_A
	MVBD
	JRP lb0101

	
	;; -----------------------------------------------
	;; CALL XXXX+12 lookup a program with the name 
	;; given in S$ and perform CALL XXXX+4 inplicit
	;; (throws an ERROR 9 if not found)
	;; -----------------------------------------------
		
ps_nfree:	
	CALL ps_y2bass
	LIA BASVAR_STR_ESC
	IYS
	; Get the address of the double zero after the
	; last program
	LIA  0xff
	CALL ps_addr
	IX			; skip the -1 gap
	LP    REG_XH
	EXAM
	EXAB
	LP    REG_XL
	EXAM
lb0101: CALL ps_word2str
        RA
        IYS
	RTN


	;; -----------------------------------------------
	;; Lookup a program with the name given in S$ and
	;; perform CALL XXXX+4 inplicit (throws an ERROR 9
	;; if not found)		
	;; -----------------------------------------------

ps_findpr:
        LIDP ps_orig_sp		; save stack ptr for fast exit w/o RTNs
        LDR
        STD

	CALL ps_epilog
	RA			; program number loop counter
	PUSH
lb0103:	CALL ps_addr
	JRNCP lb0105		; no more programs
	IX			; skip prog size 
	IX
	CALL ps_y2bass
	IY			; skip string esc char
	CALL ps_startswith
	JRNZP  lb0104

	POP
	CALL ps_addr
	LIDP  ps_exec_addr	; store it
	LP  REG_XL	
	MVDM
	LIDP  ps_exec_addr+1
	LP  REG_XH	
	MVDM
	RA
	JRP  lb0106
	
lb0104:	POP
	INCA
	PUSH
	JRM  lb0103

lb0105:	LIA  9			; Basic error 9: other errors
lb0106:	JRP  ps_exit		; there's a POP missing, but we live with
				; this, bcause ps_exit restores the stack ptr

	
ps_execpr:
	LIDP ps_exec_addr
	LP   REG_XL
	MVBD
	CALL ps_exint
	RA
	JRP  ps_exit

	
	;; -----------------------------------------------
	;; common subprog. to set Y to basic var. S$
	;; -----------------------------------------------

ps_y2bass:
	LP  REG_YL  
	LIA <(MEM_BASVAR_S-1)
	EXAM
	LP  REG_YH  
	LIA >(MEM_BASVAR_S-1)
	EXAM
	RTN
	
		
	;; -----------------------------------------------
	;;  ps_break, ps_exit
	;; Do some cleanup jobs and exits pshell.
	;; if A is 0xff then the poweroff ROM function
	;; is executed. ps_break is the same as ps_exit,
	;; but writes break
	;; label prefix 00
	;; -----------------------------------------------

ps_break:
	CALL ps_clrscr
	LP   REG_XL
	LIA  <(lb0002-1)
	EXAM
	LP   REG_XH
	LIA  >(lb0002-1)
	EXAM
	CALL ps_putstr
	RA
	
ps_exit::
	LP   REG_K		; A -> K
	EXAM

	LIDP ps_orig_x		; restore the X register
	LP   REG_XL
	MVBD

        LIDP ps_orig_sp		; restore stack pointer from memory location
        LDD
        STR

.if __PC_IS_BANKSWITCHED__
        LIDP MEM_BANKSWITCH	; restore the bankswitch location
        POP
        STD
.endif

	LP   REG_K		; A -> K
	EXAM

	CPIA  0x00
	JRZP  lb0001		; normal exit

	CPIA 0xff
	JPZ  INT_ROM_PWRDOWN	; power down

	LP   52			; if we come here, it must be a error
	EXAM			; return code ==> store A in int. ROM addr. 52

	POP			; remove 2 bytes from stack
	POP

	SC			; set carry

lb0001:	RTN			; this is the final return

lb0002:
.if __PC_HAS_LOWERCASE_CHARS__
        .ascii "Break"
.else
        .ascii "BREAK"
.endif
        .db  0x00       	; end of string 


	;; -----------------------------------------------
	;; Reads a character from the current input
	;; device
	;; Used Registers: Various because ROM call
	;;   are used
	;; -----------------------------------------------
	
ps_getchar::
	JP ps_getchar_kbd

	
	;; -----------------------------------------------
	;; Writes a character in A to the current output
	;; device
	;; Used Registers: A,B,X,Y
	;; -----------------------------------------------
	
ps_putchar::
	JP ps_putchar_con

	
	;; -----------------------------------------------
	;; sleeps A * 10ms
	;; Used Registers: A,B
	;; label prefix 08
	;; -----------------------------------------------

ps_sleep::
	
lb0801:	LIB   5
	
lb0802:	TEST  TEST_CNT_2	; Test for 2 ms timer
	JRZM  lb0802

	TEST  TEST_BRK		; Test for BREAK key
	JPNZ  ps_break    
	
	DECB			; inner loop 5 times
        JRNZM lb0802

	DECA			; outer loop A times
	JRNZM ps_sleep

	RTN


	;; -----------------------------------------------
	;; Calculates the address of a pshell program
	;; The number of the program must be given in A
	;; If a program with this number is available, the
	;; carry flag is set to 1, 0 otherwise
	;; Used Registers: A,B,X
	;; Return value: Carry, X (Contains prog. addr. -1)
	;; label prefix 10
	;; -----------------------------------------------
	
ps_addr::
        PUSH

        LIA  <(ps_start_ptr-1)	; Start pointer -> X
        LP   REG_XL
        EXAM
        LIA  >(ps_start_ptr-1)
        LP   REG_XH
        EXAM

        POP

lb1002: SBIA 1			; If input param A <= 0, then we are done
        JRCP lb1001

        PUSH

        IXL			; Load program lengh into BA
        EXAB
        IXL

        LP   REG_XL		; Add offset to X
        ADB
        DX
        DX

        POP

        JRM  lb1002

lb1001: 
        IXL			; At last, we have to check if this 
        EXAB			; is not a zero length application (which
        IXL			; means this is the end of the program list)
	DX
	DX
        LP   REG_B
        ORMA
        JRZP lb1003
        SC
        RTN

lb1003:
        RC
        RTN


	;; -----------------------------------------------
	;; Writes a zero terminated string; the string
	;; address is given in X+1 
	;; label prefix 12
	;; -----------------------------------------------
	
ps_putstr::
lb1202: IXL        
        CPIA 0
        JRZP lb1201
        EXAB

        LP   REG_XL
        EXAM
        PUSH
        LP   REG_XH
        EXAM
        PUSH

        EXAB
        CALL ps_putchar_con

        LP   REG_XH
        POP
        EXAM
        LP   REG_XL
        POP
        EXAM

        JRM  lb1202

lb1201:
        RTN

	
	;; -----------------------------------------------
	;; Executes a pshell program. The number of the
	;; program is given in A. The following steps
	;; are needed:
	;;  o calculate the address (ps_addr) and store
	;;    it for further use
	;;  o skip the program size
	;;  o skip the title string
	;;  o skip the title icon
	;;  o increase X, because it is allways one
	;;    behind the address next read
	;;  o call X
	;; label prefix 16
	;; -----------------------------------------------
	
ps_exec::
	CALL  ps_addr		; calculate address
	LIDP  ps_exec_addr	; store it
	LP  REG_XL	
	MVDM
	LIDP  ps_exec_addr+1
	LP  REG_XH	
	MVDM
ps_exint:	 		; (ps_exec call for internal use)
	IXL			; skip progsize
	IXL
lb1601:	IXL			; skip title
        CPIA 0
        JRNZM lb1601
	IXL			; length of icon pattern in BA
	LIB  0	
        LP   REG_XL		; add it to X
        ADB
	IXL			; see function header
	; now we have the call adress in X. We fake a call by
        ; it on the stack and then return
	LP   REG_XH
	LDM
	PUSH
	LP   REG_XL
	LDM
	PUSH

	RTN
	

; -----------------------------------------------
; Takes a relative address in BA and returns the
; absolute address - 1 in X(Y)
; Used Registers: A,B,X(Y)
; -----------------------------------------------
ps_rel_ab2abs_y::
	LIDP  ps_exec_addr+1		; get the stored prog. addr.
	LP  REG_YH	
	MVMD
	LIDP  ps_exec_addr
	LP  REG_YL	
        JRP lb1800
        
ps_rel_ab2abs_x::
	LIDP  ps_exec_addr+1		; get the stored prog. addr.
	LP  REG_XH	
	MVMD
	LIDP  ps_exec_addr
	LP  REG_XL	
        ; JRP lb1800

lb1800:	MVMD
        ADB
	RTN


; -----------------------------------------------
; Copies the content of AB to X
; (A is low byte)
; Used Registers: AB,X,P,Q
; -----------------------------------------------
ps_cp_ab_to_x::
	LIQ  REG_A
	LP   REG_XL
	MVB
	RTN

	
; -----------------------------------------------
; Copies the content of AB to Y
; (A is low byte)
; Used Registers: AB,Y,P,Q
; -----------------------------------------------
ps_cp_ab_to_y::
	LIQ  REG_A
	LP   REG_YL
	MVB
	RTN

	
; -----------------------------------------------
; Copies the content of X to Y
; Used Registers: X,Y,P,Q
; -----------------------------------------------
ps_cp_x_to_y::
	LIQ  REG_XL
	LP   REG_YL
	MVB
	RTN

	
; -----------------------------------------------
; Copies the content of X to Y
; Used Registers: X,Y,P,Q
; -----------------------------------------------
ps_cp_y_to_x::
	LIQ  REG_YL
	LP   REG_XL
	MVB
	RTN

	
; -----------------------------------------------
; Converts a word given in BA to a string
; buffer pointed to by Y+1. There is NO trailing
; ascii zero character writen after the string
; Used registers: A,B,Y,I,K,L,M,N,0x0C .. 0x10
; label prefix 20
; -----------------------------------------------
ps_word2str::
	PUSH			; save low byte on stack
	LII	0x07		; I is counter for 7 bits
	RA			; 0 => A
	LP	REG_L		; Clear internal RAM from L to 0x10
	FILM			; The BCD buffer is 0x0E 0x0F 0x10
lb2002:	DECB			; B-1 => B (decrement high byte)
	JRCP	lb2001		; If end of loop, jump ahead
	LIA	0x56		; 0x56 => A
	LP	0x10		; Last position of BCD buffer => P
	ADN			; BCD add 0x56 B times
	LIA	0x02		; 2 => A
	LP	0x0F		; Last position-1 of BCD buffer => P
	ADN			; BCD add = add 200
	JRM	lb2002		; Loop jump
lb2001:	POP			; Load low byte from stack
	EXAB			; A <=> B
lb2004:	DECB			; B-1 => B
	JRCP	lb2003		; If end of loop, jump ahead
	LIA	0x01		; 1 => A
	LP	0x10		; Last position of BCD buffer => P
	ADN			; BCD add 1 B times
	JRM	lb2004		; Loop jump
lb2003:	LP	REG_K		; 5 => K; counter for max. 5 BCD
	LIA	0x05		; digits
	EXAM
lb2007:	INCL			; Set L (Flag for leading zeros)
	RA			; 0 => A
	LP	0x0E		; First position of BCD buffer => A
	EXAM
	ADIA	0x30		; Add ASCII offset
	DECL			; L-1 => L; If L != 0 (no leading
	JRNZP	lb2005		; zero), jump ahead
	CPIA	0x30		; If ASCII-"0", jump ahead
	JRZP	lb2006
	INCL			; Inc. L, so it will never be zero
lb2005:	IYS			; Write character to memory location
lb2006:	LII	0x02		; Shift the three byte long BCD buffer
	LP	0x10		; on nibble to the left
	SLW
	DECK			; K-1 => K
	JRNZM	lb2007		; Loop until last BCD digit written
	RTN


; -----------------------------------------------
; Converts a string given in X+1 to a unsigned
; word. The result is in BA. The function reads
; as long as it finds characters in the range
; of 0 .. 9.
; Used registers: X,Y,A,B
; label prefix 21
; -----------------------------------------------
ps_str2word::
	RA
	EXAB
	RA
	CALL ps_cp_ab_to_y
lb2101:	IXL
	CPIA  0x30
	JRCP  lb2100		; IF < "0" return
	CPIA  0x3A		; if > "9" return
	JRNCP lb2100
	SBIA  0x30		;  subtract ASCII "0"
	PUSH
				; First: old result x 10
	LIQ   REG_YL		; Y -> BA
	LP    REG_A
	MVB
	RC			; BA x 2
	SL
	EXAB
	SL
	EXAB
	CALL  ps_cp_ab_to_y	; BA x 2 -> Y
	RC			; BA x 2 (i.e. x 4)
	SL
	EXAB
	SL
	EXAB
	RC			; BA x 2 (i.e. x 8)
	SL
	EXAB
	SL
	EXAB
	LP   REG_YL		; 16 bit addition
	ADB
	
	RA			; 16 bit add new value
	EXAB
	POP
	LP   REG_YL
	ADB

	JRM  lb2101
	 
lb2100:	LIQ  REG_YL		; Y -> BA
	LP   REG_A
	MVB

	RTN


; -----------------------------------------------
; Check if the string with address Y starts with
; the string with address X. 
; Zero flag is set if check passed; otherwise
; the carry flag indicates the sort order 
; Used registers: A,B,X,Y
; label prefix 22
; -----------------------------------------------
ps_startswith::	
	LP REG_B
lb2202:	IXL
	CPIA 0x00
	JRZP lb2201
	EXAB
	IY
	LDD
	CPMA
	JRZM lb2202
lb2201:	RTN

	
; -----------------------------------------------
; Compare to strings with addresses given in X
; and Y. 
; Zero flag is set if strings are equal;
; otherwise the carry flag indicates the sort
; order 
; Used registers: A,B,X,Y
; label prefix 23
; -----------------------------------------------
ps_cmpstr::	
	CALL ps_startswith
	JRNZP lb2301
	RA
	EXAB
	IY
	LDD
	CPMA
lb2301:	RTN
