Skip to content

Instantly share code, notes, and snippets.

@LongJohnCoder
Forked from binji/LICENSE
Created June 5, 2021 13:40
Show Gist options
  • Save LongJohnCoder/2df13db938a2edf39e35ea93f31e279c to your computer and use it in GitHub Desktop.
Save LongJohnCoder/2df13db938a2edf39e35ea93f31e279c to your computer and use it in GitHub Desktop.
pokegb.cc w/o macros
#include <SDL2/SDL.h>
#include <cstdint>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#define OP4_NX8(_) case _: case _ + 8: case _ + 16: case _ + 24:
#define OP4_NX16_REL(_) \
case _: \
case _ + 16: \
case _ + 32: \
case _ + 48: \
opcode_rel = opcode - _;
#define OP5_FLAG(_, always) \
OP4_NX8(_) \
case always: \
carry = (opcode == always) || \
(F & F_mask[(opcode - _) / 8]) == F_equals[(opcode - _) / 8];
#define OP8_NX8_REL(_) OP4_NX8(_) OP4_NX8(_ + 32) opcode_rel = opcode - _;
#define OP7_PTR(_) \
case _: \
case _ + 1: \
case _ + 2: \
case _ + 3: \
case _ + 4: \
case _ + 5: \
case _ + 7: \
ptr8 = reg8_group[opcode & 7];
#define OP7_NX8(_) OP4_NX8(_) case _ + 32 : case _ + 40 : case _ + 56:
#define OP7_NX8_PTR(_) OP7_NX8(_) ptr8 = reg8_group[(opcode - _) / 8];
#define OP49_REL(_) \
OP7_NX8(_) \
OP7_NX8(_ + 1) \
OP7_NX8(_ + 2) \
OP7_NX8(_ + 3) \
OP7_NX8(_ + 4) \
OP7_NX8(_ + 5) \
OP7_NX8(_ + 7) \
opcode_rel = opcode - _;
#define OP56_PTR_REL(_) OP49_REL(_) OP7_PTR(_ + 48) opcode_rel = opcode - _;
#define OP9_IMM_PTR(_) \
case _ + 6: \
case _ + 70: \
OP7_PTR(_) \
operand = (opcode == _ + 6) ? read8() \
: (opcode == _ + 70) ? read8_pc() \
: *ptr8;
uint8_t opcode, opcode_rel, tmp8, operand, carry, neg, *rom0, *rom1, io[512], video_ram[8192],
work_ram[16384], *extram, *extrambank,
reg8[] = {19, 0, 216, 0, 77, 1, 176, 1, 254, 255}, &F = reg8[6],
&A = reg8[7], *reg8_group[] = {reg8 + 1, reg8, reg8 + 3, reg8 + 2,
reg8 + 5, reg8 + 4, &F, &A},
&IF = io[271], &LCDC = io[320], &LY = io[324], IME, halt, *ptr8;
uint8_t const *key_state;
uint16_t PC = 256, temp16, *reg16 = (uint16_t *)reg8, &HL = reg16[2],
&SP = reg16[4], &DIV = (uint16_t &)io[259], ppu_dot = 32, *ptr16,
*reg16_group1[] = {reg16, reg16 + 1, &HL, &SP},
*reg16_group2[] = {reg16, reg16 + 1, &HL, &HL}, prev_cycles, cycles;
int tmp, tmp2,
HL_add[] = {0, 0, 1, -1}, F_mask[] = {128, 128, 16, 16},
F_equals[] = {0, 128, 0, 16}, frame_buffer[23040],
palette[] = {-1, -23197, -65536, -16777216, -1, -8092417,
-12961132, -16777216, -1, -23197, -65536, -16777216};
void tick() { cycles += 4; }
uint8_t mem_access(uint16_t addr, uint8_t val, int write) {
tick();
switch (addr >> 12) {
case 2: case 3:
if (write)
rom1 = rom0 + ((val ? val & 63 : 1) << 14);
case 0: case 1:
return rom0[addr];
case 4: case 5:
if (write && val <= 3)
extrambank = extram + (val << 13);
case 6: case 7:
return rom1[addr & 16383];
case 8: case 9:
addr &= 8191;
if (write)
video_ram[addr] = val;
return video_ram[addr];
case 10: case 11:
addr &= 8191;
if (write)
extrambank[addr] = val;
return extrambank[addr];
case 15:
if (addr >= 65024) {
if (write) {
if (addr == 65350)
for (int y = 160; --y >= 0;)
io[y] = mem_access(val * 256 + y, 0, 0);
io[addr & 511] = val;
}
if (addr == 65280) {
if (!(io[256] & 16))
return ~(16 + key_state[81] * 8 + key_state[82] * 4 + key_state[80] * 2 + key_state[79]);
if (!(io[256] & 32))
return ~(32 + key_state[40] * 8 + key_state[43] * 4 + key_state[29] * 2 + key_state[27]);
return 255;
}
return io[addr & 511];
}
case 12 ... 14:
addr &= 16383;
if (write)
work_ram[addr] = val;
return work_ram[addr];
}
}
uint8_t read8(uint16_t addr = HL) { return mem_access(addr, 0, 0); }
void write8(uint8_t val, uint16_t addr = HL) { mem_access(addr, val, 1); }
void set_flags(uint8_t mask, int Z, int N, int H, int C) {
F = (F & mask) | (!Z * 128 + N * 64 + H * 32 + C * 16);
}
uint8_t read8_pc() { return read8(PC++); }
uint16_t read16(uint16_t &addr = PC) {
tmp8 = read8(addr++);
return read8(addr++) * 256 + tmp8;
}
void push(uint16_t addr) {
write8(addr >> 8, --SP);
write8(addr, --SP);
tick();
}
int main() {
rom1 = (rom0 = (uint8_t *)mmap(0, 1048576, PROT_READ, MAP_SHARED,
open("rom.gb", O_RDONLY), 0)) +
32768;
tmp = open("rom.sav", O_CREAT|O_RDWR, 0666);
ftruncate(tmp, 32768);
extrambank = extram =
(uint8_t *)mmap(0, 32768, PROT_READ | PROT_WRITE, MAP_SHARED, tmp, 0);
LCDC = 145;
DIV = 44032;
SDL_Init(SDL_INIT_VIDEO);
SDL_Renderer *Sr = SDL_CreateRenderer(
SDL_CreateWindow("pokegb", 0, 0, 800, 720, SDL_WINDOW_SHOWN), -1,
SDL_RENDERER_PRESENTVSYNC);
SDL_Texture *St = SDL_CreateTexture(Sr, SDL_PIXELFORMAT_RGBA32,
SDL_TEXTUREACCESS_STREAMING, 160, 144);
key_state = SDL_GetKeyboardState(0);
while (1) {
prev_cycles = cycles;
if (IME & IF & io[511]) {
IF = halt = IME = 0;
cycles += 8;
carry = 1;
temp16 = 64;
goto CALL;
} else if (halt)
tick();
else
switch (opcode = read8_pc()) {
case 0:
break;
OP4_NX16_REL(1)
*reg16_group1[opcode >> 4] = read16();
break;
OP4_NX16_REL(2)
write8(A, *reg16_group2[opcode >> 4]);
HL += HL_add[opcode_rel / 16];
break;
OP4_NX16_REL(3)
(*reg16_group1[opcode >> 4])++;
tick();
break;
OP7_NX8_PTR(4)
tmp8 = ++(*ptr8);
goto INC_FLAGS;
case 52:
write8(tmp8 = read8() + 1);
INC_FLAGS:
set_flags(16, tmp8, 0, !(tmp8 & 15), 0);
break;
OP7_NX8_PTR(5)
tmp8 = --(*ptr8);
goto DEC_FLAGS;
case 53:
write8(tmp8 = read8() - 1);
DEC_FLAGS:
set_flags(16, tmp8, 1, tmp8 % 16 == 15, 0);
break;
OP7_NX8_PTR(6)
*ptr8 = read8_pc();
break;
case 7:
A += A + (carry = A >> 7);
goto CARRY_FLAG;
OP4_NX16_REL(9)
ptr16 = reg16_group1[opcode >> 4];
set_flags(128, 1, 0, HL % 4096 + *ptr16 % 4096 > 4095, HL + *ptr16 > 65535);
HL += *ptr16;
tick();
break;
OP4_NX16_REL(10)
A = read8(*reg16_group2[opcode >> 4]);
HL += HL_add[opcode_rel / 16];
break;
OP4_NX16_REL(11)
(*reg16_group1[opcode >> 4])--;
tick();
break;
case 15:
A = (carry = A & 1) * 128 + A / 2;
goto CARRY_FLAG;
case 23:
carry = A >> 7;
A += A + F / 16 % 2;
CARRY_FLAG:
set_flags(0, 1, 0, 0, carry);
break;
OP5_FLAG(32, 24)
tmp8 = read8_pc();
if (carry)
PC += (int8_t)tmp8, tick();
break;
case 39:
carry = tmp8 = 0;
if (F & 32 || (!(F & 64) && A % 15 > 9))
tmp8 = 6;
if (F & 16 || (!(F & 64) && A > 153))
tmp8 |= 96, carry = 1;
set_flags(65, A += (F & 64) ? -tmp8 : tmp8, 0, 0, carry);
break;
case 47:
A = ~A;
set_flags(129, 1, 1, 1, 0);
break;
case 54:
write8(read8_pc());
break;
case 55: case 63:
set_flags(128, 1, 0, 0, opcode == 55 ? 1 : !(F & 16));
break;
OP7_NX8_PTR(70)
*ptr8 = read8();
break;
OP49_REL(64)
*reg8_group[opcode_rel / 8] = *reg8_group[opcode & 7];
break;
OP7_PTR(112)
write8(*ptr8);
break;
case 118:
halt = 1;
break;
OP9_IMM_PTR(128)
neg = carry = 0;
goto ALU;
OP9_IMM_PTR(136)
neg = 0;
carry = F / 16 % 2;
goto ALU;
OP9_IMM_PTR(144)
carry = 1;
goto SUBTRACT;
OP9_IMM_PTR(152)
carry = !(F / 16 % 2);
SUBTRACT:
neg = 1;
operand = ~operand;
ALU:
set_flags(0, tmp8 = A + operand + carry, neg,
(A % 16 + operand % 16 + carry > 15) ^ neg,
(A + operand + carry > 255) ^ neg);
A = tmp8;
break;
OP9_IMM_PTR(160)
set_flags(0, A &= operand, 0, 1, 0);
break;
OP9_IMM_PTR(168)
set_flags(0, A ^= operand, 0, 0, 0);
break;
OP9_IMM_PTR(176)
set_flags(0, A |= operand, 0, 0, 0);
break;
OP9_IMM_PTR(184)
set_flags(0, A != operand, 1, A % 16 < operand % 16, A < operand);
break;
case 217:
carry = IME = 1;
goto RET;
OP5_FLAG(192, 201)
RET:
tick();
if (carry)
PC = read16(SP);
break;
OP4_NX16_REL(193)
reg16[opcode_rel >> 4] = read16(SP);
break;
OP5_FLAG(194, 195)
temp16 = read16();
if (carry)
PC = temp16, tick();
break;
OP5_FLAG(196, 205)
temp16 = read16();
CALL:
if (carry)
push(PC), PC = temp16;
break;
OP4_NX16_REL(197)
push(reg16[opcode_rel >> 4]);
break;
case 203:
switch (opcode = read8_pc()) {
OP7_PTR(0)
*ptr8 += *ptr8 + (carry = *ptr8 / 128 % 2);
goto CARRY_ZERO_FLAGS_PTR;
OP7_PTR(8)
*ptr8 = (carry = *ptr8 & 1) * 128 + *ptr8 / 2;
goto CARRY_ZERO_FLAGS_PTR;
case 14:
tmp8 = read8();
carry = tmp8 & 1;
write8(tmp8 = carry * 128 + tmp8 / 2);
goto CARRY_ZERO_FLAGS_U;
OP7_PTR(16)
carry = *ptr8 >> 7;
*ptr8 = *ptr8 * 2 + F / 16 % 2;
goto CARRY_ZERO_FLAGS_PTR;
OP7_PTR(24)
carry = *ptr8 & 1;
*ptr8 = *ptr8 / 2 + ((F * 8) & 128);
goto CARRY_ZERO_FLAGS_PTR;
OP7_PTR(32)
carry = *ptr8 / 128 % 2;
*ptr8 *= 2;
goto CARRY_ZERO_FLAGS_PTR;
OP7_PTR(40)
carry = *ptr8 & 1;
*ptr8 = (int8_t)*ptr8 >> 1;
goto CARRY_ZERO_FLAGS_PTR;
OP7_PTR(48)
set_flags(0, *ptr8 = *ptr8 * 16 + *ptr8 / 16, 0, 0, 0);
break;
case 54:
tmp8 = read8();
carry = 0;
write8(tmp8 = tmp8 * 16 + tmp8 / 16);
CARRY_ZERO_FLAGS_U:
set_flags(0, tmp8, 0, 0, carry);
break;
OP7_PTR(56)
carry = *ptr8 & 1;
*ptr8 /= 2;
CARRY_ZERO_FLAGS_PTR:
set_flags(0, *ptr8, 0, 0, carry);
break;
OP8_NX8_REL(70)
tmp8 = read8();
goto BIT_FLAGS;
OP56_PTR_REL(64)
tmp8 = *ptr8;
BIT_FLAGS:
set_flags(16, tmp8 & (1 << opcode_rel / 8), 0, 1, 0);
break;
OP8_NX8_REL(134)
write8(read8() & ~(1 << opcode_rel / 8));
break;
OP56_PTR_REL(128)
*ptr8 &= ~(1 << opcode_rel / 8);
break;
OP8_NX8_REL(198)
write8(read8() | (1 << opcode_rel / 8));
break;
OP56_PTR_REL(192)
*ptr8 |= 1 << opcode_rel / 8;
break;
}
break;
case 224: case 226:
write8(A, 65280 + (opcode == 224 ? read8_pc() : *reg8));
break;
case 233:
PC = HL;
break;
case 234:
write8(A, read16());
break;
case 240: case 250:
A = read8(opcode == 240 ? 65280 | read8_pc() : read16());
break;
case 243: case 251:
IME = opcode != 243;
break;
case 248:
tmp8 = read8_pc();
set_flags(0, 1, 0, (uint8_t)SP + tmp8 > 255, SP % 16 + tmp8 % 16 > 15);
HL = SP + (int8_t)tmp8;
tick();
break;
case 249:
SP = HL;
tick();
break;
}
for (DIV += cycles - prev_cycles; prev_cycles++ != cycles;)
if (LCDC & 128) {
if (++ppu_dot == 1 && LY == 144)
IF |= 1;
if (ppu_dot == 456) {
if (LY < 144)
for (tmp = 160; --tmp >= 0;) {
uint8_t is_window =
LCDC & 32 && LY >= io[330] && tmp >= io[331] - 7,
x_offset = is_window ? tmp - io[331] + 7 : tmp + io[323],
y_offset = is_window ? LY - io[330] : LY + io[322];
uint16_t tile = video_ram[((LCDC & (is_window ? 64 : 8) ? 7 : 6)
<< 10) +
y_offset / 8 * 32 + x_offset / 8],
palette_index = 0;
x_offset = (x_offset ^ 7) & 7;
uint8_t
*tile_data =
&video_ram[(LCDC & 16 ? tile : 256 + (int8_t)tile) * 16 +
y_offset % 8 * 2],
color = (tile_data[1] >> x_offset) % 2 * 2 +
(*tile_data >> x_offset) % 2;
if (LCDC & 2)
for (uint8_t *sprite = io; sprite < io + 160; sprite += 4) {
uint8_t sprite_x = tmp - sprite[1] + 8,
sprite_y = LY - *sprite + 16;
if (sprite_x < 8 && sprite_y < 8) {
sprite_x ^= sprite[3] & 32 ? 0 : 7;
tile_data =
&video_ram[sprite[2] * 16 +
(sprite_y ^ (sprite[3] & 64 ? 7 : 0)) * 2];
uint8_t sprite_color = (tile_data[1] >> sprite_x) % 2 * 2 +
(*tile_data >> sprite_x) % 2;
if (!((sprite[3] & 128) && color) && sprite_color) {
color = sprite_color;
palette_index += 1 + !!(sprite[3] & 8);
break;
}
}
}
frame_buffer[LY * 160 + tmp] =
palette[(io[327 + palette_index] >> (2 * color)) % 4 +
palette_index * 4];
}
if (LY == 144) {
void *pixels;
SDL_LockTexture(St, 0, &pixels, &tmp);
for (tmp2 = 144; --tmp2 >= 0;)
memcpy((uint8_t *)pixels + tmp2 * tmp, frame_buffer + tmp2 * 160,
640);
SDL_UnlockTexture(St);
SDL_RenderCopy(Sr, St, 0, 0);
SDL_RenderPresent(Sr);
SDL_Event e;
while (SDL_PollEvent(&e))
if (e.type == SDL_QUIT)
return 0;
}
LY = (LY + 1) % 154;
ppu_dot = 0;
}
} else
LY = ppu_dot = 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment