; codegen.s - documented scalar instruction emitter (E04-S03). ; ; The emitter writes into cg_code, a 256-byte RAM buffer. cg_len is the byte ; count (0-254); every failure sets cg_err and returns C=1. It does not emit a ; whole function yet: parser/codegen integration is E04-S04. ; ; API: ; cg_init ; cg_put A = byte ; cg_lda_imm A = value ; cg_ldx_imm A = value ; cg_ora_imm A = value ; cg_lda_abs A/X = address ; cg_ldx_abs A/X = address ; cg_sta_abs A/X = address ; cg_stx_abs A/X = address ; cg_adc_abs A/X = address ; cg_sbc_abs A/X = address ; cg_cmp_abs A/X = address ; cg_jsr_abs A/X = address ; cg_clc, cg_sec, cg_rts ; cg_branch_fwd A = branch opcode; emits opcode + placeholder, returns ; C=0 and A = fixup index ; cg_patch_here A = fixup index; target is the current end of buffer ; cg_here returns A = current length ; ; Only forward relative branches within +0..+127 bytes are accepted. This is ; enough for the scalar shapes and gives E04-S04 the checked primitive it ; needs for a later long-branch expansion. .setcpu "6502" .include "../common/codegen.inc" .export cg_init, cg_put, cg_here, cg_branch_fwd, cg_patch_here .export cg_lda_imm, cg_ldx_imm, cg_ora_imm, cg_lda_abs, cg_ldx_abs .export cg_sta_abs, cg_stx_abs, cg_adc_abs, cg_sbc_abs, cg_cmp_abs .export cg_jsr_abs, cg_clc, cg_sec, cg_rts .export cg_code, cg_len, cg_err .segment "CODE" cg_init: lda #$00 sta cg_len sta cg_err sta cg_fix_count clc rts cg_here: lda cg_len rts ; A = byte to append. C=1 on overflow. cg_put: ldy cg_len cpy #CG_MAX bcs @full sta cg_code,y inc cg_len clc rts @full: lda #CG_ERR_FULL sta cg_err sec rts ; Immediate forms. cg_lda_imm: pha lda #$A9 jsr cg_put bcs @out pla jmp cg_put @out: pla sec rts cg_ldx_imm: pha lda #$A2 jsr cg_put bcs @out pla jmp cg_put @out: pla sec rts cg_ora_imm: pha lda #$09 jsr cg_put bcs @out pla jmp cg_put @out: pla sec rts ; Absolute-address forms. Each wrapper supplies the opcode in Y; the shared ; routine stores the address before Y is reused by cg_put. cg_lda_abs: ldy #$AD jmp cg_emit_addr cg_ldx_abs: ldy #$AE jmp cg_emit_addr cg_sta_abs: ldy #$8D jmp cg_emit_addr cg_stx_abs: ldy #$8E jmp cg_emit_addr cg_adc_abs: ldy #$6D jmp cg_emit_addr cg_sbc_abs: ldy #$ED jmp cg_emit_addr cg_cmp_abs: ldy #$CD jmp cg_emit_addr cg_jsr_abs: ldy #$20 jmp cg_emit_addr cg_emit_addr: sta cg_addr stx cg_addr+1 tya jsr cg_put bcs @out lda cg_addr jsr cg_put bcs @out lda cg_addr+1 jmp cg_put @out: sec rts cg_clc: lda #$18 jmp cg_put cg_sec: lda #$38 jmp cg_put cg_rts: lda #$60 jmp cg_put ; A = branch opcode; forward target must be patched later. cg_branch_fwd: sta cg_op lda cg_fix_count cmp #CG_FIX_MAX bcs @fix_full sta cg_idx lda cg_op jsr cg_put bcs @out lda cg_len sta cg_pos ldy cg_idx sta cg_fixpos,y inc cg_fix_count lda #$00 jsr cg_put bcs @out lda cg_idx clc rts @fix_full: lda #CG_ERR_FIX sta cg_err sec rts @out: sec rts ; A = fixup index. Patch its placeholder so the branch targets cg_len. cg_patch_here: cmp cg_fix_count bcs @bad tax lda cg_len sec sbc cg_fixpos,x sec sbc #1 cmp #128 bcs @range ldy cg_fixpos,x sta cg_code,y clc rts @bad: lda #CG_ERR_FIX sta cg_err sec rts @range: lda #CG_ERR_RANGE sta cg_err sec rts .segment "BSS" cg_code: .res 256 cg_len: .res 1 cg_err: .res 1 cg_fix_count: .res 1 cg_fixpos: .res CG_FIX_MAX cg_addr: .res 2 cg_op: .res 1 cg_idx: .res 1 cg_pos: .res 1