summaryrefslogtreecommitdiff
path: root/programs/sub-suite
diff options
context:
space:
mode:
authormrb0nk500 <b0nk@b0nk.xyz>2020-06-22 17:56:52 -0400
committermrb0nk500 <b0nk@b0nk.xyz>2020-06-22 17:56:52 -0400
commit887802efcdb3b56263069cc6778a8f53ed89d599 (patch)
treecdbf377712e1a92a629e7699589ea1db689860fa /programs/sub-suite
parentda19d2f59353ade607c715ed9ab7b81ba1407411 (diff)
Did some more stuff.
- Fixed some bugs in the emulator's assembler. - Worked on SuBAsm's lexer some more. - Created a new directory for the SuB suite, and moved all of the SuB suite's files in there.
Diffstat (limited to 'programs/sub-suite')
-rw-r--r--programs/sub-suite/lexer.s413
-rw-r--r--programs/sub-suite/subasm.s388
-rw-r--r--programs/sub-suite/subeditor.s1234
-rw-r--r--programs/sub-suite/utils.s394
4 files changed, 2429 insertions, 0 deletions
diff --git a/programs/sub-suite/lexer.s b/programs/sub-suite/lexer.s
new file mode 100644
index 0000000..315bee4
--- /dev/null
+++ b/programs/sub-suite/lexer.s
@@ -0,0 +1,413 @@
+; Lexer, and supporting routines for SuBAsm.
+
+; Enums.
+
+; Directives.
+DIR_ORG = 0 ; Origin.
+DIR_BYTE = 1 ; Byte = 8 bits.
+DIR_WORD = 2 ; Word = 16 bits.
+DIR_DWORD = 3 ; Dword = 32 bits.
+DIR_QWORD = 4 ; Qword = 64 bits.
+DIR_INCL = 5 ; Include.
+
+; Tokens.
+TOK_DIR = 0 ; Directive.
+TOK_LOCAL = 1 ; Local syobol.
+TOK_LABEL = 2 ; Label.
+TOK_SYM = 3 ; Symbol.
+TOK_EXPR = 4 ; Expression.
+TOK_CSV = 5 ; Comma separated value.
+TOK_STR = 6 ; String.
+TOK_CHAR = 7 ; Character.
+TOK_IND = 8 ; Indirect addressing.
+TOK_IMM = 9 ; Immediate data.
+TOK_MNE = 10 ; Opcode/Mnemonic.
+TOK_RS = 11 ; Register size prefix.
+TOK_COMM = 12 ; Comment.
+TOK_HEX = 13 ; Hex value.
+TOK_DEC = 14 ; Decimal value.
+TOK_BIN = 15 ; Binary value.
+TOK_INCL = 16 ; Include file.
+
+; Pre-Tokens.
+PTOK_DOT = 0 ; .
+PTOK_AT = 1 ; @
+PTOK_COLON = 2 ; :
+PTOK_EQU = 3 ; =
+PTOK_PLUS = 4 ; +
+PTOK_MINUS = 5 ; -
+PTOK_GT = 6 ; >
+PTOK_LT = 7 ; <
+PTOK_LBRAK = 8 ; (
+PTOK_RBRAK = 9 ; )
+PTOK_COMMA = 10 ; ,
+PTOK_X = 11 ; x
+PTOK_Y = 12 ; y
+PTOK_DQUOT = 13 ; "
+PTOK_SQUOT = 14 ; '
+PTOK_HASH = 15 ; #
+PTOK_SCOLN = 16 ; ;
+PTOK_DOLR = 17 ; $
+PTOK_PRCNT = 18 ; %
+PTOK_NUM = 19 ; 0-9
+PTOK_ALPH = 20 ; a-z A-Z
+PTOK_OTHR = 21 ; Everything else.
+
+; Expressions.
+EXPR_PLUS = 0 ; Plus.
+EXPR_MINUS = 1 ; Minus.
+EXPR_LOW = 2 ; Lower half of address.
+EXPR_HIGH = 3 ; Upper half of address.
+EXPR_NONE = 4 ; No expression.
+
+
+; Data.
+.org lexer_data
+; Jump table for parsing pre-tokens.
+swtab:
+ .word ptok_dot ; PTOK_DOT
+ .word ptok_at ; PTOK_AT
+ .word ptok_col ; PTOK_COLON
+ .word ptok_equ ; PTOK_EQU
+ .word ptok_plus ; PTOK_PLUS
+ .word ptok_min ; PTOK_MINUS
+ .word ptok_gt ; PTOK_GT
+ .word ptok_lt ; PTOK_LT
+ .word ptok_lbrk ; PTOK_LBRAK
+ .word ptok_rbrk ; PTOK_RBRAK
+ .word ptok_com ; PTOK_COMMA
+ .word ptok_xr ; PTOK_X
+ .word ptok_yr ; PTOK_Y
+ .word ptok_dqu ; PTOK_DQUOT
+ .word ptok_squ ; PTOK_SQUOT
+ .word ptok_hash ; PTOK_HASH
+ .word ptok_scol ; PTOK_SCOLN
+ .word ptok_dolr ; PTOK_DOLR
+ .word ptok_prcn ; PTOK_PRCNT
+ .word ptok_num ; PTOK_NUM
+ .word ptok_alph ; PTOK_ALPH
+ .word ptok_othr ; PTOK_OTHR
+
+; Data entry point for utility subroutines.
+util_data:
+
+
+; Program code.
+.org lexer
+lex:
+ ldx #0 ; Reset X.
+ txa ; Reset A.
+ phy #2 ; Preserve the screen buffer index.
+ txy ; Reset Y.
+ sty.q idx0 ; Clear the first index.
+ sty.q idx1 ; Clear the second index.
+ sty.q idx2 ; Clear the third index.
+ sty b ; Clear the isop flag.
+; lda (ptr), y ; Get a character from the line.
+; pha #1 ; Preserve the character.
+; jsr isdigit ; Is this character a digit?
+; pla #1 ; Get the character back.
+@getline:
+ lda #2 ; Get the third byte, of the line table address.
+ lsl #$10 ; Shift it by 2 bytes.
+ ldb #1 ; Set the second pointer
+ lda.w ltok ; to the last line.
+ jsr set_ptr ;
+ lda.w (ptr2) ; Get the next line.
+ jsr set_ptr ; Set the second pointer to the next line.
+ sta.w ctok ; Make it the current line.
+ and #0 ; Reset A.
+@loop:
+ ldy.w idx0 ; Get the string index.
+ lda (ptr), y ; Get a character from the line.
+ jsr isdelm ; Get the delimiter.
+ cmp #1 ; Are we at the end of the line?
+ beq @end ; Yes, so we're done.
+@spaces:
+ ldy.w idx0 ; Get the string index.
+ lda (ptr), y ; Get a character from the line.
+ pha #1 ; Preserve the character.
+ jsr isdelm ; Get the delimiter.
+ and #$10 ; Is this character, a space, or tab?
+ pla #1 ; Get the character back.
+ beq @isstart ; No, so check for the start of the line.
+ inc.w idx0 ; Yes, so increment the string index.
+ cmp #' ' ; Is this character, a space?
+ beq @incs ; Yes, so increment the starting space count.
+ cmp #'\t' ; No, but is it a tab?
+ beq @inct ; Yes, so increment the starting tab count.
+ jmp @spaces ; No, so Keep looping.
+@incs:
+ inc idx1 ; Increment the space count.
+ jmp @spaces ; Keep looping.
+@inct:
+ inc idx1+1 ; Increment the tab count.
+ jmp @spaces ; Keep looping.
+@isstart:
+ pha #2 ; Preserve the character.
+ lda.w idx1 ; Was there any whitespace?
+ pla #2 ; Get the character back.
+ beq @switch ; No, so start lexing.
+ cpb #1 ; Yes, and are we at the start of the line?
+ bne @switch ; No, so start lexing.
+@whtspace:
+ ldy #2 ; Yes, so set the line index to the starting whitespace counters.
+ lda.w idx1 ; Get both indecies.
+ sta.w (ptr2), y ; Save them in the line.
+ and #0 ; Reset A.
+ sta.w idx1 ; Reset the second index.
+ deb ; Set the isstart flag to false.
+@switch:
+ ldy.w idx0 ; Get the string index.
+ lda (ptr), y ; Get the character.
+ jsr get_ptok ; Get the pre-token.
+ jsr parse_ptok ; Parse the pre-token.
+; beq @end ; We got to the end of the string.
+ jmp @loop ; Keep looping.
+@end:
+ ply #2 ; Get the screen buffer index back.
+ rts ; End of lex.
+
+
+parse_ptok:
+ pha #1 ; Preserve the pre-token.
+ ldb #2 ; Set the third pointer
+ lda.w #swtab ; to the start of the jump table.
+ jsr set_ptr ;
+ and #0 ; Reset A.
+ pla #1 ; Get the pre-token back.
+ phy #2 ; Preserve Y.
+ lsl #1 ; Multiply the pre-token by two, to get the jump index.
+ tay ; Get the index of the jump table.
+ lda.w (ptr3), y ; Get the address to jump to.
+ jsr set_ptr ; Set the third pointer to the case address.
+ and #0 ; Reset A.
+ tab ; Reset B.
+ ply #2 ; Get Y back.
+ jmp (ptr3) ; Jump to the case label.
+ptok_dot:
+ ldb #1 ; Make init_lex increment the string index.
+ jsr init_lex ; Initialize the lexeme buffer for copying.
+ ldb #$11 ; Set the delimiter comparison value to whitespace.
+ jsr delmcpy ; Copy the string, to the lexeme buffer, until delimiter.
+@isop:
+ lda b ; Has the isop flag been set?
+ beq @dir ; No, so check for a directive.
+@rs:
+ lda #TOK_RS ; Yes, so set the lexeme type to TOK_RS.
+ sta lex_type ;
+ ldy.w idx1 ; Get the lexeme index.
+ dey ; Decrement the lexeme index.
+ lda (ptr3), y ; Get the suffix character.
+ jsr get_rs ; Get the register size.
+ jmp @end ; We are done.
+@dir:
+ lda #TOK_DIR ; Set the lexeme type to TOK_DIR.
+ sta lex_type ;
+ ldb #0 ; Make the lexeme buffer, the first pointer.
+ stb.q idx1 ; Reset the first index.
+ jsr set_lexptr ; Set up the lexeme buffer.
+@dir_loop:
+ lda.w #dir ; Get pointer to the start of the directive table.
+ clc ; Prepare for a non carrying add.
+ adc.w idx2 ; Offset the pointer, by the length of the previous string.
+ pha #8 ; Preserve the directive string pointer.
+ jsr strcasecmp ; Is the lexeme buffer, the same as the directive string?
+ pla #8 ; Get the directive string pointer back.
+ beq @found ; Yes, so create a new token.
+ ldb idx1 ; No, so Get the directive ID.
+ cpb #6 ; Have we reached the end of the directive table?
+ beq @end ; Yes, so we're done.
+ inc idx1 ; No, so increment the directive ID.
+@getlen:
+ jsr strlen ; Get the string's length.
+ inx ; Add one to the length.
+ txa ; Place it in the accumulator.
+ clc ; Prepare for a non carrying add.
+ adc.w idx2 ; Add the string offset to the current length
+ sta.w idx2 ; Save the offset in the third index.
+ jmp @dir_loop ; Keep looping.
+@found:
+ nop ;
+@end:
+ jsr make_tok ; Create the token.
+ jsr set_cmdbuf ; Set the first pointer to the command buffer.
+ rts ; End of parse_ptok.
+ptok_at:
+ rts ; End of parse_ptok.
+ptok_col:
+ rts ; End of parse_ptok.
+ptok_equ:
+ rts ; End of parse_ptok.
+ptok_plus:
+ rts ; End of parse_ptok.
+ptok_min:
+ rts ; End of parse_ptok.
+ptok_gt:
+ rts ; End of parse_ptok.
+ptok_lt:
+ rts ; End of parse_ptok.
+ptok_lbrk:
+ rts ; End of parse_ptok.
+ptok_rbrk:
+ rts ; End of parse_ptok.
+ptok_com:
+ rts ; End of parse_ptok.
+ptok_xr:
+ rts ; End of parse_ptok.
+ptok_yr:
+ rts ; End of parse_ptok.
+ptok_dqu:
+ ldb #1 ; Make init_lex increment the string index.
+ jsr init_lex ; Initialize the lexeme buffer for copying.
+ ldb #4 ; Set the delimiter comparison value to a double quote.
+ jsr delmcpy ; Copy the string, to the lexeme buffer, until delimiter.
+@term:
+ rts ; End of parse_ptok.
+ptok_squ:
+ rts ; End of parse_ptok.
+ptok_hash:
+ rts ; End of parse_ptok.
+ptok_scol:
+ rts ; End of parse_ptok.
+ptok_dolr:
+ rts ; End of parse_ptok.
+ptok_prcn:
+ rts ; End of parse_ptok.
+ptok_num:
+ rts ; End of parse_ptok.
+ptok_alph:
+ ldb #0 ; Do not let init_lex increment the string index.
+ jsr init_lex ; Initialize the lexeme buffer for copying.
+ ldb #1 ; Stop at any possible delimiter, except whitespace.
+ tba ; Use isdelm2 for the comparison.
+ jsr delmcpy ; Copy the string, to the lexeme buffer, until delimiter.
+ lda #0 ; Reset A.
+ sta b ; Clear the isop flag.
+@isop:
+ ldb #0 ; Make the lexeme buffer, the first pointer.
+ stb.q idx1 ; Reset the first index.
+ jsr set_lexptr ; Set up the lexeme buffer.
+@isop_loop:
+ lda.w #mne ; Get pointer to the start of the instruction table.
+ clc ; Prepare for a non carrying add.
+ adc.w idx2 ; Offset the pointer, by the length of the previous string.
+ jsr strcasecmp ; Is the lexeme buffer, the same as the mnemonic string?
+ beq @found ; Yes, so create a new token.
+ ldb idx1 ; No, so Get the instruction ID.
+ cpb #OPNUM-1 ; Have we reached the end of the instruction table?
+ beq @end ; Yes, so we're done.
+ inc idx1 ; No, so increment the instruction ID.
+@offset:
+ lda #13 ; Get the base size of the instruction table.
+ clc ; Prepare for a non carrying multiply.
+ mul idx1 ; Multiply the base offset, by the instruction ID.
+ sta.w idx2 ; Save the offset in the third index.
+ jmp @isop_loop ; Keep looping.
+@found:
+ lda #TOK_MNE ; Set the lexeme type to TOK_MNE.
+ sta lex_type ;
+ inc b ; Set the isop flag.
+@end:
+ jsr make_tok ; Create the token.
+ jsr set_cmdbuf ; Set the first pointer to the command buffer.
+ rts ; End of parse_ptok.
+ptok_othr:
+ rts ; End of parse_ptok.
+
+
+set_lexptr:
+ lda.d #lexeme ; Set the pointer to the lexeme buffer.
+ jsr set_ptr ;
+ and #0 ; Reset A.
+ tab ; Reset B.
+ sta.q idx1 ; Reset the second index.
+ rts ; End of set_lexptr.
+
+
+set_cmdbuf:
+ ldb #0 ; Set the first pointer
+ lda.d #cmd_buf ; to the command buffer.
+ jsr set_ptr ;
+ and #0 ; Reset A.
+ tab ; Reset B.
+ rts ; End of set_cmdbuf.
+
+
+init_lex:
+ cpb #0 ; Do we need to increment the string index?
+ beq @init ; No, so skip that step.
+@inc_str:
+ inc.w idx0 ; Yes, so increment the string index.
+@init:
+ lda #0 ; Reset A.
+ sta.q idx1 ; Reset the second index
+ sta.q idx2 ; Reset the third index
+ ldb #2 ; Make the lexeme buffer, the third pointer.
+ jsr set_lexptr ; Set up the lexeme buffer.
+ rts ; End of init_lex.
+
+
+delmcpy:
+ sta a ; Save the delimiter check flag.
+ stb c ; Save the delimiter comparison value.
+@loop:
+ ldy.w idx0 ; Get the string index.
+ lda (ptr), y ; Get a character from the line.
+ pha #1 ; Preserve the character.
+ lda a ; Are we calling isdelm2?
+ pla #1 ; Get the character back.
+ bne @isdelm2 ; Yes, so use isdelm2.
+ jsr isdelm ; No, so get the delimiter value from isdelm.
+ and c ; Are both delimiter values, the same?
+ bne @end ; Yes, so we're done.
+ jmp @copy ; No, so start copying the character.
+@isdelm2:
+ jsr isdelm2 ; Get the delimiter value from isdelm2.
+ cmp c ; Are both delimiter values, the same?
+ beq @end ; Yes, so we're done.
+@copy:
+ lda (ptr), y ; Get a character from the line.
+ ldy.w idx1 ; Get the lexeme index.
+ sta (ptr3), y ; Copy the character to the lexeme buffer.
+ inc.w idx0 ; Increment the string index.
+ inc.w idx1 ; Increment the lexeme index.
+ jmp @loop ; Keep looping.
+@end:
+ lda #0 ; Terminate the lexeme buffer.
+ sta (ptr3), y ;
+ rts ; End of delmcpy.
+
+
+get_rs:
+ phb #1 ; Preserve B.
+ ldb #0 ; Set the isop flag to false.
+ plb #1 ; Get B back.
+ jsr tolower ; Convert the character to lowercase.
+ cmp #'w' ; Is it .w?
+ beq @r1 ; Yes, so return 1.
+ cmp #'d' ; No, but was it .d?
+ beq @r2 ; Yes, so return 2.
+ cmp #'q' ; No, but was it .d?
+ beq @r3 ; Yes, so return 3.
+@r0:
+ lda #0 ; Return 0.
+ rts ; End of get_rs.
+@r1:
+ lda #1 ; Return 1.
+ rts ; End of get_rs.
+@r2:
+ lda #2 ; Return 2.
+ rts ; End of get_rs.
+@r3:
+ lda #3 ; Return 3.
+ rts ; End of get_rs.
+
+
+make_tok:
+ nop ;
+@end:
+ rts ; End of make_tok.
+
+; Entry point for utility subroutines.
+utils:
diff --git a/programs/sub-suite/subasm.s b/programs/sub-suite/subasm.s
new file mode 100644
index 0000000..9c6c3f0
--- /dev/null
+++ b/programs/sub-suite/subasm.s
@@ -0,0 +1,388 @@
+; SuBAsm
+; The Sux Bootstrapped Assembler.
+;
+; by mr b0nk 500 <b0nk@b0nk.xyz>
+
+MAX_SYM = $800 ; Max symbol size.
+OPNUM = 88 ; Instruction count.
+
+.include "lexer.s"
+.include "utils.s"
+
+.org incl
+; String Constants.
+asm_name:
+ .byte "SuBAsm"
+asm_ver:
+ .byte "0.1"
+
+; Directives.
+dir:
+ .byte "org"
+ .byte "byte"
+ .byte "word"
+ .byte "dword"
+ .byte "qword"
+ .byte "include"
+
+; Short form Commands.
+sh_cmds:
+ .byte "vlahirs"
+
+; Commands.
+cmds:
+ .byte "viewmem"
+ .byte "list"
+ .byte "asm"
+ .byte "help"
+ .byte "inst"
+ .byte "run"
+ .byte "set"
+
+; Instruction mnemonics, and opcodes.
+
+; Legend.
+; mne = Mnemonic.
+; imm = Immediate data.
+; zm = Zero Matrix.
+; zmx = Zero Matrix, indexed with X.
+; zmy = Zero Matrix, indexed with Y.
+; ind = Indirect.
+; idx = Indexed Indirect.
+; idy = Indirect Indexed.
+; abs = Absolute.
+; imp = Implied.
+
+mne:
+; mne imm, zm, zmx, zmy, ind, idx, idy, abs, imp
+ .byte "CPS", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $00
+ .byte "ADC", $01, $06, $FF, $FF, $FF, $FF, $FF, $04, $FF
+ .byte "AAB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $02
+ .byte "PHP", $08, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
+ .byte "CPB", $09, $2D, $FF, $FF, $55, $AD, $AC, $2C, $FF
+ .byte "PHB", $0A, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
+ .byte "DEC", $FF, $0D, $FF, $FF, $FF, $FF, $FF, $0C, $E5
+ .byte "JMP", $FF, $0E, $FF, $FF, $CE, $FF, $FF, $10, $FF
+ .byte "SBC", $11, $16, $FF, $FF, $FF, $FF, $FF, $14, $FF
+ .byte "SAB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $12
+ .byte "ENT", $18, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
+ .byte "CPY", $19, $3D, $FF, $FF, $85, $FF, $FF, $4C, $FF
+ .byte "PLB", $1A, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
+ .byte "INC", $FF, $1D, $FF, $FF, $FF, $FF, $FF, $1C, $F5
+ .byte "JSR", $FF, $1E, $FF, $FF, $BE, $FF, $FF, $20, $FF
+ .byte "AND", $21, $26, $FF, $FF, $FF, $FF, $FF, $24, $FF
+ .byte "ABA", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $22
+ .byte "PLP", $28, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
+ .byte "CPX", $29, $4D, $FF, $FF, $B5, $FF, $FF, $3C, $FF
+ .byte "PHY", $2A, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
+ .byte "BPO", $FF, $2E, $FF, $FF, $FF, $FF, $FF, $30, $FF
+ .byte "ORA", $31, $36, $FF, $FF, $FF, $FF, $FF, $34, $FF
+ .byte "OAB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $32
+ .byte "STT", $38, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
+ .byte "PLY", $3A, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
+ .byte "BNG", $FF, $3E, $FF, $FF, $FF, $FF, $FF, $40, $FF
+ .byte "XOR", $41, $46, $FF, $FF, $FF, $FF, $FF, $44, $FF
+ .byte "XAB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $42
+ .byte "PHA", $48, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
+ .byte "PHX", $4A, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
+ .byte "BCS", $FF, $4E, $FF, $FF, $FF, $FF, $FF, $50, $FF
+ .byte "LSL", $51, $56, $FF, $FF, $FF, $FF, $FF, $54, $FF
+ .byte "LLB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $52
+ .byte "CLC", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $58
+ .byte "PLX", $5A, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
+ .byte "BCC", $FF, $5E, $FF, $FF, $FF, $FF, $FF, $60, $FF
+ .byte "LSR", $61, $66, $FF, $FF, $FF, $FF, $FF, $64, $FF
+ .byte "LRB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $62
+ .byte "PLA", $68, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
+ .byte "TAB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $6A
+ .byte "BEQ", $FF, $6E, $FF, $FF, $FF, $FF, $FF, $70, $FF
+ .byte "ROL", $71, $76, $FF, $FF, $FF, $FF, $FF, $74, $FF
+ .byte "RLB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $72
+ .byte "SEC", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $78
+ .byte "TBA", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $7A
+ .byte "BNE", $FF, $7E, $FF, $FF, $FF, $FF, $FF, $80, $FF
+ .byte "ROR", $81, $86, $FF, $FF, $FF, $FF, $FF, $84, $FF
+ .byte "RRB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $82
+ .byte "DEY", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $88
+ .byte "TAY", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $8A
+ .byte "BVS", $FF, $8E, $FF, $FF, $FF, $FF, $FF, $90, $FF
+ .byte "MUL", $91, $96, $FF, $FF, $FF, $FF, $FF, $94, $FF
+ .byte "MAB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $92
+ .byte "CLI", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $98
+ .byte "TYA", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $9A
+ .byte "BVC", $FF, $9E, $FF, $FF, $FF, $FF, $FF, $A0, $FF
+ .byte "DIV", $A1, $A6, $FF, $FF, $FF, $FF, $FF, $A4, $FF
+ .byte "DAB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $A2
+ .byte "INY", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $A8
+ .byte "TAX", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $AA
+ .byte "RTS", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $AE
+ .byte "CMP", $B1, $B6, $FF, $FF, $25, $7D, $7C, $B4, $FF
+ .byte "CAB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $B2
+ .byte "SEI", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $B8
+ .byte "LDX", $B9, $BD, $FF, $C9, $95, $FF, $FF, $BC, $FF
+ .byte "TXA", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $BA
+ .byte "RTI", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $C0
+ .byte "LDA", $C1, $C6, $79, $39, $05, $5D, $5C, $C4, $FF
+ .byte "DEX", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $C5
+ .byte "CLV", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $C8
+ .byte "TYX", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $CA
+ .byte "STA", $FF, $CD, $89, $49, $15, $6D, $6C, $CC, $FF
+ .byte "TSX", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $D0
+ .byte "LDB", $D1, $D6, $99, $59, $35, $8D, $8C, $D4, $FF
+ .byte "INX", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $D5
+ .byte "WAI", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $D8
+ .byte "TXY", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $DA
+ .byte "STB", $FF, $DD, $A9, $69, $45, $9D, $9C, $DC, $FF
+ .byte "TXS", $E0, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
+ .byte "LDY", $E1, $E6, $E9, $FF, $65, $FF, $FF, $E4, $FF
+ .byte "BRK", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $E8
+ .byte "NOP", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $EA
+ .byte "STY", $FF, $ED, $F9, $FF, $75, $FF, $FF, $EC, $FF
+ .byte "DEB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $EE
+ .byte "ASR", $F1, $F6, $FF, $FF, $FF, $FF, $FF, $F4, $FF
+ .byte "ARB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $F2
+ .byte "STX", $FF, $FD, $FF, $D9, $A5, $FF, $FF, $FC, $FF
+ .byte "INB", $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FE
+
+; Command subroutine table.
+cmd_srt:
+ .word viewmem
+ .word list
+ .word asm
+ .word help
+ .word inst
+ .word run
+ .word set
+
+; Data entry point for the lexer.
+lexer_data:
+
+
+; Token table.
+.org $20000
+tokline:
+
+.org cmd_buf+$400
+; Program Counter.
+prg_cnt:
+ .qword 0
+; Hex digit string buffer.
+hex_str:
+ .qword 0, 0
+; String buffer.
+strbuf:
+
+.org strbuf+$80
+; Subroutine pointer.
+sub_ptr:
+ .word 0
+
+; Indecies.
+idx0:
+ .qword 0
+idx1:
+ .qword 0
+idx2:
+ .qword 0
+idx3:
+ .qword 0
+
+; Current token line.
+ctok:
+ .word 0
+
+; Last token line.
+ltok:
+ .word 0
+
+; Lexeme type.
+lex_type:
+ .byte 0
+
+; Lexeme string.
+lexeme:
+
+; Symbol table.
+.org lexeme+$100
+sym:
+
+; Fixup table.
+; Fixups are unresolved symbols.
+.org sym+$8000
+fix:
+
+
+; Start of program code.
+.org parser
+subasm:
+ ldb #0 ; Set the first pointer
+ lda.d #cmd_buf ; to the command buffer.
+ jsr set_ptr ;
+ tba ; Reset A.
+ tax ; Reset X.
+ jsr chk_shcmd ; Did we get a shortend command?
+ bne @cmd ; Yes, so skip everything else.
+ jsr chk_cmd ; No, but did we get a full command?
+ bne @cmd ; Yes, so skip everything else.
+ jsr lex ; No, so start lexing this line.
+ jmp @end ; We are done.
+@cmd:
+ ldb #1 ; Set the second pointer
+ lda.d #cmd_srt ; to the command subroutine table.
+ jsr set_ptr ;
+ deb ; Reset B.
+ tba ; Reset A.
+ lda f ; Get the command ID.
+ cmp #8 ; Is the command ID greater than the command count?
+ bcs @end ; Yes, so we're done.
+ lsl #1 ; No, so multiply the command ID by two.
+ phy #2 ; Preserve the screen buffer position.
+ tay ; Set the index to the offset that we just calculated.
+ lda.w (ptr2), y ; Get the command subroutine, from the command subroutine table.
+ ply #2 ; Get back the screen buffer position.
+ ldb #2 ; Save it in the third pointer.
+ jsr set_ptr ;
+ ldb #0 ; Reset B.
+ jsr (ptr3) ; Run the command's subroutine.
+@end:
+ rts ; End of subasm.
+
+chk_shcmd:
+ tba ; Reset A.
+ inb ; Set the second pointer
+ lda.w #sh_cmds ; to the shortend command table.
+ jsr set_ptr ;
+ deb ; Reset B.
+ tba ; Reset A.
+ phy #2 ; Preserve the screen buffer position.
+ txy ; Set our index to zero.
+ lda (ptr), y ; Is there nothing in the command buffer?
+ beq @false ; Yes, so return that we failed.
+ cmp #' ' ; No, but is this character, a space?
+ beq @false ; Yes, so return that we failed.
+ jsr tolower ; No, so convert it to lowercase.
+@loop:
+ ldb (ptr2), y ; Are we at the end of the table?
+ beq @false ; Yes, so return that we failed.
+ cab ; No, so did the character match?
+ beq @found ; Yes, so check if there are any arguments.
+ iny ; No, so check the next command.
+ jmp @loop ; Keep looping.
+@found:
+ sty f ; Save the command ID.
+ ldy #1 ; Check the next character in the command buffer.
+ lda (ptr), y ; Is this the end of the buffer?
+ beq @true ; Yes, so return that we succeded.
+ cmp #' ' ; No, but is this a space?
+ beq @true ; Yes, so return that we succeded.
+ jmp @false ; No, so return that we failed.
+@true:
+ lda #1 ; Return true.
+ jmp @end ; We are done.
+@false:
+ ldb #0 ; Reset B.
+ tba ; Return false.
+ tax ; Reset X.
+@end:
+ ply #2 ; Get back the screen buffer position.
+ rts ; End of chk_shcmd.
+
+
+chk_cmd:
+ tba ; Reset A.
+ tax ; Reset X.
+ sta.q idx0 ; Reset the first index.
+ sta.q idx1 ; Reset the second index.
+@loop:
+ lda.w #cmds ; Get pointer to the start of the command table.
+ clc ; Prepare for a non carrying add.
+ adc.w idx0 ; Offset the pointer, by the length of the previous string.
+ pha #8 ; Preserve the command string pointer.
+ jsr strcasecmp ; Is the command buffer, the same as the command string?
+ pla #8 ; Get the command string pointer back.
+ beq @true ; Yes, so return true.
+ ldb idx1 ; No, so Get the command ID.
+ cpb #7 ; Have we reached the end of the command table?
+ beq @false ; Yes, so return false.
+ inc idx1 ; No, so increment the command ID.
+@getlen:
+ jsr strlen ; Get the string's length.
+ inx ; Add one to the length.
+ txa ; Place it in the accumulator.
+ clc ; Prepare for a non carrying add.
+ adc.w idx0 ; Add the string offset to the current length
+ sta.w idx0 ; Save the offset in the first index.
+ jmp @loop ; Keep looping.
+@true:
+ ldb idx1 ; Get the command ID.
+ stb f ; Return the command ID.
+ ldb #1 ; Return true.
+ jmp @end ; We are done.
+@false:
+ ldb #0 ; Return false.
+@end:
+ rts ; End of chk_cmd.
+
+viewmem:
+ lda.q prg_cnt ; Get the program counter.
+ sta.q idx0 ; Save the address in the first index.
+ and #$F0 ; Clear the first four bits of the address.
+ sta idx0 ; Overwrite the first byte, with the masked byte.
+ lda #19 ; Move the cursor to the right, by 19 columns.
+ sta scr_col ;
+ jsr update_pos ;
+ jsr print_lo ; Print the low nibble offsets.
+ ldx #0 ; Reset X.
+ ldb #0 ; Reset B.
+ stb idx1 ; Reset the byte count.
+@loop:
+ lda #'\n' ; Print a newline.
+ jsr print_char ;
+ jsr print_hi ; Place the address in the string buffer.
+ jsr print_chunk ; Place the next 16 bytes in the string buffer.
+ lda.d #strbuf ; Print the string buffer.
+ jsr print_str ;
+ inc idx1 ; Increment the chunk count.
+ ldb idx1 ; Get the chunk count.
+ cpb #$10 ; Did we print 16 chunks?
+ beq @end ; Yes, so we're done.
+ lda.q idx0 ; No, so get the address index.
+ clc ; Prepare for a non carrying add.
+ adc #$10 ; Add 16 to the address.
+ sta.q idx0 ; Put it back into the address.
+ and #0 ; Reset A.
+ jmp @loop ; Keep looping.
+@end:
+ lda #'\n' ; Print a newline.
+ jsr print_char ;
+ and #0 ; Reset A.
+ rts ; End of viewmem.
+
+
+list:
+ nop ;
+@end:
+ rts ; End of list.
+asm:
+ nop ;
+@end:
+ rts ; End of asm.
+help:
+ nop ;
+@end:
+ rts ; End of help.
+inst:
+ nop ;
+@end:
+ rts ; End of inst.
+run:
+ nop ;
+@end:
+ rts ; End of run.
+set:
+ nop ;
+@end:
+ rts ; End of set.
+
+; Entry point for utility subroutines.
+lexer:
diff --git a/programs/sub-suite/subeditor.s b/programs/sub-suite/subeditor.s
new file mode 100644
index 0000000..1768d62
--- /dev/null
+++ b/programs/sub-suite/subeditor.s
@@ -0,0 +1,1234 @@
+; SuBEditor.
+;
+; Writen in Sux assembly by
+; mr b0nk 500 <b0nk@b0nk.xyz>
+
+; I/O constants.
+status = $100 ; Keyboard status.
+scr = $101 ; Character that is to be printed.
+kbd = $102 ; Character from the Keyboard.
+step = $110 ; Enables clock stepping, when set.
+
+; Screen constants.
+maxrow = 23 ; Screen's row count.
+maxcol = 79 ; Screen's column count.
+
+; Include SuBAsm.
+.include "subasm.s"
+
+.org $A000
+; String Literals/Constants.
+tok:
+ .byte "dab"
+msg:
+ .byte "oof, you divided a, and b on me.\n"
+
+ed_name:
+ .byte "SuBEditor"
+ed_ver:
+ .byte "1"
+ed_sver:
+ .byte ".0.0"
+
+ver_str:
+ .byte ", version "
+made:
+ .byte "Created by, "
+
+author:
+ .byte "mr b0nk 500"
+
+string2:
+ .byte "You typed, "
+
+; Linewrap bitmask table.
+bits:
+ .byte $80, $40, $20, $10, $08, $04, $02, $01
+
+; This label is for any included files.
+incl:
+
+; Linewrap table.
+.org $30000
+bitabl:
+ .qword 0
+ .qword 0
+
+; SCreen buffer.
+.org bitabl+$1000
+buffer:
+
+; Command buffer.
+.org buffer+$2000
+cmd_buf:
+
+
+; Screen variables.
+.org 0
+scr_row:
+ .byte 0
+scr_col:
+ .byte 0
+scr_trow:
+ .byte 0
+scr_tcol:
+ .byte 0
+scr_ptr:
+ .word 0
+scr_ptr2:
+ .word 0
+scr_ptr3:
+ .word 0
+
+; Pseudo registers.
+a:
+ .byte 0
+b:
+ .byte 0
+c:
+ .byte 0
+d:
+ .byte 0
+e:
+ .byte 0
+f:
+ .byte 0
+g:
+ .byte 0
+; This pseudo register is always zero.
+zero:
+ .qword 0
+; End of pseudo registers.
+
+end:
+ .qword 0
+bitmask:
+ .byte 0
+scr_str:
+ .byte 0
+scr_end:
+ .byte 0
+wrapped:
+ .byte 0
+
+; Pointers
+ptr:
+ .qword 0
+ptr2:
+ .qword 0
+ptr3:
+ .qword 0
+
+; Main program
+.org $8000
+reset:
+ cps ; Reset the processor status register.
+ ldx.w #$FFFF ; Reset the stack pointer.
+ txs ;
+ ldy #0 ; Reset the Y register.
+ sty end ;
+ tyx ; Reset the X register.
+ lda #maxrow ; Set the end of the screen to the screen's max row count.
+ sta scr_end ;
+ tya ; Reset the Accumulator.
+ sta scr_str ; Set the start of the screen back to zero.
+ sta.q bitabl ; Reset the first half of the linewrap table.
+ sta.q bitabl+8 ; Reset the second half of the linewrap table.
+ inc end ;
+ lda.w #$1FFF ; Set the clear count to $1FFF.
+ sta.w scr_ptr ;
+ lda.d #buffer ; Set the array to be cleared to the screen buffer.
+ jsr clr_arr ; Clear the screen buffer.
+ jsr pnt_strt ; Print the starting message.
+ jmp start ; Goto the start of the main program.
+
+clr_arr:
+ phb #1 ; Preserve whatever was in B.
+ ldb #0 ; Clear B.
+ jsr set_ptr ; Set the first pointer to the parameter.
+ adc #8 ; Set the second pointer to the parameter, plus eight.
+ inb ; Tell set_ptr to set the second pointer.
+ jsr set_ptr ;
+ deb ; Set B back to zero.
+ tba ;
+@loop:
+ cpy.w scr_ptr ; Did we clear all of the array?
+ bcs @end ; Yes, so we're done.
+ sta.q (ptr), y ; No, so clear eight bytes.
+ sta.q (ptr2), y ; Clear eight more bytes.
+ tya ; Copy the array index.
+ adc #$10 ; Increment the index by 16.
+ tay ; Update the index.
+ tba ; Reset the Accumulator.
+ sta.q (ptr), y ; Do this one more time, to clear 32 bytes.
+ sta.q (ptr2), y ;
+ tya ;
+ adc #$10 ;
+ tay ;
+ tba ;
+ jmp @loop ; Keep looping.
+@end:
+ ldy.w zero ; Set the index back to zero.
+ plb #1 ; Get whatever was in the B register, back.
+ rts ; End of clr_arr.
+
+pnt_strt:
+ lda.w #ed_name ; Print the name of the editor.
+ jsr print_str ;
+ lda.w #ver_str ; Print the version text.
+ jsr print_str ;
+ lda.w #ed_ver ; Print the version number.
+ jsr print_str ;
+ lda.w #ed_sver ; Print the sub version number.
+ jsr print_str ;
+ lda #'\n' ; Print a newline.
+ jsr print_char ;
+ lda.w #made ; Print the "Created by" text.
+ jsr print_str ;
+ lda.w #author ; Print the name of the author.
+ jsr print_str ;
+ lda #'\n' ; Print a newline.
+ jsr print_char ;
+ rts ; End of pnt_strt.
+
+start:
+ lda #0 ; TODO: Update this for the Super VIA.
+ sta status ; Clear the control register of the I/O adapter.
+ tax ; Reset X.
+ phy #2 ; Save the cursor index for later.
+ tay ; Reset the cursor index.
+ lda.w #$3FF ; Set the clear count to $3FF.
+ sta.w scr_ptr ;
+ lda.d #cmd_buf ; Set the array to be cleared to the command buffer.
+ jsr clr_arr ; Clear the command buffer.
+ ply #2 ; Get back the cursor index.
+ and #0 ; Reset the Accumulator.
+ sta end ;
+ jmp read ; Start reading the keyboard.
+
+read:
+ lda #0 ; Reset the Accumulator.
+ sta end ; Disable the dummy flag.
+ inc end ; Enable the dummy flag.
+ lda status ; Did we get a key?
+ beq read ; No, so try again.
+ jsr getchar ; Yes, and was it a newline?
+ beq parse ; Yes, so start parsing the line.
+ jmp read ; No, so keep looping.
+
+print_str:
+ ldx #0 ; Reset X.
+ sta.q end ; Save the parameter.
+@reset:
+ lda.q end ; Get the parameter.
+ ldb #0 ; Clear the B register.
+ jsr set_ptr ; Set the first pointer to the parameter.
+ tba ; Clear the Accumulator.
+@loop:
+ ldb #1 ; Enable replace mode.
+ stb b ;
+ lda.q ptr ; Get the first pointer.
+ cmp.q end ; Did the pointer change?
+ bne @reset ; Yes, so set it back.
+ and #0 ; No, reset the accumulator.
+ phy #2 ; Save the cursor index.
+ txy ; Copy the string index into Y.
+ lda (ptr), y ; Are we at the end of the string?
+ ply #2 ; Get the cursor index back.
+ beq @end ; Yes, so we're done.
+ inx ; No, so increment the string index.
+ jsr print_char ; Print the character.
+ jmp @loop ; Keep looping.
+@end:
+ ldb #0 ; Enable insert mode.
+ stb b ;
+ rts ; End of print_str.
+
+getbit:
+ clc ; Clear the carry flag.
+ lda scr_str ; Has the screen been scrolled?
+ bne getbt0 ; Yes, so add the screen offset to the current line number.
+ ldx scr_row ; No, so just use the current line number.
+ jmp getbt1 ; Start getting the bit.
+getbt0:
+ lda scr_row ; Get the current line number.
+ adc scr_str ; Add the screen offset to it.
+ tax ; Use it as the wrap index.
+getbt1:
+ pha #1 ; Save the parameter.
+ ldb #1 ; Make sure that set_ptr sets the second pointer.
+ lda.d #bitabl ; Set the second pointer to the linewrap table.
+ jsr set_ptr ;
+ lsr #$10 ; Clear the Accumulator.
+ pla #1 ; Get the return byte back.
+ jsr bitpos ; Get the bit, and byte position.
+ phy #2 ; Save the screen index.
+ txy ; Get the byte position.
+ ldb (ptr2), y ; Get one byte of the wrap table.
+ ply #2 ; Get the screen index back.
+ aba ; Mask out the bit of the current line number.
+ cmp #1 ; Set the carry flag, if true.
+ jmp bitout ; We are done.
+
+clrbit:
+ pha #1 ; Save the parameter.
+ ldb #1 ; Make sure that set_ptr sets the second pointer.
+ lda.d #bitabl ; Set the second pointer to the linewrap table.
+ jsr set_ptr ;
+ and #0 ; Clear the Accumulator.
+ pla #1 ; Get the return byte back.
+ jsr bitpos ; Get the bit, and byte position.
+ xor #$FF ; Invert the bitmask.
+ phy #2 ; Save the screen index.
+ txy ; Get the byte position.
+ ldb (ptr2), y ; Get one byte of the wrap table.
+ aba ; Clear the bit of the current line number.
+bitsav:
+ sta (ptr2), y ; Update the wrap table.
+ ply #2 ; Get the screen index back.
+bitout:
+ ldx bitmask ; Return the bitmask.
+ rts ; We are done.
+
+setbit:
+ pha #1 ; Save the parameter.
+ ldb #1 ; Make sure that set_ptr sets the second pointer.
+ lda.d #bitabl ; Set the second pointer to the linewrap table.
+ jsr set_ptr ;
+ and #0 ; Clear the Accumulator.
+ pla #1 ; Get the return byte back.
+ jsr bitpos ; Get the bit, and byte position.
+ phy #2 ; Save the screen index.
+ txy ; Get the byte position.
+ ldb (ptr2), y ; Get one byte of the wrap table.
+ oab ; Set the bit of the current line number.
+ jmp bitsav ; Save the bit.
+
+bitpos:
+ pha #1 ; Save the parameter.
+ ldb #0 ; Make sure that set_ptr sets the first pointer.
+ lda.w #bits ; Set the first pointer to the bitmask table.
+ jsr set_ptr ;
+ and #0 ; Clear the Accumulator.
+ pla #1 ; Get the parameter back.
+ stx bitmask ; Make the line number the bitmask.
+ txa ; Copy it to the Accumulator.
+ and #7 ; Get the bit position.
+ phy #2 ; Save the cursor index.
+ tay ; Use the bit position as the index.
+ tax ; Copy it into X.
+ lda (ptr), y ; Get the bitmask.
+ ply #2 ; Get back the cursor index.
+ pha #1 ; Save the bitmask.
+ lda bitmask ; Get the line number.
+ lsr #3 ; Get the byte position.
+ tax ; Copy it into X.
+ pla #1 ; Get back the bitmask.
+ rts ; End of bitpos.
+
+getchar:
+ lda kbd ; Get the character that was typed from the keyboard.
+ ldb #0 ; Reset the B register.
+ stb e ; Set the temporary row position to zero, in case we get a newline.
+ stb b ; Enable insert mode.
+ pha #1 ; Save the character.
+ phy #2 ; Save the cursor index.
+ cmp #'\n' ; Was the character that was typed, a newline?
+ bne @print ; No, so just print the character.
+ jsr cmd_cpy ; Yes, so start copying the line to the command buffer.
+@print:
+ ply #2 ; Get back the cursor index.
+ pla #1 ; Get back the character.
+ ldb e ; Is the temporary row position non zero?
+ bne @row ; Yes, so reset the row positon.
+@print1:
+ jsr print_char ; No, so print the character.
+ lda a ; Get the return value.
+ cmp #'\n' ; Is the return value, a newline?
+ beq @true ; Yes, so return true.
+ jmp @false ; No, so return false.
+@row:
+ ldb e ; Get the temporary row position.
+ cpb #maxrow ; Is temporary row position, at, or above the bottom of the screen?
+ beq @row2 ; Yes, so leave it as is.
+ bcs @row1 ; No, so set it to the bottom of the screen.
+ jmp @row2 ; Yes, so leave it as is.
+@row1:
+ ldb #maxrow ; Set the row position to the bottom of the screen.
+@row2:
+ stb scr_row ; Set the row position.
+ jmp @print1 ; Print the character.
+@true:
+ lda #0 ; Return true.
+ jmp @end ; We are done.
+@false:
+ lda #1 ; Return false.
+@end:
+ rts ; End of getchar.
+
+
+cmd_cpy:
+ lda scr_row ; Get the row position.
+ sta scr_trow ; Save it for later.
+ jsr findend ; Find the end of the line.
+ ldb scr_str ; Has the screen been scrolled?
+ beq @start ; No, so don't subtract the screen's starting point from the line number.
+@offset:
+ sec ; Yes, so make sure that we don't subtract by the starting point, plus one.
+ sbc scr_str ; Offset the row position, back by the screen's starting point.
+ clc ; Clear the carry flag, so that nothing odd occurs.
+@start:
+ sta scr_row ; Set the row position to the end of the line.
+ sta e ; Save it into the temporary row posiition.
+ jsr findst ; Find the start of the line.
+ clc ; Clear the carry flag.
+ lda scr_row ; Get the row position.
+ adc scr_str ; Add it with the screen's starting row.
+ mul #maxcol+1 ; Multiply it with the width of the screen, plus one.
+ tay ; Place it into the index.
+ ldx.w #0 ; Reset the X register.
+ ldb #0 ; Make sure that set_ptr sets the first pointer.
+ lda.d #buffer ; Set the first pointer to the start of the screen buffer.
+ jsr set_ptr ;
+ inb ; Make sure that set_ptr sets the second pointer.
+ lda.d #cmd_buf ; Set the second pointer to the start of the command buffer.
+ jsr set_ptr ;
+ deb ; Set B back to zero.
+ tba ; Set the accumulator to zero.
+@loop:
+ ldb #0 ; Reset the B register.
+ lda.q (ptr), y ; Get eight bytes from the current line.
+@loop1:
+ phy #2 ; Save the screen index.
+ txy ; Get the command buffer index.
+ sta (ptr2), y ; Copy one byte from the screen buffer, to the command buffer.
+ inx ; Increment the command buffer index.
+ ply #2 ; Get back the screen index.
+ cpx.w #$3FF ; Are we at the end of the command buffer?
+ bcs @end ; Yes, so we're done.
+ iny ; No, so increment the screen index.
+ inb ; Increment the byte count.
+ lsr #8 ; Shift in the next byte.
+ stb g ; Save the byte count.
+ tab ; Save the string buffer.
+ and #$FF ; Is this byte of the buffer, a null terminator?
+ beq @end1 ; Yes, so we're done.
+ tba ; No so get back the string buffer.
+ ldb g ; Get back the byte count.
+ cpb #7 ; Did we shift in eight bytes?
+ beq @loop ; Yes, so get eight more bytes.
+ jmp @loop1 ; No, so keep shifting in more bytes.
+@end:
+ ldb #0 ; Reset B.
+ phy #2 ; Save the screen index.
+ txy ; Get the command buffer index.
+ stb (ptr2), y ; Terminate the command buffer.
+ ply #2 ; Get back the screen index.
+@end1:
+ tab ; The B register is zero, so clear the Accumulator.
+ rts ; End of cmd_cpy.
+
+
+findst:
+ lda #0 ; Reset A.
+@loop:
+ pha #1 ; Save the current line number.
+ jsr getbit ; Is this the start of the line?
+ pla #1 ; Get the current line number back.
+ bcc @end ; Yes, so we're done.
+ inc ; No, so check the next physical line.
+ dec scr_row ; Are we at the top of the screen?
+ bpo @loop ; No, so keep looping.
+ dec ; Yes, so move back one line.
+ inc scr_row ; Put the row postiion back to zero.
+@end:
+ cmp #0 ; Update all the flags.
+ rts ; End of findst.
+
+
+fndend:
+ phb #1 ; Save the contents of the B register.
+ ldb #0 ; Make sure that set_ptr sets the first pointer.
+ lda.d #buffer ; Set the first pointer to the start of the screen buffer.
+ jsr set_ptr ;
+ tba ; Set the Accumulator to zero.
+ plb #1 ; Restore the contents of the B register.
+ phy #2 ;
+@loop:
+ lda (ptr), y ; Are we at the end of the string?
+ beq @end ; Yes, so we're done.
+ iny ; No, so increment the cursor index.
+ jmp @loop ; Keep looping.
+@end:
+ sty.w scr_ptr3 ;
+ ply #2 ;
+ rts ; End of fndend.
+
+findend:
+ jsr fndend ;
+ lda.w scr_ptr3 ;
+ div #maxcol+1 ;
+ rts ;
+
+parse:
+ lda #0 ;
+ tax ;
+ jsr subasm ;
+ jmp start ;
+
+print_char:
+ sta a ; Save the typed character for now.
+ ldb #2 ; Make sure that set_ptr sets the third pointer.
+ lda.d #buffer ; Set the third pointer to the start of the screen buffer.
+ jsr set_ptr ;
+ ldb #0 ; Set B to zero.
+ tba ; Set the Accumulator to zero.
+ lda a ; Get back the character.
+ cmp #$1B ; Did the user type an escape character?
+ beq esc ; Yes, so go check the escape code.
+ cmp #'\n' ; No, but did the user type a newline?
+ beq nl ; Yes, so handle the newline.
+ cmp #$C ; No, but did the user type Ctrl+L?
+ beq clr_scr ; Yes, so clear the screen.
+ cmp #19 ; No, but did the user type Ctrl+S?
+ beq en_step ; Yes, so enable clock/instruction stepping.
+ cmp #18 ; No, but did the user type Ctrl+R?
+ beq dis_step ; Yes, so disable clock/instruction stepping.
+ cmp #'\b' ; No, but did the user type a backspace?
+ beq bs ; Yes, so handle the backspace.
+ cmp #$7F ; No, but did they type Delete?
+ beq bs ; Yes, so treat it as a backspace.
+printc:
+ lda #0 ; No, so start trying to print a character.
+ sta d ;
+ lda (ptr3), y ; Are we at the end of the string?
+ beq @save ; Yes, so just print the character.
+ lda b ; No, but was the flag set?
+ bne @save ; Yes, so don't shift the line.
+ sty.w scr_ptr ; No, so save the cursor index for later.
+ jsr fndend ; Find the end of the line.
+ jmp @shift ; Start shifting the line right.
+@update:
+ lda scr_col ; Save the current column position for later.
+ sta scr_tcol ;
+@update1:
+ jsr findend ; Find the end of the line.
+ sta e ; Use it for redrawing the line.
+ sta scr_row ; Set the row position to to the end of the line.
+ jsr findst ; Find the start of the line.
+ lda scr_row ; Get the start of the line.
+@update2:
+ sta f ; Set the starting line, to the start of the line.
+ jsr rdrw_ln ; Redraw the line.
+ lda scr_trow ; Get the real row position back.
+ sta scr_row ;
+ lda scr_tcol ; Get the real column position back.
+ sta scr_col ;
+ jsr update_pos ; Update the cursor's position.
+ dec d ;
+ jmp @save1 ;
+@shift:
+ ldy.w scr_ptr3 ;
+ inc scr_ptr3 ;
+ tyx ;
+ dey ;
+ ldb #1 ;
+ stb d ;
+ jsr shftln ;
+ ldb #1 ;
+ stb d ;
+ lda a ;
+ sta (ptr3), y ; store typed character into the input buffer.
+ lda scr_row ;
+ sta scr_trow ;
+ jmp @update ;
+@save:
+ ldb d ;
+ bne @update ;
+@save1:
+ lda a ;
+ sta (ptr3), y ; store typed character into the input buffer.
+@incr:
+ inc scr_col ; Increment the cursor's x coordinate.
+ iny ;
+@wrapped:
+ ldb #1 ;
+ stb f ;
+ ldb scr_col ;
+ cpb #maxcol+1 ;
+ bcs @scrolled ;
+@print:
+ sta scr ; Echo typed character.
+ ldb f ;
+ beq @wrap ;
+ jmp printc_end ;
+@scrolled:
+ ldb scr_row ;
+ cpb #maxrow ;
+ bcs @scroll ;
+@wrapped2:
+ ldb #0 ;
+ stb f ;
+ jmp @print ;
+@scroll:
+ sta scr ; Echo typed character.
+ clc ;
+ lda #1 ;
+ sta wrapped ;
+ jsr scrl_down ;
+@wrap:
+ ldb #0
+ stb scr_col ;
+ ldb scr_row ;
+ cpb #maxrow ;
+ bcs @wrap2 ;
+@wrap1:
+ inc scr_row ;
+@wrap2:
+ phx #2 ;
+ clc ;
+ lda scr_row ;
+ adc scr_str ;
+ tax ;
+ jsr setbit ;
+ plx #2 ;
+ jsr update_pos ;
+printc_end:
+ rts ;
+
+nl:
+ lda #0 ; Reset A.
+ ldb (ptr3), y ; Is this character not a null terminator?
+ bne @scroll ; Yes, so don't overwrite it.
+ sta (ptr3), y ; No, so overwrite it.
+@scroll:
+ sta scr_col ; Move the cursor to the start of the next line.
+ lda scr_row ; Get the row position.
+ cmp #maxrow ; Are we at the bottom of the screen?
+ bcc @incr ; No, so move down one line.
+ jsr scrl_down ; Yes, so scroll down one line.
+ jmp @end ; We are done.
+@incr:
+ inc scr_row ; Move the cursor down by one line.
+ jsr update_pos ; Update the cursor's position.
+@end:
+ lda #'\n' ; Print the newline.
+ sta a ;
+ jmp printc_end ;
+
+clr_scr:
+ lda #maxrow ;
+ sta scr_end ;
+ lda #0 ;
+ sta scr_str ;
+ sta.q bitabl ;
+ sta.q bitabl+8 ;
+ tay ;
+ lda.w #$1FFF ; Set the clear count to $1FFF.
+ sta.w scr_ptr ;
+ lda.d #buffer ; Set the array to be cleared to the screen buffer.
+ jsr clr_arr ; Clear the screen buffer.
+ tay ;
+ lda.w #$3FF ; Set the clear count to $3FF.
+ sta.w scr_ptr ;
+ lda.d #cmd_buf ; Set the array to be cleared to the command buffer.
+ jsr clr_arr ; Clear the screen buffer.
+ sta scr_col ;
+ sta scr_row ;
+ jsr update_pos ;
+ lda #$C ;
+ sta scr ;
+ jmp printc_end ;
+
+en_step:
+ lda step ;
+ beq step_en ;
+ jmp printc_end ;
+step_en:
+ lda #1 ;
+ sta step ;
+ jmp printc_end ;
+
+dis_step:
+ lda step ;
+ bne step_dis ;
+ jmp printc_end ;
+step_dis:
+ lda #0 ;
+ sta step ;
+ jmp printc_end ;
+
+back:
+ ldb #0 ; Reset B, and some flags.
+ stb e ;
+ stb f ;
+ lda scr_row ; Save the current row position for later.
+ sta scr_trow ;
+ jsr findend ; Find the end of the line.
+ sta scr_row ; Set our row position to the end of the line.
+@find_st:
+ jsr findst ; Does this line take up more than one real line?
+ beq @shift ; No, so skip updating any other lines.
+ bcs @update ; Yes, so update the other lines.
+ lda scr_trow ; Get the real row position back.
+ sta scr_row ;
+@shift:
+ dey ; Decrement the buffer's offset.
+ lda #0 ; Place a null terminator
+ sta (ptr3), y ; into the buffer.
+ tyx ; Copy the current cursor index to X.
+ iny ; Increment cursor index.
+ ldb #0 ; Set shifting direction to left.
+ stb d ;
+ jsr shftln ; Shift line back by one character.
+ lda #$7F ; Print a backspace to the screen.
+ sta scr ;
+ lda e ; Are we updating more than one line?
+ beq @load ; No, so skip to the next step.
+@find_end:
+ jsr findend ; Yes, so find the end of the line.
+ sta e ; Set the end parameter to it.
+ lda scr_col ; Save the current column position for now.
+ sta scr_tcol ;
+ jsr rdrw_ln ; Start redrawing the line.
+ lda scr_tcol ; Get the real column position back.
+ sta scr_col ;
+@load:
+ lda scr_trow ; Get the real row position back.
+ sta scr_row ;
+ dec scr_col ; Move the cursor back by one column,
+ jsr update_pos ; and update it's position.
+ jmp printc_end ; We are done.
+@update:
+ lda scr_row ; Set the line to start redrawing, to the start of the line.
+ sta f ;
+ inc e ; Set the redraw flag to true.
+ jmp @shift ; Start shifting the line back.
+
+bs:
+ lda scr_col ; Are we at the far left of the screen?
+ beq @wrap ; Yes, so check for a wrapped line.
+ jmp back ; No, so add the backspace to the buffer.
+@wrap:
+ jsr getbit ; Is this line, a wrapped line?
+ bcs @wrap1 ; Yes, so check if the cursor is at the top.
+ jmp printc_end ; No, so we're done.
+@wrap1:
+ lda scr_row ; Are we at the top of the screen?
+ beq @wrap2 ; Yes, so check if the screen is at the top of the buffer.
+ jmp @wrap3 ; No, so start clearing the wrap bit.
+@wrap2:
+ lda scr_str ; Are we at the top of the buffer?
+ bne @scroll ; Yes, so scroll up.
+ jmp printc_end ; No, so we're done.
+@scroll:
+ clc ; Clear the carry flag, so that we don't get odd behaviour.
+ jsr scrl_up ; Scroll up.
+ inc scr_row ; Move down by one row.
+@wrap3:
+ clc ; Clear the carry flag.
+ lda scr_row ; Add the cursor's row position,
+ adc scr_str ; and the screen's starting row.
+ tax ; Transfer that into X.
+@wrap4:
+ dec scr_row ; Move up by one row.
+ ldb #maxcol+1 ; Move the cursor to the absolute right of the screen.
+ stb scr_col ;
+ jsr update_pos ; Update the cursor's position.
+ jmp back ; Delete the previous character.
+
+shftln:
+ ldb d ; Is the flag not set?
+ beq @dec_loop ; Yes, so shift, and decrement.
+ ldb #0 ; Clear the B register.
+ jmp @inc_loop ; No, so shift, and increment.
+@neg:
+ ldy.w zero ; Set the source poition to 0.
+ stb (ptr3), y ; Clear the character that is in the source.
+ jmp @end ; We are done.
+@inc_loop:
+ sty.w scr_ptr2 ; Save the source position for later.
+ ldy.w scr_ptr ; Get the previous cursor index.
+ cpy.w scr_ptr2 ; Is the source position, at, or below the cursor index?
+ beq @inc_loop1 ; Yes, so keep looping.
+ bcs @end ; No, so we're done.
+@inc_loop1:
+ ldy.w scr_ptr2 ; Get the source position.
+ lda (ptr3), y ; Get the character from the source position.
+ phy #2 ; Save the source position for later.
+ txy ; Set our position to the destination.
+ sta (ptr3), y ; Place the character from the source position, to the destination position.
+ ply #2 ; Set our position back to the source.
+ stb (ptr3), y ; Clear the character that is in the source.
+ bng @neg ; The source underflowed, so set it back to zero,
+ dey ; Decrement the source position.
+ dex ; Decrement the destination position.
+ jmp @inc_loop ; Keep looping.
+@dec_loop:
+ stx.w scr_ptr2 ; Save the destination position for later.
+ lda (ptr3), y ; Is the character at the source position, a null terminator?
+ beq @end3 ; Yes, so we're done.
+ phy #2 ; No, so save the source position for later.
+ txy ; Set our position to the destination.
+ sta (ptr3), y ; Place the character from the source position, to the destination position.
+ inx ; Increment the destination position.
+ ply #2 ; Set our position back to the source.
+ stb (ptr3), y ; Clear the character that is in the source.
+ iny ; Increment the source position.
+ jmp @dec_loop ; Keep looping.
+@wrap:
+ tax ; Use the ending line as a parameter for setbit.
+ jsr setbit ; Set the wrap bit of the ending line.
+ jmp @end5 ; We are done.
+@wrap1:
+ tax ; Use the ending line as a parameter for clrbit.
+ jsr clrbit ; Clear the wrap bit of the ending line.
+ jmp @end5 ; We are done.
+@end:
+ lda (ptr3), y ; Is this character a null terminator?
+ bne @end1 ; No, so just find the end of the line.
+ lda #$20 ; Yes, so convert it to a space for now.
+ sta (ptr3), y ;
+@end1:
+ jsr findend ; Find the ending line.
+ sta d ; Save ending line for later.
+ lda (ptr3), y ; Is this character a space?
+ cmp #$20 ;
+ bne @end5 ; No, so skip the conversion.
+ lda #0 ; Yes, so convert it back to zero.
+ sta (ptr3), y ;
+@end2:
+ lda d ; Get the ending line.
+ cmp scr_row ; Is the ending line greater than the starting line?
+ beq @end5 ; No, so we're done.
+ bcs @wrap ; Yes, so set the wrap bit.
+ jmp @end5 ; No, so we're done.
+@end3:
+ jsr findend ; Find the ending line.
+ cpb #0 ; Is the remainder zero?
+ beq @end4 ; Yes, so check if the ending line is greater than the starting line.
+ jmp @end5 ; No, so we're done.
+@end4:
+ cmp scr_row ; Is the ending line greater than the starting line?
+ beq @end5 ; No, so we're done.
+ bcs @wrap1 ; Yes, so clear the wrap bit.
+@end5:
+ rts ; End of shftln.
+
+esc:
+ lda status ; Get the next character.
+ lda kbd ;
+ cmp #$1B ; Is this character an escape character?
+ beq shftesc ; Yes, so check the other set of escape routines.
+ lda status ; No, so wait for the next character.
+ beq printc_end ; We have an error, so discard it, and go back to getting user input.
+ lda kbd ; Get the escape code.
+ sta c ; Store the escape code, until we need it.
+ lda #0 ; Set the D pseudo register to zero.
+ sta d ;
+ jsr isup ; Check if the user pressed up.
+ lda d ; Did the user press up?
+ bne esc_end ; Yes, so we're done.
+ jsr isdown ; No, so check if the user pressed down.
+ lda d ; Did the user press down?
+ bne esc_end ; Yes, so we're done.
+ lda #0 ; No, so check if the user pressed left.
+ jsr isleft ;
+ lda d ; Did the user press left?
+ bne esc_end ; Yes, so we're done.
+ jsr isright ; No, so check if the user pressed right.
+esc_end:
+ lda #0 ; Clear the D pseudo register.
+ sta d ;
+ jmp printc_end ; We are done.
+
+shftesc:
+ lda status ; Skip the '['.
+ lda kbd ;
+ lda status ; Wait for the next character.
+ beq printc_end ; We have an error, so discard it, and go back to getting user input.
+ lda kbd ; Get the escape code.
+ sta c ; Store the escape code, until we need it.
+ lda #0 ; Use the D pseudo register as a skip flag.
+ sta d ;
+ jsr isshftup ; Check if the user pressed shift+up.
+ lda d ; Was it successful?
+ bne shftesc_end ; Yes, so we're done.
+ jsr isshftdown ; No, so check if the user pressed shift+down.
+shftesc_end:
+ lda #0 ; Clear the D pseudo register.
+ sta d ;
+ jmp printc_end ; We are done.
+
+isup:
+ lda c ; Load the escape code into the accumulator.
+ cmp #'A' ; Did the user press the up arrow key?
+ bne @end ; No, so we're done.
+ lda scr_row ; Yes, but is the cursor at the top of the screen?
+ beq @scroll ; Yes, so check if we need to scroll.
+@check2:
+ lda c ; No, so load the escape code back into the accumulator.
+ cmp #'A' ; Did the user press the up arrow key?
+ beq up ; Yes, so move the cursor up.
+ jmp @end ; No, so we're done.
+@scroll:
+ lda scr_str ; Are we at the top of the screen buffer?
+ beq @end ; Yes, so we're done.
+ jsr scrl_up ; No, so scroll up.
+ lda #1 ; Tell the escape routine that we were successful.
+ sta d ;
+@end:
+isup_done:
+ rts ; End of isup.
+
+isdown:
+ lda c ; Load the escape code into the accumulator.
+ cmp #'B' ; Did the user press the down arrow key?
+ bne @end ; No, so we're done.
+ lda scr_row ; Yes, so start checking the y coordinate of the cursor.
+ cmp #maxrow ; Is the cursor at the bottom of the screen?
+ beq @scroll ; Yes, so scroll down.
+ lda c ; No, so load the escape code back into the accumulator.
+ cmp #'B' ; Did the user press the down arrow key?
+ beq down ; Yes, so move the cursor down.
+ jmp @end ; No, so we're done.
+@scroll:
+ lda scr_row ; Save the cursor's row number.
+ sta scr_trow ;
+ lda scr_col ; Save the cursor's column number.
+ sta scr_tcol ;
+ jsr scrl_down ; Scroll down.
+ lda scr_trow ; Load the cursor's row number.
+ sta scr_row ;
+ lda scr_tcol ; Load the cursor's column number.
+ sta scr_col ;
+ lda #1 ; Tell the escape routine that we were successful.
+ sta d ;
+@end:
+isdown_done:
+ rts ; End of isdown.
+
+isright:
+ lda c ; Load the escape code into the accumulator.
+ cmp #'C' ; Did the user press the right arrow key?
+ bne @end2 ; No, so we're done.
+ lda scr_col ; Yes, so start checking the x coordinate of the cursor.
+ cmp #maxcol ; Is the cursor at the far right of the screen?
+ beq @wrap ; Yes, so check if this is a wrapped line.
+ jmp right ; No, so move the cursor right, like normal.
+@wrap:
+ inc scr_row ; Move down a row.
+ jsr getbit ; Is the current line, a wrapped line?
+ bcs @incr ; Yes, so leave the cursor where it is.
+ dec scr_row ; No, so move the cursor back up a row.
+ jmp @end2 ; We are done.
+@scroll:
+ lda scr_str ; Are we at the top of the screen buffer?
+ beq @end ; Yes, so we're done.
+ lda #1 ; No, so scroll down.
+ sta wrapped ; Set the wrapped flag.
+ jsr scrl_down ; Scroll down.
+ jmp @end ; We are done.
+@incr:
+ lda #0 ; Set the cursor to the far left of the screen.
+ sta scr_col ;
+ lda scr_row ; Get the current row number.
+ cmp #maxrow ; Are we at the bottom of the screen?
+ beq @end1 ; No, so we're done.
+ bcs @scroll ; Yes, so check if we are scrolling down.
+ jmp @end1 ; No, so we're done.
+@end:
+ dec scr_row ; Move back up a row.
+@end1:
+ jsr update_pos ; Update the cursor position.
+@end2:
+isright_dne:
+ lda #0 ; Unset the wrapped flag.
+ sta wrapped ;
+ rts ; End of isright.
+
+isleft:
+ lda c ; Load the escape code into the accumulator.
+ cmp #'C' ; Did the user press right?
+ beq @end1 ; Yes, so we're done
+ lda scr_col ; No, but is the cursor at the far left of the screen?
+ beq @wrap ; Yes, so start checking if this is a wrapped line.
+ lda c ; No, so load the escape code back into the accumulator.
+ cmp #'D' ; Did the user press the left arrow key?
+ beq left ; Yes, so move the cursor left.
+ jmp @end1 ; No, so we're done.
+@wrap:
+ jsr getbit ; Is the current line, a wrapped line?
+ bcs @decr ; Yes, so wrap back up a line.
+ jmp @end1 ; No, so we're done.
+@decr:
+ lda scr_row ; Is the cursor at the top of the screen?
+ beq @decr1 ; Yes, so don't move up a line.
+ lda #1 ; No, so set the wrapped flag.
+ sta wrapped ;
+ dec scr_row ; Move the cursor up one line.
+@decr1:
+ lda #maxcol ; Move the Cursor to the far right of the screen.
+ sta scr_col ;
+ lda #1 ; Tell the escape routine that we were successful.
+ sta d ;
+ lda scr_row ; Are we at the top of the screen?
+ beq @scroll ; Yes, so check if we need to scroll.
+ jmp @end ; No, so we're done.
+@scroll:
+ lda wrapped ; Was the wrapped flag set somewhere else?
+ bne @end ; Yes so we're done.
+ lda scr_str ; No, but are we actually at the top of the screen buffer?
+ beq @end1 ; Yes, so we're done.
+ jsr scrl_up ; No, so scroll up.
+ jmp @end1 ; We are done.
+@end:
+ jsr update_pos ; Update the cursor position.
+@end1:
+isleft_done:
+ lda #0 ; Unset the wrapped flag.
+ sta wrapped ;
+ rts ; End of isleft.
+
+up:
+ dec scr_row ; Move the cursor up a line.
+ jsr update_pos ; Update it's position.
+ lda #1 ; Tell the escape routine that we succeded.
+ sta d ;
+ jmp isup_done ; We are done.
+down:
+ inc scr_row ; Move the cursor down a line.
+ jsr update_pos ; Update it's position.
+ lda #1 ; Tell the escape routine that we succeded.
+ sta d ;
+ jmp isdown_done ; We are done.
+right:
+ inc scr_col ; Move the cursor right by one character.
+ jsr update_pos ; Update it's position.
+ jmp isright_dne ; We are done.
+left:
+ dec scr_col ; Move the cursor left a character.
+ jsr update_pos ; Update it's position.
+ lda #1 ; Tell the escape routine that we succeded.
+ sta d ;
+ jmp isleft_done ; We are done
+
+isshftup:
+ lda c ; Load the escape code back into the accumulator.
+ cmp #'A' ; Did the user press the up arrow key?
+ bne shftup_done ;
+ lda #1 ;
+ sta d ;
+ lda scr_str ;
+ beq shftup_done ;
+ jmp shftup ;
+shftup_done:
+ rts ;
+
+isshftdown:
+ lda c ; Load the escape code back into the accumulator.
+ cmp #'B' ; Did the user press the down arrow key?
+ bne shftdn_done ;
+ lda #1 ;
+ sta d ;
+ lda scr_end ;
+ cmp #71 ;
+ bcs shftdn_done ;
+ jmp shftdown ;
+shftdn_done:
+ rts ;
+
+shftup:
+ jsr scrl_up ;
+ lda #1 ;
+ sta d ;
+ jmp shftup_done ;
+
+shftdown:
+ jsr scrl_down ;
+ lda #1 ;
+ sta d ;
+ jmp shftdn_done ;
+
+update_pos:
+ ldb #1 ; Set the F pseudo register to one, to fix some bugs.
+ stb f ;
+ clc ; Clear the carry flag.
+ lda scr_row ; Add the cursor's line number,
+ adc scr_str ; with the starting line number to get the absolute line number.
+ tay ; Place it in the Y regster for now.
+ mul #maxcol+1 ; Multiply the line number by the screen's max column count, plus 1.
+ clc ; Clear the carry flag.
+ adc scr_col ; Add the cursor's column number to get the screen index.
+ tay ; Place the index into the Y register.
+ tba ; Reset A.
+ lda #$1B ; Print an escape character
+ sta scr ; to the screen.
+ lda #'[' ; Print '['
+ sta scr ; to the screen, and start the escape sequence.
+ jsr getrow ; Start printing the row number to the screen.
+ jsr getcol ; Start printing the column number to the screen.
+ lda #'H' ; Print 'H'
+ sta scr ; to the screen.
+ rts ; End of update_pos.
+
+getrow:
+ lda scr_row ; Get the cursor's y coordinate.
+ jmp bcd ; Convert it to BCD.
+getcol:
+ lda #';' ; Print ';'
+ sta scr ; to the screen.
+ lda scr_col ; Get the cursor's x coordinate.
+bcd:
+ div #10 ; Divide A by 10.
+ ora #'0' ; Convert it to ascii, and
+ sta scr ; print to the screen.
+ tba ; Get the remainder.
+ ora #'0' ; Convert it to ascii, and
+ sta scr ; print to the screen.
+ rts ; End of bcd.
+
+scrl_down:
+ inc scr_str ; Increment the starting line of the screen.
+ inc scr_end ; Increment the ending line of the screen.
+ lda #$1B ; Print an escape character
+ sta scr ; to the screen.
+ lda #'[' ; Print '['
+ sta scr ; to the screen, and start the escape sequence.
+ lda #'T' ; Print 'T'
+ sta scr ; to the screen, and end the escape sequence.
+ lda scr_row ; Get the cursor's line number.
+ pha #1 ; Save it in the stack.
+ lda wrapped ; Was the wrapped flag set?
+ beq @save ; Yes, so save the cursor position.
+@redraw:
+ jsr rdrw_row ; No, so redraw this row.
+ lda wrapped ; Was the wrapped flag set?
+ beq @load ; Yes, so load the previous cursor position back.
+ jmp @end ; No, so we're done.
+@save:
+ lda scr_col ; Get the cursor's column number.
+ pha #1 ; Save it in the stack.
+ jmp @redraw ; Start redrawing the current row.
+@load:
+ pla #1 ; Get the cursor's previous column number back.
+ sta scr_col ;
+@end:
+ pla #1 ; Get the cursor's previous line number back.
+ sta scr_row ;
+ jsr update_pos ; Update the cursor's position.
+ lda #0 ; Clear the wrapped flag.
+ sta wrapped ;
+@end1:
+ rts ; End of scrl_down.
+
+scrl_up:
+ dec scr_str ;
+ dec scr_end ;
+ lda #$1B ; Print an escape character
+ sta scr ; to the screen.
+ lda #'[' ; Print '['
+ sta scr ; to the screen, and start the escape sequence.
+ lda #'S' ; Print 'S'
+ sta scr ; to the screen, and end the escape sequence.
+ lda scr_row ;
+ pha #1 ;
+ lda scr_col ;
+ pha #1 ;
+ lda #0 ;
+ sta scr_row ;
+ jsr rdrw_row ;
+ pla #1 ;
+ sta scr_col ;
+ pla #1 ;
+ sta scr_row ;
+ jsr update_pos ;
+@end:
+ rts ;
+
+rdrw_row:
+ lda #0 ;
+ sta scr_col ;
+ jsr update_pos ;
+@loop:
+ lda (ptr3), y ;
+ beq @incr ;
+ sta scr ;
+@incr:
+ inc scr_col ;
+ lda (ptr3), y ;
+ beq @skip ;
+@incr1:
+ iny ;
+@incr2:
+ lda scr_col ;
+ cmp #maxcol+1 ;
+ bcs @end ;
+ jmp @loop ;
+@skip:
+ lda #' ' ;
+ sta scr ; to the screen.
+ jmp @incr1 ;
+@end:
+ lda #0 ;
+ sta scr_col ;
+ jsr update_pos ;
+@end1:
+ rts ;
+
+rdrw_ln:
+ lda scr_row ;
+ pha #1 ;
+ lda f ;
+ sta scr_row ;
+ lda scr_col ;
+ pha #1 ;
+ jsr update_pos ;
+@loop:
+ lda scr_row ;
+ cmp e ;
+ beq @loop1 ;
+ bcs @end ;
+@loop1:
+ jsr rdrw_row ;
+@incr:
+ inc scr_row ;
+ jmp @loop ;
+@end:
+ pla #1 ;
+ sta scr_col ;
+ pla #1 ;
+ sta scr_row ;
+ jsr update_pos ;
+ lda #0 ;
+ sta e ;
+ sta f ;
+ rts ;
+
+set_ptr:
+ cpb #1 ; Are we setting the second pointer?
+ beq @ptr2 ; Yes, so start setting it.
+ cpb #2 ; No, but are we setting the third pointer?
+ beq @ptr3 ; Yes, so start setting it.
+@ptr1:
+ stb.q ptr ; Reset the first pointer.
+ sta.q ptr ; No, so set the first pointer.
+ jmp @end ; We are done.
+@ptr2:
+ stb.q ptr2 ; Reset the second pointer.
+ sta.q ptr2 ; Set the second pointer.
+ jmp @end ; We are done.
+@ptr3:
+ stb.q ptr3 ; Reset the third pointer.
+ sta.q ptr3 ; Set the third pointer.
+@end:
+ rts ; End of set_ptr.
+
+; Entry point for SuBAsm.
+parser:
+
+.org $FFC0
+.qword reset
+a
+d
+
diff --git a/programs/sub-suite/utils.s b/programs/sub-suite/utils.s
new file mode 100644
index 0000000..580e4da
--- /dev/null
+++ b/programs/sub-suite/utils.s
@@ -0,0 +1,394 @@
+; Utility subroutines for SuBAsm.
+
+.org util_data
+; Hex character table.
+hex_char:
+ .byte "0123456789ABCDEF"
+
+; Compare, and return table for pre-tokens.
+ptok_tab:
+ .byte ".@:=+-><(),xy\"\'#;$%"
+; Compare, and return table for isdelm.
+dtab:
+ .byte "\n,\"\' "
+; Compare, and return table for isdelm2.
+dtab2:
+ .byte "),.+<>-=;\n"
+
+.org utils
+
+print_hi:
+ and #0 ; Reset A.
+ sta idx3 ; Clear the string index.
+ lda #'$' ; Print the hex delimiter.
+ jsr charcpy ;
+ lda.q idx0 ; Get the masked address.
+ ldx #$10 ; Set digit count to 16.
+ jsr print_hex ; Print the address.
+ lda.q hex_str ; Get the lower half of the string.
+ sta.q strbuf+1 ; Save it in the string buffer.
+ lda.q hex_str+8 ; Get the upper half of the string.
+ sta.q strbuf+9 ; Save it in the string buffer.
+ ldx #$11 ; Add 16 to the index.
+ stx idx3 ;
+ lda #':' ; Print a colon.
+ jsr charcpy ;
+ lda # ' ' ; Print a space.
+ jsr charcpy ;
+ rts ; End of print_hi.
+
+print_lo:
+ lda #0 ; Reset A.
+ sta idx3 ; Clear the string index.
+@loop:
+ ldx #2 ; Set digit count to 2.
+ pha #1 ; Preserve the nibble offset.
+ jsr print_hex ; Print the low nibble offset.
+ lda.w (ptr3) ; Get the two digits.
+ jsr charcpy ; Copy the first digit.
+ lsr #8 ; Copy the next digit.
+ jsr charcpy ;
+ pla #1 ; Get the nibble offset back.
+ inc ; Increment the offset.
+ cmp #$10 ; Are we at the last offset?
+ bcs @end ; Yes, so we're done.
+@loop1:
+ pha #1 ; No, so preserve the nibble offset.
+ lda #' ' ; Add a space to the string buffer.
+ jsr charcpy ;
+ pla #1 ; Get the nibble offset back.
+ jmp @loop ; Keep looping.
+@end:
+ inx ; Increment the index by one.
+ lda #0 ; Null terminate the string buffer.
+ sta strbuf, x ;
+ tax ; Reset X.
+ lda.d #strbuf ; Print the string buffer.
+ jsr print_str ;
+ rts ; End of print_lo.
+
+print_chunk:
+ ldx #0 ; Reset X.
+ phy #2 ; Preserve the screen buffer index.
+ txy ; Copy the byte index to it.
+@loop:
+ and #0 ; Reset A.
+ ldx #2 ; Set the digit count to 2.
+ lda (idx0), y ; Get the byte at that address.
+ jsr print_hex ; Print the byte.
+ lda.w (ptr3) ; Get the two digits.
+ jsr charcpy ; Copy the first digit.
+ lsr #8 ; Copy the next digit.
+ jsr charcpy ;
+ iny ; Increment the byte index.
+ cpy #$10 ; Have we read 16 bytes?
+ beq @end ; Yes, so we're done.
+ lda #' ' ; No, so add a soace to the string buffer.
+ jsr charcpy ;
+ jmp @loop ; Keep looping.
+@end:
+ ply #2 ; Get the screen buffer index back.
+ inx ; Increment the index by one.
+ and #0 ; Null terminate the string.
+ sta strbuf, x ;
+ tax ; Reset X.
+ sta idx3 ; Clear the string index.
+ rts ; End of print_chunk.
+
+
+print_hex:
+ pha #8 ; Preserve the hex value.
+ and #0 ; Reset A.
+ ldb #1 ; Set the second pointer
+ lda.w #hex_char ; to the start of hex character table.
+ jsr set_ptr ;
+ inb ; Set the third pointer
+ lda.d #hex_str ; to the end of hex string buffer.
+ clc ; Do a non carrying add.
+ adc #$10 ;
+ jsr set_ptr ;
+ ldb #0 ; Reset B.
+ pla #8 ; Get the hex value back.
+@loop:
+ pha #8 ; Preserve the hex value.
+ and #$F ; Mask the lowest nibble.
+ phy #2 ; Preserve the screen buffer position.
+ tay ; Get the index for the hex digit.
+ lda (ptr2), y ; Get the hex digit.
+ dec ptr3 ; Decrement the string pointer.
+ sta (ptr3) ; Save the hex digit character in the string.
+ ply #2 ; Get back the screen buffer position.
+ pla #8 ; Get the hex value back.
+@isauto:
+ cpx #1 ; Is the digit count less than one?
+ bcc @auto ; Yes, so don't decrement the digit count.
+ dex ; No, but was the digit count zero, when decremented?
+ beq @end ; Yes, so we're done.
+ jmp @next ; No, so get the next nibble.
+@auto:
+ ldb #1 ; Enable auto digit count.
+@next:
+ lsr #4 ; Is the next nibble, a zero?
+ beq @isauto1 ; Yes, so check if auto digit count is enabled.
+ jmp @loop ; No, so print the next digit.
+@isauto1:
+ cpb #1 ; Is auto digit count enabled?
+ beq @end ; Yes, so we're done.
+ jmp @loop ; No, so keep printing more digits.
+@end:
+ rts ; End of print_hex.
+
+
+charcpy:
+ ldx idx3 ; Get the string index.
+ sta strbuf, x ; Save it in the string buffer.
+ inc idx3 ; Increment the string index.
+ rts ; End of charcpy.
+
+
+strlen:
+ ldb #1 ; Set the second pointer
+ jsr set_ptr ; to the passed pointer.
+ deb ; Reset B.
+ tba ; Reset A.
+ tax ; Reset X.
+ phy #2 ; Preserve Y.
+ txy ; Reset Y.
+@loop:
+ lda (ptr2), y ; Are we at the end of the string?
+ beq @end ; Yes, so we're done.
+ iny ; No, so increment the index.
+ jmp @loop ; Keep looping.
+@end:
+ tyx ; Return the length in X.
+ ply #2 ; Get the preserved value back.
+ rts ; End of strlen.
+
+
+strcmp:
+ ldb #1 ; Set the second pointer
+ jsr set_ptr ; to the passed pointer.
+ deb ; Reset B.
+ tba ; Reset A.
+ phy #2 ; Preserve Y.
+ tay ; Reset Y.
+@loop:
+ ldb #0 ; Set the islong flag to false.
+ lda (ptr), y ; Are we at the end of the first string?
+ beq cmpr ; Yes, so check if we're too short, or too long.
+ ldb #1 ; No, so set the islong flag to true.
+ cmp (ptr2), y ; Is the character of both strings, the same?
+ bne cmpr ; No, so check if we're too short, or too long.
+ iny ; Yes, so increment the index.
+ jmp @loop ; Keep looping.
+
+strcasecmp:
+ ldb #1 ; Set the second pointer
+ jsr set_ptr ; to the passed pointer.
+ deb ; Reset B.
+ tba ; Reset A.
+ phy #2 ; Preserve Y.
+ tay ; Reset Y.
+@loop:
+ ldb #0 ; Set the islong flag to false.
+ lda (ptr), y ; Are we at the end of the first string?
+ beq cmpr ; Yes, so check if we're too short, or too long.
+ ldb #1 ; No, so set the islong flag to true.
+ jsr tolower ; Convert the character of string 1 to lowercase.
+ phb #1 ; Preserve the islong flag.
+ pha #1 ; Preserve the converted character.
+ lda (ptr2), y ; Get the character of the second string.
+ jsr tolower ; Convert the character of string 2 to lowercase.
+ tab ; Place it in B.
+ pla #1 ; Get the character of string 1 back.
+ cab ; Is the character of both strings, the same?
+ plb #1 ; Get the islong flag back.
+ bne cmpr ; No, so check if we're too short, or too long.
+ iny ; Yes, so increment the index.
+ jmp @loop ; Keep looping.
+
+cmpr:
+ lda (ptr2), y ; Are we at the end of the second string?
+ beq @islong ; Yes, so check the islong flag.
+@isshort:
+ lda (ptr), y ; No, but are we at the end of the first string?
+ beq @short ; Yes, so return -1.
+@islong:
+ cpb #1 ; Is the islong flag true?
+ bne @equ ; No, so return 0.
+@long:
+ lda #1 ; Yes, so return 1.
+ jmp @end ; We are done.
+@equ:
+ lda #0 ; Return 0.
+ jmp @end ; We are done.
+@short:
+ lda #$FF ; Return -1.
+@end:
+ ply #2 ; Get the preserved value back.
+ rts ; End of strcmp.
+
+
+isdigit:
+ sec ; Prepare for a non carrying subtraction.
+ sbc #'0' ; Subtract $30 from the passed character.
+ and #$FF ; Make sure that we have only one byte.
+ cmp #10 ; Is the subtracted value, less than 10?
+ bcs @false ; No, so return false.
+@true:
+ lda #1 ; Yes, so return true.
+ jmp @end ; We are done.
+@false:
+ lda #0 ; Return false.
+@end:
+ rts ; End of isdigit.
+
+isxdigit:
+ pha #1 ; Preserve the character.
+ jsr isdigit ; Is this character, a decimal digit?
+ pla #1 ; Get the character back.
+ bne @true ; Yes, so return true.
+@alpha:
+ sec ; No, so prepare for a non carrying subtract.
+ ora #$20 ; Convert it to lowercase.
+ sbc #'a' ; Subtract $61 from the character.
+ and #$FF ; Make sure that we have only one byte.
+ cmp #6 ; Is the subtracted value, less than 6?
+ bcs @false ; No, so return false.
+@true:
+ lda #1 ; Yes, so return true.
+ jmp @end ; We are done.
+@false:
+ lda #0 ; Return false.
+@end:
+ rts ; End of isxdigit.
+
+
+isupper:
+ sec ; Prepare for a non carrying subtraction.
+ sbc #'A' ; Subtract $41 from the passed character.
+ jmp isletter ; Check if it's less than 26.
+islower:
+ sec ; Prepare for a non carrying subtraction.
+ sbc #'a' ; Subtract $61 from the passed character.
+isletter:
+ and #$FF ; Make sure that we have only one byte.
+ cmp #26 ; Is the subtracted value, less than 26?
+ bcs @false ; No, so return false.
+@true:
+ lda #1 ; Yes, so return true.
+ jmp @end ; We are done.
+@false:
+ lda #0 ; Return false.
+@end:
+ rts ; End of isletter.
+
+
+tolower:
+ pha #1 ; Preserve the character.
+ jsr isupper ; Is this character, an uppercase character?
+ pla #1 ; Get the character back.
+ beq @end ; No, so we're done.
+@lower:
+ ora #$20 ; Yes, so convert it to lowercase.
+@end:
+ rts ; End of tolower.
+
+
+toupper:
+ pha #1 ; Preserve the character.
+ jsr islower ; Is this character, a lowercase character?
+ pla #1 ; Get the character back.
+ beq @end ; No, so we're done.
+@upper:
+ and #$5F ; Yes, so convert it to uppercase.
+@end:
+ rts ; End of toupper.
+
+
+isdelm2:
+ ldx #0 ; Reset X.
+@loop:
+ ldb dtab2, x ; Get the compare value.
+ beq @other ; We hit the end of the table, so check for the others.
+ cab ; Are they the same?
+ beq @r1 ; Yes, so return 1.
+ inx ; No, so increment the table index.
+ jmp @loop ; Keep looping.
+@other:
+ ldx #0 ; Reset X.
+ cmp #0 ; Is this a null terminator?
+ beq @r1 ; Yes, so return 1.
+ cmp #'\t' ; No, but is it a tab?
+ beq @r2 ; Yes, so return 2.
+ cmp #' ' ; No, but is it a space?
+ beq @r2 ; Yes, so also return 2.
+@r0:
+ lda #0 ; Return 0.
+ rts ; End of isdelm2.
+@r1:
+ ldx #0 ; Reset X.
+ lda #1 ; Return 1.
+ rts ; End of isdelm2.
+@r2:
+ lda #2 ; Return 2.
+ rts ; End of isdelm2.
+
+
+isdelm:
+ ldx #0 ; Reset X.
+ stx a ; Reset the shift value.
+@loop:
+ ldb dtab, x ; Get the compare value.
+ beq @other ; We hit the end of the table, so check for the others.
+ cab ; Are they the same?
+ beq @rshft ; Yes, so return 1 << index.
+ inx ; No, so increment the table index.
+ jmp @loop ; Keep looping.
+@other:
+ ldx #0 ; Reset X.
+ cmp #0 ; Is this a null terminator?
+ beq @rshft ; Yes, so return 1.
+ ldx #4 ; No, so set the shift amount to 4.
+ cmp #'\t' ; Is this a tab?
+ beq @rshft ; Yes, so return 16.
+ ldx #0 ; No, so reset X.
+@r0:
+ lda #0 ; Return 0.
+ rts ; End of isdelm.
+@rshft:
+ stx a ; Save the shift value.
+ ldx #0 ; Reset X.
+ lda #1 ; Set up the bitshift.
+ lsl a ; Return 1 << X.
+ rts ; End of isdelm.
+
+
+get_ptok:
+ ldx #0 ; Reset X.
+ jsr tolower ; Conver the character to lowercase.
+@loop:
+ ldb ptok_tab, x ; Get the compare value.
+ beq @other ; We hit the end of the table, so check for the others.
+ cab ; Are they the same?
+ beq @rtab ; Yes, so return X.
+ inx ; No, so increment the table index.
+ jmp @loop ; Keep looping.
+@rtab:
+ txa ; Return X.
+ rts ; End of get_ptok.
+@other:
+ tab ; Preserve the character.
+ jsr isdigit ; Is this character a digit?
+ bne @rnum ; Yes, so return PTOK_NUM.
+ tba ; No, so get the character back.
+ jsr islower ; Is it an alphabetical character?
+ bne @ralph ; Yes, so return PTOK_ALPH.
+ lda #PTOK_OTHR ; No, so return PTOK_OTHR.
+ rts ; End of get_ptok.
+@rnum:
+ lda #PTOK_NUM ; Return PTOK_NUM.
+ rts ; End of get_ptok.
+@ralph:
+ lda #PTOK_ALPH ; Return PTOK_ALPH.
+ rts ; End of get_ptok.