Files
gb6/compiler/flags.c
Tanner Fokkens c0a30de0b2 Fix inc/dec clobbering carry flag, breaking Pokemon Crystal menus
The GB inc/dec instructions must preserve the carry flag and only
modify Z. The JIT was capturing both Z and C from the 68k CCR,
causing dec to incorrectly set carry on borrow (e.g. 0-1=FF).

This broke Pokemon Crystal's xor_a_dec_a helper which returns with
C=0 to signal "no action". The corrupted C=1 caused the scrolling
menu to exit prematurely when pressing down past CANCEL, showing
phantom items and garbage prices.

Fix compile_set_z_flag to merge new Z with old C, and
compile_set_c_flag to merge new C with old Z. Move and/or/xor
immediates to compile_set_zc_flags since they should clear C.
2026-02-06 17:11:15 -08:00

40 lines
1.5 KiB
C

#include "compiler.h"
#include "emitters.h"
// how to do the flags properly:
// 8-bit and, or, xor -> set Z for result, set C=0
// 8-bit add, adc, sub, sbc, cp -> set both Z and C for result
// 16 bit add -> set C for result, leave Z alone
// 8-bit incs/decs -> set Z for result, leave C alone
// 16-bit incs/decs -> leave both alone
// non-cb rotates -> set C for result, set Z=0
// cb rotates/shifts -> set both Z and C for result
// swap -> set Z for result, set C=0
// bit -> set Z for result, leave C alone
void compile_set_zc_flags(struct code_block *block)
{
emit_move_sr_dn(block, REG_68K_D_FLAGS);
}
void compile_set_z_flag(struct code_block *block)
{
// inc/dec/bit must set Z from result but preserve C from previous flags.
// Save new CCR to D3, extract Z, merge with old C from D7.
// Then load merged flags into CCR so fused branches see correct Z.
emit_move_sr_dn(block, REG_68K_D_NEXT_PC);
emit_andi_b_dn(block, REG_68K_D_NEXT_PC, 0x04);
emit_andi_b_dn(block, REG_68K_D_FLAGS, 0x01);
emit_or_b_dn_dn(block, REG_68K_D_NEXT_PC, REG_68K_D_FLAGS);
emit_move_dn_ccr(block, REG_68K_D_FLAGS);
}
void compile_set_c_flag(struct code_block *block)
{
// add hl,rr sets C from result but preserves Z from previous flags.
// Save new CCR to D3, extract C, merge with old Z from D7.
emit_move_sr_dn(block, REG_68K_D_NEXT_PC);
emit_andi_b_dn(block, REG_68K_D_NEXT_PC, 0x01);
emit_andi_b_dn(block, REG_68K_D_FLAGS, 0x04);
emit_or_b_dn_dn(block, REG_68K_D_NEXT_PC, REG_68K_D_FLAGS);
}