Gameboy - unsure about jr instruction

mentor93

New member
I'm writing Gameboy emulator. My approach to writing an emulator here is simple - I load Dr. Mario rom into my emulator which then reads opcode after opcode. If there's opcode program doesn't know it stops executing and gives me opcode's hex number. I then check what instruction this opcode corresponds to (several opcodes may be for one instruction, just taking different registers for parameter) and I write function which implements what this instruction does. In the end I should have somewhere around 70-80 such functions (I think there are about 77 instructions, but I don't remember exact number) for over 250 opcodes. This way I should implement CPU emulation relatively quick.

I'm constantly checking my emulator work with No$gmb debugger. I look up if my emulator executes same opcodes and then jumps to the correct next opcode, so I know if my emulator will emulate Dr. Mario properly (at least in terms of CPU right now), just as No$gmb does it. And everything went smoothely so far, but I encountered situation where my emulator did not jump to the same opcode as No$gmb did. And that rather shouldn't happen, because both emulators should work the same way. Instruction I'm talking about is JR - it does a conditional jump (it checks if certain condition is true and then adds a signed byte value to program counter and jumps to location being the result of such addition). In this particular case instruction goes like this:

Code:
jr nz,01F0

So basicaly it tells us "if zero flag is reset, jump to 01F0" (at least I think so). In hex it is:

Code:
20 FC

And here's what I'm concerned about. 20 is opcode for this instruction and FC is this value emulator should add to program counter. But I don't get why there's FC in the second piece of code, but 01F0 in the first one? And what's more important to me, Dr. Mario on No$gmb actually jumps to 01F0, although on my emulator it doesn't. So there's something wrong I think. Both emulators should work the same way, so the game should jump to 01F0 on my emulator also. Instead it goes to next opcode in line (on 01F4 actually). That could mean that I have not implemented setting and resetting zero flag correctly, but I believe I did it correctly. But where does 01F0 come from? If FC is value that should be added to current value in program counter, which is 01F2, that should be 02EE, right? Even if it should concatanate both values using | operator the result would be different.

My current implementation for JR instruction is:

Code:
void JR(BYTE jump, int b, int val) {
	if (checkbit(F, b, val)) {
		pc = pc + jump;
		no_incr = true;
	}
	else pc++;
	printf("jr %02X\n", jump);
}
//...
bool checkbit(BYTE &reg, int b, int val) {
	return ((reg >> b) & 1 == val);
}

That's pretty simple. There are only 4 variations of this instruction (4 different opcodes) and each one just checks if certain flag is set or reset. So I could just compare certain bit in F register with either 1 or 0 and if values match then do the jump:

Code:
	BYTE *opcode = &memory[pc];

	switch (*opcode) {
		case 0x20:
			JR(opcode[1], 7, 0);
			break;
	}

I think that's ok, but why do I get different results compared to No$gmb? And why is there 01F0 which doesn't make sense to me? Perhaps some more experienced programmer who did code emulators or coded his own GB emulator could help me here, because this problem means that my emulator could not work properly = could not run the game properly and running games properly is all what emulator should do.
 

Robert

Member
The program counter points to the next instruction before the current instruction is evaluated. FC is -4, so 1F4 - 4 = 1F0.

You should do some learning about relative jumps before continuing with your emulator.

The CPU used in the gameboy is a Z80 variant, and there's oodles of stuff available on the Z80.
 

mentor93

New member
OK, I was using only GBCPUman. I've read a little bit more about JR. This instruction can only jump 128 bytes ahead or behind. So if the given parameter is greater than 128, it is a signed value. Is that correct? But how should I calculate what exactly the value is? FC is 252 in decimal, should I do 256 - parameter to get the correct value (and substraction should only take place if parameter is greater than 128)?
 

ulaoulao

Controller Man
Staff member
This instruction can only jump 128 bytes ahead or behind.

rjmp limited to x bytes depending on CPU.
jmp unlimited (I believe) but requires an extra clock.
 
Last edited:

mentor93

New member
I've been looking up my code and seems that I also have setting half-carry flag wrong. For instruction DEC n, half-carry flag should be set if there was no borrow from bit 4. Normally half-carry flag is set when there was a carry from bit 3 to bit 4, which is something like that:

Code:
	BYTE hc = ((val1 & 0xF) + (val2 & 0xF)) & 0x10;
	if (hc == 0x10) setbit(F, 5); //set half-carry flag
	else resetbit(F, 5); //reset hc flag

But here situation is different and I can't get correct results. No$gmb shows me that after executing DEC B (decrease register B) half-carry flag should be set, but my emulator doesn't set it. So above code should set half carry flag in a different way, but I don't know what way.

EDIT:

I'm not sure if this is correct for this DEC n instruction, but perhaps setting half-carry flag should look like this?:
Code:
	BYTE hc = ((reg & 0xF) - (1 & 0xF)) & 0x10;
	if (hc == 0x10) setbit(F, 5);
 
Last edited:

mentor93

New member
Now I'm confused about RES instruction. It resets specified bit in one of the registers, but I don't get where do I get the specified bit from? Because everything that No$gmb debugger shows is:

Code:
CB 87        res 0,a

I don't get where this 0 bit comes from. CB 87 is opcode for instruction RES b,A, so it's ok there's A register - I know why it is there. But why 0? There's no parameter after CB 87, so where does this 0 come from?
 

Robert

Member
The above link explains it all.
CB 87 is RES 0,a while CB 8F is RES 1,a
CB 80 is RES 0,b.
So the opcode selects both the bit and the register. Further, other CB codes choose RES, SET or BIT.

RES means to turn the specified bit to 0;
SET means to turn the specified bit to 1;
BIT means to return the status of the specified bit in the Z flag.
 
Last edited:

mentor93

New member
Thank you for all your answers. I managed to implement all opcodes and most of the games I checked can get into menu or intro, but only a few (Soccer, Robin Hood: Prince of Thieves, Wario Land and Double Dragon 2 are worth mention here) can get past menu. I revisioned my code already once and found several serious mistakes, but still my emulator doesn't really play any game. I probably need to revision code once more, but for now I don't get what did I do wrong. For emulating graphics, joypad, timers etc. I followed a tutorial at http://codeslinger.co.uk/ and I checked these modules several times already and no errors there. So it must be something in implentation of cpu instructions.
 
Top