What's new

Game Boy

Cyberman

Moderator
Moderator
Code:
opcode = byteMemory[instructionPointer] ;

switch(opcode)
{
 case 06: 
   register[REG_B] = byteMemory[instructionPointer+1];
   instructionPointer += 2;  // is this correct? for a load opcode anyway     
}
Thanks for any help

Edit:

Whats the deal with opcode 7F?

You have to Load RegisterA into RegisterA... whats the point?
It's another variant of the NO OP Op Code :)
And try posting in the Game Boy thread next time (moved).
 
Last edited:

bcrew1375

New member
The basic operation is correct. However, don't forget you also need to account for timing. You need to keep track of the CPU cycles if you don't want your emu to go insanely fast. Some operations will also require manipulation of flags. There are Z, N, H, C. Some of the redirection operations require the flags be set properly in order for the program to run as intended. If you need more help, feel free to ask.
 

cloudy

New member
Thanks for the reply,

Although I am aware of the timing issues with the gameboy i havent thought of the best way to implement them, i'll need to think about that.

Another question I have is to do with the following Opcode

Load Register HL into Register A

Registers are 1 bytes large and to get 2 byte registers they combine two of the registers (as im sure you are aware). Now the above instruction seems to be copying 2 bytes of data into 1 byte of storage space. As this isnt possible im guessing that the HL register in this case is acting as a pointer and i need to dereference and then copy that content into the regiser A?

So if i Register H contained 0x2A and Register L contained 0xFF, would i need to do the following?

Register A = m_GameMemory[0x2AFF] ; // this is a byte array

Thanks for any help
 

bcrew1375

New member
Yes, that's exactly what you're supposed to do with that instruction. As far as timing, I'm not sure if my own is right, so it's probably better to let someone with a more compatible emu answer that.
 

aprentice

Moderator
Was reading through some of the programming thread and realized i've released the source to my chip8 emu but not gameboy emu, so here it is! my cpu uses precalculated flags for speed if anyone is interested in having a look. I never got around to getting sound to work although theres sound code in there for staters. Theres also zip support if anyone is interested if seeing how its done. Hopefully new emu coders can learn off this code and make some use of it rather than it just dusting on my hd. Any feedback welcome.
 

Exophase

Emulator Developer
Was reading through some of the programming thread and realized i've released the source to my chip8 emu but not gameboy emu, so here it is! my cpu uses precalculated flags for speed if anyone is interested in having a look. I never got around to getting sound to work although theres sound code in there for staters. Theres also zip support if anyone is interested if seeing how its done. Hopefully new emu coders can learn off this code and make some use of it rather than it just dusting on my hd. Any feedback welcome.

Using tables for flags are an interesting approach.. the biggest concern I have would be cache thrashing. Especially if the higher order index changes a lot (happens to be a loop variable, for instance) you'll lose a lot of locality for it. Other than that, you could make the add table 257x256, rather than 257x257, then have the carry potential be in the first part. That way you won't have to do a multiplication to get to the index. And I'd get rid of the zero flag table.
 

hap

New member
Yes, big tables will definitely have an impact on speed, try to stay away from them with simple calculations (the reason I use one table is for the P flag). Or did you use a profiler and found out this method was faster?

You've released your GB emu source already in Jan. 2006 btw ;)
 

aprentice

Moderator
Yes, big tables will definitely have an impact on speed, try to stay away from them with simple calculations (the reason I use one table is for the P flag). Or did you use a profiler and found out this method was faster?

You've released your GB emu source already in Jan. 2006 btw ;)

i've compared my tables to a non tabled build and the speed increase was huge, but when i coded it i kind of expected more, oh well, you learn from trying and i always like to try new approaches when it comes to things to find out what works better
 

Cyberman

Moderator
Moderator
How about a Dynamic Recompiler? :)
Actually a static recompiler might work well, it would however take a LONG time to recompile a game into new code. Dynamic Recompile has an advantage over static in that it requires no optimization passes. Self modifying code though is a nasty habit of certain programers.

Cyb
 

Exophase

Emulator Developer
How about a Dynamic Recompiler? :)
Actually a static recompiler might work well, it would however take a LONG time to recompile a game into new code. Dynamic Recompile has an advantage over static in that it requires no optimization passes. Self modifying code though is a nasty habit of certain programers.

Cyb

Assuming pure static recompilation is really possible in the first place (self modifying code was very common in old games, and in consoles with RAM and ROM spaces you often saw code copied around to RAM for higher performance), why would it require optimization where dynamic recompilation wouldn't? And it wouldn't take that long, even if you recompiled the entire cartridge, which you'd have to (and have a table for all possible entry points).
 

bronxbomber92

New member
Hi,

I'm just starting my CPU, and I think I found a mistake in the doc (it's the GB.pdf posted earlier, page 65).

1. LD nn,n
Description:
Put value nn into n.
Use with:
nn = B,C,D,E,H,L,BC,DE,HL,SP
n = 8 bit immediate value

Opcodes:
Instruction Parameters Opcode Cycles
LD B,n 06 8

So, that's saying: n = register B. Isn't it suppose to be the other way around? If this is an error, does anyone know of any other errors?

Thanks

Edit - I also can't find this specific opcode (written like in this format) in any other docs. Which is the most complete document for the cpu I can follow?
 

Exophase

Emulator Developer
Edit - I also can't find this specific opcode (written like in this format) in any other docs. Which is the most complete document for the cpu I can follow?

Try this:

http://www.work.de/nocash/pandocs.htm

And this:

http://www.romhacking.net/docs/GBCribSheet000129.zip

Pan docs appears to be missing some vital information. Judging by the crib sheet here are some ambiguous ones:

First, 8bit registers are encoded like this:

0 - b
1 - c
2 - d
3 - e
4 - h
5 - l
6 - (hl) (memory location)
7 - a

16bit registers are encoded like this:

0 - bc
1 - de
2 - hl
3 - sp

Now for the unclear instructions.


ld r,n xx nn
00XXX110 NN

X is destination register, NN is 8bit immediate.

ld r,r xx
ld r,(HL) xx
01XXXYYY

X is source register, Y is destination register.

ld rr,nn x1 nn nn
00XX0001 NNNN

X is destination, NNNN is 16bit immediate.

ALU operations are identified by an 3bit code:

0 - add
1 - adc
2 - sub
3 - sbc
4 - and
5 - xor
6 - or

ALU operations against against A are as follows:

10OOOXXX

O is the operation, X is an 8bit register (destination)

ALU operations against an immediate are:

11OOO110 NN

push rr x5
110XX101

pop rr x1
110XX001

X is a 16bit register.


... I don't feel like doing anymore, you can probably figure them out from here. >_<
 

bronxbomber92

New member
Hi, I've got another question.

I'm working on opcode 0xF8 - LDHL SP, n

The part I'm unsure is setting the half carry and carry flags. In the docs it says if a carry occurred in the lower nibble then you set this the half carry flag. So, that would mean the lowest 4 bits. But in the pandocs here: http://www.work.de/nocash/pandocs.htm#cpuregistersandflags it says 0xFF, not 0xF. I'm not sure what to go by.

Here's what I've got. Does it seem correct? I think it is... But if everything I thought was right, was.. Well then, I think the world would be a very odd place :)
Code:
//0xf8
	void ldhl_sp_n()
	{
		int n = Fetch();
		int value = (sp + n) & 0xFFFF;
		h = value >> 8;//value & 0xFF00;
		l = value & 0x00FF;
		SetFlag(Z);
		SetFlag(N);
		if( sp + n > 0xFFFF )
			SetFlag(C);
		else
			ResetFlag(C);
		
		if( (sp & 0xF) + (n & 0xF) > 0xF )
			SetFlag(H);
		else
			ResetFlag(H);
	}
 

Ananke

New member
Hi

I'm also working on a gameboy emulator and my question is if it is essential when I call the Hblank Interrupt.
Until now I did it like this, but I couldn't get anything on the screen:


Code:
while (Halt ==false)
{
     opcode = GetOpcode(PC);
     PC++;

     ExecuteOpcode(opcode);

     if(MachineCycles>=451)
     {
          Hblank();
          MachineCycles = 0;
     }
}
 

Exophase

Emulator Developer
Hi, I've got another question.

I'm working on opcode 0xF8 - LDHL SP, n

The part I'm unsure is setting the half carry and carry flags. In the docs it says if a carry occurred in the lower nibble then you set this the half carry flag. So, that would mean the lowest 4 bits. But in the pandocs here: http://www.work.de/nocash/pandocs.htm#cpuregistersandflags it says 0xFF, not 0xF. I'm not sure what to go by.

Here's what I've got. Does it seem correct? I think it is... But if everything I thought was right, was.. Well then, I think the world would be a very odd place :)
Code:
//0xf8
	void ldhl_sp_n()
	{
		int n = Fetch();
		int value = (sp + n) & 0xFFFF;
		h = value >> 8;//value & 0xFF00;
		l = value & 0x00FF;
		SetFlag(Z);
		SetFlag(N);
		if( sp + n > 0xFFFF )
			SetFlag(C);
		else
			ResetFlag(C);
		
		if( (sp & 0xF) + (n & 0xF) > 0xF )
			SetFlag(H);
		else
			ResetFlag(H);
	}

I think you're right about carry/halfcarry (and didn't see anything in the Pan Docs to suggest otherwise..). However, both docs confirm that Z and N should be set to 0 by this operation (not 1).

You might want to factor this out for the other two 16bit adds since the logic is basically the same for both (except the increment HL one doesn't set Z to 0)
 

|\/|-a-\/

Uli Hecht
I searched in this thread for the keyword "sound", but found nothing what could help me. I don't understand exactly, when to play sound. Only after a write to the channel's "initialize" flag?
 

Ananke

New member
Can anyone tell me how to calculate the number of cycles till the HBlank period?
Because when I calculate 4194304/9198000 the result is not an even number.
 

yosh64

New member
hey

@Ananke: Well I think you should only need to worry about HBlank timing for the STAT interrupt, which is explained in the Pan Docs.

Hmm, if you are just trying to get video then all you need to worry about is the VBlank timing/interrupt.

I think the STAT interrupt is used only for special effects and such, so most games should work with just the VBlank interrupt. Hmm, maybe the Timer counter/interrupt, and maybe the DIV counter also, but I don't really remember.

Anyhows for VBlank, I just calculated how many instruction cycles occur per scanline, and used a counter... so when the counter hits 144, you are in VBlank.

Scanline Rate = Clock Speed / Horiz Sync = 4194304Hz / 9198Hz = 456.001739509.

So every 456 instruction cycles you increment the scanline counter :).

BTW, I think alot of docs refer to Horiz Sync as 9198KHz, but I think it is really 9198Hz, and all my calcs indicate so.

Well if you do... Clock Speed / Vert Sync = Refresh Rate (number of instruction cycles per screen refresh), and then... Refresh Rate / Number of Scanlines = Scanline Rate (number of instruction cycles per scanline)???

So... 4194304Hz / 59.73Hz = 70221.061443160890674702829398962 and... 70221.061443160890674702829398962 / 154 = 455.98091846208370567988850259065.

Anyhows someone might wanna confirm all of this, as I'm not completly sure on things.

edit
To answer you question I will just quote from the LCD Status Register section of the Pan Docs...

Mode 0 is present between 201-207 clks, 2 about 77-83 clks, and 3 about 169-175 clks. A complete cycle through these states takes 456 clks. VBlank lasts 4560 clks. A complete screen refresh occurs every 70224 clks.)

I think all this seems to agree with what I have said above :). As they say a complete cycle through these states is 456 cycles, and a complete screen refresh occurs every 70224 cycles. Although the later figure differs slightly? Hmm, not exactly sure why either?

cya
 
Last edited:

CodeSlinger

New member
Hi Guys,

After finishing my chip8 emu I thought the next logical step would be a gameboy emulator. I'm currently implementing the cpu opcodes but getting stuck on the basics. Without knowing any assembler language and only codng a chip8 emulator the learning curve is seeming very steep at the moment. Much steeper than learning the chip8 was.

Still sounds like a fun project.

So please excuse my n00b questions:

1. Opcode 0xC3 is a jump opcode and the description is the following:

Jump to address nn, where nn is two byte immediate data, LS byte first.

So if i had the following in memory

C3 AB CD

the C3 command will do the following?

Code:
int jmp = m_MemoryAddress[m_ProgramCounter+2] << 8 ;
jmp |= m_MemoryAddress[m_ProgramCounter+1] ;
m_ProgramCounter = jmp ;

2. As you can see with the above opcode, when it uses 2 immediate data bytes does it always do LS byte first, or only if it specifies?

For example opcode 0x01 loads two immediate byte into register BC. But this doesn't specify if it is LS byte first.

3. One of my main issues is with the difference between the following:

ld HL, nn ;
ld (HL), nn ;

What difference does putting brackets around the register signify and how is this implemented?

I'm guessing that it is something to do with the address of the register and not its contents, but im getting a bit lost with how it works.

Actually i just had an idea, could (HL) be reffering to the actual game memory, for example if the contents of HL was 0xAB then would it be the following:

m_GameMemory[0xAB] = nn ;

4. Last question :p

Opcode E2 is LD ($FF00+C), A

what does $FF00 represent? Is it game memory again? So if C was 0xBC would i have the following:

m_GameMemory[0xFFBC] = A;


Thanks guys for any help.

BTW im reading the entire rom into a byte array and not accessing it via pages or anything, just one big byte array. Anything wrong with this?
 

Top