; SuBAsm ; The Sux Bootstrapped Assembler. ; ; by mr b0nk 500 MAX_SYM = $800 ; Max symbol size. .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 ; Hex character table. hex_char: .byte "0123456789ABCDEF" ; 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 ; Lexeme string. lexeme: ; Symbol tables. .org lexeme+$100 sym_val: .org sym_val+$4000 sym_id: .org sym_id+$1000 sym_def: .org sym_def+$100 sym_name: ; Fixup table. ; Fixups are unresolved symbols. .org sym_name+$1000 fix_sym: .org fix_sym+$1000 fix_ln: .org fix_ln+$1000 fix_val: ; 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 parse_cmd ; Yes, so skip everything else. jmp subasm_end ; jsr chk_cmd ; No, but did we get a full command? bne parse_cmd ; Yes, so skip everything else. jsr lexer ; No, so start lexing this line. subasm_end: rts ; End of subasm. parse_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 subasm_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. jmp subasm_end ; We are done. 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 shcmd_fail ; Yes, so return that we failed. cmp #' ' ; No, but is this character, a space? beq shcmd_fail ; Yes, so return that we failed. shcmd_loop: ldb (ptr2), y ; Are we at the end of the table? beq shcmd_fail ; Yes, so return that we failed. cab ; No, so did the character match? beq shcmd_fnd ; Yes, so check if there are any arguments. iny ; No, so check the next command. jmp shcmd_loop ; Keep looping. shcmd_fnd: 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 shcmd_true ; Yes, so return that we succeded. cmp #' ' ; No, but is this a space? beq shcmd_true ; Yes, so return that we succeded. jmp shcmd_fail ; No, so return that we failed. shcmd_true: lda #1 ; Return true. jmp shcmd_end ; We are done. shcmd_fail: ldb #0 ; Reset B. tba ; Return false. tax ; Reset X. shcmd_end: ply #2 ; Get back the screen buffer position. rts ; End of chk_shcmd. 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. pnthex_lp: 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. pnthex_lp1: cpx #1 ; Is the digit count less than one? bcc pnthex_lp2 ; Yes, so don't decrement the digit count. dex ; No, but was the digit count zero, when decremented? beq pnthex_end ; Yes, so we're done. jmp pnthex_lp3 ; No, so get the next nibble. pnthex_lp2: ldb #1 ; Enable auto digit count. pnthex_lp3: lsr #4 ; No, but is the next nibble, a zero? beq pnthex_lp4 ; Yes, so check if auto digit count is enabled. jmp pnthex_lp ; No, so print the next digit. pnthex_lp4: cpb #1 ; Is auto digit count enabled? beq pnthex_end ; Yes, so we're done. jmp pnthex_lp ; No, so keep printing more digits. pnthex_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. 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. pntlo_lp: 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 pntlo_end ; Yes, so we're done. pntlo_lp1: 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 pntlo_lp ; Keep looping. pntlo_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. pntchnk_lp: 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 pntchnk_end ; Yes, so we're done. lda #' ' ; No, so add a soace to the string buffer. jsr charcpy ; jmp pntchnk_lp ; Keep looping. pntchnk_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. 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. vmem_lp0: 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 vmem_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 vmem_lp0 ; Keep looping. vmem_end: lda #'\n' ; Print a newline. jsr print_char ; and #0 ; Reset A. rts ; End of viewmem. list: nop ; list_end: rts ; End of list. asm: nop ; asm_end: rts ; End of asm. help: nop ; help_end: rts ; End of help. inst: nop ; inst_end: rts ; End of inst. run: nop ; run_end: rts ; End of run. set: nop ; set_end: rts ; End of set.