3

[Day 7] - 100 days of code em C

Bom hoje eu fui buscar implementar um pouco mais dos registradores, dessa vez estava focado em entender o que exatamente algumas implementações que eu vi queriam com o uso das flags do registrador F, basicamente vi uma implementação que utilizava uma struct para isso o que parece ser promissor mas sei lá, não queria muito fazer dessa forma(ZEBRA - zero evidence but really arrogant - da minha parte) então tentei ver uma implementação que achei bem arrumadinha em C e vi algumas abordagens bem maneiras que decidi dar uma explorada, basicamente ele utilizava:

commons.h

#define BIT(a, n) ((a & (1 << n)) ? 1 : 0)

cpu.h

#define CPU_FLAG_Z BIT(context->regs.f, 7)
#define CPU_FLAG_N BIT(context->regs.f, 6)
#define CPU_FLAG_H BIT(context->regs.f, 5)
#define CPU_FLAG_C BIT(context->regs.f, 4)

e daí achei interessante a forma que ele utilizou o as operações de bit para fazer essas mudanças especificas, daí tentei fazer uma implementação mais simples e dar uma brincada com essa implementação pra ir aprendendo como isso funciona no C, bem a minha implementação ficou extremamente simples mas já consigo enxergar que eu poderia encapsular melhor minha implementação para tornar mais interessante, depois que eu fizer isso vou refatorar meus testes, porém por hoje meu tempo acabou; bem meu registers.h ficou:

#pragma once
#include <stdint.h>

typedef struct {
    uint16_t PC;
    uint16_t SP;
    uint8_t A;
    uint8_t B;
    uint8_t C;
    uint8_t D;
    uint8_t E;
    uint8_t F;
    uint8_t H;
    uint8_t L;
} Registers;

#define FLAG_Z (1 << 7); // ZERO
#define FLAG_N (1 << 6); // NEGATIVE
#define FLAG_H (1 << 5); // HALF-CARRY
#define FLAG_C (1 << 4); // CARRY

uint16_t get_af_register(const Registers* reg);
void set_af_register(Registers* reg, const uint8_t value_a, const uint8_t value_f);

uint16_t get_bc_register(const Registers* reg);
void set_bc_register(Registers* reg, const uint8_t value_b, const uint8_t value_c);

uint16_t get_de_register(const Registers* reg);
void set_de_register(Registers* reg, const uint8_t value_d, const uint8_t value_e);

uint16_t get_hl_register(const Registers* reg);
void set_hl_register(Registers* reg, const uint8_t value_l, const uint8_t value_h);

e os meus testes feitos no assert.h ficou:

#include <stdio.h>
#include <assert.h>
#include "registers.h"

void test_af_register() {
    printf("Testing virtual 16 btis registers \n");
    Registers reg = {
        .PC = 0x0000,
        .SP = 0x0000,
        .A = 0x00,
        .B = 0x00,
        .C = 0x00,
        .D = 0x00,
        .E = 0x00,
        .F = 0x00,
        .H = 0x00,
        .L = 0x00,
    };

    printf("    Getters and setters AF with normal values: ");
    set_af_register(&reg, 0xAA, 0x1A);
    uint16_t expected = 0xAA1A;
    uint16_t got = get_af_register(&reg);
    if (got == expected) {
        printf("    PASS\n");
    } else {
        printf("    FAIL\n");
    }

    printf("    Getters and setters AF with zero values: ");
    set_af_register(&reg, 0x00, 0x00);
    expected = 0x0000;
    got = get_af_register(&reg);
    if (got == expected) {
        printf("    PASS\n");
    } else {
        printf("    FAIL\n");
        printf("    expected: 0x%04X, got: 0x%04X\n", expected, got);
    }
    //
    // printf("    Getters and setters AF with values greater than 8 bits: ");
    // set_af_register(&reg, 0xAAAF, 0x00);
    // expected = 0x0000;
    // got = get_af_register(&reg);
    // if (got == expected) {
    //     printf("    PASS\n");
    // } else {
    //     printf("    FAIL\n");
    //     printf("    expected: 0x%04X, got: 0x%04X\n", expected, got);
    // }
}

void test_flag_flow() {
    printf("Testing register f flags dynamic \n");
    printf("    Verifying change only one flag");
    Registers reg = {
        .PC = 0x0000,
        .SP = 0x0000,
        .A = 0x00,
        .B = 0x00,
        .C = 0x00,
        .D = 0x00,
        .E = 0x00,
        .F = 0x00,
        .H = 0x00,
        .L = 0x00,
    };
    uint8_t expected = 0b10000000;
    reg.F |= FLAG_Z;
    uint8_t got = reg.F;
    if (got == expected) {
        printf("    PASS\n");
    } else {
        printf("    FAIL\n");
        printf("    expected: 0x%02X, got: 0x%02X\n", expected, got);
    }

    printf("    Verifying if a bit subscribe the previous");
    Registers reg_2 = {
        .PC = 0x0000,
        .SP = 0x0000,
        .A = 0x00,
        .B = 0x00,
        .C = 0x00,
        .D = 0x00,
        .E = 0x00,
        .F = 0x00,
        .H = 0x00,
        .L = 0x00,
    };
    expected = 0b10100000;
    reg_2.F |= FLAG_Z;
    reg_2.F |= FLAG_H;
    got = reg_2.F;
    if (got == expected) {
        printf("    PASS\n");
    } else {
        printf("    FAIL\n");
        printf("    expected: 0x%02X, got: 0x%02X\n", expected, got);
    }
    printf("    Reverse previous valition [BETTER NAMES IS NECESSARY]");
    Registers reg_3 = {
        .PC = 0x0000,
        .SP = 0x0000,
        .A = 0x00,
        .B = 0x00,
        .C = 0x00,
        .D = 0x00,
        .E = 0x00,
        .F = 0x00,
        .H = 0x00,
        .L = 0x00,
    };
    expected = 0b10100000;

    reg_3.F |= FLAG_H;
    reg_3.F |= FLAG_Z;

    got = reg_3.F;
    if (got == expected) {
        printf("    PASS\n");
    } else {
        printf("    FAIL\n");
        printf("    expected: 0x%02X, got: 0x%02X\n", expected, got);
    }

    printf("    Using all flags");
    Registers reg_4 = {
        .PC = 0x0000,
        .SP = 0x0000,
        .A = 0x00,
        .B = 0x00,
        .C = 0x00,
        .D = 0x00,
        .E = 0x00,
        .F = 0x00,
        .H = 0x00,
        .L = 0x00,
    };
    expected = 0b11110000;

    reg_4.F |= FLAG_Z;
    reg_4.F |= FLAG_N;
    reg_4.F |= FLAG_H;
    reg_4.F |= FLAG_C;

    got = reg_4.F;
    if (got == expected) {
        printf("    PASS\n");
    } else {
        printf("    FAIL\n");
        printf("    expected: 0x%02X, got: 0x%02X\n", expected, got);
    }

    printf("    Cleaning just one flag");
    Registers reg_5 = {
        .PC = 0x0000,
        .SP = 0x0000,
        .A = 0x00,
        .B = 0x00,
        .C = 0x00,
        .D = 0x00,
        .E = 0x00,
        .F = 0x00,
        .H = 0x00,
        .L = 0x00,
    };
    expected = 0b11010000;

    reg_5.F |= FLAG_Z;
    reg_5.F |= FLAG_N;
    reg_5.F |= FLAG_H;
    reg_5.F |= FLAG_C;

    //Cleaning
    reg_5.F &= ~FLAG_H;

    got = reg_5.F;
    if (got == expected) {
        printf("    PASS\n");
    } else {
        printf("    FAIL\n");
        printf("    expected: 0x%02X, got: 0x%02X\n", expected, got);
    }
}

int main() {
    printf("Init tests... \n");
    test_af_register();
    test_flag_flow();
    printf("Finish tests... \n");
    return 0;
}

Como eu tinha dito essa forma de fazer testes vai precisar melhorar mas caramba é muito maneiro o quanto me sinto mais confortável depois de poder brincar com os testes.

Referências

https://github.com/AliM3ski/Gameboy-emulator/tree/main
https://aquova.net/emudev/gb/04-cpu-setup.html

Carregando publicação patrocinada...