What's new

Gameboy Advance

Flerovium

Member
Hello
I thought it would be useful to open a thread for information exchange about programme gameboy advance emulators.
And there I go with my first question :D

I'm currently writing the function that decodes thumb instructions. Currently I have to approaches for executing and decoding those instructions:
1. Decode opcode information to an struct at corresponding decode stage of the pipeline and execute it with the information from the struct at execution stage.
This approach would strictly follow the rules of pipelining. Also this is my current approach
2. Do nothing at instruction corresponding decode stage and do both decoding and execution at the execution stage.

What do you think would be better for my emulator?

This is my current code:
Code:
#define OPERAND_REG 0
#define OPERAND_IMM 1

typedef struct _DecodeData
{
    uint32_t opcode;

    uint32_t instruction;
    uint32_t operand1;
    uint32_t operand2;
    uint32_t operand3;
    uint8_t operand1_type;
    uint8_t operand2_type;
    uint8_t operand3_type;
    uint8_t operand_count;

    uint8_t condition;
    uint32_t instWidth;
} DecodeData;

#define OP_LSL 0
#define OP_LSR 1
#define OP_ASR 2
#define OP_ADD 3
#define OP_SUB 4
#define OP_MOV 5
#define OP_CMP 6
#define OP_AND 7
#define OP_EOR 8
#define OP_ADC 9
#define OP_SBC 10
#define OP_ROR 11
#define OP_TST 12
#define OP_NEG 13
#define OP_CMN 14
#define OP_ORR 15
#define OP_MUL 16
#define OP_BIC 17
#define OP_MVN 18

void arm7_decode_thumb(uint32_t opcode, DecodeData* dec)
{
    /* Decode logic goes here! */
    uint8_t identifier = opcode >> 8;

    memset(dec, 0, sizeof(DecodeData));

    dec->opcode = opcode;
    dec->condition = 0xE; // execute always

    if (identifier < 0b00011000) // Move shifted register
    {
        dec->operand1 = opcode & 7;
        dec->operand1_type = OPERAND_REG;
        dec->operand2 = (opcode >> 3) & 7;
        dec->operand2_type = OPERAND_REG;
        dec->operand3 = (opcode >> 6) & 0x1F;
        dec->operand3_type = OPERAND_IMM;
        dec->operand_count = 3;
        switch (identifier & 0b00011000)
        {
        case 0b00000000: dec->instruction = OP_LSL; break;
        case 0b00001000: dec->instruction = OP_LSR; break;
        case 0b00010000: dec->instruction = OP_ASR; break;
        }
    } else if (identifier < 0b00100000) { // Add / Subtract
        dec->operand1 = opcode & 7;
        dec->operand1_type = OPERAND_REG;
        dec->operand2 = (opcode >> 3) & 7;
        dec->operand2_type = OPERAND_REG;
        dec->operand3 = (opcode >> 6) & 7;
        dec->operand_count = 3;
        if ((identifier & 0b00000100) == 0b00000100) // Immediate?
            dec->operand3_type = OPERAND_IMM;
        else
            dec->operand3_type = OPERAND_REG;
        if ((identifier & 0b00000010) == 0b00000010) // Subtraction or addition?
            dec->instruction = OP_SUB;
        else
            dec->instruction = OP_ADD;
    } else if (identifier < 0b01000000) { // Move / compare / add / subtract immediate
        dec->operand1 = (opcode >> 8) & 7;
        dec->operand1_type = OPERAND_REG;
        dec->operand2 = opcode & 0xFF;
        dec->operand2_type = OPERAND_IMM;
        dec->operand_count = 2;
        switch (identifier & 0b00011000)
        {
        case 0b00000000: dec->instruction = OP_MOV; break;
        case 0b00001000: dec->instruction = OP_CMP; break;
        case 0b00010000: dec->instruction = OP_ADD; break;
        case 0b00011000: dec->instruction = OP_SUB; break;
        }
    } else if ((identifier & 0b11111100) == 0b01000000) { // ALU operations
        dec->operand1 = opcode & 7;
        dec->operand1_type = OPERAND_REG;
        dec->operand2 = (opcode >> 3) & 7;
        dec->operand2_type = OPERAND_REG;
        dec->operand_count = 2;
        switch ((opcode >> 6) & 0xF)
        {
        case 0x0: dec->instruction = OP_AND; break;
        case 0x1: dec->instruction = OP_EOR; break;
        case 0x2: dec->instruction = OP_LSL; break;
        case 0x3: dec->instruction = OP_LSR; break;
        case 0x4: dec->instruction = OP_ASR; break;
        case 0x5: dec->instruction = OP_ADC; break;
        case 0x6: dec->instruction = OP_SBC; break;
        case 0x7: dec->instruction = OP_ROR; break;
        case 0x8: dec->instruction = OP_TST; break;
        case 0x9: dec->instruction = OP_NEG; break;
        case 0xA: dec->instruction = OP_CMP; break;
        case 0xB: dec->instruction = OP_CMN; break;
        case 0xC: dec->instruction = OP_ORR; break;
        case 0xD: dec->instruction = OP_MUL; break;
        case 0xE: dec->instruction = OP_BIC; break;
        case 0xF: dec->instruction = OP_MVN; break;
        }
    }

    dec->instWidth = 2;
}

Greetz Flerovium
 
Last edited:

Cyberman

Moderator
Moderator
Hello
I thought it would be useful to open a thread for information exchange about programme gameboy advance emulators.
And there I go with my first question :D

I'm currently writing the function that decodes thumb instructions. Currently I have to approaches for executing and decoding those instructions:
1. Decode opcode information to an struct at corresponding decode stage of the pipeline and execute it with the information from the struct at execution stage.
This approach would strictly follow the rules of pipelining. Also this is my current approach
2. Do nothing at instruction corresponding decode stage and do both decoding and execution at the execution stage.

What do you think would be better for my emulator?

This is my current code:

Greetz Flerovium


  1. Its your emulator
  2. What are you trying to acomplish?

You can use this data in a number of ways.
For example you can run it through an interpretor for step by step execution
You could have a dynamic recompilor generate pointers to function calls.
You could have a quasi static recompilor generate intermediate data that can be used to generate code to run in the emulator with self modifying code detection.
Not sure how much memory you plan on using and each approach has different requirements.

Anyhow you should expiriment see what works best for what you want to do.

Cyb
 

Shonumi

EmuTalk Member
I think Flerovium was asking if it's strictly necessary to emulate the decoding stage as a separate stage (true to a real ARM processor) or if the decoding and execution stage can be lumped together.

In the end though, I don't think it matters. I emulate the pipeline stage-by-stage, but this is actually a matter of preference. It might be easier in your codebase to put the decoding and execution stages together, or you might want them separate to be explicit about how the emulator works. I have not delved too deeply into the GBA's ARM CPU, but ordinarily I would not expect the two approaches you listed to cause two different results.

Also, looks like you beat me to making this thread. :D In the spirit of the other emulator programming threads, I'll post some helpful links:

http://problemkaputt.de/gbatek.htm - GBATEK, the king of GBA and DS documentation
http://simplemachines.it/doc/arm_inst.pdf - Generic ARM instruction overview
http://www.gbadev.org/ - Demos, homebrew, tutorials and more
http://exophase.devzero.co.uk/armwrestler.zip - ARMWrestler, a test ROM for verifying the accuracy of ARM and THUMB instructions (like blargg's GB CPU tests).

Guess I should get back to work. I haven't touched my GBA emulator in a bit. Implementing ARM and THUMB instructions myself as a learning experience, but it's not exactly a fast learning experience you know.
 

Cyberman

Moderator
Moderator
I suppose I may have too stick this, (LOL).
The irony of the discussion regarding ARM emulation is it can be difficult to emulate an ARM on an ARM, as all instructions sets are not the same on the ARM.
For the GBA programmers often had 2 sets of code. A tight library that executed from the internal RAM that was almost purely tradional ARM code (mostly sound processing and things like JPEG DCT things)
and a set in Thumb mode which was executed from the ROM space. Slow stuff was kept in ROM space along with data. I was always facinated by how good a job they had done for what space they had.

Erstwhile some 'stuff' may be added too this thread by me if I get some time.

Cyb
 

Shonumi

EmuTalk Member
Spent a lot of time today working on ARM emulation (more so today than in days before). For the most part I have ARM.3 through ARM.5 done (Branches + Data Processing basically), and THUMB.1 and THUMB.2 done. I thought I'd tackle ARM first given how complex it is, but I seem to be shifting back to THUMB. THUMB really is reduced in scope, just like they say (not complaining, that makes it easy to implement :D) I think the bulk of the work one has to do with emulating the ARM instructions is with Data Processing and coming up with an efficient design for that. Everything else doesn't seem so bad after that. Big step up from a modified Z80 though.

Still some little nagging TODO stuff (like flushing the pipeline whenever an instruction manually changes the PC) but I'm really enjoying this, now that I've got a feel for how the GBA's CPU works. No timing implemented just yet (no need to, no code worth running can run). Long ways to go. We'll see where I am next weekend :)
 
OP
F

Flerovium

Member
http://exophase.devzero.co.uk/armwrestler.zip - ARMWrestler, a test ROM for verifying the accuracy of ARM and THUMB instructions (like blargg's GB CPU tests).

Guess I should get back to work. I haven't touched my GBA emulator in a bit. Implementing ARM and THUMB instructions myself as a learning experience, but it's not exactly a fast learning experience you know.
This is actually really nice! I think that this will help me a lot!
 

Shonumi

EmuTalk Member
So, is any one working on anything? Been quite busy myself. I have made two small test ROMs with a focus on LCD emulation at the most basic level. Currently I have most of the THUMB instructions finished (just two more to implement) and I've got LCD emulation too. I'm aiming to make this emulator as cycle-accurate as I can, and everything runs on a per-pixel basis at the moment. I figured I'd share the test ROMs for anyone else interested in writing a GBA emulator for themselves. When I was making GB Enhanced, Imran Nazar gave a lot of little ROMs that demonstrated key concepts in action and was a good way to measure how well progress on one's own emulator was coming along. Hopefully I can do the same.

Stripes Demo #1 - Displays alternating black and white vertical bars on the screen. The file is called checkerboard, but I was too lazy to make a checkerboard pattern :p Later versions will actually have the checkerboard pattern by manipulating BG scrolling.

i9njdL7RLtWJX.png

checker_board_final.gba
Source Code

TV Test #1 - Displays colored stripes, similar to some of the old test patterns found on TVs. This is the first version, more will come to test even more aspects of the GBA LCD. For now, something simple.

ibkltFKaoJPDDw.png

tv_test_final.gba
Source Code

Hope this helps someone. Please note that this was all written by hand with a hex editor so the "source code" isn't something you can run through a compiler. They're more like super-detailed notes. They only detail the THUMB instructions. Each Test ROM runs in ARM mode for a few instructions just to switch to THUMB mode.

At any rate, I'm still a bit too far from being able to test commercial games, and I still need to get a Flash Cart to verify my own homebrew tests (these work in VBA-M and naturally my own WIP emulator though). Next up, more tests, more ARM7 emulation, and more tests :D
 
OP
F

Flerovium

Member
I've been too lazy in last time to write on my emulator =P
Still haven't finished thumb lol. Maybe I get myself the next days to pickup work again =)
Your doing great progress! BTW: Why don't you use an assembler? There are quite good ones out there =)
I like to use the one provided by GCC for ARM.
 

Shonumi

EmuTalk Member
Well, unless that assembler will attach a GBA ROM header and make sure the bytes are little-endian, there's still a lot to do to produce a functioning GBA ROM. An assembler dedicated for the GBA sounds better to me.

I have a little JavaScript that builds the code I need for several instructions. I just copy and paste the hex output into a hex editor. That's as close to an assembler as I get. You know me though, I do things with the bare minimum tools :p

I think I'll tackle interrupts next, at least the VBlank interrupt. We'll see how it goes.
 
Last edited:
OP
F

Flerovium

Member
Sometime ago I wrote some small demos in Assembler, creating a header should be quite easy.

Code:
.align 4
.arm

header:
b main_arm
@ If you want, you can define the header values here, but for emulators they aren't required in most cases

.org 0xC0
main_arm:
@ If you want, you can setup CPU mode etc here, I never did it back then :D
ldr r0, =0x080000E1
bx r0

.align 2
.thumb

.org 0xE0
main:
@ Your thumb code here =D

This should generate a executable and at least Visual Boy Advance should be able to run.
 

Shonumi

EmuTalk Member
Code:
ldr r0, =0x080000E1

Oh, this is a problem for me when it comes to assemblers. To my knowledge, LDR (ARM.9) can either only use a 12-bit immediate value or a shifted register as the offset. The default value of R0 on the GBA (after exiting BIOS) is zero. So the assembler has to be doing something other than LDR to get a value of 0x080000E1 into R0; there's no way to get that value by just using the LDR instruction (as defined in ARM.9), especially since all other registers at this time are 0 except R14 and R15. I would guess that the assembler actually adds other instructions (MOVS and such) to the binary output so that the value of 0x080000E1 can be placed into R0 when LDR is called.

This is great for programming demos from the perspective that you do not really care what goes on, so long as in the end R0 holds the value of 0x080000E1, but when you are trying to develop a cycle-accurate emulator or you want to test specific behavior, it will be a major pain. Sure, it is convenient on one hand, but when you are trying to recreate a system as accurately as possible, assemblers obfuscate certain things like what I pointed out. Ideally, I need to know every input (yes, on a binary level) I'm feeding into my program to measure the output. It's not just a matter of printing or drawing things to the screen like the demos I posted above; it's important for verifying accurate CPU emulation.

Assemblers are good for "quick and dirty" work. I have no issues with that, but I'm very strict about details :p
 
OP
F

Flerovium

Member
The GNU Assembler translates it to somewhat like that:
Code:
ldr r0, .offset 
 [MENTION=41704]...[/MENTION]
.offset:
.word 0x080000E1
 

Shonumi

EmuTalk Member
My bad, I completely forgot about loading from an address. I have not extensively gone over ARM.9, so ignore the above post.

But how does the assembler grab the word in .offset? When writing assembly by hand, you would first set a register as Rn (the base address) after loading 0x80000E1 into it. Now this is something that gets obfuscated.
 

ikeboy

New member
I'm trying to port some GBA emulator to rockbox. The target I have for testing is the Fuze+, which has a stmp3780 processor and a 454 MHz clock speed, which seemed enough for GBA.

I'd like a recommendation for which open-source GBA compilers it would be easiest to port. I know Gpsp was ported to Ipodlinux, which implies it might be easier to port in general.

Preferably, something written in C, so I don't have to re-write everything. I've found the Gpsp source code, although there seems to be several forks of it which aren't easy to find source code for. (Gpsp-J and others.)

I know how to code in C. I've never done any emulator work before, though. How long can I realistically expect it to take, and can anyone give me tips? What do I need to know about emulation for porting?
 

Shonumi

EmuTalk Member
So, any one else been working on their stuff? Flerovium? Well, there's been a flurry of work on my end recently. Super Dodge Ball Advance is basically the only thing that boots and displays something, only because I've focused on that exclusively for the time being:

iwDwHO2saJxNv.png
izaxUsWChy3QC.png


Sprites are not yet implemented (weekend project :)) and neither are timers or input. Hopefully the game will be mostly playable once that's added. Then I'll try to get other games running.
 
OP
F

Flerovium

Member
I'm too lazy, just as always currently. Not happy with that :(
Haven't worked on it for some time. I hope I'll be able to continue my work after my next exams..

Greetz
 
OP
F

Flerovium

Member
Yay, I got THUMB finally implemented! I'm hoping there aren't too much bugs :D
You can take a look at it here: https://github.com/hackiosa/fba/

EDIT:
x6ykca9h.png

mvmossgo.png

Wow! The first looks quite good, the second one freaks the **** out of me xD
Thanks to your test I already was able to fix some bugs in my Load / Store instructions! I hope I get the second test fully working too :D

EDIT2: I actually got it fixed :D It was a bug in my drawing routine
 
Last edited:

Shonumi

EmuTalk Member
Hey, that's great that you were able to work on it! If you have anything specific you want, I could make some more tests.

Currently I'm dealing with bugs, bugs, and more bugs. I have Super Dodge Ball Advance, Kirby: Nightmare in Dreamland, and Mega Man Battle Network 4: Blue Moon working to some extent. I can play around in all of them, but eventually errors prevent further gameplay. Bomberman Tournament boots, but it freezes for some reason, fixing this will be key since it seems to be the same kind of freeze I recently introduced as a regression (I bet it's when I changed the code that calculates the Overflow flag...) I really want to get Mega Man Zero to boot, that game's one of my favorites and a classic :)

Good luck with things on FakeBoyAdvance, nice name btw :D

EDIT: Hmm.. yeah, my new method of calculating the V flag sucks. The old implementation boots Bomberman Tournament just fine. The new one fails pretty hard and breaks other games. Back to the drawing board. But at least Bomberman Tournament goes in game (with bad graphics however...) but progress is progress. I've been trying to get this game to work for a week now, so I'm glad something is showing up now.

ibrwj4C0KWUBOV.png
ibgikML4YZ3WG3.png
ibtBNWuBWX3sUd.png


Goes in-game, but it draws a lot of nothing once the intro (pictured above) is finished. Maybe the data is there in VRAM but my LCD emulation isn't complete enough? It seems to work (pressing A goes through text, and I can start a new game if I just keep tapping Start). I'll have to investigate later. I have my eyes set on booting Final Fantasy Tactics Advance next. Looks this is one of the few games I've encountered so far that use DMA0 and DMA3. I only implemented DMA3, since that's what commonly used in most games.
 
Last edited:
OP
F

Flerovium

Member
What methods do you use for overflow? I use this one:
Code:
overflow = (result >> 31 != reg(rs) >> 31) && (reg(rs) >> 31 == (n) >> 31);
result = result of the addition or subtraction
reg(rs) = first operand of addition / subtraction (in this case the value of a register rs)
(n) = second operand

Don't know if this method is correct but maybe it helps you..
 

Shonumi

EmuTalk Member
I figured it out. I was flipping some bits incorrectly. I read a portion of an ARM assembly book that had rules for the V Flag and basically described them as such

Addition

* Are MSBs of the Input and Operand different?
* Clear V Flag​

* Are the MSBs of the Input and Operand the same?
* Is the MSB of the Result the same as the MSBs of the Input and Operand?​
* Clear V Flag​
* Is the MSB of the Result different from the MSBs of the Input and Operand?​
* Set V Flag​

Subtration

* Are MSBs of the Input and Operand the same?
* Clear V Flag​

* Are the MSBs of the Input and Operand different?
* Is the MSB of the Result the same as the MSB of the Operand?​
* Set V Flag​
* Is the MSB of the Result different from the MSB of the Operand?​
* Clear V Flag​

I was reversing when to set or clear the V flag during addition. No more freezes :)
 

Top