@@ -39,15 +39,19 @@ static int is_signed_type(type_t type) {
3939}
4040
4141
42- // Find the ZP slot assigned to a var/temp operand. Returns 0 if not found.
42+ // Sentinel for "operand not in ZP map". Must be a ZP address that is never
43+ // assigned by the map — $FF lives in the ABI param zone ($EF–$FF).
44+ #define ZP_NOT_FOUND 0xFF
45+
46+ // Find the ZP slot assigned to a var/temp operand. Returns ZP_NOT_FOUND if not found.
4347static uint8_t zp_map_lookup (zp_map_t * map , tac_operand_t * op ) {
4448 for (unsigned i = 0 ; i < map -> count ; i ++ ) {
4549 zp_entry_t * e = & map -> entries [i ];
4650 if (e -> kind != op -> kind ) continue ;
4751 if (e -> kind == OPERAND_VAR && strcmp (e -> name , op -> name ) == 0 ) return e -> zp_addr ;
4852 if (e -> kind == OPERAND_TEMP && e -> temp_id == op -> temp_id ) return e -> zp_addr ;
4953 }
50- return 0 ;
54+ return ZP_NOT_FOUND ;
5155}
5256
5357
@@ -57,9 +61,12 @@ static void zp_map_add(zp_map_t *map, tac_operand_kind_t kind,
5761 tac_operand_t probe = { .kind = kind };
5862 if (kind == OPERAND_VAR ) probe .name = name ;
5963 else probe .temp_id = temp_id ;
60- if (zp_map_lookup (map , & probe ) != 0 )
64+ if (zp_map_lookup (map , & probe ) != ZP_NOT_FOUND )
65+ return ;
66+ if (map -> count >= ZP_MAP_MAX ) {
67+ fprintf (stderr , "codegen: ZP map overflow (>%d operands)\n" , ZP_MAP_MAX );
6168 return ;
62- if ( map -> count >= ZP_MAP_MAX ) return ;
69+ }
6370
6471 unsigned size = codegen_type_size (type );
6572 zp_entry_t * e = & map -> entries [map -> count ++ ];
@@ -111,7 +118,10 @@ static void zp_map_build(zp_map_t *map, cfg_t *cfg) {
111118static void register_func_label (emitter_t * e , char * name , uint16_t addr ) {
112119 if (e -> func_label_count >= e -> func_label_capacity ) {
113120 unsigned cap = e -> func_label_capacity ? e -> func_label_capacity * 2 : 8 ;
114- e -> func_labels = realloc (e -> func_labels , cap * sizeof (func_label_t ));
121+ func_label_t * grown = arena_alloc (& e -> arena , cap * sizeof (func_label_t ));
122+ if (e -> func_labels )
123+ memcpy (grown , e -> func_labels , e -> func_label_count * sizeof (func_label_t ));
124+ e -> func_labels = grown ;
115125 e -> func_label_capacity = cap ;
116126 }
117127 func_label_t * l = & e -> func_labels [e -> func_label_count ++ ];
@@ -148,7 +158,10 @@ static int resolve_func_fixups(emitter_t *e) {
148158static void add_fixup (emitter_t * e , char * func_name ) {
149159 if (e -> fixup_count >= e -> fixup_capacity ) {
150160 unsigned cap = e -> fixup_capacity ? e -> fixup_capacity * 2 : 8 ;
151- e -> fixups = realloc (e -> fixups , cap * sizeof (fixup_t ));
161+ fixup_t * grown = arena_alloc (& e -> arena , cap * sizeof (fixup_t ));
162+ if (e -> fixups )
163+ memcpy (grown , e -> fixups , e -> fixup_count * sizeof (fixup_t ));
164+ e -> fixups = grown ;
152165 e -> fixup_capacity = cap ;
153166 }
154167 fixup_t * f = & e -> fixups [e -> fixup_count ++ ];
@@ -179,7 +192,10 @@ static int resolve_local_fixups(emitter_t *e) {
179192static void add_local_fixup (emitter_t * e , unsigned label_id ) {
180193 if (e -> local_fixup_count >= e -> local_fixup_capacity ) {
181194 unsigned cap = e -> local_fixup_capacity ? e -> local_fixup_capacity * 2 : 8 ;
182- e -> local_fixups = realloc (e -> local_fixups , cap * sizeof (fixup_t ));
195+ fixup_t * grown = arena_alloc (& e -> arena , cap * sizeof (fixup_t ));
196+ if (e -> local_fixups )
197+ memcpy (grown , e -> local_fixups , e -> local_fixup_count * sizeof (fixup_t ));
198+ e -> local_fixups = grown ;
183199 e -> local_fixup_capacity = cap ;
184200 }
185201
@@ -208,7 +224,7 @@ static void allocate_globals(emitter_t *e, ir_gen_t *gen) {
208224 unsigned count = gen -> module .global_count ;
209225 if (count == 0 ) return ;
210226
211- e -> global_entries = malloc ( count * sizeof (global_entry_t ));
227+ e -> global_entries = arena_alloc ( & e -> arena , count * sizeof (global_entry_t ));
212228 e -> global_entry_count = count ;
213229
214230 for (unsigned i = 0 ; i < count ; i ++ ) {
@@ -255,6 +271,9 @@ OP_EMITTER_SINGLE_ARG(ldx_imm, 0xA2)
255271OP_EMITTER_SINGLE_ARG (ldy_imm , 0xA0 )
256272OP_EMITTER_SINGLE_ARG (sta_zpg , 0x85 )
257273
274+ OP_EMITTER_SINGLE_ARG (ora_imm , 0x09 )
275+ OP_EMITTER_SINGLE_ARG (ora_zpg , 0x05 )
276+
258277OP_EMITTER_SINGLE_ARG (cmp_imm , 0xC9 )
259278OP_EMITTER_SINGLE_ARG (cmp_zpg , 0xC5 )
260279OP_EMITTER_SINGLE_ARG (beq_rel , 0xF0 )
@@ -280,6 +299,7 @@ OP_EMITTER_NO_ARG(sec, 0x38)
280299OP_EMITTER_ABS (jmp_abs , 0x4C )
281300OP_EMITTER_ABS (sta_abs , 0x8D )
282301OP_EMITTER_ABS (lda_abs , 0xAD )
302+ OP_EMITTER_ABS (ora_abs , 0x0D )
283303OP_EMITTER_ABS (cmp_abs , 0xCD )
284304OP_EMITTER_ABS (inc_abs , 0xEE )
285305OP_EMITTER_ABS (dec_abs , 0xCE )
@@ -308,7 +328,10 @@ static void jsr(emitter_t *e, char *func_name) {
308328static void add_data_fixup (emitter_t * e , unsigned global_idx , uint8_t byte ) {
309329 if (e -> data_fixup_count >= e -> data_fixup_capacity ) {
310330 unsigned cap = e -> data_fixup_capacity ? e -> data_fixup_capacity * 2 : 8 ;
311- e -> data_fixups = realloc (e -> data_fixups , cap * sizeof (data_fixup_t ));
331+ data_fixup_t * grown = arena_alloc (& e -> arena , cap * sizeof (data_fixup_t ));
332+ if (e -> data_fixups )
333+ memcpy (grown , e -> data_fixups , e -> data_fixup_count * sizeof (data_fixup_t ));
334+ e -> data_fixups = grown ;
312335 e -> data_fixup_capacity = cap ;
313336 }
314337 data_fixup_t * f = & e -> data_fixups [e -> data_fixup_count ++ ];
@@ -455,79 +478,40 @@ static void emit_store_byte(emitter_t *e, zp_map_t *map,
455478}
456479
457480
458- // Emit CMP against byte N of an operand. Global-aware: uses abs for globals.
459- static void emit_cmp_byte (emitter_t * e , zp_map_t * map ,
460- tac_operand_t * op , unsigned byte ) {
461- switch (op -> kind ) {
462- case OPERAND_CONST_INT :
463- cmp_imm (e , (uint8_t )((op -> int_val >> (8 * byte )) & 0xFF ));
464- break ;
465- case OPERAND_VAR : {
466- global_entry_t * g = lookup_global (e , op -> name );
467- if (g )
468- cmp_abs (e , (uint16_t )(g -> ram_addr + byte ));
469- else
470- cmp_zpg (e , (uint8_t )(zp_map_lookup (map , op ) + byte ));
471- break ;
472- }
473- case OPERAND_TEMP :
474- cmp_zpg (e , (uint8_t )(zp_map_lookup (map , op ) + byte ));
475- break ;
476- default : break ;
481+ #define GLOBAL_AWARE_ALU_HELPER (NAME , IMM_FN , ZPG_FN , ABS_FN ) \
482+ static void NAME(emitter_t *e, zp_map_t *map, \
483+ tac_operand_t *op, unsigned byte) { \
484+ switch (op->kind) { \
485+ case OPERAND_CONST_INT: \
486+ IMM_FN(e, (uint8_t)((op->int_val >> (8 * byte)) & 0xFF)); \
487+ break; \
488+ case OPERAND_VAR: { \
489+ global_entry_t *g = lookup_global(e, op->name); \
490+ if (g) \
491+ ABS_FN(e, (uint16_t)(g->ram_addr + byte)); \
492+ else \
493+ ZPG_FN(e, (uint8_t)(zp_map_lookup(map, op) + byte)); \
494+ break; \
495+ } \
496+ case OPERAND_TEMP: \
497+ ZPG_FN(e, (uint8_t)(zp_map_lookup(map, op) + byte)); \
498+ break; \
499+ default: break; \
500+ } \
477501 }
478- }
479502
503+ GLOBAL_AWARE_ALU_HELPER (emit_ora_byte , ora_imm , ora_zpg , ora_abs )
504+ GLOBAL_AWARE_ALU_HELPER (emit_cmp_byte , cmp_imm , cmp_zpg , cmp_abs )
505+ GLOBAL_AWARE_ALU_HELPER (emit_adc_byte , adc_imm , adc_zpg , adc_abs )
506+ GLOBAL_AWARE_ALU_HELPER (emit_sbc_byte , sbc_imm , sbc_zpg , sbc_abs )
480507
481- // Emit ADC against byte N of an operand. Global-aware: uses abs for globals.
482- static void emit_adc_byte (emitter_t * e , zp_map_t * map ,
483- tac_operand_t * op , unsigned byte ) {
484- switch (op -> kind ) {
485- case OPERAND_CONST_INT :
486- adc_imm (e , (uint8_t )((op -> int_val >> (8 * byte )) & 0xFF ));
487- break ;
488- case OPERAND_VAR : {
489- global_entry_t * g = lookup_global (e , op -> name );
490- if (g )
491- adc_abs (e , (uint16_t )(g -> ram_addr + byte ));
492- else
493- adc_zpg (e , (uint8_t )(zp_map_lookup (map , op ) + byte ));
494- break ;
495- }
496- case OPERAND_TEMP :
497- adc_zpg (e , (uint8_t )(zp_map_lookup (map , op ) + byte ));
498- break ;
499- default : break ;
500- }
501- }
502-
503-
504- // Emit SBC against byte N of an operand. Global-aware: uses abs for globals.
505- static void emit_sbc_byte (emitter_t * e , zp_map_t * map ,
506- tac_operand_t * op , unsigned byte ) {
507- switch (op -> kind ) {
508- case OPERAND_CONST_INT :
509- sbc_imm (e , (uint8_t )((op -> int_val >> (8 * byte )) & 0xFF ));
510- break ;
511- case OPERAND_VAR : {
512- global_entry_t * g = lookup_global (e , op -> name );
513- if (g )
514- sbc_abs (e , (uint16_t )(g -> ram_addr + byte ));
515- else
516- sbc_zpg (e , (uint8_t )(zp_map_lookup (map , op ) + byte ));
517- break ;
518- }
519- case OPERAND_TEMP :
520- sbc_zpg (e , (uint8_t )(zp_map_lookup (map , op ) + byte ));
521- break ;
522- default : break ;
523- }
524- }
525-
526-
527- // Emit conditional jump: LDA src; BEQ skip; JMP target. Jumps when nonzero.
508+ // Emit conditional jump: LDA src; [ORA src+1]; BEQ skip; JMP target. Jumps when nonzero.
528509static void emit_cond_jump (emitter_t * e , zp_map_t * map ,
529510 tac_operand_t * src , unsigned label_id ) {
511+ unsigned width = codegen_type_size (src -> type );
530512 emit_load_byte (e , map , src , 0 );
513+ if (width > 1 )
514+ emit_ora_byte (e , map , src , 1 );
531515 beq_rel (e , 3 );
532516 if (e -> local_labels [label_id ]) {
533517 jmp_abs (e , e -> local_labels [label_id ]);
@@ -549,8 +533,7 @@ static int emit_function_from_cfg(emitter_t *e, cfg_t *cfg) {
549533
550534 e -> local_label_count = cfg -> next_label ;
551535 e -> local_fixup_count = 0 ;
552- e -> local_labels = realloc (e -> local_labels , cfg -> next_label * sizeof (uint16_t ));
553- memset (e -> local_labels , 0 , cfg -> next_label * sizeof (uint16_t ));
536+ e -> local_labels = arena_alloc (& e -> arena , cfg -> next_label * sizeof (uint16_t ));
554537
555538 for (unsigned i = 0 ; i < cfg -> block_count ; ++ i ) {
556539 basic_block_t * block = cfg -> blocks [i ];
@@ -610,6 +593,10 @@ static int emit_function_from_cfg(emitter_t *e, cfg_t *cfg) {
610593 emit_load_byte (e , & map , & instruction -> src1 , b );
611594 sta_abs (e , (uint16_t )(base_addr + b ));
612595 }
596+ } else {
597+ fprintf (stderr , "codegen: unhandled TAC_STORE with non-const destination (op kind %d)\n" ,
598+ instruction -> dst .kind );
599+ return 0 ;
613600 }
614601 break ;
615602 }
@@ -648,8 +635,16 @@ static int emit_function_from_cfg(emitter_t *e, cfg_t *cfg) {
648635
649636 case TAC_NOT : {
650637 uint8_t dst_addr = zp_map_lookup (& map , & instruction -> dst );
638+ unsigned width = codegen_type_size (instruction -> src1 .type );
651639 emit_load_byte (e , & map , & instruction -> src1 , 0 );
652- eor_imm (e , 0x01 );
640+ if (width > 1 )
641+ emit_ora_byte (e , & map , & instruction -> src1 , 1 );
642+ // A is now nonzero iff the original value was truthy.
643+ // Convert to boolean: 0 → 1, nonzero → 0.
644+ beq_rel (e , 4 ); // +4: skip LDA #0 + BEQ +2
645+ lda_imm (e , 0 );
646+ beq_rel (e , 2 ); // +2: skip LDA #1 (always taken, Z=1)
647+ lda_imm (e , 1 );
653648 sta_zpg (e , dst_addr );
654649 break ;
655650 }
@@ -898,22 +893,18 @@ static int emit_function_from_cfg(emitter_t *e, cfg_t *cfg) {
898893// Main code gen
899894// ----------------------------------------------------------------
900895
901- // Free all heap-allocated emitter resources (labels, fixups, globals).
902896static void emitter_free (emitter_t * e ) {
903- free (e -> func_labels );
904- free (e -> fixups );
905- free (e -> local_labels );
906- free (e -> local_fixups );
907- free (e -> global_entries );
908- free (e -> data_fixups );
897+ arena_free (& e -> arena );
909898}
910899
911900
912901uint8_t * generate_rom (ir_gen_t * gen , size_t * final_rom_size ) {
913902 emitter_t e = { 0 };
914903
904+ if (!arena_init (& e .arena , 4096 )) return NULL ;
905+
915906 e .rom = malloc (ROM_SIZE );
916- if (!e .rom ) return NULL ;
907+ if (!e .rom ) { arena_free ( & e . arena ); return NULL ; }
917908
918909 memset (e .rom , 0xEA , ROM_SIZE );
919910 * final_rom_size = ROM_SIZE ;
0 commit comments