├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── deobfuscated.cc
├── empty.sav
├── img
├── o1.gif
├── o10.gif
├── o11.gif
├── o2.gif
├── o3.gif
├── o4.gif
├── o5.gif
├── o6.gif
├── o7.gif
├── o8.gif
├── o9.gif
└── pokegb.png
└── pokegb.cc
/.gitignore:
--------------------------------------------------------------------------------
1 | pokegb
2 | rom.gb
3 | rom.sav
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2021 Ben Smith
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | pokegb: pokegb.cc
2 | $(CC) -O2 -Wall -Wno-return-type -Wno-misleading-indentation -Wno-parentheses -o $@ $< -lSDL2
3 |
4 | rom.sav: empty.sav
5 | cp $< $@
6 |
7 | clean:
8 | rm -f pokegb rom.sav
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pokegb
2 |
3 | A gameboy emulator that only plays Pokemon Blue, in ~50 lines of c++.
4 |
5 | See the [technical write-up](https://binji.github.io/posts/pokegb/).
6 |
7 | ## Features
8 |
9 | Plays Pokemon Blue (and Red).
10 |
11 | ## Screenshots
12 |
13 | 
14 | 
15 | 
16 | 
17 | 
18 | 
19 | 
20 | 
21 | 
22 | 
23 | 
24 |
25 | 
26 |
27 | ## Building
28 |
29 | Only builds on Linux and macOS AFAIK.
30 |
31 | ```
32 | $ make
33 | ```
34 |
35 | On macOS, you'll need to create a save file too (just the first time):
36 |
37 | ```
38 | $ make rom.sav
39 | ```
40 |
41 | ## Running
42 |
43 | Get a Pokemon Blue (or Pokemon Red) ROM file. The files that are known to work have the following sha1s:
44 |
45 | | Name | sha1 |
46 | | - | - |
47 | | Pokemon - Blue Version (USA, Europe) (SGB Enhanced).gb | `d7037c83e1ae5b39bde3c30787637ba1d4c48ce2` |
48 | | Pokemon - Red Version (USA, Europe) (SGB Enhanced).gb | `ea9bcae617fdf159b045185467ae58b2e4a48b9a` |
49 |
50 | Others might work too, but these are the ones that I've tried.
51 |
52 | Rename the file to `rom.gb` and put it in the current directory. Then run:
53 |
54 | ```
55 | $ ./pokegb
56 | ```
57 |
58 | The save file is written to `rom.sav`.
59 |
60 | Keys:
61 |
62 | | Action | Key |
63 | | --- | --- |
64 | | DPAD-UP | ↑ |
65 | | DPAD-DOWN | ↓ |
66 | | DPAD-LEFT | ← |
67 | | DPAD-RIGHT | → |
68 | | B | Z |
69 | | A | X |
70 | | START | Enter |
71 | | SELECT | Tab |
72 |
73 | ## Updating keys
74 |
75 | Look for [line 24](https://github.com/binji/pokegb/blob/5444936aa7f12cb8c5c9c78e3c0c391ca4102f9b/pokegb.cc#L24) the source.
76 | The following table shows which numbers map to which keyboard keys:
77 |
78 | | number | default key | gameboy button |
79 | | - | - | - |
80 | | 27 | X | A Button |
81 | | 29 | Z | B Button |
82 | | 43 | Tab | Select Button |
83 | | 40 | Return | Start Button |
84 | | 79 | Arrow Right | DPAD Right |
85 | | 80 | Arrow Left | DPAD Left |
86 | | 81 | Arrow Down | DPAD Down |
87 | | 82 | Arrow Up | DPAD Up |
88 |
89 | Replace the numbers on this line with one from the [SDL scancode list](https://www.libsdl.org/tmp/SDL/include/SDL_scancode.h).
90 |
--------------------------------------------------------------------------------
/deobfuscated.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #define OPCREL(_) opcrel = (opcode - _) / 8
8 |
9 | #define OP4_NX8(_,X) case _: case _ + 8*X: case _ + 16*X: case _ + 24*X:
10 |
11 | #define OP4_NX16_REL(_) OP4_NX8(_, 2) opcrel = (opcode - _) / 16;
12 |
13 | #define OP5_FLAG(_, always) \
14 | OP4_NX8(_, 1) \
15 | case always: \
16 | OPCREL(_), carry = opcode == always || !(F & F_mask[opcrel]) ^ opcrel & 1;
17 |
18 | #define OP8_REL(_) \
19 | case _ ... _ + 7: \
20 | tmp8 = reg8_access(0, 0, opcrel = opcode);
21 |
22 | #define OP8_NX8_REL(_) \
23 | OP4_NX8(_, 1) OP4_NX8(_ + 32, 1) tmp8 = reg8_access(0, 0, OPCREL(_));
24 |
25 | #define OP64_REL(_) \
26 | case _ ... _ + 55: OP8_REL(_ + 56) OPCREL(_);
27 |
28 | #define OP9_IMM_PTR(_) \
29 | OP8_REL(_) case _ + 70 : operand = opcode & 64 ? mem8(PC++) : tmp8;
30 |
31 | uint8_t opcode, opcrel, tmp8, operand, carry, neg, *rom0, *rom1, io[512],
32 | video_ram[8192], work_ram[16384], *extram, *extrambank,
33 | reg8[] = {19, 0, 216, 0, 77, 1, 176, 1}, &F = reg8[6], &A = reg8[7],
34 | *reg8_group[] = {reg8 + 1, reg8, reg8 + 3, reg8 + 2,
35 | reg8 + 5, reg8 + 4, &F, &A},
36 | &IF = io[271], &LCDC = io[320], &LY = io[324], IME, halt;
37 |
38 | uint8_t const *key_state;
39 |
40 | uint16_t PC = 256, *reg16 = (uint16_t *)reg8, &HL = reg16[2], SP = 65534,
41 | &DIV = (uint16_t &)io[259], ppu_dot = 32,
42 | *reg16_group1[] = {reg16, reg16 + 1, &HL, &SP},
43 | *reg16_group2[] = {reg16, reg16 + 1, &HL, &HL}, prev_cycles, cycles;
44 |
45 | int tmp, tmp2, F_mask[] = {128, 128, 16, 16}, frame_buffer[23040],
46 | palette[] = {-1, -23197, -65536, -1 << 24,
47 | -1, -8092417, -12961132, -1 << 24};
48 |
49 | void tick() { cycles += 4; }
50 |
51 | uint8_t mem8(uint16_t addr = HL, uint8_t val = 0, int write = 0) {
52 | tick();
53 | switch (addr >> 13) {
54 | case 1:
55 | if (write)
56 | rom1 = rom0 + ((val ? val & 63 : 1) << 14);
57 |
58 | case 0:
59 | return rom0[addr];
60 |
61 | case 2:
62 | if (write && val <= 3)
63 | extrambank = extram + (val << 13);
64 |
65 | case 3:
66 | return rom1[addr & 16383];
67 |
68 | case 4:
69 | addr &= 8191;
70 | if (write)
71 | video_ram[addr] = val;
72 | return video_ram[addr];
73 |
74 | case 5:
75 | addr &= 8191;
76 | if (write)
77 | extrambank[addr] = val;
78 | return extrambank[addr];
79 |
80 | case 7:
81 | if (addr >= 65024) {
82 | if (write) {
83 | if (addr == 65350)
84 | for (int y = 160; --y >= 0;)
85 | io[y] = mem8(val << 8 | y);
86 | io[addr & 511] = val;
87 | }
88 |
89 | if (addr == 65280) {
90 | if (~io[256] & 16)
91 | return ~(16 + key_state[SDL_SCANCODE_DOWN] * 8 +
92 | key_state[SDL_SCANCODE_UP] * 4 +
93 | key_state[SDL_SCANCODE_LEFT] * 2 +
94 | key_state[SDL_SCANCODE_RIGHT]);
95 | if (~io[256] & 32)
96 | return ~(32 + key_state[SDL_SCANCODE_RETURN] * 8 +
97 | key_state[SDL_SCANCODE_TAB] * 4 +
98 | key_state[SDL_SCANCODE_Z] * 2 +
99 | key_state[SDL_SCANCODE_X]);
100 | return 255;
101 | }
102 | return io[addr & 511];
103 | }
104 |
105 | case 6:
106 | addr &= 16383;
107 | if (write)
108 | work_ram[addr] = val;
109 | return work_ram[addr];
110 | }
111 | }
112 |
113 | void set_flags(uint8_t mask, int Z, int N, int H, int C) {
114 | F = F & mask | !Z << 7 | N << 6 | H << 5 | C << 4;
115 | }
116 |
117 | uint16_t read16(uint16_t &addr = PC) {
118 | tmp8 = mem8(addr++);
119 | return mem8(addr++) << 8 | tmp8;
120 | }
121 |
122 | void push(uint16_t val) {
123 | mem8(--SP, val >> 8, 1);
124 | mem8(--SP, val, 1);
125 | tick();
126 | }
127 |
128 | uint8_t reg8_access(uint8_t val, int write = 1, uint8_t o = opcrel) {
129 | return (o &= 7) == 6 ? mem8(HL, val, write)
130 | : write ? *reg8_group[o] = val
131 | : *reg8_group[o];
132 | }
133 |
134 | uint8_t get_color(int tile, int y_offset, int x_offset) {
135 | uint8_t *tile_data = &video_ram[tile * 16 + y_offset * 2];
136 | return (tile_data[1] >> x_offset) % 2 * 2 + (*tile_data >> x_offset) % 2;
137 | }
138 |
139 | int main() {
140 | rom1 = (rom0 = (uint8_t *)mmap(0, 1 << 20, PROT_READ, MAP_SHARED,
141 | open("rom.gb", O_RDONLY), 0)) +
142 | 32768;
143 | tmp = open("rom.sav", O_CREAT|O_RDWR, 0666);
144 | ftruncate(tmp, 32768);
145 | extrambank = extram =
146 | (uint8_t *)mmap(0, 32768, PROT_READ | PROT_WRITE, MAP_SHARED, tmp, 0);
147 | LCDC = 145;
148 | DIV = 44032;
149 | SDL_Init(SDL_INIT_VIDEO);
150 | SDL_Renderer *renderer = SDL_CreateRenderer(
151 | SDL_CreateWindow("pokegb", 0, 0, 800, 720, SDL_WINDOW_SHOWN), -1,
152 | SDL_RENDERER_PRESENTVSYNC);
153 | SDL_Texture *texture = SDL_CreateTexture(
154 | renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, 160, 144);
155 | key_state = SDL_GetKeyboardState(0);
156 |
157 | while (1) {
158 | prev_cycles = cycles;
159 | if (IME & IF & io[511]) {
160 | IF = halt = IME = 0;
161 | cycles += 8;
162 | push(PC);
163 | PC = 64;
164 | } else if (halt)
165 | tick();
166 | else
167 | switch (opcode = mem8(PC++)) {
168 | OP4_NX16_REL(1) // LD r16, u16
169 | *reg16_group1[opcrel] = read16();
170 | case 0: // NOP
171 | break;
172 |
173 | OP4_NX16_REL(10) // LD A, (r16)
174 | OP4_NX16_REL(2) // LD (r16), A
175 | tmp = opcode & 8;
176 | reg8_access(mem8(*reg16_group2[opcrel], A, !tmp), tmp, 7);
177 | HL += opcrel < 2 ? 0 : 5 - 2 * opcrel;
178 | break;
179 |
180 | OP4_NX16_REL(11) // DEC r16
181 | OP4_NX16_REL(3) // INC r16
182 | *reg16_group1[opcrel] += opcode & 8 ? -1 : 1;
183 | tick();
184 | break;
185 |
186 | OP8_NX8_REL(5) // DEC r8 / DEC (HL)
187 | OP8_NX8_REL(4) // INC r8 / INC (HL)
188 | neg = opcode & 1;
189 | reg8_access(tmp8 += 1 - neg * 2);
190 | set_flags(16, tmp8, neg, !(tmp8 + neg & 15), 0);
191 | break;
192 |
193 | OP8_NX8_REL(6) // LD r8, u8 / LD (HL), u8
194 | reg8_access(mem8(PC++));
195 | break;
196 |
197 | OP4_NX16_REL(9) // ADD HL, r16
198 | tmp = *reg16_group1[opcrel];
199 | set_flags(128, 1, 0, HL % 4096 + tmp % 4096 > 4095, HL + tmp > 65535);
200 | HL += tmp;
201 | tick();
202 | break;
203 |
204 | OP4_NX8(7,1)
205 | neg = 1;
206 | goto ROTATE;
207 |
208 | OP5_FLAG(32, 24) // JR i8 / JR , i8
209 | tmp8 = mem8(PC++);
210 | if (carry)
211 | PC += (int8_t)tmp8, tick();
212 | break;
213 |
214 | case 39: // DAA
215 | carry = tmp8 = 0;
216 | if (F & 32 || ~F & 64 && A % 16 > 9)
217 | tmp8 = 6;
218 | if (F & 16 || ~F & 64 && A > 153)
219 | tmp8 |= 96, carry = 1;
220 | set_flags(65, A += F & 64 ? -tmp8 : tmp8, 0, 0, carry);
221 | break;
222 |
223 | case 47: // CPL
224 | A = ~A;
225 | set_flags(144, 1, 1, 1, 0);
226 | break;
227 |
228 | case 55: case 63: // SCF / CCF
229 | set_flags(128, 1, 0, 0, opcode & 8 ? !(F & 16) : 1);
230 | break;
231 |
232 | OP64_REL(64) // LD r8, r8 / LD r8, (HL) / LD (HL), r8 / HALT
233 | opcode == 118 ? halt = 1 : reg8_access(tmp8);
234 | break;
235 |
236 | OP9_IMM_PTR(128) // ADD A, r8 / ADD A, (HL) / ADD A, u8
237 | neg = carry = 0;
238 | goto ALU;
239 |
240 | OP9_IMM_PTR(136) // ADC A, r8 / ADC A, (HL) / ADC A, u8
241 | neg = 0;
242 | carry = F / 16 % 2;
243 | goto ALU;
244 |
245 | OP9_IMM_PTR(184) // CP A, r8 / CP A, (HL) / CP A, u8
246 | goto SUB;
247 | OP9_IMM_PTR(144) // SUB A, r8 / SUB A, (HL) / SUB A, u8
248 | SUB:
249 | carry = 1;
250 | goto SUBTRACT;
251 |
252 | OP9_IMM_PTR(152) // SBC A, r8 / SBC A, (HL) / SBC A, u8
253 | carry = !(F / 16 % 2);
254 | SUBTRACT:
255 | neg = 1;
256 | operand = ~operand;
257 | ALU:
258 | set_flags(0, tmp8 = A + operand + carry, neg,
259 | (A % 16 + operand % 16 + carry > 15) ^ neg,
260 | (A + operand + carry > 255) ^ neg);
261 | if (~(opcode / 8) & 7)
262 | A = tmp8;
263 | break;
264 |
265 | OP9_IMM_PTR(160) // AND A, r8 / AND A, (HL) / AND A, u8
266 | set_flags(0, A &= operand, 0, 1, 0);
267 | break;
268 |
269 | OP9_IMM_PTR(168) // XOR A, r8 / XOR A, (HL) / XOR A, u8
270 | set_flags(0, A ^= operand, 0, 0, 0);
271 | break;
272 |
273 | OP9_IMM_PTR(176) // OR A, r8 / OR A, (HL) / OR A, u8
274 | set_flags(0, A |= operand, 0, 0, 0);
275 | break;
276 |
277 | case 217: // RETI
278 | carry = IME = 1;
279 | goto RET;
280 |
281 | OP5_FLAG(192, 201) // RET / RET
282 | RET:
283 | tick();
284 | if (carry)
285 | PC = read16(SP);
286 | break;
287 |
288 | OP4_NX16_REL(193) // POP r16
289 | reg16[opcrel] = read16(SP);
290 | break;
291 |
292 | OP5_FLAG(194, 195) // JP u16 / JP , u16
293 | goto CALL;
294 | OP5_FLAG(196, 205) // CALL u16 / CALL , u16
295 | CALL:
296 | tmp = read16();
297 | if (carry)
298 | opcode & 4 ? push(PC) : tick(), PC = tmp;
299 | break;
300 |
301 | OP4_NX16_REL(197) // PUSH r16
302 | push(reg16[opcrel]);
303 | break;
304 |
305 | case 203:
306 | neg = 0;
307 | opcode = mem8(PC++);
308 | ROTATE:
309 | switch (opcode) {
310 | OP8_REL(0) // RLC r8 / RLC (HL)
311 | OP8_REL(16) // RL r8 / RL (HL)
312 | OP8_REL(32) // SLA r8 / SLA (HL)
313 | carry = tmp8 >> 7;
314 | tmp8 += tmp8 + (opcode & 16 ? F / 16 % 2 : opcode & 32 ? 0 : carry);
315 | goto CARRY_ZERO_FLAGS_U;
316 |
317 | OP8_REL(48) // SWAP r8 / SWAP (HL)
318 | carry = 0;
319 | tmp8 = tmp8 * 16 + tmp8 / 16;
320 | goto CARRY_ZERO_FLAGS_U;
321 |
322 | OP8_REL(8) // RRC r8 / RRC (HL)
323 | OP8_REL(24) // RR r8 / RR (HL)
324 | OP8_REL(40) // SRA r8 / SRA (HL)
325 | OP8_REL(56) // SRL r8 / SRL (HL)
326 | carry = tmp8 & 1;
327 | tmp8 = (opcode & 48) == 32
328 | ? (int8_t)tmp8 >> 1
329 | : tmp8 / 2 + (opcode & 32 ? 0
330 | : opcode & 16 ? (F * 8 & 128)
331 | : carry * 128);
332 | CARRY_ZERO_FLAGS_U:
333 | reg8_access(tmp8);
334 | set_flags(0, neg || tmp8, 0, 0, carry);
335 | break;
336 |
337 | OP64_REL(64) // BIT bit, r8 / BIT bit, (HL)
338 | set_flags(16, tmp8 & 1 << opcrel, 0, 1, 0);
339 | break;
340 |
341 | OP64_REL(128) // RES bit, r8 / RES bit, (HL)
342 | reg8_access(tmp8 & ~(1 << opcrel),1,opcode);
343 | break;
344 |
345 | OP64_REL(192) // SET bit, r8 / SET bit, (HL)
346 | reg8_access(tmp8 | 1 << opcrel,1,opcode);
347 | }
348 | break;
349 |
350 | case 224: case 226: case 234:
351 | case 240: case 242: case 250:
352 | // LD A, (FF00 + u8) / LD A, (FF00 + C) / LD A, (u16)
353 | // LD (FF00 + u8), A / LD (FF00 + C), A / LD (u16), A
354 | tmp = opcode & 16;
355 | reg8_access(mem8(opcode & 8
356 | ? read16()
357 | : 65280 + (opcode & 2 ? *reg8 : mem8(PC++)),
358 | A, !tmp),
359 | tmp, 7);
360 | break;
361 |
362 | case 233: // JP HL
363 | PC = HL;
364 | break;
365 |
366 | case 243: case 251: // DI / EI
367 | IME = opcode != 243;
368 | break;
369 |
370 | case 248: // LD HL, SP + i8
371 | HL = SP + (int8_t)(tmp8 = mem8(PC++));
372 | set_flags(0, 1, 0, SP % 16 + tmp8 % 16 > 15, (uint8_t)SP + tmp8 > 255);
373 | tick();
374 | break;
375 |
376 | case 249: // LD SP, HL
377 | SP = HL;
378 | tick();
379 | }
380 |
381 | for (DIV += cycles - prev_cycles; prev_cycles++ != cycles;)
382 | if (LCDC & 128) {
383 | if (++ppu_dot == 456) {
384 | if (LY < 144)
385 | for (tmp = 160; --tmp >= 0;) {
386 | uint8_t is_window =
387 | LCDC & 32 && LY >= io[330] && tmp >= io[331] - 7,
388 | x_offset = is_window ? tmp - io[331] + 7 : tmp + io[323],
389 | y_offset = is_window ? LY - io[330] : LY + io[322];
390 | uint16_t
391 | palette_index = 0,
392 | tile = video_ram[(LCDC & (is_window ? 64 : 8) ? 7 : 6) << 10 |
393 | y_offset / 8 * 32 + x_offset / 8],
394 | color = get_color(LCDC & 16 ? tile : 256 + (int8_t)tile,
395 | y_offset & 7, 7 - x_offset & 7);
396 |
397 | if (LCDC & 2)
398 | for (uint8_t *sprite = io; sprite < io + 160; sprite += 4) {
399 | uint8_t sprite_x = tmp - sprite[1] + 8,
400 | sprite_y = LY - *sprite + 16,
401 | sprite_color = get_color(
402 | sprite[2], sprite_y ^ (sprite[3] & 64 ? 7 : 0),
403 | sprite_x ^ (sprite[3] & 32 ? 0 : 7));
404 | if (sprite_x < 8 && sprite_y < 8 &&
405 | !(sprite[3] & 128 && color) && sprite_color) {
406 | color = sprite_color;
407 | palette_index = 1 + !!(sprite[3] & 16);
408 | break;
409 | }
410 | }
411 |
412 | frame_buffer[LY * 160 + tmp] =
413 | palette[(io[327 + palette_index] >> 2 * color) % 4 +
414 | palette_index * 4 &
415 | 7];
416 | }
417 |
418 | if (LY == 143) {
419 | IF |= 1;
420 | SDL_UpdateTexture(texture, 0, frame_buffer, 640);
421 | SDL_RenderCopy(renderer, texture, 0, 0);
422 | SDL_RenderPresent(renderer);
423 | SDL_Event event;
424 | while (SDL_PollEvent(&event))
425 | if (event.type == SDL_QUIT)
426 | return 0;
427 | }
428 |
429 | LY = (LY + 1) % 154;
430 | ppu_dot = 0;
431 | }
432 | } else
433 | LY = ppu_dot = 0;
434 | }
435 | }
436 |
--------------------------------------------------------------------------------
/empty.sav:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/img/o1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binji/pokegb/0434c7687e75eb003cd77bd42c1ae0ffb2654a5d/img/o1.gif
--------------------------------------------------------------------------------
/img/o10.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binji/pokegb/0434c7687e75eb003cd77bd42c1ae0ffb2654a5d/img/o10.gif
--------------------------------------------------------------------------------
/img/o11.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binji/pokegb/0434c7687e75eb003cd77bd42c1ae0ffb2654a5d/img/o11.gif
--------------------------------------------------------------------------------
/img/o2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binji/pokegb/0434c7687e75eb003cd77bd42c1ae0ffb2654a5d/img/o2.gif
--------------------------------------------------------------------------------
/img/o3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binji/pokegb/0434c7687e75eb003cd77bd42c1ae0ffb2654a5d/img/o3.gif
--------------------------------------------------------------------------------
/img/o4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binji/pokegb/0434c7687e75eb003cd77bd42c1ae0ffb2654a5d/img/o4.gif
--------------------------------------------------------------------------------
/img/o5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binji/pokegb/0434c7687e75eb003cd77bd42c1ae0ffb2654a5d/img/o5.gif
--------------------------------------------------------------------------------
/img/o6.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binji/pokegb/0434c7687e75eb003cd77bd42c1ae0ffb2654a5d/img/o6.gif
--------------------------------------------------------------------------------
/img/o7.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binji/pokegb/0434c7687e75eb003cd77bd42c1ae0ffb2654a5d/img/o7.gif
--------------------------------------------------------------------------------
/img/o8.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binji/pokegb/0434c7687e75eb003cd77bd42c1ae0ffb2654a5d/img/o8.gif
--------------------------------------------------------------------------------
/img/o9.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binji/pokegb/0434c7687e75eb003cd77bd42c1ae0ffb2654a5d/img/o9.gif
--------------------------------------------------------------------------------
/img/pokegb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/binji/pokegb/0434c7687e75eb003cd77bd42c1ae0ffb2654a5d/img/pokegb.png
--------------------------------------------------------------------------------
/pokegb.cc:
--------------------------------------------------------------------------------
1 | #include /******************************************/
2 | #include /* POKEGB by Ben Smith (June 2021) */
3 | #include /* -------------------------------------- */
4 | #include /* A GB emulator that can only play */
5 | #include /* Pokemon Blue/Red, written in C++. */
6 | #define P case /* Requires gcc/clang and Linux/macOS. */
7 | #define O goto /* Many features are not implemented! */
8 | #define K break; /* */
9 | #define E return /* $ cc pokegb.cc -lSDL2 -o pokegb */
10 | #define M(_) L(_,2)f=(b-_)/16; /* $ ./pokegb # reads from rom.gb */
11 | #define o(_) P _..._+7:a=q(0,0,f=b); /* # writes to rom.sav */
12 | #define N(_) o(_)P _+70:u=b&64?i(k++):a; /* */
13 | #define B(_) P _..._+55:o(_+56)f=(b-_)/8; /* Controls: Up/Down/Left/Right=Arrow Keys*/
14 | #define L(_,__) P _:P _+8*__:P _+16*__:P _+24*__: /* B Button=Z, A Button=X */
15 | #define U(_) L(_,1)L(_+32,1)a=q(0,0,f=(b-_)/8); /* Start=Enter, Select=Tab */
16 | #define e(_,__) L(_,1)P __:f=(b-_)/8,d=b==__||!(F&aj[f])^f&1; /******************************************/
17 | using g=uint8_t ;g b,f,a,u,d,m ,*S,*T,h[512],
18 | Z[8192],ac[16384],*ad,*C ,r[]={19,0,216,0,77,1,176 ,1},&F=r[6],&j=r[7],*ae[
19 | ]={r+1,r,r+3,r+2,r+5,r+4,&F,&j },&H=h[271],&z=h[320],&s=h[324 ],A,V,*v;using x=uint16_t;x k=
20 | 256,*y=(x*)r,&n=y[2],w=65534,&af=( x&)h[259],W=32,*X[]={y,y+1,&n,&w}, *ai[]={y,y+1,&n,&n},Y,I;using p=int
21 | ;p c,aj[]={128,128,16,16},ag[23040],ak []={-1,-23197,-65536,-1<<24,-1,-8092417 ,-12961132,-1<<24};g i(x a=n,g b=0,p c
22 | =0){I+=4;switch(a>>13){P 1:if(c)T=S+((b?b& 63:1)<<14);P 0:E S[a];P 2:if(c&&b<=3)C=ad+ (b<<13);P 3:E T[a&16383];P 4:a&=8191;if(c)
23 | Z[a]=b;E Z[a];P 5:a&=8191;if(c)C[a]=b;E C[a] ;P 7:if(a>=65024){if(c){if(a==65350)for(p y= 160;--y>=0;)h[y]=i(b<<8|y);h[a&511]=b;}if(a
24 | ==65280){if(~h[256]&16)E~(16+v[81]*8+v[82]*4+v [80]*2+v[79]);if(~h[256]&32)E~(32+v[40]*8+v[43 ]*4+v[29]*2+v[27]);E 255;}E h[a&511];}P 6:a&=
25 | 16383;if(c)ac[a]=b;E ac[a];}}g D(g a,p b,p c,p d ,p R){E F=F&a|!b<<7|c<<6|d<<5|R<<4;}x J(x&b=k){a =i(b++);E i(b++)<<8|a;}g t(x a){i(--w,a>>8,1);i(
26 | --w,a,1);E I+=4;}g q(g a,p b=1,g c=f){E(c&=7)==6?i (n,a,b):b?*ae[c]=a:*ae[c];}g ah(p a,p b,p c){g*d=& Z[a*16+b*2];E(d[1]>>c)%2*2+(*d>>c)%2;}p main(){T=(
27 | S=(g*)mmap(0,1<<20,1,1,open("rom.gb",0),0))+32768;c= open("rom.sav",66,438);ftruncate(c,32768);SDL_Init( 32+0+0);auto*aa=SDL_CreateRenderer(SDL_CreateWindow(
28 | "pokegb",0,0,800,720,4),-1,4);C=ad=(g*)mmap(0,32768,3, 1,c,0);v=(g*)SDL_GetKeyboardState(0);z=145;af=44032; auto*ab=SDL_CreateTexture(aa,376840196,1,160,144);for(
29 | ;;){Y=I;if(A&H&h[511])H=V=A=0,I+=8,t(k),k=64;else if(V )I+=4;else switch(b=i(k++)){M(1)*X[f]=J();P 0:K M(10)M (2)c=b&8;q(i(*ai[f],j,!c),c,7);n+=f<2?0:5-2*f;K M(11)M
30 | (3)*X[f]+=b&8?-1:1;I+=4; K U(5)U(4)m=b&1;q(a+=1-m*2);D(16,a,m,!(a+m&15),0 );K U(6)q(i(k++));K M(9)c=*X[f];D(128,1,0,n%4096 +c%4096>4095,n+c>65535);
31 | n+=c;I+=4;K L(7,1)m=1 ;O J;e(32,24)a=i(k++);if(d)k+=(int8_t)a,I+= 4;K P 39:d=a=0;if(F&32||~F&64&&j%16>9)a=6; if(F&16||~F&64&&j>153
32 | )a|=96,d=1;D(65,j+=F& 64?-a:a,0,0,d);K P 47:j=~j;D(144,1,1,1,0); K P 55:P 63:D(128,1,0,0,b&8?!(F&16):1);K B (64)b==118?V=1:q(a);K
33 | N(128)m=d=0;O F;N(136 )m=0;d=F/16%2;O F;N(184)O e;N(144)e:d=1;O B;N(152)d=!(F/16%2);B:m=1;u=~u;F:D(0,a=j+u +d,m,(j%16+u%16+d>15)
34 | ^m,(j+u+d>255)^m);if( ~(b/8)&7)j=a;K N(160)D(0,j&=u,0,1,0);K N( 168)D(0,j^=u,0,0,0);K N(176)D(0,j|=u,0,0,0 );K P 217:d=A=1;O Z;e(
35 | 192,201) Z:I +=4 ;if(d)k=J(w);K M( 193 )y[ f]=J(w);K e(194, 195 )O C ;e(196,
36 | 205)C:c= J() ;if (d)b&4?t(k),0:I+= 4,k =c; K M(197)t(y[f]); K P 203 :m=0;b=i(
37 | k++);J:;; switch (b){ o(0)o(16)o(32)d=a>> 7;a+= a+(b &16?F/16%2:b&32?0: d);O I;o( 48)d=0;a=
38 | a*16+a/ 16;O I;o(8)o(24 )o(40)o( 56)d=a&1 ;a=(b&48)==32? (int8_t) a>>1:a/2 +(b&32?0:b&16? (F*8&128
39 | ):d*128); I:q(a);D (0,m||a,0 ,0,d);K B (64)D(16 ,a&1<15,(g )w+a>255);I +=4;K P 249
44 | :w=n;I+=4;}for (af+=I-Y;Y++ !=I;)if(z&128 ){if(++W==456) {if(s<144)for (c=160;--c>=
45 | 0;){g b=z&32&&s >=h[330]&&c>=h[ 331]-7,d=b?c-h[ 331]+7:c+h[323] ,R=b?s-h[330]:s +h[322];x f=0,i
46 | =Z[(z&(b?64:8)?7:6)<<10|R/8*32+d/8],j= ah(z&16?i:256+(int8_t)i,R&7,7-d&7);if( z&2)for(g*a=h;a>2*j)%4+f*4&7];}if(s==143){H |=1;SDL_Event b;SDL_UpdateTexture
49 | (ab,0,ag,640);SDL_RenderCopy (aa,ab,0,0);SDL_RenderPresent (aa);for(;SDL_PollEvent(&b
50 | );)if(b.type==256 )E 0;}s=(s+1)%154; W=0;}}else s=W=0;}}
51 |
--------------------------------------------------------------------------------