Unlike a normal Z80, the Game Boy has no CALL pe/po
, JP pe/po
or RET pe/po
instructions. I've been trying to figure out how to detect integer overflow on the Game Boy. (It doesn't have those instructions for the sign flag either but that can be done with BIT 7
.)
This is what I've been trying to come up with but it's an absolute mess. After doing a few calculations on paper, such as &81 - &02
, &7F-&01
, etc. I came to the (possibly faulty) conclusion that overflow is equivalent to sign change xor Carry Flag. It almost works but there seems to be a problem. If A and B have opposite signs but they subtract to less than 7F the compare is wrong. The second picture shows an incorrect result.
Like I said, the source code is spaghetti, but I couldn't come up with a better way.
;compares A to B signed.
bit 7,a ;returns nz if A is negative, z if positive.
call GetZeroFlag ;stores this result
sub b
bit 7,a
push af
call TestForSignChange
jr z,NoSignChange
pop af
;at this point, if carry is clear, overflow occurred.
jr c,NoOverflow
jr OverflowOccurred
NoSignChange:
;at this point, if carry is set, overflow occurred.
pop af
jr nc,NoOverflow
OverflowOccurred:
;test bit 7 of A again. If A is positive, A < B. If A is negative, A >= B
bit 7,a
jr nz, GreaterThanSigned
LessThanSigned:
scf ;set carry if less than, just like an unsigned compare does.
ret
GreaterThanSigned:
or a ;clear carry if greater than or equal, just like an unsigned compare does.
ret
NoOverflow:
;Test bit 7 of A again. If A is positive, A >= B. If A is negative, A < B.
bit 7,a
jr nz, GreaterThanSigned
;I don't understand why this isn't "JR Z, GreaterThanSigned"
jr LessThanSigned
GetZeroFlag:
push af
push bc
push af
pop bc ;get the flags into the C register
ld a,c
and %01000000 ;zero flag is where the 1 is.
ld (tempflags),a ;store the result in ram
pop bc
pop af
ret
TestForSignChange:
;compares bit 7 of A before and after the subtraction.
;If the value of bit 7 of A changed, return nz, otherwise return z.
push bc
push af
pop bc
ld a,c
and %01000000 ;ignore all but the zero flag
ld c,a ;store the current state of the zero flag in C
ld a,(tempflags) ;get the state of the zero flag before the subtraction in A
cp c ;compare it to the state of the zero flag after the subtraction.
pop bc
ret