configure.ac: fix typo
[rofl0r-gnuboy.git] / mem.c
blob963aceb479b37737f9a90bf5b84f7dc20790e847
3 #include <stdlib.h>
5 #include "defs.h"
6 #include "hw.h"
7 #include "regs.h"
8 #include "mem.h"
9 #include "rtc.h"
10 #include "lcd.h"
11 #include "lcdc.h"
12 #include "sound.h"
14 struct mbc mbc;
15 struct rom rom;
16 struct ram ram;
17 struct rom bootrom;
20 * In order to make reads and writes efficient, we keep tables
21 * (indexed by the high nibble of the address) specifying which
22 * regions can be read/written without a function call. For such
23 * ranges, the pointer in the map table points to the base of the
24 * region in host system memory. For ranges that require special
25 * processing, the pointer is NULL.
27 * mem_updatemap is called whenever bank changes or other operations
28 * make the old maps potentially invalid.
31 void mem_mapbootrom() {
32 if (!bootrom.bank) return;
33 mbc.rmap[0x0] = bootrom.bank[0];
36 void mem_updatemap()
38 int n;
39 byte **map;
41 mbc.rombank &= (mbc.romsize - 1);
42 mbc.rambank &= (mbc.ramsize - 1);
44 map = mbc.rmap;
45 /* don't unmap bootrom unless RI_BOOT was locked */
46 if (REG(RI_BOOT) & 1) map[0x0] = rom.bank[0];
47 map[0x1] = rom.bank[0];
48 map[0x2] = rom.bank[0];
49 map[0x3] = rom.bank[0];
50 if (mbc.rombank < mbc.romsize)
52 map[0x4] = rom.bank[mbc.rombank] - 0x4000;
53 map[0x5] = rom.bank[mbc.rombank] - 0x4000;
54 map[0x6] = rom.bank[mbc.rombank] - 0x4000;
55 map[0x7] = rom.bank[mbc.rombank] - 0x4000;
57 else map[0x4] = map[0x5] = map[0x6] = map[0x7] = NULL;
58 if (0 && (R_STAT & 0x03) == 0x03)
60 map[0x8] = NULL;
61 map[0x9] = NULL;
63 else
65 map[0x8] = lcd.vbank[R_VBK & 1] - 0x8000;
66 map[0x9] = lcd.vbank[R_VBK & 1] - 0x8000;
68 if (mbc.enableram && !(rtc.sel&8))
70 map[0xA] = ram.sbank[mbc.rambank] - 0xA000;
71 map[0xB] = ram.sbank[mbc.rambank] - 0xA000;
73 else map[0xA] = map[0xB] = NULL;
74 map[0xC] = ram.ibank[0] - 0xC000;
75 n = R_SVBK & 0x07;
76 map[0xD] = ram.ibank[n?n:1] - 0xD000;
77 map[0xE] = ram.ibank[0] - 0xE000;
78 map[0xF] = NULL;
80 map = mbc.wmap;
81 map[0x0] = map[0x1] = map[0x2] = map[0x3] = NULL;
82 map[0x4] = map[0x5] = map[0x6] = map[0x7] = NULL;
83 map[0x8] = map[0x9] = NULL;
84 if (mbc.enableram && !(rtc.sel&8))
86 map[0xA] = ram.sbank[mbc.rambank] - 0xA000;
87 map[0xB] = ram.sbank[mbc.rambank] - 0xA000;
89 else map[0xA] = map[0xB] = NULL;
90 map[0xC] = ram.ibank[0] - 0xC000;
91 n = R_SVBK & 0x07;
92 map[0xD] = ram.ibank[n?n:1] - 0xD000;
93 map[0xE] = ram.ibank[0] - 0xE000;
94 map[0xF] = NULL;
99 * ioreg_write handles output to io registers in the FF00-FF7F,FFFF
100 * range. It takes the register number (low byte of the address) and a
101 * byte value to be written.
104 void ioreg_write(byte r, byte b)
106 if (!hw.cgb)
108 switch (r)
110 case RI_VBK:
111 case RI_BCPS:
112 case RI_OCPS:
113 case RI_BCPD:
114 case RI_OCPD:
115 case RI_SVBK:
116 case RI_KEY1:
117 case RI_HDMA1:
118 case RI_HDMA2:
119 case RI_HDMA3:
120 case RI_HDMA4:
121 case RI_HDMA5:
122 return;
126 switch(r)
128 case RI_TIMA:
129 case RI_TMA:
130 case RI_TAC:
131 case RI_SCY:
132 case RI_SCX:
133 case RI_WY:
134 case RI_WX:
135 REG(r) = b;
136 break;
137 case RI_BGP:
138 if (R_BGP == b) break;
139 pal_write_dmg(0, 0, b);
140 pal_write_dmg(8, 1, b);
141 R_BGP = b;
142 break;
143 case RI_OBP0:
144 if (R_OBP0 == b) break;
145 pal_write_dmg(64, 2, b);
146 R_OBP0 = b;
147 break;
148 case RI_OBP1:
149 if (R_OBP1 == b) break;
150 pal_write_dmg(72, 3, b);
151 R_OBP1 = b;
152 break;
153 case RI_IF:
154 case RI_IE:
155 REG(r) = b & 0x1F;
156 break;
157 case RI_P1:
158 REG(r) = b;
159 pad_refresh();
160 break;
161 case RI_SC:
162 /* FIXME - this is a hack for stupid roms that probe serial */
163 if ((b & 0x81) == 0x81)
165 R_SB = 0xff;
166 hw_interrupt(IF_SERIAL, IF_SERIAL);
167 hw_interrupt(0, IF_SERIAL);
169 R_SC = b; /* & 0x7f; */
170 break;
171 case RI_SB:
172 REG(r) = b;
173 break;
174 case RI_DIV:
175 REG(r) = 0;
176 break;
177 case RI_LCDC:
178 lcdc_change(b);
179 break;
180 case RI_STAT:
181 stat_write(b);
182 break;
183 case RI_LYC:
184 REG(r) = b;
185 stat_trigger();
186 break;
187 case RI_VBK:
188 REG(r) = b | 0xFE;
189 mem_updatemap();
190 break;
191 case RI_BCPS:
192 R_BCPS = b & 0xBF;
193 R_BCPD = lcd.pal[b & 0x3F];
194 break;
195 case RI_OCPS:
196 R_OCPS = b & 0xBF;
197 R_OCPD = lcd.pal[64 + (b & 0x3F)];
198 break;
199 case RI_BCPD:
200 R_BCPD = b;
201 pal_write(R_BCPS & 0x3F, b);
202 if (R_BCPS & 0x80) R_BCPS = (R_BCPS+1) & 0xBF;
203 break;
204 case RI_OCPD:
205 R_OCPD = b;
206 pal_write(64 + (R_OCPS & 0x3F), b);
207 if (R_OCPS & 0x80) R_OCPS = (R_OCPS+1) & 0xBF;
208 break;
209 case RI_SVBK:
210 REG(r) = b & 0x07;
211 mem_updatemap();
212 break;
213 case RI_BOOT:
214 if(!(b&1)) break;
215 if(REG(r)&1) break;
216 REG(r) = 0xff;
217 mem_updatemap();
218 break;
219 case RI_DMA:
220 hw_dma(b);
221 break;
222 case RI_KEY1:
223 REG(r) = (REG(r) & 0x80) | (b & 0x01);
224 break;
225 case RI_HDMA1:
226 REG(r) = b;
227 break;
228 case RI_HDMA2:
229 REG(r) = b & 0xF0;
230 break;
231 case RI_HDMA3:
232 REG(r) = b & 0x1F;
233 break;
234 case RI_HDMA4:
235 REG(r) = b & 0xF0;
236 break;
237 case RI_HDMA5:
238 hw_hdma_cmd(b);
239 break;
241 switch (r)
243 case RI_BGP:
244 case RI_OBP0:
245 case RI_OBP1:
246 /* printf("palette reg %02X write %02X at LY=%02X\n", r, b, R_LY); */
247 case RI_HDMA1:
248 case RI_HDMA2:
249 case RI_HDMA3:
250 case RI_HDMA4:
251 case RI_HDMA5:
252 /* printf("HDMA %d: %02X\n", r - RI_HDMA1 + 1, b); */
253 break;
255 /* printf("reg %02X => %02X (%02X)\n", r, REG(r), b); */
259 byte ioreg_read(byte r)
261 switch(r)
263 case RI_BOOT:
264 return 0xfe | (REG(r) & 1);
265 case RI_SC:
266 r = R_SC;
267 R_SC &= 0x7f;
268 return r;
269 case RI_P1:
270 case RI_SB:
271 case RI_DIV:
272 case RI_TIMA:
273 case RI_TMA:
274 case RI_TAC:
275 case RI_LCDC:
276 case RI_STAT:
277 case RI_SCY:
278 case RI_SCX:
279 case RI_LY:
280 case RI_LYC:
281 case RI_BGP:
282 case RI_OBP0:
283 case RI_OBP1:
284 case RI_WY:
285 case RI_WX:
286 case RI_IE:
287 case RI_IF:
288 return REG(r);
289 case RI_VBK:
290 case RI_BCPS:
291 case RI_OCPS:
292 case RI_BCPD:
293 case RI_OCPD:
294 case RI_SVBK:
295 case RI_KEY1:
296 case RI_HDMA1:
297 case RI_HDMA2:
298 case RI_HDMA3:
299 case RI_HDMA4:
300 case RI_HDMA5:
301 if (hw.cgb) return REG(r);
302 default:
303 return 0xff;
310 * Memory bank controllers typically intercept write attempts to
311 * 0000-7FFF, using the address and byte written as instructions to
312 * change rom or sram banks, control special hardware, etc.
314 * mbc_write takes an address (which should be in the proper range)
315 * and a byte value written to the address.
318 void mbc_write(int a, byte b)
320 byte ha = (a>>12);
322 /* printf("mbc %d: rom bank %02X -[%04X:%02X]-> ", mbc.type, mbc.rombank, a, b); */
323 switch (mbc.type)
325 case MBC_MBC1:
326 switch (ha & 0xE)
328 case 0x0:
329 mbc.enableram = ((b & 0x0F) == 0x0A);
330 break;
331 case 0x2:
332 if ((b & 0x1F) == 0) b = 0x01;
333 mbc.rombank = (mbc.rombank & 0x60) | (b & 0x1F);
334 break;
335 case 0x4:
336 if (mbc.model)
338 mbc.rambank = b & 0x03;
339 break;
341 mbc.rombank = (mbc.rombank & 0x1F) | ((int)(b&3)<<5);
342 break;
343 case 0x6:
344 mbc.model = b & 0x1;
345 break;
347 break;
348 case MBC_MBC2: /* is this at all right? */
349 if ((a & 0x0100) == 0x0000)
351 mbc.enableram = ((b & 0x0F) == 0x0A);
352 break;
354 if ((a & 0xE100) == 0x2100)
356 mbc.rombank = b & 0x0F;
357 break;
359 break;
360 case MBC_MBC3:
361 switch (ha & 0xE)
363 case 0x0:
364 mbc.enableram = ((b & 0x0F) == 0x0A);
365 break;
366 case 0x2:
367 if ((b & 0x7F) == 0) b = 0x01;
368 mbc.rombank = b & 0x7F;
369 break;
370 case 0x4:
371 rtc.sel = b & 0x0f;
372 mbc.rambank = b & 0x03;
373 break;
374 case 0x6:
375 rtc_latch(b);
376 break;
378 break;
379 case MBC_RUMBLE:
380 switch (ha & 0xF)
382 case 0x4:
383 case 0x5:
384 /* FIXME - save high bit as rumble state */
385 /* mask off high bit */
386 b &= 0x7;
387 break;
389 /* fall thru */
390 case MBC_MBC5:
391 switch (ha & 0xF)
393 case 0x0:
394 case 0x1:
395 mbc.enableram = ((b & 0x0F) == 0x0A);
396 break;
397 case 0x2:
398 if ((b & 0xFF) == 0) b = 0x01;
399 mbc.rombank = (mbc.rombank & 0x100) | (b & 0xFF);
400 break;
401 case 0x3:
402 mbc.rombank = (mbc.rombank & 0xFF) | ((int)(b&1)<<8);
403 break;
404 case 0x4:
405 case 0x5:
406 mbc.rambank = b & 0x0f;
407 break;
409 break;
410 case MBC_HUC1: /* FIXME - this is all guesswork -- is it right??? */
411 switch (ha & 0xE)
413 case 0x0:
414 mbc.enableram = ((b & 0x0F) == 0x0A);
415 break;
416 case 0x2:
417 if ((b & 0x1F) == 0) b = 0x01;
418 mbc.rombank = (mbc.rombank & 0x60) | (b & 0x1F);
419 break;
420 case 0x4:
421 if (mbc.model)
423 mbc.rambank = b & 0x03;
424 break;
426 mbc.rombank = (mbc.rombank & 0x1F) | ((int)(b&3)<<5);
427 break;
428 case 0x6:
429 mbc.model = b & 0x1;
430 break;
432 break;
433 case MBC_HUC3:
434 switch (ha & 0xE)
436 case 0x0:
437 mbc.enableram = ((b & 0x0F) == 0x0A);
438 break;
439 case 0x2:
440 b &= 0x7F;
441 mbc.rombank = b ? b : 1;
442 break;
443 case 0x4:
444 rtc.sel = b & 0x0f;
445 mbc.rambank = b & 0x03;
446 break;
447 case 0x6:
448 rtc_latch(b);
449 break;
451 break;
453 /* printf("%02X\n", mbc.rombank); */
454 mem_updatemap();
459 * mem_write is the basic write function. Although it should only be
460 * called when the write map contains a NULL for the requested address
461 * region, it accepts writes to any address.
464 void mem_write(int a, byte b)
466 int n;
467 byte ha = (a>>12) & 0xE;
469 /* printf("write to 0x%04X: 0x%02X\n", a, b); */
470 switch (ha)
472 case 0x0:
473 case 0x2:
474 case 0x4:
475 case 0x6:
476 mbc_write(a, b);
477 break;
478 case 0x8:
479 /* if ((R_STAT & 0x03) == 0x03) break; */
480 vram_write(a & 0x1FFF, b);
481 break;
482 case 0xA:
483 if (!mbc.enableram) break;
484 if (rtc.sel&8)
486 rtc_write(b);
487 break;
489 ram.sbank[mbc.rambank][a & 0x1FFF] = b;
490 break;
491 case 0xC:
492 if ((a & 0xF000) == 0xC000)
494 ram.ibank[0][a & 0x0FFF] = b;
495 break;
497 n = R_SVBK & 0x07;
498 ram.ibank[n?n:1][a & 0x0FFF] = b;
499 break;
500 case 0xE:
501 if (a < 0xFE00)
503 mem_write(a & 0xDFFF, b);
504 break;
506 if ((a & 0xFF00) == 0xFE00)
508 /* if (R_STAT & 0x02) break; */
509 if (a < 0xFEA0) lcd.oam.mem[a & 0xFF] = b;
510 break;
512 /* return writehi(a & 0xFF, b); */
513 if (a >= 0xFF10 && a <= 0xFF3F)
515 sound_write(a & 0xFF, b);
516 break;
518 if ((a & 0xFF80) == 0xFF80 && a != 0xFFFF)
520 ram.hi[a & 0xFF] = b;
521 break;
523 ioreg_write(a & 0xFF, b);
529 * mem_read is the basic read function...not useful for much anymore
530 * with the read map, but it's still necessary for the final messy
531 * region.
534 byte mem_read(int a)
536 int n;
537 byte ha = (a>>12) & 0xE;
539 /* printf("read %04x\n", a); */
540 switch (ha)
542 case 0x0:
543 case 0x2:
544 return rom.bank[0][a];
545 case 0x4:
546 case 0x6:
547 return rom.bank[mbc.rombank][a & 0x3FFF];
548 case 0x8:
549 /* if ((R_STAT & 0x03) == 0x03) return 0xFF; */
550 return lcd.vbank[R_VBK&1][a & 0x1FFF];
551 case 0xA:
552 if (!mbc.enableram && mbc.type == MBC_HUC3)
553 return 0x01;
554 if (!mbc.enableram)
555 return 0xFF;
556 if (rtc.sel&8)
557 return rtc.regs[rtc.sel&7];
558 return ram.sbank[mbc.rambank][a & 0x1FFF];
559 case 0xC:
560 if ((a & 0xF000) == 0xC000)
561 return ram.ibank[0][a & 0x0FFF];
562 n = R_SVBK & 0x07;
563 return ram.ibank[n?n:1][a & 0x0FFF];
564 case 0xE:
565 if (a < 0xFE00) return mem_read(a & 0xDFFF);
566 if ((a & 0xFF00) == 0xFE00)
568 /* if (R_STAT & 0x02) return 0xFF; */
569 if (a < 0xFEA0) return lcd.oam.mem[a & 0xFF];
570 return 0xFF;
572 /* return readhi(a & 0xFF); */
573 if (a == 0xFFFF) return REG(0xFF);
574 if (a >= 0xFF10 && a <= 0xFF3F)
575 return sound_read(a & 0xFF);
576 if ((a & 0xFF80) == 0xFF80)
577 return ram.hi[a & 0xFF];
578 return ioreg_read(a & 0xFF);
580 return 0xff; /* not reached */
583 void mbc_reset()
585 mbc.rombank = 1;
586 mbc.rambank = 0;
587 mbc.enableram = 0;
588 mem_updatemap();
591 const char* mbc_to_string(int mbc_type) {
592 switch (mbc_type) {
593 case MBC_NONE: return "NONE";
594 case MBC_MBC1: return "MBC1";
595 case MBC_MBC2: return "MBC2";
596 case MBC_MBC3: return "MBC3";
597 case MBC_MBC5: return "MBC5";
598 case MBC_RUMBLE: return "RUMBL";
599 case MBC_HUC1: return "HUC1";
600 case MBC_HUC3: return "HUC3";
601 default: return "UNKN";