-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathiomem.c
More file actions
443 lines (392 loc) · 14.3 KB
/
iomem.c
File metadata and controls
443 lines (392 loc) · 14.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
/*
* iomem.c
*
* P/EMU - P/ECE Emulator
* Copyright (C) 2003 Naoyuki Sawa
*
* * Mon Apr 14 00:00:00 JST 2003 Naoyuki Sawa
* - 作成開始。
* * Wed Apr 22 00:00:00 JST 2003 Naoyuki Sawa
* - サウンド対応。(途中)
* * Wed Apr 22 05:37:00 JST 2003 Naoyuki Sawa
* - muslibのノイズ回避。(サウンド割り込みのレベルを下げた)
*/
#include "app.h"
#include <math.h>
/****************************************************************************
* グローバル変数
****************************************************************************/
//IOMEM iomem;
/****************************************************************************
* I/Oマップ定義
****************************************************************************/
//#define IOMAP_TBL
//#include "iomap.h"
//#undef IOMAP_TBL
// private
DECLSPEC void SDLCALL SDLAudioCallback(void* userdata, Uint8* stream, int len);
/****************************************************************************
* グローバル関数
****************************************************************************/
void iomem_init(PIEMU_CONTEXT* context)
{
int i;
IOMAP* p;
IOMAP iomap;
WAVEBUFFER* buffer;
memset(&context->iomem, 0, sizeof context->iomem);
context->iomem.iomap_tbl = (IOMAP*)calloc(IOMAP_SIZE, sizeof(IOMAP));
p = context->iomem.iomap_tbl;
/* PAD */
// IOR_(pK5_K5D)
iomap.addr = PIOR_(pK5_K5D);
iomap.read = IOR_(pK5_K5D);
iomap.write = NULL;
*p++ = iomap;
// IOR_(pK6_K6D)
iomap.addr = PIOR_(pK6_K6D);
iomap.read = IOR_(pK6_K6D);
iomap.write = NULL;
*p++ = iomap;
/* LCDC */
// IO_W(pSIF3_TXD)
iomap.addr = PIO_W(pSIF3_TXD);
iomap.read = NULL;
iomap.write = IO_W(pSIF3_TXD);
*p++ = iomap;
/* SOUND */
// IO_W(pHS1_EN)
iomap.addr = PIO_W(pHS1_EN);
iomap.read = NULL;
iomap.write = IO_W(pHS1_EN);
*p++ = iomap;
/* LCDC */
bSIF3_STATUS_TDBE3 = 1; /* 常に転送データバッファエンプティ */
/* SOUND */
SDL_AudioSpec* desired = &context->iomem.desired;
SDL_memset(desired, 0, sizeof(SDL_AudioSpec));
desired->freq = 32000;
desired->channels = 1;
desired->format = AUDIO_S16LSB;
desired->samples = WAVEBUFFER_SAMPLES; // 変えちゃだめ
desired->userdata = (void*)context;
desired->callback = SDLAudioCallback;
for (i = 0, buffer = context->iomem.buffer; i < BLKN; i++, buffer++) {
buffer->dwBufferLength = desired->samples * sizeof(short);
buffer->pData = malloc(buffer->dwBufferLength);
buffer->dwBytesRecorded = 0;
buffer->nReady = WAVEBUFFER_NOT_READY;
buffer->next = NULL;
}
context->iomem.head = context->iomem.tail = NULL;
context->iomem.nQueuedBuffers = 0;
SDL_AudioSpec* obtained = &context->iomem.obtained;
context->audio_device = SDL_OpenAudioDevice(NULL, 0, desired, obtained, 0);
if (context->audio_device <= 0) {
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Cannot open Audio: %s", SDL_GetError());
}
SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "Opened fs %d, ch %d, fmt %d, samples %d",
obtained->freq, obtained->channels, obtained->format, obtained->samples);
context->iomem.cond_queued = SDL_CreateCond();
context->iomem.cond_avail = SDL_CreateCond();
}
static IOMAP*
iomap_sel(PIEMU_CONTEXT* context, unsigned ofs)
{
IOMAP* iomap;
/* 呼び出し側で「ofs &= IOMEM_SIZE - 1;」しておくこと。 */
for (iomap = context->iomem.iomap_tbl; iomap->addr; iomap++) {
if (ofs == IOOFS(iomap->addr))
return iomap;
}
return NULL;
}
int iomem_read(PIEMU_CONTEXT* context, unsigned ofs, int size)
{
IOMAP* iomap;
ofs &= IOMEM_SIZE - 1; /* 必要 */
/* ハンドラ検索。 */
iomap = iomap_sel(context, ofs);
if (iomap && iomap->read) {
return iomap->read(context, ofs, size);
}
/* ハンドラがなければ、iomem.mem[]から単純に読み出す。 */
// dbg("未対応I/O読み出し: %07x", ofs);
return iomem_read_default(context, ofs, size);
}
/* iomem.mem[]からの読み出し。各I/Oハンドラからも利用可能です。 */
int iomem_read_default(PIEMU_CONTEXT* context, unsigned ofs, int size)
{
if (ofs & ~(IOMEM_SIZE - 1))
DIE();
switch (size) {
case 1:
return READ_MEM_B(context->iomem.mem + ofs);
case 2:
return READ_MEM_H(context->iomem.mem + ofs);
case 4:
return READ_MEM_W(context->iomem.mem + ofs);
}
DIE();
return -1; /* 警告抑制 */
}
void iomem_write(PIEMU_CONTEXT* context, unsigned ofs, int data, int size)
{
IOMAP* iomap;
ofs &= IOMEM_SIZE - 1; /* 必要 */
/* ハンドラ検索。 */
iomap = iomap_sel(context, ofs);
if (iomap && iomap->write) {
iomap->write(context, ofs, data, size);
return;
}
/* ハンドラがなければ、iomem.mem[]に単純に書き込む。 */
// dbg("未対応I/O書き込み: %07x", ofs);
iomem_write_default(context, ofs, data, size);
}
/* iomem.mem[]への書き込み。各I/Oハンドラからも利用可能です。 */
void iomem_write_default(PIEMU_CONTEXT* context, unsigned ofs, int data, int size)
{
if (ofs & ~(IOMEM_SIZE - 1))
DIE();
switch (size) {
case 1:
*(char*)&context->iomem.mem[ofs] = data;
return;
case 2:
*(short*)&context->iomem.mem[ofs] = data;
return;
case 4:
*(int*)&context->iomem.mem[ofs] = data;
return;
}
DIE();
}
/****************************************************************************
* I/Oハンドラ
****************************************************************************/
int IOR_(pK5_K5D)(struct tagPIEMU_CONTEXT* context, unsigned ofs, int size)
{
int data = -1;
if (context->keystate[KEY_SELECT])
data &= ~(1 << 3); /* SELECT */
if (context->keystate[KEY_START])
data &= ~(1 << 4); /* START */
return data;
}
int IOR_(pK6_K6D)(struct tagPIEMU_CONTEXT* context, unsigned ofs, int size)
{
int data = -1;
if (context->keystate[KEY_RIGHT])
data &= ~(1 << 0);
if (context->keystate[KEY_LEFT])
data &= ~(1 << 1);
if (context->keystate[KEY_DOWN])
data &= ~(1 << 2);
if (context->keystate[KEY_UP])
data &= ~(1 << 3);
if (context->keystate[KEY_B])
data &= ~(1 << 4); /* B */
if (context->keystate[KEY_A])
data &= ~(1 << 5); /* A */
return data;
}
/*
1f5 pSIF3_TXD
1f8 pSIF3_CTL default
2d9 pP2_P2D default
8220 pHS0_CNT default
8224 pHS0_SADR default
8228 pHS0_DADR default
822c pHS0_EN default
822e pHS0_TF default
*/
void IO_W(pSIF3_TXD)(struct tagPIEMU_CONTEXT* context, unsigned ofs, int data, int size)
{
for (;;) {
lcdc_write(context, (unsigned char)data); /* シリアルCh.3データ→LCDCへ */
if (!bHS0_EN_HS0EN)
break; /* HSDMA Ch.0停止なら抜ける */
data = mem_read_nowait(context, bHS0_SADR_S0ADR, 1); /* 次のデータを取得 */
bHS0_SADR_S0ADR++; /* 転送元アドレスを進める */
if (!--bHS0_CNT_SIG_TC0)
bHS0_EN_HS0EN = 0; /* 転送カウンタを減らし、0になったらDMA停止 */
}
}
void IO_W(pHS1_EN)(struct tagPIEMU_CONTEXT* context, unsigned ofs, int data, int size)
{
int en_save;
/* HSDMA1イネーブルビットの書き込み。 */
en_save = bHS1_EN_HS1EN;
iomem_write_default(context, ofs, data, size);
if (bHS1_EN_HS1EN == en_save)
return; /* DISABLE→DISABLE or ENABLE→ENABLE */
if (!bHS1_EN_HS1EN) /* ENABLE→DISABLE */
// 発音停止→バッファを破棄
SDL_AtomicSet(&context->iomem.hsdma1_en, HSDMA1_EDGE_DISABLED);
else
/* 以下、DISABLE→ENABLE */
SDL_AtomicSet(&context->iomem.hsdma1_en, HSDMA1_EDGE_ENABLED);
}
// オーディオコールバックが別スレッドから呼ばれるのは
// core module にとってきりのいいタイミングでないと困る
void iomem_work(PIEMU_CONTEXT* context)
{
int edge = SDL_AtomicSet(&context->iomem.hsdma1_en, HSDMA1_EDGE_NONE);
switch (edge) {
case HSDMA1_EDGE_DISABLED: {
// ENABLE -> DISABLE, stop audio
SDL_PauseAudioDevice(context->audio_device, 1);
SDL_LockAudioDevice(context->audio_device);
{
int i;
WAVEBUFFER* buffer;
// then free queued buffers
for (i = 0, buffer = context->iomem.buffer; i < BLKN; i++, buffer++)
buffer->nReady = WAVEBUFFER_NOT_READY;
context->iomem.head = context->iomem.tail = NULL;
context->iomem.nQueuedBuffers = 0;
}
SDL_UnlockAudioDevice(context->audio_device);
break;
}
case HSDMA1_EDGE_ENABLED: {
// DISABLE -> ENABLE
WAVEBUFFER* buffer;
SDL_LockAudioDevice(context->audio_device);
{
int i;
/* 空きバッファを探します。 */
for (i = 0, buffer = context->iomem.buffer; i < BLKN; i++, buffer++) {
if (buffer->nReady == WAVEBUFFER_DONE) {
buffer->nReady = WAVEBUFFER_NOT_READY;
context->iomem.nQueuedBuffers--;
break;
}
if (buffer->nReady == WAVEBUFFER_NOT_READY)
break;
}
if (i == BLKN)
DIE("sound: no free buffers available to receive DMA!"); // no buffers available
}
// SDL_UnlockAudioDevice(context->audio_device);
/* 必要に応じてバッファメモリを拡張。 */
uint32_t buflen = bHS1_CNT_SIG_TC1 * sizeof(short);
if (buffer->dwBufferLength < (unsigned)buflen) {
buffer->pData = realloc(buffer->pData, buflen);
if (!buffer->pData)
DIE();
buffer->dwBufferLength = buflen;
}
buffer->dwBytesRecorded = buflen;
/* ウェーブデータ変換。 */
int16_t* p = (int16_t*)buffer->pData;
do {
int v;
v = (short)mem_read_nowait(context, bHS1_SADR_S1ADR, 2); /* 0〜750〜 1500 */
v -= 750; /* -750〜 0〜 750 */
v <<= 16;
v /= 750; /* -32767〜 0〜32767 */
if (v > 32767)
v = 32767;
if (v < -32768)
v = -32768;
*p++ = (int16_t)v;
bHS1_SADR_S1ADR += 2;
} while (--bHS1_CNT_SIG_TC1);
// SDL_LockAudioDevice(context->audio_device);
{
buffer->nReady = WAVEBUFFER_READY;
buffer->next = NULL; // こいつが末尾のノード
if (context->iomem.head) // 先客がいる
context->iomem.tail->next = buffer; // 現在の末尾の次に割り当てる
else
context->iomem.head = buffer; // 先頭に割り当てる
context->iomem.tail = buffer; // 末尾を更新する
context->iomem.nQueuedBuffers++;
}
SDL_UnlockAudioDevice(context->audio_device);
if (context->iomem.nQueuedBuffers > WAVEBUFFER_MIN_FILLED_BUFFERS)
SDL_PauseAudioDevice(context->audio_device, 0);
break;
}
default: {
SDL_LockAudioDevice(context->audio_device);
{
int i;
WAVEBUFFER* buffer;
for (i = 0, buffer = context->iomem.buffer; i < BLKN; i++, buffer++) {
// 使用済みバッファを片付ける
if (buffer->nReady == WAVEBUFFER_DONE) {
buffer->nReady = WAVEBUFFER_NOT_READY;
context->iomem.nQueuedBuffers--;
}
/* HSDMA1動作中で、空きバッファがあれば、HSDMA1転送完了と見なして割り込み要因発生。 */
if (bHS1_EN_HS1EN && buffer->nReady == WAVEBUFFER_NOT_READY) {
bHS1_EN_HS1EN = 0;
bINT_FDMA_FHDM1 = 1;
}
}
}
SDL_UnlockAudioDevice(context->audio_device);
}
}
/* HSDMA1割り込みが許可されていて、割り込み要因が発生していたら、割り込み発行。 */
if (bINT_EDMA_EHDM1 && bINT_FDMA_FHDM1) {
//core_trap(context, TRAP_HDM1, bINT_PHSD01L_PHSD1L);
//↑本当はこうですが...
// P/ECEカーネルのサウンドドライバは多重割り込みを許可しているのですが、
// サウンド処理中(IL=5で多重割り込み受付)にサウンドの割り込み(IL=7なので割り込み可能)が発生すると、
// ノイズになるようです。
// 通常のWave再生なら軽いので大丈夫みたいですけれど、muslibによるBGM再生でノイズが乗りまくります。
//↓muslibのノイズを防ぐために、超法規的にサウンドの割り込みレベルを下げてみました。
// 本物のカーネルでも、サウンド処理中のサウンド割り込み受け付けはなくした方がよさそうな気が…(^^;
core_trap_from_devices(context, TRAP_HDM1, 1 /*5以下ならなんでもいいけど、とりあえず割り込み可能な最低レベルにしてみた*/);
}
}
DECLSPEC void SDLCALL SDLAudioCallback(void* userdata, Uint8* stream, int len)
{
int nBytesToRender = len;
PIEMU_CONTEXT* context = (PIEMU_CONTEXT*)userdata;
WAVEBUFFER* node;
Uint8* p = stream;
memset(stream, 0, len);
node = context->iomem.head;
while (node && nBytesToRender) {
if (node->nReady == WAVEBUFFER_READY) {
SDL_memcpy(p, node->pData, node->dwBytesRecorded);
p += node->dwBytesRecorded;
nBytesToRender -= node->dwBytesRecorded;
node->nReady = WAVEBUFFER_DONE;
}
node = node->next;
context->iomem.head = node;
}
if (nBytesToRender > 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_AUDIO, "Buffer underrun, expect noises");
}
}
IOHANDLER_R(iomem, B)
{
return iomem_read(context, ofs, 1);
}
IOHANDLER_W(iomem, B)
{
iomem_write(context, ofs, data, 1);
}
IOHANDLER_R(iomem, H)
{
return iomem_read(context, ofs, 2);
}
IOHANDLER_W(iomem, H)
{
iomem_write(context, ofs, data, 2);
}
IOHANDLER_R(iomem, W)
{
return iomem_read(context, ofs, 4);
}
IOHANDLER_W(iomem, W)
{
iomem_write(context, ofs, data, 4);
}