; cmd - parse and interpret command
;
; *** Consts ***

; address type

.equ	ABSOLUTE	0
; handles +, - and ".". For +, easy. For -, addr is negative. For ., it's 0.
.equ	RELATIVE	1
.equ	EOF		2

; *** Variables ***

; An address is a one byte type and a two bytes line number (0-indexed)
.equ	CMD_ADDR1	CMD_RAMSTART
.equ	CMD_ADDR2	@+3
.equ	CMD_TYPE	@+3
.equ	CMD_RAMEND	@+1

; *** Code ***

; Parse command line that HL points to and set unit's variables
; Sets Z on success, unset on error.
cmdParse:
	ld	a, (hl)
	cp	'q'
	jr	z, .simpleCmd
	cp	'w'
	jr	z, .simpleCmd
	ld	ix, CMD_ADDR1
	call	.readAddr
	ret	nz
	; Before we check for the existence of a second addr, let's set that
	; second addr to the same value as the first. That's going to be its
	; value if we have to ",".
	ld	a, (ix)
	ld	(CMD_ADDR2), a
	ld	a, (ix+1)
	ld	(CMD_ADDR2+1), a
	ld	a, (ix+2)
	ld	(CMD_ADDR2+2), a
	ld	a, (hl)
	cp	','
	jr	nz, .noaddr2
	inc	hl
	ld	ix, CMD_ADDR2
	call	.readAddr
	ret	nz
.noaddr2:
	; We expect HL (rest of the cmdline) to be a null char or an accepted
	; cmd, otherwise it's garbage
	ld	a, (hl)
	or	a
	jr	z, .nullCmd
	cp	'p'
	jr	z, .okCmd
	cp	'd'
	jr	z, .okCmd
	cp	'a'
	jr	z, .okCmd
	cp	'i'
	jr	z, .okCmd
	; unsupported cmd
	ret			; Z unset
.nullCmd:
	ld	a, 'p'
.okCmd:
	ld	(CMD_TYPE), a
	ret			; Z already set

.simpleCmd:
	; Z already set
	ld	(CMD_TYPE), a
	ret

; Parse the string at (HL) and sets its corresponding address in IX, properly
; considering implicit values (current address when nothing is specified).
; advances HL to the char next to the last parsed char.
; It handles "+" and "-" addresses such as "+3", "-2", "+", "-".
; Sets Z on success, unset on error. Line out of bounds isn't an error. Only
; overflows.
.readAddr:
	ld	a, (hl)
	cp	'+'
	jr	z, .plusOrMinus
	cp	'-'
	jr	z, .plusOrMinus
	cp	'.'
	jr	z, .dot
	cp	'$'
	jr	z, .eof

	; inline parseDecimalDigit
	add	a, 0xff-'9'	; maps '0'-'9' onto 0xf6-0xff
	sub	0xff-9		; maps to 0-9 and carries if not a digit

	jr	c, .notHandled
	; straight number
	ld	a, ABSOLUTE
	ld	(ix), a
	call	.parseDecimalM
	ret	nz
	dec	de	; from 1-based to 0-base
	jr	.end
.dot:
	inc	hl	; advance cmd cursor
	; the rest is the same as .notHandled
.notHandled:
	; something else. It's probably our command. Our addr is therefore "."
	ld	a, RELATIVE
	ld	(ix), a
	xor	a		; sets Z
	ld	(ix+1), a
	ld	(ix+2), a
	ret
.eof:
	inc	hl		; advance cmd cursor
	ld	a, EOF
	ld	(ix), a
	ret			; Z set during earlier CP
.plusOrMinus:
	push	af		; preserve that + or -
	ld	a, RELATIVE
	ld	(ix), a
	inc	hl		; advance cmd cursor
	ld	a, (hl)
	ld	de, 1		; if .pmNoSuffix

	; inline parseDecimalDigit
	add	a, 0xff-'9'	; maps '0'-'9' onto 0xf6-0xff
	sub	0xff-9		; maps to 0-9 and carries if not a digit

	jr	c, .pmNoSuffix
	call	.parseDecimalM	; --> DE
.pmNoSuffix:
	pop	af		; bring back that +/-
	cp	'-'
	jr	nz, .end
	; we had a "-". Negate DE
	push	hl
	ld	hl, 0
	sbc	hl, de
	ex	de, hl
	pop	hl
.end:
	; we still have to save DE in memory
	ld	(ix+1), e
	ld	(ix+2), d
	cp	a		; ensure Z
	ret

; call parseDecimal and set HL to the character following the last digit
.parseDecimalM:
	push	bc
	push	ix
	push	hl
.loop:
	inc	hl
	ld	a, (hl)

	; inline parseDecimalDigit
	add	a, 0xff-'9'	; maps '0'-'9' onto 0xf6-0xff
	sub	0xff-9		; maps to 0-9 and carries if not a digit

	jr	nc, .loop
	; We're at the first non-digit char. Let's save it because we're going
	; to temporarily replace it with a null.
	ld	b, (hl)		; refetch (HL), A has been mucked with in
				; parseDecimalDigit
	xor	a
	ld	(hl), a
	; Now, let's go back to the beginning of the string and parse it.
	; but before we do this, let's save the end of string in DE
	ex	de, hl
	pop	hl
	call	parseDecimal
	; Z is set properly at this point. nothing touches Z below.
	ld	a, b
	ld	(de), a
	ex	de, hl	; put end of string back from DE to HL
	; Put addr in its final register, DE
	push	ix \ pop de
	pop	ix
	pop	bc
	ret

