Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libmeteor/source/io.cpp
2 views
1
// Meteor - A Nintendo Gameboy Advance emulator
2
// Copyright (C) 2009-2011 Philippe Daouadi
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17
#include "ameteor/io.hpp"
18
#include "ameteor/dma.hpp"
19
#include "globals.hpp"
20
#include "ameteor.hpp"
21
#include <cstring>
22
23
#include "debug.hpp"
24
25
#define W8(add, val) \
26
m_iomem[(add) & 0xFFF] = (val)
27
#define W16(add, val) \
28
*(uint16_t*)(m_iomem + ((add) & 0xFFF)) = (val)
29
#define W32(add, val) \
30
*(uint32_t*)(m_iomem + ((add) & 0xFFF)) = (val)
31
32
namespace AMeteor
33
{
34
Io::Io ()
35
{
36
m_iomem = new uint8_t[IO_SIZE];
37
Reset ();
38
}
39
40
Io::~Io ()
41
{
42
delete [] m_iomem;
43
}
44
45
void Io::Reset ()
46
{
47
std::memset(m_iomem, 0, IO_SIZE);
48
49
// TODO use clears intead
50
51
// TODO DISPCNT should be 0x80 by default
52
// TODO do it also for clears
53
// TODO lcd should draw white lines when hblank forced
54
// TODO when passing disabling hblank forced, lcd should start drawing from
55
// first line
56
W16(SOUNDBIAS, 0x0200); // default value
57
W16(KEYINPUT, 0x03FF); // all keys released
58
W8(HALTCNT, 0xFF); // normal mode (internal)
59
W16(DISPSTAT, 0x0004); // vcount match (since default vcount is 0)
60
W16(BG2PA, 0x0100);
61
W16(BG2PD, 0x0100);
62
W16(BG3PA, 0x0100);
63
W16(BG3PD, 0x0100);
64
W16(RCNT, 0x8000); // we start in general purpose mode
65
}
66
67
void Io::ClearSio ()
68
{
69
// TODO
70
W16(RCNT, 0x8000);
71
}
72
73
void Io::ClearSound ()
74
{
75
// TODO
76
}
77
78
void Io::ClearOthers ()
79
{
80
// FIXME !! shouldn't we call Write*() ?
81
// lcd
82
for (uint8_t i = 0x0; i < 0x56; i += 2)
83
Write16(i, 0x0000);
84
// dma
85
for (uint8_t i = 0xB0; i < 0xE0; i += 4)
86
Write32(i, 0x0000);
87
// FIXME : should timers be set to 0 too ? (vba does not)
88
W8(HALTCNT, 0xFF); // normal mode (internal)
89
W16(IE, 0x0000);
90
W16(IF, 0x0000);
91
W16(IME, 0x0000);
92
Write16(WAITCNT, 0x0000);
93
W16(BG2PA, 0x0100);
94
W16(BG2PD, 0x0100);
95
W16(BG3PA, 0x0100);
96
W16(BG3PD, 0x0100);
97
}
98
99
// TODO implement unreadable or write-only io
100
uint8_t Io::Read8 (uint32_t add)
101
{
102
if ((add & 0xFFE) == KEYINPUT)
103
keyupdate_bizhawk();
104
//debug ("IO Read8 at " << IOS_ADD << add << " of " << IOS_ADD << (int)*(uint8_t*)(m_iomem + (add & 0xFFF)));
105
if ((add & 0xFF0) == 0x100)
106
switch (add & 0xF)
107
{
108
case 0x0:
109
case 0x4:
110
case 0x8:
111
case 0xC:
112
met_abort("Misaligned reading of timers");
113
}
114
return m_iomem[add & 0xFFF];
115
}
116
117
uint16_t Io::Read16 (uint32_t add)
118
{
119
if ((add & 0xFFE) == KEYINPUT)
120
keyupdate_bizhawk();
121
//debug ("IO Read16 at " << IOS_ADD << add << " of " << IOS_ADD << *(uint16_t*)(m_iomem + (add & 0xFFF)));
122
// special case, reading timers
123
if ((add & 0xFF0) == 0x100)
124
switch (add & 0xF)
125
{
126
case 0x0: return TIMER0.GetCount();
127
case 0x4: return TIMER1.GetCount();
128
case 0x8: return TIMER2.GetCount();
129
case 0xC: return TIMER3.GetCount();
130
}
131
return *(uint16_t*)(m_iomem + (add & 0xFFF));
132
}
133
134
uint32_t Io::Read32 (uint32_t add)
135
{
136
if ((add & 0xFFC) == KEYINPUT)
137
keyupdate_bizhawk();
138
//debug ("IO Read32 at " << IOS_ADD << add << " of " << IOS_ADD << *(uint32_t*)(m_iomem + (add & 0xFFF)));
139
// special case, reading timers
140
if ((add & 0xFF0) == 0x100)
141
switch (add & 0xF)
142
{
143
case 0x0: return TIMER0.GetCount();
144
case 0x4: return TIMER1.GetCount();
145
case 0x8: return TIMER2.GetCount();
146
case 0xC: return TIMER3.GetCount();
147
}
148
return *(uint32_t*)(m_iomem + (add & 0xFFF));
149
}
150
151
void Io::Write8 (uint32_t add, uint8_t val)
152
{
153
//debug ("IO Write8 at " << IOS_ADD << add << " of " << IOS_ADD << (int)val);
154
switch (add & 0xFFF)
155
{
156
case NR10+1:
157
case NR52+1:
158
case NR52+2:
159
case NR52+3:
160
break;
161
case NR10:
162
case NR11:
163
case NR13:
164
case NR21:
165
case NR23:
166
case NR41:
167
case NR43:
168
case NR50:
169
case NR51:
170
case SOUNDCNT_H:
171
W8(add, val);
172
break;
173
case NR12:
174
W8(add, val);
175
if (!(val & (0xF << 4)))
176
SOUND.ResetSound1Envelope();
177
break;
178
case NR14:
179
W8(add, val & 0xC7);
180
if (val & (0x1 << 7))
181
SOUND.ResetSound1();
182
break;
183
case NR22:
184
W8(add, val);
185
if (!(val & (0xF << 4)))
186
SOUND.ResetSound2Envelope();
187
break;
188
case NR24:
189
W8(add, val & 0xC7);
190
if (val & (0x1 << 7))
191
SOUND.ResetSound2();
192
break;
193
case NR42:
194
W8(add, val);
195
if (!(val & (0xF << 4)))
196
SOUND.ResetSound4Envelope();
197
break;
198
case NR44:
199
W8(add, val & 0xC7);
200
if (val & (0x1 << 7))
201
SOUND.ResetSound4();
202
break;
203
case SOUNDCNT_H+1:
204
W8(add, val);
205
SOUND.UpdateCntH1(val);
206
break;
207
case NR52:
208
// this will also reset the sound on flags
209
W8(add, val & 0x80);
210
break;
211
case POSTFLG:
212
// FIXME is this right, i have no idea about why i should do that
213
if (val)
214
val &= 0xFE;
215
W8(add, val);
216
break;
217
case HALTCNT:
218
W8(add, val);
219
break;
220
default:
221
//W8(add, val);
222
// TODO make a function which will apply masks to IO memory and trigger
223
// the update functions, this function will be called by write8 and
224
// write16
225
#if 1
226
add &= 0xFFF;
227
if (add % 2)
228
Write16(add & ~0x1, (val << 8) | m_iomem[add & ~0x1]);
229
else
230
Write16(add, (m_iomem[add | 0x1] << 8) | val);
231
#endif
232
break;
233
}
234
}
235
236
void Io::Write16 (uint32_t add, uint16_t val)
237
{
238
//debug ("IO Write16 at " << IOS_ADD << add << " of " << IOS_ADD << val);
239
//*(uint16_t*)(m_iomem + (add & 0xFFF)) = val;
240
241
switch (add & 0xFFF)
242
{
243
case KEYINPUT:
244
case VCOUNT:
245
break;
246
case DMA0CNT_L:
247
case DMA1CNT_L:
248
case DMA2CNT_L:
249
case DMA3CNT_L:
250
//W16(add, val);
251
DMA.SetReload(((add & 0xFFF) - DMA0CNT_L) / DMA_CHANSIZE, val);
252
break;
253
case KEYCNT:
254
W16(add, val & 0xC3FF);
255
break;
256
case IME:
257
W16(add, val & 0x0001);
258
CPU.CheckInterrupt();
259
break;
260
case IE:
261
W16(add, val & 0x3FFF);
262
CPU.CheckInterrupt();
263
break;
264
case IF:
265
*((uint16_t*)(m_iomem+IF)) ^= (val & (*((uint16_t*)(m_iomem+IF))));
266
CPU.CheckInterrupt();
267
break;
268
case BG0CNT:
269
W16(add, val & 0xFFCF);
270
LCD.UpdateBg0Cnt(val & 0xFFCF);
271
break;
272
case BG1CNT:
273
W16(add, val & 0xFFCF);
274
LCD.UpdateBg1Cnt(val & 0xFFCF);
275
break;
276
case BG2CNT:
277
W16(add, val & 0xFFCF);
278
LCD.UpdateBg2Cnt(val & 0xFFCF);
279
break;
280
case BG3CNT:
281
W16(add, val & 0xFFCF);
282
LCD.UpdateBg3Cnt(val & 0xFFCF);
283
break;
284
case DISPSTAT:
285
// the first 3 bits are read only and they are used by the lcd
286
// NOTE : we are in LITTLE ENDIAN
287
// FIXME : if vcount setting has changed to current vcount, we should
288
// update the vcounter flag and eventually trigger an interrupt
289
W16(add, (val & 0xFFF8) | (m_iomem[add & 0xFFF] & 0x07));
290
break;
291
// The BG*OFS are write-only, we don't need to W16()
292
// You do if you're ever going to load them from a savestate...
293
case BG0HOFS:
294
W16(add, val & 0x1FF);
295
LCD.UpdateBg0XOff(val & 0x1FF);
296
break;
297
case BG0VOFS:
298
W16(add, val & 0x1FF);
299
LCD.UpdateBg0YOff(val & 0x1FF);
300
break;
301
case BG1HOFS:
302
W16(add, val & 0x1FF);
303
LCD.UpdateBg1XOff(val & 0x1FF);
304
break;
305
case BG1VOFS:
306
W16(add, val & 0x1FF);
307
LCD.UpdateBg1YOff(val & 0x1FF);
308
break;
309
case BG2HOFS:
310
W16(add, val & 0x1FF);
311
LCD.UpdateBg2XOff(val & 0x1FF);
312
break;
313
case BG2VOFS:
314
W16(add, val & 0x1FF);
315
LCD.UpdateBg2YOff(val & 0x1FF);
316
break;
317
case BG3HOFS:
318
W16(add, val & 0x1FF);
319
LCD.UpdateBg3XOff(val & 0x1FF);
320
break;
321
case BG3VOFS:
322
W16(add, val & 0x1FF);
323
LCD.UpdateBg3YOff(val & 0x1FF);
324
break;
325
case BG2X_H:
326
val &= 0x0FFF;
327
case BG2X_L:
328
W16(add, val);
329
LCD.UpdateBg2RefX(IO.DRead32(Io::BG2X_L));
330
break;
331
case BG2Y_H:
332
val &= 0x0FFF;
333
case BG2Y_L:
334
W16(add, val);
335
LCD.UpdateBg2RefY(IO.DRead32(Io::BG2Y_L));
336
break;
337
case BG3X_H:
338
val &= 0x0FFF;
339
case BG3X_L:
340
W16(add, val);
341
LCD.UpdateBg3RefX(IO.DRead32(Io::BG3X_L));
342
break;
343
case BG3Y_H:
344
val &= 0x0FFF;
345
case BG3Y_L:
346
W16(add, val);
347
LCD.UpdateBg3RefY(IO.DRead32(Io::BG3Y_L));
348
break;
349
case WIN0H:
350
case WIN1H:
351
case WIN0V:
352
case WIN1V:
353
case WININ:
354
case WINOUT:
355
W16(add, val);
356
break;
357
case BLDCNT:
358
W16(add, val);
359
break;
360
case MOSAIC:
361
W16(add, val);
362
break;
363
case DISPCNT:
364
W16(add, val);
365
LCD.UpdateDispCnt(val);
366
break;
367
case DMA0CNT_H:
368
case DMA1CNT_H:
369
case DMA2CNT_H:
370
case DMA3CNT_H:
371
W16(add, val & 0xFFE0);
372
DMA.UpdateCnt(((add & 0xFFF) - DMA0CNT_H) / DMA_CHANSIZE);
373
break;
374
case WAITCNT:
375
W16(add, val & 0xDFFF);
376
MEM.UpdateWaitStates (val & 0xDFFF);
377
break;
378
case SOUND1CNT_L:
379
case SOUND1CNT_H:
380
case SOUND1CNT_X:
381
case SOUND2CNT_L:
382
case SOUND2CNT_H:
383
case SOUND4CNT_L:
384
case SOUND4CNT_H:
385
case SOUNDCNT_L:
386
case SOUNDCNT_H:
387
case SOUNDCNT_X:
388
case POSTFLG:
389
Write8(add, val & 0xFF);
390
Write8(add + 1, val >> 8);
391
break;
392
case TM0CNT_L:
393
TIMER0.SetReload(val);
394
break;
395
case TM1CNT_L:
396
TIMER1.SetReload(val);
397
break;
398
case TM2CNT_L:
399
TIMER2.SetReload(val);
400
break;
401
case TM3CNT_L:
402
TIMER3.SetReload(val);
403
break;
404
case TM0CNT_H:
405
W16(add, val & 0x00C7);
406
TIMER0.Reload();
407
break;
408
case TM1CNT_H:
409
W16(add, val & 0x00C7);
410
TIMER1.Reload();
411
break;
412
case TM2CNT_H:
413
W16(add, val & 0x00C7);
414
TIMER2.Reload();
415
break;
416
case TM3CNT_H:
417
W16(add, val & 0x00C7);
418
TIMER3.Reload();
419
break;
420
default:
421
//met_abort("Unknown IO at " << IOS_ADD << add);
422
W16(add, val);
423
break;
424
}
425
}
426
427
void Io::Write32 (uint32_t add, uint32_t val)
428
{
429
//debug ("IO Write32 at " << IOS_ADD << add << " of " << IOS_ADD << val);
430
switch (add & 0xFF)
431
{
432
case DMA1DAD:
433
case DMA0SAD:
434
case DMA1SAD:
435
case DMA2SAD:
436
case DMA3SAD:
437
case DMA0DAD:
438
case DMA2DAD:
439
case DMA3DAD:
440
W32(add, val);
441
break;
442
case BG0HOFS:
443
case BG1HOFS:
444
case BG2HOFS:
445
case BG3HOFS:
446
Write16(add, val & 0xFFFF);
447
Write16(add+2, val >> 16);
448
break;
449
case BG2X_L:
450
W32(add, val & 0x0FFFFFFF);
451
LCD.UpdateBg2RefX(IO.DRead32(Io::BG2X_L));
452
break;
453
case BG2Y_L:
454
W32(add, val & 0x0FFFFFFF);
455
LCD.UpdateBg2RefY(IO.DRead32(Io::BG2Y_L));
456
break;
457
case BG3X_L:
458
W32(add, val & 0x0FFFFFFF);
459
LCD.UpdateBg3RefX(IO.DRead32(Io::BG3X_L));
460
break;
461
case BG3Y_L:
462
W32(add, val & 0x0FFFFFFF);
463
LCD.UpdateBg3RefY(IO.DRead32(Io::BG3Y_L));
464
break;
465
case BG2PA:
466
case BG2PC:
467
case BG3PA:
468
case BG3PC:
469
case WIN0H:
470
case WIN0V:
471
case WININ:
472
Write16(add, val & 0xFFFF);
473
Write16(add+2, val >> 16);
474
break;
475
case DMA0CNT_L:
476
case DMA1CNT_L:
477
case DMA2CNT_L:
478
case DMA3CNT_L:
479
Write16(add, val & 0xFFFF);
480
Write16(add+2, val >> 16);
481
break;
482
case FIFO_A:
483
case FIFO_B:
484
// TODO
485
break;
486
default:
487
//met_abort("Unknown IO at " << IOS_ADD << add);
488
//*(uint32_t*)(m_iomem + (add & 0xFFF)) = val;
489
Write16(add, val & 0xFFFF);
490
Write16(add+2, val >> 16);
491
break;
492
}
493
}
494
495
bool Io::SaveState (std::ostream& stream)
496
{
497
SS_WRITE_DATA(m_iomem, IO_SIZE);
498
499
return true;
500
}
501
502
bool Io::LoadState (std::istream& stream)
503
{
504
SS_READ_DATA(m_iomem, IO_SIZE);
505
506
return true;
507
}
508
}
509
510