;;; ;;; 6301/6303 assembler for MCFS ;;; By: Daniel Tufvesson 2017-2022 ;;; sys_return: equ $d003 sys_call: equ $d006 console_status: equ $7fe5 console_output: equ $7fe8 org $a100 ;;; Go to first argument (file name) * ldx #tstargs ; DEBUG: static file name stx args jsr str_skp_next stx args_file ; save pointer to file name argument ldaa ,x ; check for empty arg string bne *+5 jmp program_help jsr parse_args ;;; ;;; Pass ONE ;;; clr pass2_flag ; this is not pass 2 ldx #0 stx program_counter stx errors ldx label_table clr ,x stx label_table_position pass1_load_file: ldx args_file ; open file jsr fopen beq *+5 jmp exit_program ldx #1 stx line_counter pass1_loop: clr assembly_length clr program_counter_bump clr program_counter_bump+1 jsr fgetline ; read line from file bcc *+5 jmp pass1_read_eof ; end of file? jsr segment_line ; identify line segments (label,opcode,operand) ;;; Check if line has a valid label tst line_pointer_label beq pass1_no_label ; there is no label ldx line_pointer_label jsr find_label ; check if label already is registered bcs pass1_label_ok jsr report_duplicate_label jmp exit_program pass1_label_ok: ldx line_pointer_label jsr read_label pass1_no_label: ;;; Check if line has a valid op-code tst line_pointer_opcode beq pass1_line_done ; there is no op-code ldx line_pointer_opcode jsr read_opcode jsr lookup_opcode bcc pass1_opcode_ok ;;; Handle invalid op-code jsr report_invalid_opcode jmp exit_program ;;; Assemble op-code and operand pass1_opcode_ok: jsr assemble ; assemble line bcc pass1_line_done ;;; Handle invalid op-code jsr report_invalid_opcode jmp exit_program ;;; Print input line line and loop pass1_line_done: * jsr print_current_line ; uncomment to print pass 1 listing ldx program_counter ; adjust program counter according to the generated assembly length ldab assembly_length abx stx program_counter ldd program_counter addd program_counter_bump std program_counter ldx line_counter ; increase line counter inx stx line_counter jmp pass1_loop pass1_read_eof: cmpa #0 beq pass1_end ; EOF ldx #txt_read_error jsr str_print pass1_end: * jsr print_labels ; uncomment to print pass 1 lable table ;;; Check if there are more files to read ldx args_file jsr str_skp_next ldaa ,x beq pass1_complete cmpa #'- beq pass1_complete stx args_file jmp pass1_load_file pass1_complete: ;;; ;;; Pass TWO ;;; ldx args jsr str_skp_next stx args_file ; save pointer to file name argument inc pass2_flag ; this is pass 2 ldx #0 stx program_counter stx errors pass2_load_file: ldx args_file ; open file jsr fopen beq *+5 jmp exit_program ldx #1 stx line_counter pass2_loop: clr assembly_length clr program_counter_bump clr program_counter_bump+1 jsr fgetline ; read line from file bcc *+5 jmp pass2_read_eof ; end of file? jsr segment_line ; identify line segments (label,opcode,operand) ;;; Check if line has a valid op-code tst line_pointer_opcode beq pass2_line_done ; there is no op-code ldx line_pointer_opcode jsr read_opcode jsr lookup_opcode bcc pass2_opcode_ok ;;; Handle invalid op-code jsr report_invalid_opcode jmp exit_program ;;; Assemble op-code and operand pass2_opcode_ok: jsr assemble ; assemble line bcc pass2_line_done ;;; Handle invalid op-code jsr report_invalid_opcode jmp exit_program ;;; Print input line line and loop pass2_line_done: tst assembly_length ; any assembly bytes generated? beq *+5 jsr store_line_bytes ; save assembled bytes tst listing_flag beq *+5 jsr print_current_line ; print listing line ldx program_counter ; adjust program counter according to the generated assembly length ldab assembly_length abx stx program_counter ldd program_counter addd program_counter_bump std program_counter ldx line_counter ; increase line counter inx stx line_counter jmp pass2_loop pass2_read_eof: cmpa #0 beq pass2_end ; EOF ldx #txt_read_error jsr str_print pass2_end: ;;; Check if there are more files to read ldx args_file jsr str_skp_next ldaa ,x beq pass2_complete cmpa #'- beq pass2_complete stx args_file jmp pass2_load_file pass2_complete: tst listing_flag beq *+5 jsr print_labels ; print lable table jsr report_errors ; print error count ;;; Program ends here exit_program: jmp sys_return ;;; ;;; Print program help ;;; program_help: ldx #txt_help jsr str_print jmp exit_program ;;; ;;; Errors ;;; report_invalid_opcode: tst noerr_flag bne report_end jsr report_file_line ldx #txt_invalid_opcode jsr str_print bra report_end report_duplicate_label: tst noerr_flag bne report_end jsr report_file_line ldx #txt_duplicate_label jsr str_print bra report_end report_label_not_found: tst noerr_flag bne report_end jsr report_file_line ldx #txt_label_not_found jsr str_print bra report_end report_invalid_expr: tst pass2_flag beq report_end tst noerr_flag bne report_end jsr report_file_line ldx #txt_invalid_expression jsr str_print bra report_end report_branch_range: tst pass2_flag beq report_end tst noerr_flag bne report_end jsr report_file_line ldx #txt_branch_range jsr str_print report_end: ldx errors inx stx errors rts report_errors: ldx errors beq report_no_errors ldx #txt_errors jsr str_print ldx #errors jsr outdec jsr pcrlf jsr pcrlf rts report_no_errors: ldx #txt_no_errors jsr str_print rts report_file_line: ldx args_file jsr str_print_word * ldaa #': * jsr console_output jsr outs ldx #line_counter jsr outdec jsr outs rts ;;; ;;; Print current line ;;; print_current_line: ldx #line_counter jsr outdec jsr outs ldx #program_counter jsr out4hs clr temp jsr print_assembly ; print resulting assembly bytes jsr outs jsr outs ldx #line_buffer jsr str_print jsr pcrlf ldaa assembly_length ; assembly bytes left to print? cmpa temp beq print_current_line_done jsr outs jsr outs jsr outs jsr outs jsr outs jsr outs jsr outs jsr outs jsr outs jsr outs jsr outs jsr print_remaining_ass jsr pcrlf print_current_line_done: rts ;;; Print contents of assembly_bytes print_assembly: clrb ldx #assembly_bytes print_assembly_loop: cmpb assembly_length beq print_assembly_fill cmpb #4 beq print_assembly_end jsr out2hs incb stab temp ; save number of printed bytes bra print_assembly_loop print_assembly_fill: cmpb #4 beq print_assembly_end jsr outs jsr outs jsr outs incb bra print_assembly_fill print_assembly_end: rts ;;; Print remaining assembly_bytes print_remaining_ass: ldab temp ldx #assembly_bytes abx print_remaining_ass_loop: cmpb assembly_length beq print_remaining_ass_end jsr out2hs incb bitb #$03 beq print_remaining_ass_nl bra print_remaining_ass_loop print_remaining_ass_end: rts print_remaining_ass_nl: jsr pcrlf jsr outs jsr outs jsr outs jsr outs jsr outs jsr outs jsr outs jsr outs jsr outs jsr outs jsr outs bra print_remaining_ass_loop ;;; ;;; Assemble op-code and operand based on opcode_pointer and line_pointer_operand ;;; Result in assembly_bytes and assembly_length ;;; assemble: clr assembly_length ldx opcode_pointer ; get pointer to entry in op-code table ldaa 9,x ; get op-code type (last byte of entry) bpl *+5 jmp assemble_pseudo ; is it a pseudo op-code? cmpa #OP5 ; is op-code implied? beq assemble_implied cmpa #OP4 ; is op-code branch? beq assemble_branch ldx line_pointer_operand ldaa ,x cmpa #'# ; is it an immediate operand? bne *+5 jmp assemble_immediate cmpa #', ; is it an indexed operand? bne *+5 jmp assemble_indexed ldaa 1,x cmpa #', ; is it an indexed operand? bne *+5 jmp assemble_indexed ldaa 2,x cmpa #', ; is it an indexed operand? bne *+5 jmp assemble_indexed ldaa 3,x cmpa #', ; is it an indexed operand? bne *+5 jmp assemble_indexed ldx line_pointer_operand ldaa ,x cmpa #'< ; is direct addressing forced? beq *+5 jmp assemble_extended inx stx line_pointer_operand jmp assemble_direct assemble_implied: ldaa 8,x staa assembly_bytes ldaa #1 staa assembly_length clc rts assemble_branch: ldaa 4,x staa assembly_bytes ldaa #2 staa assembly_length ldx line_pointer_operand jsr read_expression bcc *+5 jmp assemble_expr_fail ldd exp_result subd program_counter subd #2 ; subtract operation itself stab assembly_bytes+1 tsta ; check branch limits beq assemble_branch_ok cmpa #$ff beq assemble_branch_ok jsr report_branch_range assemble_branch_ok: clc rts assemble_immediate: ldx opcode_pointer ldaa 4,x bne *+5 jmp assemble_error ; does this op-code have an immediate version? staa assembly_bytes ldaa 9,x inca staa assembly_length ldx line_pointer_operand inx ; skip "#" jsr read_expression bcc *+5 jmp assemble_expr_fail ldaa assembly_length cmpa #$02 beq assemble_immediate1 ldx exp_result stx assembly_bytes+1 clc rts assemble_immediate1: ldaa exp_result+1 staa assembly_bytes+1 clc rts assemble_indexed: ldx opcode_pointer ldaa 6,x beq assemble_error ; does this op-code have an immediate version? staa assembly_bytes ldaa #2 staa assembly_length ldx line_pointer_operand jsr read_expression ldaa exp_result+1 ; truncate value to 8 bits staa assembly_bytes+1 tst exp_result ; check offset limit beq assemble_indexed_ok jsr report_invalid_expr assemble_indexed_ok: clc rts assemble_extended: ldx opcode_pointer ldaa 7,x beq assemble_error ; does this op-code have an extended version? staa assembly_bytes ldaa #3 staa assembly_length ldx line_pointer_operand jsr read_expression bcc *+5 jmp assemble_expr_fail ldx exp_result stx assembly_bytes+1 clc rts assemble_direct: ldx opcode_pointer ldaa 5,x beq assemble_error ; does this op-code have a direct version? staa assembly_bytes ldaa #2 staa assembly_length ldx line_pointer_operand jsr read_expression bcc *+5 jmp assemble_expr_fail ldaa exp_result+1 staa assembly_bytes+1 clc rts assemble_error: sec rts assemble_pseudo: cmpa #PS_ORG bne *+5 jmp assemble_pseudo_org cmpa #PS_RMB bne *+5 jmp assemble_pseudo_rmb cmpa #PS_EQU bne *+5 jmp assemble_pseudo_equ cmpa #PS_FCB bne *+5 jmp assemble_pseudo_fcb cmpa #PS_FDB bne *+5 jmp assemble_pseudo_fdb cmpa #PS_FCC bne *+5 jmp assemble_pseudo_fcc cmpa #PS_NUL bne *+5 jmp assemble_pseudo_null sec rts assemble_pseudo_org: ldx line_pointer_operand jsr read_expression bcc *+5 jmp assemble_expr_fail ldx exp_result stx program_counter clc rts assemble_pseudo_rmb: ldx line_pointer_operand jsr read_expression bcc *+5 jmp assemble_expr_fail ldd exp_result std program_counter_bump clc rts assemble_pseudo_equ: ldx line_pointer_operand ; Parse expression jsr read_expression bcc *+5 jmp assemble_expr_fail ldx line_pointer_label ; Find line label in table jsr find_label bcc *+5 jmp report_label_not_found ldx str2 ; Label table search result jsr str_skp_next inx ldd exp_result std ,x clc rts assemble_pseudo_fcb: ldx line_pointer_operand assemble_pseudo_fcb_next: jsr read_expression bcc *+5 jmp assemble_expr_fail ldaa exp_result+1 ldx #assembly_bytes ldab assembly_length abx staa ,x inc assembly_length ldx exp_pointer ; are there more data to read? ldaa ,x inx cmpa #', beq assemble_pseudo_fcb_next clc rts assemble_pseudo_fdb: ldx line_pointer_operand assemble_pseudo_fdb_next: jsr read_expression bcc *+5 jmp assemble_expr_fail ldx #assembly_bytes ldab assembly_length abx ldd exp_result std ,x inc assembly_length inc assembly_length ldx exp_pointer ; are there more data to read? ldaa ,x inx cmpa #', beq assemble_pseudo_fdb_next clc rts assemble_pseudo_fcc: ldx line_pointer_operand ldaa ,x staa temp ; save string delimiter assemble_pseudo_fcc_loop: inx ldaa ,x beq assemble_pseudo_fcc_end ; unexpected end on line cmpa temp ; compare with delimiter beq assemble_pseudo_fcc_end ; end of string? pshx ldx #assembly_bytes ldab assembly_length abx staa ,x inc assembly_length pulx beq assemble_pseudo_fcc_end ; assembly line overflow bra assemble_pseudo_fcc_loop assemble_pseudo_fcc_end: clc rts assemble_pseudo_null: clc rts assemble_expr_fail: jsr report_invalid_expr clc ; assembly OK but expression failed rts ;;; ;;; Parse line_buffer and create pointers to opcode and operand (if they exist) ;;; Updates line_pointer_label, line_pointer_opcode and line_pointer_operand ;;; segment_line: ldx #0 stx line_pointer_label stx line_pointer_opcode stx line_pointer_operand ldx #line_buffer ldaa ,x ; empty line? beq segment_line_end cmpa #'* ; comment? beq segment_line_end cmpa #'; ; comment? beq segment_line_end cmpa #$20 ; space? beq segment_line_nolabel cmpa #$09 ; tab? beq segment_line_nolabel stx line_pointer_label segment_line_nolabel: jsr str_skp_next ; go to opcode tst ,x ; end of line? beq segment_line_end stx line_pointer_opcode jsr str_skp_next ; go to operand tst ,x ; end of line? beq segment_line_end stx line_pointer_operand segment_line_end: rts ;;; ;;; Read label from string at X poiner and store in label table ;;; read_label: ldaa ,x jsr char_is_alpha ; first char must be alpha a-z or A-Z bcs read_label_end read_label_loop: ldaa ,x beq read_label_end cmpa #$20 beq read_label_end cmpa #$09 beq read_label_end cmpa #': beq read_label_end pshx ldx label_table_position staa ,x inx stx label_table_position pulx inx bra read_label_loop read_label_end: ldx label_table_position clr ,x ldd program_counter std 1,x inx inx inx clr 0,x ; mark end of table stx label_table_position rts ;;; ;;; Find label ;;; Search string given by X ;;; Carry clear on found ;;; Label value in variable 'number' ;;; Pointer to label name in 'str2' ;;; find_label: stx str1 ldx label_table stx str2 find_label_loop: tst ,x ; end of table? beq find_label_end jsr str_cmp bcc find_label_found ldx str2 jsr str_skp_next inx ; skip string termination inx ; skip label value inx stx str2 bra find_label_loop find_label_found: ldx str2 jsr str_skp_next inx ldx ,x stx number clc rts find_label_end: clr number clr number+1 sec rts ;;; ;;; Print all labels ;;; print_labels: ldx #txt_labels jsr str_print ldx label_table print_labels_loop: tst ,x beq print_labels_end jsr str_print inx ; skip string terminator jsr outs jsr out4hs ; print label value jsr pcrlf bra print_labels_loop print_labels_end: rts ;;; ;;; Read op-code from string at X poiner and store in op-code buffer ;;; read_opcode: ldaa 0,x anda #$df staa opcode_buffer+0 ldaa 1,x anda #$df staa opcode_buffer+1 ldaa 2,x anda #$df staa opcode_buffer+2 ldaa 3,x cmpa #$40 bls read_opcode2 cmpa #$7a bhi read_opcode2 anda #$df read_opcode1: staa opcode_buffer+3 rts read_opcode2: ldaa #$20 bra read_opcode1 ;;; ;;; Compare op-code at X with opcode in buffer ;;; Carry clear if found ;;; cmp_opcode: ldaa 0,x cmpa opcode_buffer+0 bne cmp_opcode_nomatch ldaa 1,x cmpa opcode_buffer+1 bne cmp_opcode_nomatch ldaa 2,x cmpa opcode_buffer+2 bne cmp_opcode_nomatch ldaa 3,x cmpa opcode_buffer+3 bne cmp_opcode_nomatch clc rts cmp_opcode_nomatch: sec rts ;;; ;;; Lookup op-code in table ;;; Carry clear if found ;;; lookup_opcode: ldab #10 ; table entry length is 10 bytes ldx #opcode_table lookup_opcode_loop: tst ,x ; end of table? beq lookup_opcode_notfound jsr cmp_opcode bcc lookup_opcode_found abx bra lookup_opcode_loop lookup_opcode_found: stx opcode_pointer clc rts lookup_opcode_notfound: sec rts ;;; ;;; Compare two alphanumeric strings given by str1 and str2 ;;; Carry clear on match ;;; str_cmp: ldx str1 pshx ldx str2 pshx ldx str2 str_cmp_loop: ldaa ,x bsr str_cmp_eos ; special end of string routine beq str_cmp_end pshx ldx str1 ldab ,x inx stx str1 pulx cba bne str_cmp_end_mismatch inx bra str_cmp_loop str_cmp_end_mismatch: pulx stx str2 pulx stx str1 sec rts str_cmp_end: ldx str1 ldaa ,x bsr str_cmp_eos ; special end of string routine bne str_cmp_end_mismatch pulx stx str2 pulx stx str1 clc rts str_cmp_eos: tsta beq str_cmp_eos_force ; end of string cmpa #$20 beq str_cmp_eos_force ; end of word cmpa #$09 beq str_cmp_eos_force ; end of word cmpa #$20 beq str_cmp_eos_force ; end of word cmpa #': beq str_cmp_eos_force ; end of word cmpa #', beq str_cmp_eos_force ; end of word cmpa #'+ beq str_cmp_eos_force ; end of word cmpa #'- beq str_cmp_eos_force ; end of word rts str_cmp_eos_force: clra rts ;;; ;;; Print string at X ;;; str_print_word: ldaa ,x beq str_print_end cmpa #$20 beq str_print_end cmpa #$09 beq str_print_end jsr console_output inx bra str_print_word ;;; ;;; Print string at X ;;; str_print: ldaa ,x beq str_print_end jsr console_output inx bra str_print str_print_end: rts ;;; ;;; Print CR + LF ;;; pcrlf: psha ldaa #$0d jsr console_output ldaa #$0a jsr console_output pula rts ;;; ;;; Print hex values ;;; outhl: lsra lsra lsra lsra outhr: anda #$0f adda #$30 cmpa #$39 bls outhe adda #$27 ; $07=lower case & $27=upper case outhe: jmp console_output out2h: ldaa 0,x jsr outhl ldaa 0,x inx jmp outhr out4hs: bsr out2h out2hs: bsr out2h ;;; ;;; Print space ;;; outs: psha ldaa #$20 jsr console_output pula rts ;;; ;;; Decimal output routine borrowed from FLEX. Modified to make use of D-reg. ;;; IN: X = pointer to 16-bit unsigned binary number ;;; outdec: clr outdec_digit ldaa #4 staa outdec_loop1 ldd 0,x ldx #decimal_table ;;; Iterate over table for each position outdec_1: bsr outdec_2 inx inx dec outdec_loop1 bne outdec_1 tba jmp outhr ;;; Binary to decimal conversion outdec_2: clr outdec_loop2 outdec_3: cmpa 0,x bcs outdec_5 bhi outdec_4 cmpb 1,x bcs outdec_5 outdec_4: subd 0,x inc outdec_loop2 bra outdec_3 ;;; Print digit or leading zero outdec_5: psha ldaa outdec_loop2 bne outdec_6 tst outdec_digit bne outdec_6 ldaa #'0 jsr console_output bra outdec_7 outdec_6: inc outdec_digit jsr outhr outdec_7: pula rts ;;; Decimal table decimal_table: fdb 10000 fdb 1000 fdb 100 fdb 10 ;;; ;;; Skip to next word in string at X ;;; str_skp_next: ldaa ,x beq str_skp_next_e ; end of string? cmpa #$20 ; space beq str_skp_next_1 cmpa #$09 ; tab beq str_skp_next_1 inx bra str_skp_next str_skp_next_1: ldaa ,x beq str_skp_next_e ; end of string? cmpa #$20 ; space beq str_skp_next_2 cmpa #$09 ; tab beq str_skp_next_2 str_skp_next_e: rts str_skp_next_2: inx bra str_skp_next_1 ;;; ;;; Read expression from string at X ;;; Resolve labels and handle add/sub of two values ;;; Result in variable 'number' ;;; Carry clear on valid expression ;;; read_expression: clr exp_result clr exp_result+1 read_expression_cont: ldaa #'+ staa exp_sign ; default sign is + ldaa ,x ;;; Check if there is a sign in string cmpa #'+ bne read_expression_nplus staa exp_sign inx bra read_expression_read read_expression_nplus: cmpa #'- bne read_expression_read staa exp_sign inx read_expression_read: ;;; Eventual sign has been extracted - read value stx exp_pointer ldaa ,x cmpa #'$ ; is it a hex value? beq read_expression_hex cmpa #'% ; is it a binary value? beq read_expression_bin cmpa #'' ; is it a char value? beq read_expression_chr cmpa #'* ; is it a program counter reference? beq read_expression_pc jsr char_is_alpha ; is it decimal value? bcs read_expression_dec ;;; If non of the above - try to resolve as label tst pass2_flag ; ignore labels for pass1 (speed improvement) beq read_expression_dummy jsr find_label bcc *+5 jsr report_label_not_found bra read_expression_end read_expression_dummy: clr exp_result ; set 0 as dummy value clr exp_result+1 clc bra read_expression_end read_expression_hex: jsr parse_hex bra read_expression_end read_expression_dec: jsr parse_dec bra read_expression_end read_expression_bin: jsr parse_bin bra read_expression_end read_expression_chr: inx ; skip "'" ldaa ,x ; get char value staa exp_result+1 clr exp_result bra read_expression_stop read_expression_pc: ldd program_counter std number clc bra read_expression_end read_expression_end: ;;; Expression number/part complete - add together and check for more bcs read_expression_abort ; error in processing expression ldaa exp_sign ; check sign of operation cmpa #'- bne read_expression_add ldd exp_result subd number bra read_expression_res read_expression_add: ldd exp_result addd number read_expression_res: std exp_result jsr exp_next ldaa ,x beq read_expression_stop cmpa #'+ ; there is more of the expression to do bne *+5 jmp read_expression_cont cmpa #'- ; there is more of the expression to do bne *+5 jmp read_expression_cont read_expression_stop: stx exp_pointer ; save end of expression clc rts read_expression_abort: stx exp_pointer ; save end of expression sec rts ;;; ;;; Go to next part of expression ;;; exp_next: ldx exp_pointer exp_next_loop: ldaa ,x jsr str_cmp_eos beq exp_next_end inx bra exp_next_loop exp_next_end: stx exp_pointer rts ;;; ;;; Parse hexadecimal value from ASCII string at X ;;; Result in variable 'number' ;;; parse_hex: clr number clr number+1 ldaa ,x cmpa #'$ ; skip eventual $ in the beginning bne parse_hex_loop inx parse_hex_loop: ldaa ,x jsr str_cmp_eos beq parse_hex_end jsr char_to_hex bcc parse_hex_err std temp ldd number asld asld asld asld orab temp std number inx bra parse_hex_loop parse_hex_end: clc rts parse_hex_err: sec rts ;;; ;;; Parse decimal value from ASCII string at X ;;; Result in variable 'number' ;;; parse_dec: clr number clr number+1 parse_dec_loop: ldaa ,x jsr str_cmp_eos beq parse_dec_end jsr char_to_hex bcc parse_dec_err cmpa #$09 bgt parse_dec_err jsr mult10 clr temp ; add value staa temp+1 ldd number addd temp std number inx bra parse_dec_loop parse_dec_end: clc rts parse_dec_err: sec rts ;;; ;;; Multiply variable 'number' by 10 ;;; mult10: psha pshb ldd number asld ; * 2 std temp asld ; * 4 asld ; * 8 addd temp ; D = D*8+D*2 std number pulb pula rts ;;; ;;; Parse binary value from ASCII string at X ;;; Result in variable 'number' ;;; parse_bin: clr number clr number+1 ldaa ,x cmpa #'% ; skip eventual % in the beginning bne parse_bin_loop inx parse_bin_loop: ldaa ,x jsr str_cmp_eos beq parse_bin_end jsr char_to_hex bcc parse_dec_err cmpa #$01 bgt parse_bin_err staa temp ldd number asld orab temp std number inx bra parse_bin_loop parse_bin_end: clc rts parse_bin_err: sec rts ;;; ;;; Convert ASCII char to hex ;;; IN: Character in A ;;; OUT: Value in A. Carry set on OK ;;; char_to_hex: beq char_to_hex_fail ; end of string suba #$30 bmi char_to_hex_fail ; not hex cmpa #$09 ble char_to_hex_ok ; $9 and below anda #$df ; to upper case cmpa #$11 blt char_to_hex_fail ; not hex cmpa #$16 bgt char_to_hex_fail ; not hex suba #7 char_to_hex_ok: sec rts char_to_hex_fail: clc rts ;;; ;;; Check if char in A is _, a-z or A-Z ;;; Carry clear on true ;;; char_is_alpha: psha char_is_alpha1: cmpa #'_ beq char_is_alpha_ok anda #$df ; to uppe case cmpa #$40 ; below 'A bls char_is_alpha_false cmpa #$5a ; above 'Z bgt char_is_alpha_false char_is_alpha_ok: pula clc rts char_is_alpha_false: pula sec rts ;;; ;;; Store generated bytes from current line ;;; store_line_bytes: tst direct_mem_flag beq store_line_bytes_end clrb store_direct_loop: cmpb assembly_length beq store_line_bytes_end ldx #assembly_bytes abx ldaa ,x ldx program_counter abx staa ,x incb bra store_direct_loop store_line_bytes_end: rts ;;; ;;; Parse command line arguments ;;; parse_args: ldx #$0100 ;set label_table default position stx label_table ldx args parse_args_loop: ldaa ,x beq parse_args_end ; end of line cmpa #'- ; look for flags beq parse_arg_flag parse_args_next: jsr str_skp_next ; go to next argument bra parse_args_loop parse_arg_flag: inx ; skip "-" ldaa ,x anda #$df ; to upper case cmpa #'L ; print linting bne *+5 inc listing_flag cmpa #'E ; hide errors bne *+5 inc noerr_flag cmpa #'D ; write directly to memory bne *+5 inc direct_mem_flag cmpa #'T ; move label table bne *+5 jsr parse_args_label_table bra parse_args_next parse_args_end: rts parse_args_label_table: inx jsr parse_hex bcs parse_args_end ldd number std label_table rts ;;; ;;; Open file for reading ;;; In: X - pointer to file name ;;; Out: Z set on success, error code in A ;;; fopen: stx fname0 stx fname1 ;;; Get file info ldx #fcb0 jsr sys_call tst fcb0+1 ; read error code bne fopen_not_found ;;; Check if directory ldaa fcb0+4 ; get flags bita #$40 bne fopen_is_directory ;;; Setup file for reading ldx #0 stx fcnth stx fcntl stx fsect clra rts fopen_not_found: ldx #txt_not_found jsr str_print ldaa fcb0+1 ; read error code rts fopen_is_directory: ldx #txt_is_directory jsr str_print ldaa #$ff rts ;;; ;;; Read one byte from file (must call fopen first) ;;; Out: Byte in A, carry set on error or eof ;;; fread: ;;; Check if we have reached end of file ldx fcnth cpx fsizeh bhi fread_eof ldx fcntl cpx fsizel beq fread_eof ;;; We have bytes to read - check if first byte i sector xgdx anda #$01 ; mask away upper 7 bits from LOW file byte bointer xgdx cpx #0 ; first byte of new sector? bne fread_getbyte ;;; Load a new sector into buffer ldx #fcb1 jsr sys_call tst fcb1+1 ; read error code bne fread_error ldx fsect inx stx fsect ;;; Pull one byte from buffer fread_getbyte: ldd fcntl anda #$01 ; mask away upper 7 bits from D addd #secbuf ; calculate xgdx ldaa ,x ; load byte from sector buffer xgdx ;;; Increase file byte counter ldd fcntl addd #1 std fcntl bcc fread_getbyte1 ldd fcnth addd #1 std fcnth fread_getbyte1: xgdx clc rts fread_eof: clra fread_error: sec rts ;;; ;;; Get line from file and put in line buffer ;;; Carry set = EOF ;;; fgetline: ldx #line_buffer stx line_pointer fgetline_loop: jsr fread ; get byte from file bcs fgetline_eof cmpa #$0a ; end of line? beq fgetline_end cmpa #$0d ; ignore return beq fgetline_loop ldx line_pointer staa ,x inx cpx #line_buffer+255 bhi fgetline_end stx line_pointer bra fgetline_loop fgetline_end: ldx line_pointer clr ,x ; end of string clc rts fgetline_eof: ldx line_pointer cpx #line_buffer bne fgetline_end clr ,x sec rts ;;; ;;; Op code table ;;; ;;; Name ;;; Immediate,Direct,Indexed,Extended,Implied,Argtype ;;; NUL = no op-code exist ;;; NUL: equ $00 ; No op-code exists OP0: equ $00 ; Operand type 0 - no immediate operand OP1: equ $01 ; Operand type 1 - one byte immediate operand OP2: equ $02 ; Operand type 2 - two byte immediate operand OP3: equ $03 ; Operand type 3 - one+addr byte immediate operand OP4: equ $04 ; Operand type 4 - branch OP5: equ $05 ; Operand type 5 - no operand PS_ORG: equ $80 ; Pseudo op-code - org PS_FCB: equ $81 ; Pseudo op-code - fcb PS_FDB: equ $82 ; Pseudo op-code - fdb PS_FCC: equ $83 ; Pseudo op-code - fcc PS_RMB: equ $84 ; Pseudo op-code - rmb PS_EQU: equ $85 ; Pseudo op-code - equ PS_NUL: equ $86 ; Pseudo op-code - end opcode_table: ;;; Accumulator and memory manipulation instructions fcc "ADDA" fcb $8b,$9b,$ab,$bb,NUL,OP1 fcc "ADDB" fcb $cb,$db,$eb,$fb,NUL,OP1 fcc "ADDD" fcb $c3,$d3,$e3,$f3,NUL,OP2 fcc "ABA " fcb NUL,NUL,NUL,NUL,$1b,OP5 fcc "ADCA" fcb $89,$99,$a9,$b9,NUL,OP1 fcc "ADCB" fcb $c9,$d9,$e9,$f9,NUL,OP1 fcc "ANDA" fcb $84,$94,$a4,$b4,NUL,OP1 fcc "ANDB" fcb $c4,$d4,$e4,$f4,NUL,OP1 fcc "BITA" fcb $85,$95,$a5,$b5,NUL,OP1 fcc "BITB" fcb $c5,$d5,$e5,$f5,NUL,OP1 fcc "CLR " fcb NUL,NUL,$6f,$7f,NUL,OP0 fcc "CLRA" fcb NUL,NUL,NUL,NUL,$4f,OP5 fcc "CLRB" fcb NUL,NUL,NUL,NUL,$5f,OP5 fcc "CMPA" fcb $81,$91,$a1,$b1,NUL,OP1 fcc "CMPB" fcb $c1,$d1,$e1,$f1,NUL,OP1 fcc "CBA " fcb NUL,NUL,NUL,NUL,$11,OP5 fcc "COM " fcb NUL,NUL,$63,$73,NUL,OP0 fcc "COMA" fcb NUL,NUL,NUL,NUL,$43,OP5 fcc "COMB" fcb NUL,NUL,NUL,NUL,$53,OP5 fcc "NEG " fcb NUL,NUL,$60,$70,NUL,OP0 fcc "NEGA" fcb NUL,NUL,NUL,NUL,$40,OP5 fcc "NEGB" fcb NUL,NUL,NUL,NUL,$50,OP5 fcc "DAA " fcb NUL,NUL,NUL,NUL,$19,OP5 fcc "DEC " fcb NUL,NUL,$6a,$7a,NUL,OP0 fcc "DECA" fcb NUL,NUL,NUL,NUL,$4a,OP5 fcc "DECB" fcb NUL,NUL,NUL,NUL,$5a,OP5 fcc "EORA" fcb $88,$98,$a8,$b8,NUL,OP0 fcc "EORB" fcb $c8,$d8,$e8,$f8,NUL,OP0 fcc "INC " fcb NUL,NUL,$6c,$7c,NUL,OP0 fcc "INCA" fcb NUL,NUL,NUL,NUL,$4c,OP5 fcc "INCB" fcb NUL,NUL,NUL,NUL,$5c,OP5 fcc "LDAA" fcb $86,$96,$a6,$b6,NUL,OP1 fcc "LDAB" fcb $c6,$d6,$e6,$f6,NUL,OP1 fcc "LDD " fcb $cc,$dc,$ec,$fc,NUL,OP2 fcc "MUL " fcb NUL,NUL,NUL,NUL,$3d,OP5 fcc "ORAA" fcb $8a,$9a,$aa,$ba,NUL,OP1 fcc "ORAB" fcb $ca,$da,$ea,$fa,NUL,OP1 fcc "PSHA" fcb NUL,NUL,NUL,NUL,$36,OP5 fcc "PSHB" fcb NUL,NUL,NUL,NUL,$37,OP5 fcc "PULA" fcb NUL,NUL,NUL,NUL,$32,OP5 fcc "PULB" fcb NUL,NUL,NUL,NUL,$33,OP5 fcc "ROL " fcb NUL,NUL,$69,$79,NUL,OP0 fcc "ROLA" fcb NUL,NUL,NUL,NUL,$49,OP5 fcc "ROLB" fcb NUL,NUL,NUL,NUL,$59,OP5 fcc "ROR " fcb NUL,NUL,$66,$76,NUL,OP0 fcc "RORA" fcb NUL,NUL,NUL,NUL,$46,OP5 fcc "RORB" fcb NUL,NUL,NUL,NUL,$56,OP5 fcc "ASL " fcb NUL,NUL,$68,$78,NUL,OP0 fcc "ASLA" fcb NUL,NUL,NUL,NUL,$48,OP5 fcc "ASLB" fcb NUL,NUL,NUL,NUL,$58,OP5 fcc "ASLD" fcb NUL,NUL,NUL,NUL,$05,OP5 fcc "ASR " fcb NUL,NUL,$67,$77,NUL,OP0 fcc "ASRA" fcb NUL,NUL,NUL,NUL,$47,OP5 fcc "ASRB" fcb NUL,NUL,NUL,NUL,$57,OP5 fcc "LSR " fcb NUL,NUL,$64,$74,NUL,OP0 fcc "LSRA" fcb NUL,NUL,NUL,NUL,$44,OP5 fcc "LSRB" fcb NUL,NUL,NUL,NUL,$54,OP5 fcc "LSRD" fcb NUL,NUL,NUL,NUL,$04,OP5 fcc "STAA" fcb NUL,$97,$a7,$b7,NUL,OP0 fcc "STAB" fcb NUL,$d7,$e7,$f7,NUL,OP0 fcc "STD " fcb NUL,$dd,$ed,$fd,NUL,OP0 fcc "SUBA" fcb $80,$90,$a0,$b0,NUL,OP1 fcc "SUBB" fcb $c0,$d0,$e0,$f0,NUL,OP1 fcc "SUBD" fcb $83,$93,$a3,$b3,NUL,OP2 fcc "SBA " fcb NUL,NUL,NUL,NUL,$10,OP5 fcc "SBCA" fcb $82,$92,$a2,$b2,NUL,OP1 fcc "SBCB" fcb $c2,$d2,$e2,$f2,NUL,OP1 fcc "TAB " fcb NUL,NUL,NUL,NUL,$16,OP5 fcc "TBA " fcb NUL,NUL,NUL,NUL,$17,OP5 fcc "TST " fcb NUL,NUL,$6d,$7d,NUL,OP0 fcc "TSTA" fcb NUL,NUL,NUL,NUL,$4d,OP5 fcc "TSTB" fcb NUL,NUL,NUL,NUL,$5d,OP5 fcc "AIM " fcb NUL,$71,$61,NUL,NUL,OP3 fcc "OIM " fcb NUL,$71,$61,NUL,NUL,OP3 fcc "EIM " fcb NUL,$75,$65,NUL,NUL,OP3 fcc "TIM " fcb NUL,$7b,$6b,NUL,NUL,OP3 ;;; Index register and stack manipulation instructions fcc "CPX " fcb $8c,$9c,$ac,$bc,NUL,OP2 fcc "DEX " fcb NUL,NUL,NUL,NUL,$09,OP5 fcc "DES " fcb NUL,NUL,NUL,NUL,$34,OP5 fcc "INX " fcb NUL,NUL,NUL,NUL,$08,OP5 fcc "INS " fcb NUL,NUL,NUL,NUL,$31,OP5 fcc "LDX " fcb $ce,$de,$ee,$fe,NUL,OP2 fcc "LDS " fcb $8e,$9e,$ae,$be,NUL,OP2 fcc "STX " fcb NUL,$df,$ef,$ff,NUL,OP0 fcc "STS " fcb NUL,$9f,$af,$bf,NUL,OP0 fcc "TXS " fcb NUL,NUL,NUL,NUL,$35,OP5 fcc "TSX " fcb NUL,NUL,NUL,NUL,$30,OP5 fcc "ABX " fcb NUL,NUL,NUL,NUL,$3a,OP5 fcc "PSHX" fcb NUL,NUL,NUL,NUL,$3c,OP5 fcc "PULX" fcb NUL,NUL,NUL,NUL,$38,OP5 fcc "XGDX" fcb NUL,NUL,NUL,NUL,$18,OP5 ;;; Jump and branch instructions fcc "BRA " fcb $20,NUL,NUL,NUL,NUL,OP4 fcc "BRN " fcb $21,NUL,NUL,NUL,NUL,OP4 fcc "BCC " fcb $24,NUL,NUL,NUL,NUL,OP4 fcc "BCS " fcb $25,NUL,NUL,NUL,NUL,OP4 ; same as BLO fcc "BLO " fcb $25,NUL,NUL,NUL,NUL,OP4 ; same as BCS fcc "BEQ " fcb $27,NUL,NUL,NUL,NUL,OP4 fcc "BGE " fcb $2c,NUL,NUL,NUL,NUL,OP4 fcc "BGT " fcb $2e,NUL,NUL,NUL,NUL,OP4 fcc "BHI " fcb $22,NUL,NUL,NUL,NUL,OP4 fcc "BLE " fcb $2f,NUL,NUL,NUL,NUL,OP4 fcc "BLS " fcb $23,NUL,NUL,NUL,NUL,OP4 fcc "BLT " fcb $2d,NUL,NUL,NUL,NUL,OP4 fcc "BMI " fcb $2b,NUL,NUL,NUL,NUL,OP4 fcc "BNE " fcb $26,NUL,NUL,NUL,NUL,OP4 fcc "BVC " fcb $28,NUL,NUL,NUL,NUL,OP4 fcc "BVS " fcb $29,NUL,NUL,NUL,NUL,OP4 fcc "BPL " fcb $2a,NUL,NUL,NUL,NUL,OP4 fcc "BSR " fcb $8d,NUL,NUL,NUL,NUL,OP4 fcc "JMP " fcb NUL,NUL,$6e,$7e,NUL,OP0 fcc "JSR " fcb NUL,$9d,$ad,$bd,NUL,OP0 fcc "NOP " fcb NUL,NUL,NUL,NUL,$01,OP5 fcc "RTI " fcb NUL,NUL,NUL,NUL,$3b,OP5 fcc "RTS " fcb NUL,NUL,NUL,NUL,$39,OP5 fcc "SWI " fcb NUL,NUL,NUL,NUL,$3f,OP5 fcc "WAI " fcb NUL,NUL,NUL,NUL,$3e,OP5 fcc "SLP " fcb NUL,NUL,NUL,NUL,$1a,OP5 ;;; Condition code register manipulation fcc "CLC " fcb NUL,NUL,NUL,NUL,$0c,OP5 fcc "CLI " fcb NUL,NUL,NUL,NUL,$0e,OP5 fcc "CLV " fcb NUL,NUL,NUL,NUL,$0a,OP5 fcc "SEC " fcb NUL,NUL,NUL,NUL,$0d,OP5 fcc "SEI " fcb NUL,NUL,NUL,NUL,$0f,OP5 fcc "SEV " fcb NUL,NUL,NUL,NUL,$0b,OP5 fcc "TAP " fcb NUL,NUL,NUL,NUL,$06,OP5 fcc "TPA " fcb NUL,NUL,NUL,NUL,$07,OP5 ;;; Pseudo op-codes fcc "ORG " fcb NUL,NUL,NUL,NUL,NUL,PS_ORG fcc "FCB " fcb NUL,NUL,NUL,NUL,NUL,PS_FCB fcc "FDB " fcb NUL,NUL,NUL,NUL,NUL,PS_FDB fcc "FCC " fcb NUL,NUL,NUL,NUL,NUL,PS_FCC fcc "RMB " fcb NUL,NUL,NUL,NUL,NUL,PS_RMB fcc "EQU " fcb NUL,NUL,NUL,NUL,NUL,PS_EQU fcc "END " fcb NUL,NUL,NUL,NUL,NUL,PS_NUL ; do nothing fcc "SPC " fcb NUL,NUL,NUL,NUL,NUL,PS_NUL ; do nothing fcc "NAM " fcb NUL,NUL,NUL,NUL,NUL,PS_NUL ; do nothing fcc "NAME" fcb NUL,NUL,NUL,NUL,NUL,PS_NUL ; do nothing fcc "TTL " fcb NUL,NUL,NUL,NUL,NUL,PS_NUL ; do nothing fcc "OPT " fcb NUL,NUL,NUL,NUL,NUL,PS_NUL ; do nothing fcc "PAG " fcb NUL,NUL,NUL,NUL,NUL,PS_NUL ; do nothing fcc "PAGE" fcb NUL,NUL,NUL,NUL,NUL,PS_NUL ; do nothing ;;; End of table fcb $00 ;;; ;;; Text strings ;;; txt_help: fcc "6301/6303 assembler v1.0.2" fcb $0d,$0a fcc "Daniel Tufvesson 2017-2022" fcb $0d,$0a fcc "Syntax: asm [files] [-l] [-e] [-d] [-tXXXX]" fcb $0d,$0a fcc "Options:" fcb $0d,$0a fcc " -l Print listing" fcb $0d,$0a fcc " -e Hide errors" fcb $0d,$0a fcc " -d Assemble directly to memory" fcb $0d,$0a fcc " -tXXXX Move symbol table to address $XXXX (default $0100)" fcb $0d,$0a,$00 txt_labels: fcb $0d,$0a fcc "Labels:" fcb $0d,$0a,$00 txt_errors: fcb $0d,$0a fcc "Errors: " fcb $00 txt_no_errors: fcb $0d,$0a fcc "No errors" fcb $0d,$0a,$0a,$00 txt_not_found: fcc "File not found" fcb $0d,$0a,$00 txt_read_error: fcc "File read error" fcb $0d,$0a,$00 txt_is_directory: fcc "Entry is a directory" fcb $0d,$0a,$00 txt_invalid_opcode: fcc "Invalid op-code" fcb $0d,$0a,$00 txt_duplicate_label: fcc "Duplicate label" fcb $0d,$0a,$00 txt_label_not_found: fcc "Label not found" fcb $0d,$0a,$00 txt_invalid_expression: fcc "Invalid expression" fcb $0d,$0a,$00 txt_branch_range: fcc "Branch out of range" fcb $0d,$0a,$00 ;;; ;;; File system variables ;;; fcb0: fcb $01 ; command fcb 0 ; error code fname0: fdb 0 ; file name fcb 0 ; flags fdb 0 ; sectors fsizeh: fdb 0 ; size MSB fsizel: fdb 0 ; size LSB fdb 0 ; year fcb 0 ; month fcb 0 ; day fcb 0 ; hours fcb 0 ; minutes fcb 0 ; seconds * fcb1: fcb $11 ; command fcb 0 ; error code fname1: fdb 0 ; file name fdb secbuf ; destination address fsect: fdb 0 ; sector number * fcnth: fdb 0 ; bytes read MSB fcntl: fdb 0 ; bytes read LSB * secbuf: rmb 512 ;;; ;;; Program variables ;;; temp: fdb 0 ; general temp storage str1: fdb 0 ; string compare string 1 str2: fdb 0 ; string compare string 2 number: fdb 0 ; result of numeric operation exp_sign: fcb 0 ; sign of current expression operation exp_pointer: fdb 0 ; pointer to current part of expression exp_result: fdb 0 ; result of expression pass2_flag: fcb 0 ; set on assembly pass 2 errors: fdb 0 ; number of reported errors listing_flag: fcb 0 ; set for output listing noerr_flag: fcb 0 ; set to hide errors direct_mem_flag: fcb 0 ; set to write result directly to memory outdec_digit: rmb 1 ; decimal print digit counter outdec_loop1: rmb 1 ; decimal print loop1 counter outdec_loop2: rmb 1 ; decimal print loop2 counter * *tstargs: fcc "asm test.asm -l" * fcb 0 * args: rmb 2 ; pointer to input argument string args_file: rmb 2 ; pointer to input file name argument line_buffer: rmb 256 ; buffer containing current assembly line line_pointer: rmb 2 ; pointer within current assembly line line_pointer_label: rmb 2 ; pointer to label in assembly line (set to zero if no label) line_pointer_opcode: rmb 2 ; pointer to op-code in assembly line (set to zero if no op-code) line_pointer_operand: rmb 2 ; pointer to operand in assembly line (set to zero if no operand) line_counter: rmb 2 ; assembly line number label_table: rmb 2 ; pointer to beginning of label table (default $0100) label_table_position: rmb 2 ; pointer to next free space in label table program_counter: rmb 2 ; assembly program counter program_counter_bump: rmb 2 ; assembly program counter bump by pseudo instructions opcode_buffer: rmb 4 ; op-code from current line opcode_pointer: rmb 2 ; pointer to current op-code in table assembly_length: rmb 1 ; length of assembly assembly_bytes: rmb 256 ; result of assembly ;;; ;;; Label table ;;; Format: 00|00|...|00|00 ;;;