; SuBAsm ; The Sux Bootstrapped Assembler. ; ; by mr b0nk 500 ; Variables .org $1000 prg_name: .byte "SuBAsm" ver_txt: .byte ", version " ver_num: .byte "0.1" ; Directives. dir: .byte "org" .byte "byte" .byte "word" .byte "dword" .byte "qword" ; Instruction mnemonics. mne: .byte "CPS" .byte "ADC" .byte "AAB" .byte "PHB" .byte "PHP" .byte "PHA" .byte "PHY" .byte "TAY" .byte "PHX" .byte "TAX" .byte "TYX" .byte "JMP" .byte "SBC" .byte "SAB" .byte "PLB" .byte "PLP" .byte "PLA" .byte "PLY" .byte "TYA" .byte "PLX" .byte "TXA" .byte "TXY" .byte "JSR" .byte "AND" .byte "ABA" .byte "STT" .byte "TAB" .byte "TSX" .byte "BPO" .byte "ORA" .byte "OAB" .byte "TBA" .byte "SEI" .byte "TXS" .byte "BNG" .byte "XOR" .byte "XAB" .byte "CLI" .byte "BCS" .byte "LSL" .byte "LLB" .byte "STB" .byte "SEC" .byte "STA" .byte "STY" .byte "STX" .byte "BCC" .byte "LSR" .byte "LRB" .byte "LDB" .byte "CLC" .byte "LDA" .byte "LDY" .byte "LDX" .byte "BEQ" .byte "ROL" .byte "RLB" .byte "SSP" .byte "BNE" .byte "ROR" .byte "RRB" .byte "CSP" .byte "BVS" .byte "MUL" .byte "MAB" .byte "SEV" .byte "BVC" .byte "DIV" .byte "DAB" .byte "CLV" .byte "RTS" .byte "CMP" .byte "CAB" .byte "CPY" .byte "CPX" .byte "CPB" .byte "ENT" .byte "RTI" .byte "INC" .byte "IAB" .byte "INY" .byte "INX" .byte "DEC" .byte "DBA" .byte "DEY" .byte "DEX" .byte "WAI" .byte "JSL" .byte "ASR" .byte "ARB" .byte "NOP" .byte "RTL" .byte "BRK" scr_row: .byte $0 scr_col: .byte $0 a: .word $0 b: .word $0 c: .word $0 d: .word $0 str_buf: ; Input buffer. .org $2000 buf: .org $2400 ptr1: .qword $2500 ptr2: .qword $2900 ; Control Register. .org $C000 ctrl_reg: ; Screen. .org $C001 scr: ; Keyboard. .org $C002 kbd: ; Main program. .org $0 reset: cps ldx.w #$FFFF txs ldy #0 jsr clr_buf ldx.w #0 ; Reset x. ldy #0 ; Reset y. jmp print_title read: lda ctrl_reg ; Is the keyboard ready? beq read ; Loop until the keyboard is ready. lda #0 ; Start resetting the control register. sta ctrl_reg ; Reset the control register. jmp getchar ; We got a key. rset_x: ldx #0 ; Reset x. stx.w x rts print_title: lda prg_name, x beq print_ver ; Did we find a null terminator? sta ; Print character. inx ; Increment offset. inc x jmp print_title ; Keep printing more characters. print_ver jsr rset_x lda ver_txt, x beq print_num ; Did we find a null terminator? sta scr ; Print character. inx ; Increment offset. jmp print_ver ; Keep printing more characters. print_num: lda ver_num, x beq getline ; Did we find a null terminator? sta scr ; Print character. inx ; Increment offset. jmp print_num ; Keep printing more characters. getline: lda #$A sta scr ; Print newline inc scr_row lda #0 sta scr_col inc y jsr rset_x jmp read getchar: lda kbd ; Get typed character. cmp #$1B ; Did the user type an escape? beq esc ; Yes, so start getting the escape code. cmp #$A ; Did the user type a newline? beq nl ; Yes, so start parsing the input. cmp #8 ; Did the user type a backspace? beq bs ; Yes, so start checking the buffer. jsr echo ; Print character to screen. store_char: sta buf, y ; Store typed character into the input buffer. iny ; Increment buffer offset. jmp read ; Get another character. esc: lda ctrl_reg ; Skip the '['. lda ctrl_reg ; Get the next character. beq read ; 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. jsr isup ; Check if the user pressed up. lda d cmp #0 bne esc_end jsr isdown ; Check if the user pressed down. lda d cmp #0 bne esc_end lda #0 jsr isleft ; Check if the user pressed left. lda d cmp #0 bne esc_end jsr isright ; Check if the user pressed right. esc_end: lda #$0 sta d jmp rset_a ; Go back to getting user input. isup: lda scr_row ; Is the cursor at the top of the screen? beq isup_done ; Yes, so return. lda c ; No, so load the escape code back into the accumulator. cmp #$41 ; Did the user press the up arrow key? beq up ; Yes, so move the cursor up. isup_done: rts ; End of isup. isdown: lda scr_row ; Start checking the y coordinate of the cursor. cmp #$17 ; Is the cursor at the bottom of the screen? beq isdown_done ; Yes, so return. lda c ; No, so load the escape code back into the accumulator. cmp #$42 ; Did the user press the down arrow key? beq down ; Yes, so move the cursor down. isdown_done: rts ; End of isdown. isright: lda scr_col ; Start checking the x coordinate of the cursor. cmp #$4F ; Is the cursor at the far right of the screen? beq isright_end ; Yes, so return. lda c ; No, so load the escape code back into the accumulator. cmp #$43 ; Did the user press the right arrow key? beq right ; Yes, so move the cursor right. isright_end: rts ; End of isright. isleft: lda scr_col ; Is the cursor at the far left of the screen? beq isleft_done ; Yes, so return. lda c ; No, so load the escape code back into the accumulator. cmp #$44 ; Did the user press the left arrow key? beq left ; Yes, so move the cursor left. isleft_done: rts ; End of isleft. up: dec scr_row jsr update_pos lda #1 sta d jmp isup_done down: inc scr_row jsr update_pos lda #1 sta d jmp isdown_done right: inc scr_col jsr update_pos jmp isright_end left: dec scr_col jsr update_pos lda #1 sta d jmp isleft_done update_pos: lda #$1B ; Print an escape character sta scr ; to the screen. lda #$5B ; 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 #$48 ; Print 'H' sta scr ; to the screen. rts ; End of update_pos. getrow: lda scr_row ; Get the cursor's y coordinate. div #$A ; Divide A by 10. adc #$30 ; Convert it to ascii, and sta scr ; print to the screen. tba ; Get the remainder. adc #$30 ; Convert it to ascii, and sta scr ; print to the screen. rts ; End of getrow. getcol: lda #$3B ; Print ';' sta scr ; to the screen. lda scr_col ; Get the cursor's x coordinate. div #$A ; Divide A by 10. adc #$30 ; Convert it to ascii, and sta scr ; print to the screen. tba ; Get the remainder. adc #$30 ; Convert it to ascii, and sta scr ; print to the screen. rts ; End of getrow. nl: sta scr ; Print newline. inc scr_row ; Move the cursor down one line. lda #0 ; Put a null terminator, in place of the newline. sta scr_col ; Move the cursor back to column 0. sta buf, y ; Place it into the input buffer. ldy.w #0 ; Reset y, to parse the input. jsr parse ; Start parsing input. back: sta scr ; Print backspace. lda #0 ; Put a null terminator, in place of the backspace. sta buf, y ; Place it into the input buffer. dey ; Decrement buffer offset. dec scr_col jmp read ; Get next character. bs: cpy #0 ; Are we at the start of the buffer? beq read ; We are, so do not store the backspace into the buffer. jmp back ; We are not, so add the backspace to the buffer. parse: jsr getdir jsr rts getdir: iny ; Increment offset. lda buf, y jsr iswhite bcs getdir ; Reset y, if we hit the null terminator. cmp #$2E ; Is this character a '.'? bne getdir_exit ; No, so return. jsr clr_ptr getdir_end: iny ; Increment offset. lda buf, y jsr iswhite bcs getdir_cmp ora #%00100000 ; Make character lower case. sta (ptr1), y jmp getdir_end gettok: ply #1 iny gettok2: lda buf, y jsr istoken bcs gettok3 jmp gettok2 gettok3: cmp #$ cmp #$24 ; Is this character, a '$'? beq getaddr ; Yes, so start getting the address. getdir_cmp: phy #1 ldy.w #0 tyx getdir_cpl: ldb dir, x ; Start checking if the directive we're using, is "org". stb (ptr2), y beq getdir_scmp inx iny jmp getdir_cpl getdir_scmp: ldy #0 jsr strcmp cpx #0 beq getdir_tok getdir_exit: rts ; End of getdir. istoken: cmp #$20 ; Is this character a space? beq istoken_f ; Yes, so return false. cmp #$09 ; Is this character a tab? beq istoken_f ; Yes, so return false. cmp #$A ; Is this character a newline? beq istoken_f ; Yes, so return false. cmp #$3B ; Is this character a ';'? beq istoken_f ; Yes, so return false. cmp #0 ; Is this character a null terminator? beq istoken_f ; Yes, so return false. sec ; Return true. rts ; End of istoken. istoken_f clc ; Return false. rts ; End of istoken_f. iswhite: cmp #$20 ; Is this character a space? beq iswhite_t ; Yes, so return true. cmp #9 ; Is this character a tab? beq iswhite_t ; Yes, so return true. clc ; No, so return false. rts ; End of iswhite. iswhite_t: sec ; Return true. rts ; End of iswhite_t. rset_y: ldy.w #0 jmp print_buf ; Print the input buffer. print_buf: lda buf, y ; Get a character from the input buffer. beq fin ; Are we done with printing the buffer? sta scr ; Print said character. iny jmp print_buf ; Keep printing the buffer. spin: nop nop nop jmp spin clr_buf: lda #0 cpy.w #$3FF beq clr_end sta buf, y iny jmp clr_buf clr_sbuf: lda #0 cpy.w #$1FF beq clr_end sta str_buf, y iny jmp clr_sbuf clr_end: rts echo: tab ldx scr_col cpx #$4F bne echo_print cmp #$A beq linewrap linewrap: inc scr_row ldx #0 stx scr_col jsr update_pos echo_print: tba sta scr ; Echo typed character. inc scr_col ; Increment the cursor's x coordinate. sta buf, y ; Store typed character into the input buffer. iny ; Increment the buffer offset. rts ; Return. clr_ptr: lda #0 cpy.w #$3FF beq ptr_end sta (ptr1), y sta (ptr2), y iny jmp clr_ptr ptr_end: rts strcmp: lda (ptr1), y cmp (ptr2), y bne strcmp_l1 tax beq strcmp_l3 iny bne strcmp jmp strcmp strcmp_l1: bcs strcmp_l2 ldx #0 dex rts strcmp_l2: ldx #1 strcmp_l3: rts .org $FFC0 .qword reset .org $FF50 .qword spin .qword spin .qword spin .qword spin .qword spin .qword spin .qword spin .org $FFA0 .qword irq_routine .org $0 v .org $100 v .org $200 v .org $300 v .org $1000 v .org $1100 v .org $1200 v .org $1300 v .org $1400 v q