src/gb/io.c (view raw)
1/* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "io.h"
7
8#include "gb/gb.h"
9
10mLOG_DEFINE_CATEGORY(GB_IO, "GB I/O");
11
12const static uint8_t _registerMask[] = {
13 [REG_SC] = 0x7E, // TODO: GBC differences
14 [REG_IF] = 0xE0,
15 [REG_TAC] = 0xF8,
16 [REG_NR10] = 0x80,
17 [REG_NR11] = 0x3F,
18 [REG_NR12] = 0x00,
19 [REG_NR13] = 0xFF,
20 [REG_NR14] = 0xBF,
21 [REG_NR21] = 0x3F,
22 [REG_NR22] = 0x00,
23 [REG_NR23] = 0xFF,
24 [REG_NR24] = 0xBF,
25 [REG_NR30] = 0x7F,
26 [REG_NR31] = 0xFF,
27 [REG_NR32] = 0x9F,
28 [REG_NR33] = 0xFF,
29 [REG_NR34] = 0xBF,
30 [REG_NR41] = 0xFF,
31 [REG_NR42] = 0x00,
32 [REG_NR43] = 0x00,
33 [REG_NR44] = 0xBF,
34 [REG_NR50] = 0x00,
35 [REG_NR51] = 0x00,
36 [REG_NR52] = 0x70,
37 [REG_STAT] = 0x80,
38 [REG_VBK] = 0xFE,
39 [REG_OCPS] = 0x40,
40 [REG_BCPS] = 0x40,
41 [REG_UNK6C] = 0xFE,
42 [REG_SVBK] = 0xF8,
43 [REG_UNK75] = 0x8F,
44 [REG_IE] = 0xE0,
45};
46
47void GBIOInit(struct GB* gb) {
48 memset(gb->memory.io, 0, sizeof(gb->memory.io));
49}
50
51void GBIOReset(struct GB* gb) {
52 memset(gb->memory.io, 0, sizeof(gb->memory.io));
53
54 GBIOWrite(gb, REG_TIMA, 0);
55 GBIOWrite(gb, REG_TMA, 0);
56 GBIOWrite(gb, REG_TAC, 0);
57 GBIOWrite(gb, REG_IF, 1);
58 GBIOWrite(gb, REG_NR52, 0xF1);
59 GBIOWrite(gb, REG_NR10, 0x80);
60 GBIOWrite(gb, REG_NR11, 0xBF);
61 GBIOWrite(gb, REG_NR12, 0xF3);
62 GBIOWrite(gb, REG_NR13, 0xF3);
63 GBIOWrite(gb, REG_NR14, 0xBF);
64 GBIOWrite(gb, REG_NR21, 0x3F);
65 GBIOWrite(gb, REG_NR22, 0x00);
66 GBIOWrite(gb, REG_NR24, 0xBF);
67 GBIOWrite(gb, REG_NR30, 0x7F);
68 GBIOWrite(gb, REG_NR31, 0xFF);
69 GBIOWrite(gb, REG_NR32, 0x9F);
70 GBIOWrite(gb, REG_NR34, 0xBF);
71 GBIOWrite(gb, REG_NR41, 0xFF);
72 GBIOWrite(gb, REG_NR42, 0x00);
73 GBIOWrite(gb, REG_NR43, 0x00);
74 GBIOWrite(gb, REG_NR44, 0xBF);
75 GBIOWrite(gb, REG_NR50, 0x77);
76 GBIOWrite(gb, REG_NR51, 0xF3);
77 GBIOWrite(gb, REG_LCDC, 0x91);
78 GBIOWrite(gb, REG_SCY, 0x00);
79 GBIOWrite(gb, REG_SCX, 0x00);
80 GBIOWrite(gb, REG_LYC, 0x00);
81 GBIOWrite(gb, REG_BGP, 0xFC);
82 GBIOWrite(gb, REG_OBP0, 0xFF);
83 GBIOWrite(gb, REG_OBP1, 0xFF);
84 GBIOWrite(gb, REG_WY, 0x00);
85 GBIOWrite(gb, REG_WX, 0x00);
86 GBIOWrite(gb, REG_IE, 0x00);
87}
88
89void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
90 switch (address) {
91 case REG_DIV:
92 GBTimerDivReset(&gb->timer);
93 return;
94 case REG_NR10:
95 if (gb->audio.enable) {
96 GBAudioWriteNR10(&gb->audio, value);
97 } else {
98 value = 0;
99 }
100 break;
101 case REG_NR11:
102 if (gb->audio.enable) {
103 GBAudioWriteNR11(&gb->audio, value);
104 } else {
105 if (gb->audio.style == GB_AUDIO_DMG) {
106 GBAudioWriteNR11(&gb->audio, value & _registerMask[REG_NR11]);
107 }
108 value = 0;
109 }
110 break;
111 case REG_NR12:
112 if (gb->audio.enable) {
113 GBAudioWriteNR12(&gb->audio, value);
114 } else {
115 value = 0;
116 }
117 break;
118 case REG_NR13:
119 if (gb->audio.enable) {
120 GBAudioWriteNR13(&gb->audio, value);
121 } else {
122 value = 0;
123 }
124 break;
125 case REG_NR14:
126 if (gb->audio.enable) {
127 GBAudioWriteNR14(&gb->audio, value);
128 } else {
129 value = 0;
130 }
131 break;
132 case REG_NR21:
133 if (gb->audio.enable) {
134 GBAudioWriteNR21(&gb->audio, value);
135 } else {
136 if (gb->audio.style == GB_AUDIO_DMG) {
137 GBAudioWriteNR21(&gb->audio, value & _registerMask[REG_NR21]);
138 }
139 value = 0;
140 }
141 break;
142 case REG_NR22:
143 if (gb->audio.enable) {
144 GBAudioWriteNR22(&gb->audio, value);
145 } else {
146 value = 0;
147 }
148 break;
149 case REG_NR23:
150 if (gb->audio.enable) {
151 GBAudioWriteNR23(&gb->audio, value);
152 } else {
153 value = 0;
154 }
155 break;
156 case REG_NR24:
157 if (gb->audio.enable) {
158 GBAudioWriteNR24(&gb->audio, value);
159 } else {
160 value = 0;
161 }
162 break;
163 case REG_NR30:
164 if (gb->audio.enable) {
165 GBAudioWriteNR30(&gb->audio, value);
166 } else {
167 value = 0;
168 }
169 break;
170 case REG_NR31:
171 if (gb->audio.enable || gb->audio.style == GB_AUDIO_DMG) {
172 GBAudioWriteNR31(&gb->audio, value);
173 } else {
174 value = 0;
175 }
176 break;
177 case REG_NR32:
178 if (gb->audio.enable) {
179 GBAudioWriteNR32(&gb->audio, value);
180 } else {
181 value = 0;
182 }
183 break;
184 case REG_NR33:
185 if (gb->audio.enable) {
186 GBAudioWriteNR33(&gb->audio, value);
187 } else {
188 value = 0;
189 }
190 break;
191 case REG_NR34:
192 if (gb->audio.enable) {
193 GBAudioWriteNR34(&gb->audio, value);
194 } else {
195 value = 0;
196 }
197 break;
198 case REG_NR41:
199 if (gb->audio.enable || gb->audio.style == GB_AUDIO_DMG) {
200 GBAudioWriteNR41(&gb->audio, value);
201 } else {
202 value = 0;
203 }
204 break;
205 case REG_NR42:
206 if (gb->audio.enable) {
207 GBAudioWriteNR42(&gb->audio, value);
208 } else {
209 value = 0;
210 }
211 break;
212 case REG_NR43:
213 if (gb->audio.enable) {
214 GBAudioWriteNR43(&gb->audio, value);
215 } else {
216 value = 0;
217 }
218 break;
219 case REG_NR44:
220 if (gb->audio.enable) {
221 GBAudioWriteNR44(&gb->audio, value);
222 } else {
223 value = 0;
224 }
225 break;
226 case REG_NR50:
227 if (gb->audio.enable) {
228 GBAudioWriteNR50(&gb->audio, value);
229 } else {
230 value = 0;
231 }
232 break;
233 case REG_NR51:
234 if (gb->audio.enable) {
235 GBAudioWriteNR51(&gb->audio, value);
236 } else {
237 value = 0;
238 }
239 break;
240 case REG_NR52:
241 GBAudioWriteNR52(&gb->audio, value);
242 value &= 0x80;
243 value |= gb->memory.io[REG_NR52] & 0x0F;
244 break;
245 case REG_WAVE_0:
246 case REG_WAVE_1:
247 case REG_WAVE_2:
248 case REG_WAVE_3:
249 case REG_WAVE_4:
250 case REG_WAVE_5:
251 case REG_WAVE_6:
252 case REG_WAVE_7:
253 case REG_WAVE_8:
254 case REG_WAVE_9:
255 case REG_WAVE_A:
256 case REG_WAVE_B:
257 case REG_WAVE_C:
258 case REG_WAVE_D:
259 case REG_WAVE_E:
260 case REG_WAVE_F:
261 if (!gb->audio.playingCh3 || gb->audio.style != GB_AUDIO_DMG) {
262 gb->audio.ch3.wavedata8[address - REG_WAVE_0] = value;
263 } else if(gb->audio.ch3.readable) {
264 gb->audio.ch3.wavedata8[gb->audio.ch3.window >> 1] = value;
265 }
266 break;
267 case REG_JOYP:
268 case REG_TIMA:
269 case REG_TMA:
270 case REG_LYC:
271 // Handled transparently by the registers
272 break;
273 case REG_TAC:
274 value = GBTimerUpdateTAC(&gb->timer, value);
275 break;
276 case REG_IF:
277 gb->memory.io[REG_IF] = value | 0xE0;
278 GBUpdateIRQs(gb);
279 return;
280 case REG_LCDC:
281 // TODO: handle GBC differences
282 value = gb->video.renderer->writeVideoRegister(gb->video.renderer, address, value);
283 GBVideoWriteLCDC(&gb->video, value);
284 break;
285 case REG_DMA:
286 GBMemoryDMA(gb, value << 8);
287 break;
288 case REG_SCY:
289 case REG_SCX:
290 case REG_WY:
291 case REG_WX:
292 GBVideoProcessDots(&gb->video);
293 value = gb->video.renderer->writeVideoRegister(gb->video.renderer, address, value);
294 break;
295 case REG_BGP:
296 case REG_OBP0:
297 case REG_OBP1:
298 GBVideoProcessDots(&gb->video);
299 GBVideoWritePalette(&gb->video, address, value);
300 break;
301 case REG_STAT:
302 GBVideoWriteSTAT(&gb->video, value);
303 break;
304 case REG_IE:
305 gb->memory.ie = value;
306 GBUpdateIRQs(gb);
307 return;
308 default:
309 if (gb->model >= GB_MODEL_CGB) {
310 switch (address) {
311 case REG_VBK:
312 GBVideoSwitchBank(&gb->video, value);
313 break;
314 case REG_BCPS:
315 gb->video.bcpIndex = value & 0x3F;
316 gb->video.bcpIncrement = value & 0x80;
317 break;
318 case REG_BCPD:
319 GBVideoProcessDots(&gb->video);
320 GBVideoWritePalette(&gb->video, address, value);
321 break;
322 case REG_OCPS:
323 gb->video.ocpIndex = value & 0x3F;
324 gb->video.ocpIncrement = value & 0x80;
325 break;
326 case REG_OCPD:
327 GBVideoProcessDots(&gb->video);
328 GBVideoWritePalette(&gb->video, address, value);
329 break;
330 case REG_SVBK:
331 GBMemorySwitchWramBank(&gb->memory, value);
332 break;
333 default:
334 goto failed;
335 }
336 goto success;
337 }
338 failed:
339 mLOG(GB_IO, STUB, "Writing to unknown register FF%02X:%02X", address, value);
340 if (address >= GB_SIZE_IO) {
341 return;
342 }
343 break;
344 }
345 success:
346 gb->memory.io[address] = value;
347}
348
349static uint8_t _readKeys(struct GB* gb) {
350 uint8_t keys = *gb->keySource;
351 switch (gb->memory.io[REG_JOYP] & 0x30) {
352 case 0x20:
353 keys >>= 4;
354 break;
355 case 0x10:
356 break;
357 default:
358 // ???
359 keys = 0;
360 break;
361 }
362 return 0xC0 | (gb->memory.io[REG_JOYP] | 0xF) ^ (keys & 0xF);
363}
364
365uint8_t GBIORead(struct GB* gb, unsigned address) {
366 switch (address) {
367 case REG_JOYP:
368 return _readKeys(gb);
369 case REG_SB:
370 case REG_SC:
371 // TODO
372 break;
373 case REG_IE:
374 return gb->memory.ie;
375 case REG_WAVE_0:
376 case REG_WAVE_1:
377 case REG_WAVE_2:
378 case REG_WAVE_3:
379 case REG_WAVE_4:
380 case REG_WAVE_5:
381 case REG_WAVE_6:
382 case REG_WAVE_7:
383 case REG_WAVE_8:
384 case REG_WAVE_9:
385 case REG_WAVE_A:
386 case REG_WAVE_B:
387 case REG_WAVE_C:
388 case REG_WAVE_D:
389 case REG_WAVE_E:
390 case REG_WAVE_F:
391 if (gb->audio.playingCh3) {
392 if (gb->audio.ch3.readable || gb->audio.style != GB_AUDIO_DMG) {
393 return gb->audio.ch3.wavedata8[gb->audio.ch3.window >> 1];
394 } else {
395 return 0xFF;
396 }
397 } else {
398 return gb->audio.ch3.wavedata8[address - REG_WAVE_0];
399 }
400 break;
401 case REG_IF:
402 case REG_NR10:
403 case REG_NR11:
404 case REG_NR12:
405 case REG_NR14:
406 case REG_NR21:
407 case REG_NR22:
408 case REG_NR24:
409 case REG_NR30:
410 case REG_NR32:
411 case REG_NR34:
412 case REG_NR41:
413 case REG_NR42:
414 case REG_NR43:
415 case REG_NR44:
416 case REG_NR50:
417 case REG_NR51:
418 case REG_NR52:
419 case REG_DIV:
420 case REG_TIMA:
421 case REG_TMA:
422 case REG_TAC:
423 case REG_STAT:
424 case REG_LCDC:
425 case REG_SCY:
426 case REG_SCX:
427 case REG_LY:
428 case REG_LYC:
429 case REG_BGP:
430 case REG_OBP0:
431 case REG_OBP1:
432 case REG_WY:
433 case REG_WX:
434 // Handled transparently by the registers
435 break;
436 default:
437 if (gb->model >= GB_MODEL_CGB) {
438 switch (address) {
439 case REG_SVBK:
440 case REG_VBK:
441 // Handled transparently by the registers
442 goto success;
443 default:
444 break;
445 }
446 }
447 mLOG(GB_IO, STUB, "Reading from unknown register FF%02X", address);
448 return 0xFF;
449 }
450 success:
451 return gb->memory.io[address] | _registerMask[address];
452}