SDL2 Does not draw pixels correctly

Asked

Viewed 37 times

1

It’s been a while since I’ve been developing a CHIP8 emulator to learn a little bit of emulation, I’ve implemented all the instructions, except for the sound I’m going to implement, but I’m already catching up a long time to create a way that I can draw the sprites on the screen, this is my source code:

main.cpp

#include <iostream>
#include <fstream>
#include <SDL/SDL.h>
#include "chip8.hpp"

#ifdef _WIN32
#include "targetver.hpp"
#endif // _WIN32

std::uint8_t keys[16] = {
    SDLK_1, SDLK_2, SDLK_3, SDLK_4,
    SDLK_q, SDLK_w, SDLK_e, SDLK_r,
    SDLK_a, SDLK_s, SDLK_d, SDLK_f,
    SDLK_z, SDLK_x, SDLK_c, SDLK_v
};


int main(int argc, char *argv[])
{

    bool quit = false;

    CHIP8 *chip8 = new CHIP8();

    std::cout << "Input file: ";
    std::cin >> argv[0];

    std::fstream logfile("log.txt", std::ios::in | std::ios::out | std::ios::app);
    std::cerr.rdbuf(logfile.rdbuf());
    std::clog.rdbuf(logfile.rdbuf());

    std::clog << "Jogo/Programa atual: " << argv[0] << " . \n" << std::endl;

    if (!chip8->load(argv[0])) {

        std::cerr << "Could not start emulator!" << std::endl;
        logfile.close();

        delete chip8;
        return EXIT_FAILURE;
    }

    //Inicializa o SDL2
    if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {

        std::cerr << "Could not init SDL2:" << SDL_GetError() << std::endl;
        logfile.close();

        delete chip8;
        return EXIT_FAILURE;
    }

    //Cria a janela
    SDL_Window *window = SDL_CreateWindow("CHIP8", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 320, SDL_WINDOW_SHOWN);
    if (window == NULL) {
        std::cerr << "Could not init window:" << SDL_GetError() << std::endl;
        SDL_Quit();
        logfile.close();

        delete chip8;
        return EXIT_FAILURE;
    }

    //Cria o renderizador
    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (renderer == NULL) {
        std::cerr << "Could not create the renderer:" << SDL_GetError() << std::endl;
        SDL_DestroyWindow(window);
        SDL_Quit();
        logfile.close();

        delete chip8;
        return EXIT_FAILURE;
    }

    //Cria a textura para desenhar os pixels na janela
    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 64, 32);
    if (texture == NULL) {
        std::cerr << "Could not create the texture:" << SDL_GetError() << std::endl;
        SDL_DestroyTexture(texture);
        SDL_DestroyWindow(window);
        SDL_Quit();
        logfile.close();

        delete chip8;
        return EXIT_FAILURE;
    }

    //Processa os eventos
    SDL_Event ev;
    while (!quit) {

        while (SDL_PollEvent(&ev)) {

            chip8->exec();

            if (chip8->hasDraw) {
                chip8->hasDraw = false;

                Uint32 pixels[2048];
                for (int i = 0; i < 2048; ++i) {
                    std::uint8_t pixel = chip8->gfx[i];
                    pixels[i] = (0x00FFFFFF * pixel) | 0xFF000000;
                }

                //SDL_UpdateTexture(texture, NULL, chip8->gfx, 2048);
                SDL_UpdateTexture(texture, NULL, pixels, 64 * sizeof(Uint32));

                SDL_RenderClear(renderer);
                SDL_RenderCopy(renderer, texture, NULL, NULL);
                SDL_RenderPresent(renderer);

            }

            switch (ev.type) {

            case SDL_QUIT:
                quit = true;
                break;

            case SDL_KEYDOWN:

                for (int i = 0; i < 16; i++) {
                    if (ev.key.keysym.sym == keys[i]) {
                        chip8->keys[i] = 1;
                    }
                }

                break;

            case SDL_KEYUP:

                for (int i = 0; i < 16; i++) {
                    if (ev.key.keysym.sym == keys[i]) {
                        chip8->keys[i] = 0;
                    }
                }

                break;

            }

        }

    }

    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    std::clog << "\n---------------------------------------------\n";

    logfile.close();

    delete chip8;
    return EXIT_SUCCESS;
}

chip8.hpp

#ifndef CHIP8_HPP
#define CHIP8_HPP

#include <cstdint>

class CHIP8 {

public:

    CHIP8();
    ~CHIP8();

    //Ler o conteudo da ROM
    bool load(const char* fileName);

    //Busca e executa os opcodes
    void exec();

private:

    void message_log(const char *message);
    void message_log(const char *message, std::uint16_t opcode);

private:

    std::uint8_t rom[0xFFF];

    //Registers
    std::uint8_t Vx[16], SP = 0, DT = 0, ST = 0;
    std::uint16_t I = 0, PC = 0x200, stack[16];

public:

    bool debug = true, hasDraw = false;
    std::uint8_t gfx[2048], keys[16];

};

#endif // !CHIP8_HPP

chip8.cpp

#include <fstream>
#include <iostream>
#include <cstdlib>
#include "chip8.hpp"

const std::uint8_t fontset[80] = {
    0xF0, 0x90, 0x90, 0x90, 0xF0,       // 0
    0x20, 0x60, 0x20, 0x20, 0x70,       // 1
    0xF0, 0x10, 0xF0, 0x80, 0xF0,       // 2
    0xF0, 0x10, 0xF0, 0x10, 0xF0,       // 3
    0x90, 0x90, 0xF0, 0x10, 0x10,       // 4
    0xF0, 0x80, 0xF0, 0x10, 0xF0,       // 5
    0xF0, 0x80, 0xF0, 0x90, 0xF0,       // 6
    0xF0, 0x10, 0x20, 0x40, 0x40,       // 7
    0xF0, 0x90, 0xF0, 0x90, 0xF0,       // 8
    0xF0, 0x90, 0xF0, 0x10, 0xF0,       // 9
    0xF0, 0x90, 0xF0, 0x90, 0x90,       // A
    0xE0, 0x90, 0xE0, 0x90, 0xE0,       // B
    0xF0, 0x80, 0x80, 0x80, 0xF0,       // C
    0xE0, 0x90, 0x90, 0x90, 0xE0,       // D
    0xF0, 0x80, 0xF0, 0x80, 0xF0,       // E
    0xF0, 0x80, 0xF0, 0x80, 0x80        // F
};

CHIP8::CHIP8()
{

    //Inicializa os valores
    memset(rom, 0, sizeof(rom));
    memset(gfx, 0, sizeof(gfx));
    memset(Vx, 0, sizeof(Vx));
    memset(stack, 0, sizeof(stack));
    memset(keys, 0, sizeof(keys));

    for (int i = 0x000; i < 0x1FF; i++) {
        rom[i] = fontset[i];
    }

}

CHIP8::~CHIP8()
{
    memset(rom, 0, sizeof(rom));
    memset(gfx, 0, sizeof(gfx));
    memset(Vx, 0, sizeof(Vx));
    memset(stack, 0, sizeof(stack));
    memset(keys, 0, sizeof(keys));

    DT = 0;
    ST = 0;
}

void CHIP8::message_log(const char *message)
{
    if (debug) {
        std::clog << message << std::endl;
    }
}

void CHIP8::message_log(const char *message, std::uint16_t opcode)
{
    if (debug) {
        std::clog << message << std::uppercase << std::hex << opcode << std::endl;
    }
}

bool CHIP8::load(const char *fileName)
{

    std::fstream inputFile(fileName, std::ios::in | std::ios::binary);
    if (!inputFile.is_open()) {
        return false;
    }

    for (int i = 0x200; i < 0xFFF; i++) {
        rom[i] = (std::uint8_t)inputFile.get();
    }

    inputFile.close();

    return true;
}

void CHIP8::exec()
{

    std::uint16_t opcode = rom[PC] << 8 | rom[PC+1];

    unsigned nnn = (opcode & 0x0FFF);         //nnn or addr - A 12-bit value, the lowest 12 bits of the instruction
    unsigned n   = (opcode & 0x000F);         //n or nibble - A 4-bit value, the lowest 4 bits of the instruction
    unsigned x   = (opcode & 0x0F00) >> 8;    //x - A 4-bit value, the lower 4 bits of the high byte of the instruction
    unsigned y   = (opcode & 0x00F0) >> 4;    //y - A 4-bit value, the upper 4 bits of the low byte of the instruction
    unsigned kk = (opcode & 0x00FF);          //kk or byte - An 8-bit value, the lowest 8 bits of the instruction

    switch (opcode & 0xF000) {

    case 0x0000:

        switch (opcode & 0x00FF) {

        case 0x00E0:

            memset(gfx, 0, sizeof(gfx));
            hasDraw = true;
            PC += 2;

            message_log("Opcode CLS executado: 0x", opcode);
            break;

        case 0x00EE:

            PC = stack[--SP];

            message_log("Opcode RET executado: 0x", opcode);
            break;

        }

        break;

    case 0x1000:

        PC = nnn;

        message_log("Opcode JP addr executado: 0x", opcode);
        break;

    case 0x2000:

        stack[SP++] = PC + 2;
        PC = nnn;

        message_log("Opcode CALL addr executado: 0x", opcode);
        break;

    case 0x3000:

        if (Vx[x] == kk)
            PC += 2;

        PC += 2;

        message_log("Opcode SE Vx, byte executado: 0x", opcode);
        break;

    case 0x4000:

        if (Vx[x] != kk)
            PC += 2;

        PC += 2;

        message_log("Opcode SNE Vx, byte executado: 0x", opcode);
        break;

    case 0x5000:

        if (Vx[x] == Vx[y])
            PC += 2;

        PC += 2;

        message_log("Opcode SE Vx, Vy executado: 0x", opcode);
        break;

    case 0x6000:

        Vx[x] = kk;
        PC += 2;

        message_log("Opcode LD Vx, byte executado: 0x", opcode);
        break;

    case 0x7000:

        Vx[x] = Vx[x] + kk;
        PC += 2;

        message_log("Opcode ADD Vx, byte executado: 0x", opcode);
        break;

    //8xy_
    case 0x8000:

        switch (opcode & 0x000F) {

        case 0x0000:

            Vx[x] = Vx[y];
            PC += 2;

            message_log("Opcode LD Vx, Vy executado: 0x", opcode);
            break;

        case 0x0001:

            Vx[x] |= Vx[y];
            PC += 2;

            message_log("Opcode OR Vx, Vy executado: 0x", opcode);
            break;

        case 0x0002:

            Vx[x] &= Vx[y];
            PC += 2;

            message_log("Opcode AND Vx, Vy executado: 0x", opcode);
            break;

        case 0x0003:

            Vx[x] ^= Vx[y];
            PC += 2;

            message_log("Opcode XOR Vx, Vy executado: 0x", opcode);
            break;

        case 0x0004:

            if ((Vx[x] += Vx[y]) > 255)
                Vx[0xF] = 1;
            else
                Vx[0xF] = 0;

            PC += 2;

            message_log("Opcode ADD Vx, Vy executado: 0x", opcode);
            break;

        case 0x0005:

            if (Vx[x] > Vx[y])
                Vx[0xF] = 1;
            else
                Vx[0xF] = 0;

            Vx[x] -= Vx[y];
            PC += 2;

            message_log("Opcode SUB Vx, Vy executado: 0x", opcode);
            break;

        case 0x0006:

            Vx[0xF] = Vx[x] & 0x1;
            Vx[x] >>= 1;
            PC += 2;

            message_log("Opcode SHR Vx {, Vy} executado: 0x", opcode);
            break;

        case 0x0007:

            if (Vx[y] > Vx[x])
                Vx[0xF] = 1;
            else
                Vx[0xF] = 0;

            Vx[x] -= Vx[y];

            PC += 2;

            message_log("Opcode SUBN Vx, Vy executado: 0x", opcode);
            break;

        case 0x000E:

            Vx[0xF] = Vx[x] >> 7;
            Vx[x] <<= 1;
            PC += 2;

            message_log("Opcode SHL Vx {, Vy} executado: 0x", opcode);
            break;

        default:
            message_log("Opcode nao inplementado: 0x8xy", (opcode & 0x000F));
            break;
        }

        break;

    case 0x9000:

        if (Vx[x] != Vx[y])
            PC += 2;

        PC += 2;

        message_log("Opcode SNE Vx, Vy executado: 0x", opcode);
        break;

    case 0xA000:

        I = nnn;
        PC += 2;

        message_log("Opcode LD I, addr executado: 0x", opcode);
        break;

    case 0xB000:

        PC = nnn + Vx[0];

        message_log("Opcode JP V0, addr executado: 0x", opcode);
        break;

    case 0xC000:

        Vx[x] = (std::rand() % 255) & kk;
        PC += 2;

        message_log("Opcode RND V x , byte executado: 0x", opcode);
        break;

    case 0xD000:


    {
        Vx[0xF] = 0;

        for (int yline = 0; yline < Vx[x]; yline++) {

            std::uint16_t pixel = rom[I + yline];
            for (int xline = 0; xline < 8; xline++) {
                if ((pixel & (0x80 >> xline)) != 0) {

                    if (gfx[Vx[x] + xline + ((Vx[y] + yline) * 64)] == 1)
                        Vx[0xF] = 1;

                    gfx[Vx[x] + xline + ((Vx[y] + yline) * 64)] ^= 1;
                }
            }

        }
    }

        hasDraw = true;
        PC += 2;

        message_log("Opcode DRW Vx, Vy, nibble executado: 0x", opcode);
        break;

    case 0xE000:

        switch (opcode & 0x00FF) {

        case 0x009E:

            if (keys[Vx[x]] == 1)
                PC += 2;

            PC += 2;

            message_log("Opcode SKP Vx executado: 0x", opcode);
            break;

        case 0x00A1:

            if (keys[Vx[x]] == 0)
                PC += 2;

            PC += 2;

            message_log("Opcode SKNP Vx executado: 0x", opcode);
            break;

        }

        break;

    case 0xF000:

        switch (opcode & 0x00FF) {

        case 0x0007:

            Vx[x] = DT;
            PC += 2;

            message_log("Opcode LD Vx, DT executado: 0x", opcode);
            break;

        case 0x000A:

        {
            bool pressed = false;
            for (int i = 0; i < 16; i++) {
                if (i == 1) {
                    Vx[x] = keys[i];
                    pressed;
                }
            }

            if (!pressed)
                return;

            PC += 2;
        }

            message_log("Opcode LD Vx, K executado: 0x", opcode);
            break;

        case 0x0015:

            DT = Vx[x];
            PC += 2;

            message_log("Opcode LD DT, Vx executado: 0x", opcode);
            break;

        case 0x0018:

            ST = Vx[x];
            PC += 2;

            message_log("Opcode LD ST, Vx executado: 0x", opcode);
            break;

        case 0x001E:

            I = I + Vx[x];
            PC += 2;

            message_log("Opcode ADD I, Vx executado: 0x", opcode);
            break;

        case 0x0029:

            I = Vx[x] * 5;
            PC += 2;

            message_log("Opcode LD F, Vx executado: 0x", opcode);
            break;

        case 0x0033:

            rom[I] = Vx[x] % 1000 / 100;
            rom[I + 1] = Vx[x] % 100 / 10;
            rom[I + 2] = Vx[x] % 10;

            PC += 2;

            message_log("Opcode LD B, Vx executado: 0x", opcode);
            break;

        case 0x0055:

            for (int i = 0; i <= x; i++) {
                rom[I + i] = Vx[i];
            }

            PC += 2;

            message_log("Opcode LD [I], Vx executado: 0x", opcode);
            break;

        case 0x0065:

            for (int i = 0; i <= x; i++) {
                Vx[i] = rom[I + i];
            }

            PC += 2;

            message_log("Opcode LD Vx, [I] executado: 0x", opcode);
            break;

        default:
            message_log("Opcode nao inplementado: 0x", (opcode & 0x00FF));
            break;
        }

        break;

    default:
        message_log("Opcode nao inplementado: 0x", (opcode & 0xF000));
        break;
    }

    if (DT > 0)
        --DT;

    if (ST > 0)
        --ST;

}

Note: The targetver.hpp header only configures the windows SDK version for Windows 7

I based myself on this documentation to write the emulator and in this repository for the graphics events because I had no idea how to do this and I wanted to go testing my code and go showing the result on the screen, the opcodes responsible for drawing sprites are 0xD000 and 0x00E0, the first thing I didn’t understand was, if pixels are stored in an array of bytes the size of 2048, then why he stored it in a Uint32 Array of the same size and why he did that masking and then in the size he indicated 64 * sizeof(Uint32), if the buffer is 2048 pixels then why not just put 2048? Thinking about it I just did:

SDL_UpdateTexture(texture, NULL, chip8->gfx, 2048);

And I got this result:

inserir a descrição da imagem aqui

As you can see right away I was disappointed, so I tried it the repository way, this time it worked (or almost), but the sprites are shown randomly:

inserir a descrição da imagem aqui inserir a descrição da imagem aqui inserir a descrição da imagem aqui

I plan to study the operation of the opcode Dxyn later to improve this code but I would need the graphical part running for now to test the emulator actually, if the keys are working well etc, but I can’t find a solution to this problem.

No answers

Browser other questions tagged

You are not signed in. Login or sign up in order to post.