Estex-DSS/SHELL/Commands/GOTO.ASM

276 lines
6.5 KiB
NASM

;///////////////////////////////////////////////////
;
; GOTO / GOSUB / RETURN. Jumps inside a BAT-file by
; ":label", MS-DOS / 4DOS style.
;
; GOTO label - jump to ":label"
; GOSUB label - call subroutine at ":label"
; RETURN - return to the line after the GOSUB
;
;///////////////////////////////////////////////////
GOTO_LABEL_MAX EQU 15 ; max label name length
GOSUB_MAX EQU 16 ; max GOSUB nesting depth
;-------------------------------------------------
; GOTO label
; in: de -> argument string (label, may start with ':')
;-------------------------------------------------
cmd_goto:
ex de,hl ; hl -> argument text
call GOTO_PARSE ; -> goto_label (upper, 0-term)
ld a,(goto_label)
or a
jp z,.empty ; no label name
jp GOTO_START ; rewind + start the label search
.empty: ld de,MAIN_MSG.LABEL_NOT_FOUND
jp gAbort
;-------------------------------------------------
; GOSUB label
; in: de -> argument string (label, may start with ':')
;-------------------------------------------------
cmd_gosub:
ex de,hl ; hl -> argument text
call GOTO_PARSE ; -> goto_label (saved in memory)
ld a,(goto_label)
or a
jp z,cmd_goto.empty
call GOSUB_PUSH ; remember the return position
jp GOTO_START ; then jump like GOTO
;-------------------------------------------------
; RETURN - resume after the matching GOSUB
;-------------------------------------------------
cmd_return:
ld a,(gosub_sp)
or a
jr z,.empty ; RETURN without GOSUB
dec a
ld (gosub_sp),a
add a,a
add a,a ; a = sp*4 (byte offset in stack)
ld e,a
ld d,0
ld hl,gosub_stk
add hl,de
ld e,(hl) ; saved offset (low word)
inc hl
ld d,(hl)
inc hl
ld (gosub_lo),de
ld e,(hl) ; saved offset (high word)
inc hl
ld d,(hl)
ld (gosub_hi),de
ld a,(BAT_FM) ; bat-file handle
ld hl,(gosub_hi) ; HL = offset high 16 bits
ld ix,(gosub_lo) ; IX = offset low 16 bits
ld bc,Dss.Move_FP.FrStart ; B=0 (from start), C=#15
RST ToDSS
jr c,.io
xor a
ld (MOVWORD.count),a ; force re-read from saved position
ret ; back to NEWLINE -> line after GOSUB
.empty: ld de,MAIN_MSG.RET_NO_GOSUB
jp gAbort
.io: call print_err_message
jp gAbort.silent
;-------------------------------------------------
; Parse a label argument into goto_label
; in: hl -> text (optional spaces/TAB, optional ':')
; out: goto_label = upper-cased, 0-terminated token
;-------------------------------------------------
GOTO_PARSE:
.sp0: ld a,(hl) ; skip leading blanks (space / TAB)
cp " "
jr z,.sp0i
cp "\t"
jr nz,.nosp0
.sp0i: inc hl
jr .sp0
.nosp0: cp ":" ; allow "GOTO :label"
jr nz,.sp1
inc hl
.sp1: ld a,(hl) ; skip blanks after ':'
cp " "
jr z,.sp1i
cp "\t"
jr nz,.copy
.sp1i: inc hl
jr .sp1
.copy: ld de,goto_label
ld b,GOTO_LABEL_MAX
.cloop: ld a,(hl)
cp " "+1 ; a <= ' ' -> end of token
jr c,.cend
call COMPARE.cmp_AZ ; -> upper case
ld (de),a
inc hl
inc de
djnz .cloop
.cend: xor a
ld (de),a ; 0-terminate
ret
;-------------------------------------------------
; Rewind the BAT-file and start the label search
;-------------------------------------------------
GOTO_START:
ld a,(BAT_FM) ; bat-file handle
ld hl,0 ; offset low
ld ix,0 ; offset high
ld bc,Dss.Move_FP.FrStart ; B=0 (from start), C=#15
RST ToDSS
jr c,.io
xor a
ld (MOVWORD.count),a ; invalidate NEWLINE read buffer
ld a,1
ld (goto_active),a ; activate the label search
ret
.io: call print_err_message
jp gAbort.silent
;-------------------------------------------------
; Push the return position (file offset of the line
; that follows the current GOSUB) onto the stack.
;-------------------------------------------------
GOSUB_PUSH:
ld a,(gosub_sp)
cp GOSUB_MAX
jr nc,.deep ; stack overflow
; current file pointer (tell): FrCurrent, offset 0
ld a,(BAT_FM)
ld hl,0
ld ix,0
ld bc,Dss.Move_FP.FrCurrent ; B=1, C=#15
RST ToDSS
jr c,.io
; on return: IX = pointer low 16, HL = pointer high 16
ld (gosub_hi),hl ; save high word
push ix
pop hl ; hl = pointer low 16
; offset = pointer - (bytes still unparsed in buffer1)
ld a,(MOVWORD.count)
ld e,a
ld d,0
or a ; CF=0
sbc hl,de ; hl = low - count
ld (gosub_lo),hl
jr nc,.nob
ld hl,(gosub_hi) ; borrow into high word
dec hl
ld (gosub_hi),hl
.nob:
; store at gosub_stk[gosub_sp]
ld a,(gosub_sp)
ld c,a ; c = old depth
add a,a
add a,a ; a = sp*4
ld e,a
ld d,0
ld hl,gosub_stk
add hl,de
ld de,(gosub_lo)
ld (hl),e
inc hl
ld (hl),d
inc hl
ld de,(gosub_hi)
ld (hl),e
inc hl
ld (hl),d
ld a,c
inc a
ld (gosub_sp),a ; depth++
ret
.deep: ld de,MAIN_MSG.GOSUB_DEEP
jp gAbort
.io: call print_err_message
jp gAbort.silent
;-------------------------------------------------
; Print message (de=index) and abort the BAT-file
;-------------------------------------------------
gAbort:
call ECHO_MESSAGE
.silent:
xor a
ld (goto_active),a ; don't double-print in cmd_break.exit
jp cmd_break.exit
;-------------------------------------------------
; Called from CMDMODE for every BAT-file line.
; Handles ":label" lines and the GOTO/GOSUB search.
;
; out: CF=1 - skip this line (label line, or searching)
; CF=0 - execute this line normally
;-------------------------------------------------
BAT_CHECK_LABEL:
ld hl,Buffers.input_line.Path
.sp: ld a,(hl) ; skip leading blanks (space / TAB)
cp " "
jr z,.spi
cp "\t"
jr nz,.first
.spi: inc hl
jr .sp
.first: cp ":" ; is it a label line ?
jr z,.label
; not a label line
ld a,(goto_active)
or a
ret z ; CF=0 -> run normally
scf ; searching -> skip line
ret
;
.label: ld a,(goto_active)
or a
jr nz,.match
; not searching: ":label" -> skip (no echo, no exec)
scf
ret
; searching: does this label match goto_label ?
.match: inc hl ; skip ':'
.msp: ld a,(hl) ; skip blanks after ':'
cp " "
jr z,.mspi
cp "\t"
jr nz,.mcmp
.mspi: inc hl
jr .msp
.mcmp: ld de,goto_label
.mlp: ld a,(de) ; target char (upper, 0-term)
or a
jr z,.mend ; target consumed
ld c,a
ld a,(hl)
cp " "+1 ; line token ended too early ?
jr c,.skip ; -> no match
call COMPARE.cmp_AZ
cp c
jr nz,.skip
inc hl
inc de
jr .mlp
.mend: ld a,(hl) ; target ended: line token must end here
cp " "+1 ; a <= ' ' -> full match
jr nc,.skip ; line label is longer -> no match
; match ! stop searching, skip the label line itself
xor a
ld (goto_active),a
scf
ret
;
.skip: scf ; keep searching, skip this line
ret
;
goto_active: db 0 ; 1 = searching for a label
goto_label: ds GOTO_LABEL_MAX+1,0 ; target label (upper, 0-term)
gosub_sp: db 0 ; GOSUB nesting depth
gosub_lo: dw 0 ; scratch (offset low)
gosub_hi: dw 0 ; scratch (offset high)
gosub_stk: ds GOSUB_MAX*4,0 ; return offsets (32-bit each)