Reading binary file in C++ skips 0s?

Started by
15 comments, last by mentor 7 years, 5 months ago

First things first - I actually never read file as binary in C++ (although, of course, I used C++ o/i/fstream a lot of times) and I'm also new to emulation stuff.

Recently I got into this tutorial and coded my very first emulator (which is Chip 8 emulator):

http://www.multigesture.net/articles/how-to-write-an-emulator-chip-8-interpreter/

But when I ran Pong on my emulator all I saw were a couple of "O"s at the top of the console window (right now I'm just printing Os and white spaces in the console, as I just want to check out if my emulator works). Clearly there was something very wrong with my code, so I downloaded and opened source code included in the above tutorial to check for any errors I might've done, but found none. I compared each function from sources with my own code and couldn't find anything wrong with my code.

Therefore I thought that there must be some kind of error ongoing when the program loads a file as binary. Although I did included some simple error checking in the function responsible for loading a file and none of this error checkings ever showed me an error. First I wrote a simple printf in emulateCycle function:


printf("%X\n", opcode);

And then in main function I replaced infinite for loop with 8-step for loop, so my app would just show me first 8 opcodes. And then I opened pong2.ch8 file (which is the file I'm trying to load) in hex editor, so I could compare these opcodes with "original" ones. You can see what I got in readingerror.jpg. Only the first opcode matches its respective opcode in pong2.ch8 hex code. (File size is actually ok here, it should be 294 as it is).

I checked loadprogram function (which loads a binary file) for any errors, but there were none. So I decided to check if actually everything from pong2.ch8 is read and written to buffer and my emulator memory correctly. After writing contents of pong2.ch8 into buffer (which is of type char *) I did a printf to view contents of buffer variable. File readingerror2.jpg shows the results - first three nibbles are ok, but 4th one misses a zero before C! And this error continues to appear later.

I'm not sure if this is actually the issue and if this is the cause of Pong not working correctly on my emulator, but it seems to me there's something really wrong with loading a file as binary. I'm attaching loadprogram function here and you can see my whole code in main.txt. I searched Internet, but I cannot find what I'm doing wrong in this loadprogram function. I also compiled my project on VS2010 and Devcpp, but results are the same.


bool loadprogram(const char * filename) {
    fstream pfile;
    pfile.open(filename, ios::in | ios::binary);
    
    if (pfile == NULL) {
        fputs("File error", stderr);
        return false;
    }

    pfile.seekg(0, ios::end);
    long bufferSize = pfile.tellg();
    pfile.seekg(0, ios::beg);
    printf("File size: %d\n", (int)bufferSize);
    
    char * buffer = (char*)malloc(sizeof(char) * bufferSize);
    if (buffer == NULL) {
        fputs("Memory error", stderr);
        return false;
    }
    
    pfile.read(buffer, bufferSize);

    if ((4096-512) > bufferSize) {
        for (int i = 0; i<bufferSize; i++) {
            memory[i+512] = buffer[i];
            printf("%X\n", buffer[i]);
        }
    }
    else
        printf("Error: ROM too big for memory.\n");
    
    pfile.close();
    free(buffer);

    return true;
}

Guy in the tutorial actually coded this function in C, but I rewrote it in C++ to see if it would help me resolve this issue.

Advertisement
Is that really the code you used to generate the screenshot? Because it shouldn't be outputting 4 digits per line.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

The second screenshot shows contents of buffer from loadprogram function. The first one shows you opcodes from emulateCycle function and I think that both screenshots show quite right what they're supposed to. In emulateCycle the first line is actually:


opcode = memory[pc] << 8 | memory[pc+1];

This combines into one two adjacent pieces of opcode and that's where 4 digits you're probably refering to got from. Each opcode in Chip8 is 2 bytes long, so there should be 4 digits in hex.

The screenshot on the right looks like it matches the code and what the hexeditor displays. The screenshot on the left is... what? Printing uninitialized memory or something?

OK, as I said I'm new to emulation stuff and cannot really explain more than what's in tutorial I gave link to in the first post. But important thing is that I compared my code to the tutorial's source code and they were the same (besides that I'm not using classes here). I later rewrote loadprogram function from C to C++, but it wasn't working when it was C exactly the same as in the source code either.

Chip 8 opcode's are all 2 bytes long. So to get one opcode you need to actually combine 1 byte from memory to the next byte in memory and this is what this line does:


opcode = memory[pc] << 8 | memory[pc+1];

After that I'm printing above opcode and that's what screenshot on the left shows:


printf("%X\n", opcode);

I hope I explained that ok, English is not my native language.

The screenshot on the right looks like it matches the code and what the hexeditor displays.

I'm not sure, I'm new to this stuff. As you can see, code in hex editor goes like this:


22 FC 6B 0C

While what screenshot on the right shows seems to go like this:


22 FC 6B C

I'm not sure if this is correct and if there shouldn't be any zero before C.

But to be honest, I'm not really sure if this is the root of my problem with my emulator and if this is why my emulator doesn't work properly. But if this isn't the case, I don't really know what is. Best if anyone interested would look into main.txt. Although there are about 460 lines of code, I can assure that emulateCycle function is fully correct (just the same as in tutorial's source code) and this function takes a lot of place. The rest is rather short.

The problem is that %X by itself doesn't print leading 0s. You probably want %02X.

Damn, that's so stupid of me, but I must agree with you. But then again it doesn't solve this issue with my emulator not working properly.

So, I know now that contents of buffer and memory variables are correct and that they match contents of pong2.ch8 hex. But what screenshot on the left shows still doesn't feel right. You can see that first opcode is 22FC which is correct, but second one is 6B20 while it should be 6B0C. So does this line is not doing its job right?


opcode = memory[pc] << 8 | memory[pc+1];

The point is it's exactly the same as in the tutorial's source code and that's what is misleading me. I mean, there are binaries included in tutorial, not only source code, and tutorial's emulator from binaries works perfect, although it is the same code as mine.

EDIT:

And perhaps there should be ">>" instead of "<<"? Because "<<" "adds" 8 zeros to the left, right? While it should add 8 zeros to the right where memory[pc+1] goes?

Perhaps one way to solve my issue would be not to combine memory[pc] with memory[pc+1], but rather check just memory[pc] in the first switch instruction like this:


switch (memory[pc] & 0xF0) {
   case 0x00:
      //...
      break;

   case 0x10:
      //...
      break;
}

And when this is not enough, because there are multiple instructions starting with 0x8000 for example, I could add another switch inside appropiate case like this:


switch (memory[pc+1] & 0x0F) {
   case 0x01:
     //...and so on...
}

But also the problem is that Chip 8 has some opcodes which actually would require me to combine memory[pc] with memory[pc+1] in order to read, let's say, proper memory address (for example, some instruction like "0x6nnn" might jump to address nnn). And I'm still interested why the line with opcode variable combining two memory pieces doesn't really work. Or perhaps there is something I just don't get?

To elaborate on what SiCrane said:

%X just means "print this number in capitalized hexadecimal". It's just like how %d is "print this number in decimal".

printf doesn't put leading zeroes on numbers if you don't explicitly tell it to.

0C has a leading zero.

To get leading zeroes with %X you put %0<number of digits you want to pad with zeroes>X.


I saw this in the original post but I assumed you were talking about the 00 in 6C00 not being in the right screenshot.

I checked my emulator with two other .ch8 files - Tetris and Tron. Tetris repeats the issue - first opcode is correct, the rest is completely different. But Tron actually gave me all first eight opcodes right. But Tron doesn't work properly either, so it seems that some mistake in opcodes must appear later.

So, to be clear - my emulator reads a binary file correctly and saves Chip 8 program into memory array correctly. That's what I'm kinda sure of. But emulator does not read opcodes in emulateCycle correctly and at lleast it seems so. Not all opcodes at least. And that's rather strange. I must've done some mistake that I just cannot find now at all.

I would recommend brushing up on your bitwise arithmetic. Your understanding of shift operators seems incorrect, and your description of masking bytes is also incorrect.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

This topic is closed to new replies.

Advertisement