src/platform/wii/main.c (view raw)
1/* Copyright (c) 2013-2015 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#define asm __asm__
7
8#include <fat.h>
9#include <gccore.h>
10#include <malloc.h>
11
12#include "util/common.h"
13
14#include "gba/gba.h"
15#include "gba/renderers/video-software.h"
16#include "gba/serialize.h"
17#include "gba/supervisor/overrides.h"
18#include "gba/video.h"
19#include "util/vfs.h"
20
21#define SAMPLES 1024
22
23static void GBAWiiLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args);
24static void GBAWiiFrame(void);
25static bool GBAWiiLoadGame(const char* path);
26
27static void _postVideoFrame(struct GBAAVStream*, struct GBAVideoRenderer* renderer);
28static void _audioDMA(void);
29
30static struct GBA gba;
31static struct ARMCore cpu;
32static struct GBAVideoSoftwareRenderer renderer;
33static struct VFile* rom;
34static struct VFile* save;
35static struct GBAAVStream stream;
36static FILE* logfile;
37static GXRModeObj* mode;
38static Mtx model, view, modelview;
39static uint16_t* texmem;
40static GXTexObj tex;
41
42static void* framebuffer[2];
43static int whichFb = 0;
44
45static struct GBAStereoSample audioBuffer[2][SAMPLES] __attribute__ ((__aligned__(32)));
46static size_t audioBufferSize = 0;
47static int currentAudioBuffer = 0;
48
49int main() {
50 VIDEO_Init();
51 PAD_Init();
52 AUDIO_Init(0);
53 AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
54 AUDIO_RegisterDMACallback(_audioDMA);
55
56 memset(audioBuffer, 0, sizeof(audioBuffer));
57
58#if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5)
59#error This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
60#endif
61
62 mode = VIDEO_GetPreferredMode(0);
63 framebuffer[0] = SYS_AllocateFramebuffer(mode);
64 framebuffer[1] = SYS_AllocateFramebuffer(mode);
65
66 VIDEO_Configure(mode);
67 VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
68 VIDEO_SetBlack(FALSE);
69 VIDEO_Flush();
70 VIDEO_WaitVSync();
71 if (mode->viTVMode & VI_NON_INTERLACE) {
72 VIDEO_WaitVSync();
73 }
74
75 GXColor bg = { 0, 0, 0, 0xFF };
76 void* fifo = memalign(32, 0x40000);
77 memset(fifo, 0, 0x40000);
78 GX_Init(fifo, 0x40000);
79 GX_SetCopyClear(bg, 0x00FFFFFF);
80 GX_SetViewport(0, 0, mode->fbWidth, mode->efbHeight, 0, 1);
81
82 f32 yscale = GX_GetYScaleFactor(mode->efbHeight, mode->xfbHeight);
83 u32 xfbHeight = GX_SetDispCopyYScale(yscale);
84 GX_SetScissor(0, 0, mode->viWidth, mode->viWidth);
85 GX_SetDispCopySrc(0, 0, mode->fbWidth, mode->efbHeight);
86 GX_SetDispCopyDst(mode->fbWidth, xfbHeight);
87 GX_SetCopyFilter(mode->aa, mode->sample_pattern, GX_TRUE, mode->vfilter);
88 GX_SetFieldMode(mode->field_rendering, ((mode->viHeight == 2 * mode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
89
90 GX_SetCullMode(GX_CULL_NONE);
91 GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
92 GX_SetDispCopyGamma(GX_GM_1_0);
93
94 GX_ClearVtxDesc();
95 GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
96 GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
97
98 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
99 GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0);
100
101 GX_SetNumChans(1);
102 GX_SetNumTexGens(1);
103 GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
104 GX_SetTevOp(GX_TEVSTAGE0, GX_REPLACE);
105
106 GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
107 GX_InvVtxCache();
108 GX_InvalidateTexAll();
109
110 Mtx44 proj;
111 guOrtho(proj, 0, VIDEO_VERTICAL_PIXELS, 0, VIDEO_HORIZONTAL_PIXELS, 0, 300);
112 GX_LoadProjectionMtx(proj, GX_ORTHOGRAPHIC);
113
114 guVector cam = { 0.0f, 0.0f, 0.0f };
115 guVector up = { 0.0f, 1.0f, 0.0f };
116 guVector look = { 0.0f, 0.0f, -1.0f };
117 guLookAt(view, &cam, &up, &look);
118
119 guMtxIdentity(model);
120 guMtxTransApply(model, model, 0.0f, 0.0f, -6.0f);
121 guMtxConcat(view, model, modelview);
122 GX_LoadPosMtxImm(modelview, GX_PNMTX0);
123
124 texmem = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
125 memset(texmem, 0, 256 * 256 * BYTES_PER_PIXEL);
126 GX_InitTexObj(&tex, texmem, 256, 256, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE);
127
128 fatInitDefault();
129
130 logfile = fopen("/mgba.log", "w");
131
132 stream.postAudioFrame = 0;
133 stream.postAudioBuffer = 0;
134 stream.postVideoFrame = _postVideoFrame;
135
136 GBACreate(&gba);
137 ARMSetComponents(&cpu, &gba.d, 0, 0);
138 ARMInit(&cpu);
139 gba.logLevel = 0; // TODO: Settings
140 gba.logHandler = GBAWiiLog;
141 gba.stream = &stream;
142 gba.idleOptimization = IDLE_LOOP_REMOVE; // TODO: Settings
143 rom = 0;
144
145 GBAVideoSoftwareRendererCreate(&renderer);
146 renderer.outputBuffer = memalign(32, 256 * 256 * BYTES_PER_PIXEL);
147 renderer.outputBufferStride = 256;
148 GBAVideoAssociateRenderer(&gba.video, &renderer.d);
149
150 GBAAudioResizeBuffer(&gba.audio, SAMPLES);
151
152#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
153 blip_set_rates(gba.audio.left, GBA_ARM7TDMI_FREQUENCY, 48000);
154 blip_set_rates(gba.audio.right, GBA_ARM7TDMI_FREQUENCY, 48000);
155#endif
156
157 if (!GBAWiiLoadGame("/rom.gba")) {
158 return 1;
159 }
160
161 while (true) {
162#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
163 int available = blip_samples_avail(gba.audio.left);
164 if (available + audioBufferSize > SAMPLES) {
165 available = SAMPLES - audioBufferSize;
166 }
167 if (available > 0) {
168 blip_read_samples(gba.audio.left, &audioBuffer[currentAudioBuffer][audioBufferSize].left, available, true);
169 blip_read_samples(gba.audio.right, &audioBuffer[currentAudioBuffer][audioBufferSize].right, available, true);
170 audioBufferSize += available;
171 }
172 if (audioBufferSize == SAMPLES && !AUDIO_GetDMAEnableFlag()) {
173 _audioDMA();
174 AUDIO_StartDMA();
175 }
176#endif
177 PAD_ScanPads();
178 u16 padkeys = PAD_ButtonsHeld(0);
179 int keys = 0;
180 gba.keySource = &keys;
181 if (padkeys & PAD_BUTTON_A) {
182 keys |= 1 << GBA_KEY_A;
183 }
184 if (padkeys & PAD_BUTTON_B) {
185 keys |= 1 << GBA_KEY_B;
186 }
187 if (padkeys & PAD_TRIGGER_L) {
188 keys |= 1 << GBA_KEY_L;
189 }
190 if (padkeys & PAD_TRIGGER_R) {
191 keys |= 1 << GBA_KEY_R;
192 }
193 if (padkeys & PAD_BUTTON_START) {
194 keys |= 1 << GBA_KEY_START;
195 }
196 if (padkeys & (PAD_BUTTON_X | PAD_BUTTON_Y)) {
197 keys |= 1 << GBA_KEY_SELECT;
198 }
199 if (padkeys & PAD_BUTTON_LEFT) {
200 keys |= 1 << GBA_KEY_LEFT;
201 }
202 if (padkeys & PAD_BUTTON_RIGHT) {
203 keys |= 1 << GBA_KEY_RIGHT;
204 }
205 if (padkeys & PAD_BUTTON_UP) {
206 keys |= 1 << GBA_KEY_UP;
207 }
208 if (padkeys & PAD_BUTTON_DOWN) {
209 keys |= 1 << GBA_KEY_DOWN;
210 }
211 if (padkeys & PAD_TRIGGER_Z) {
212 break;
213 }
214 int frameCount = gba.video.frameCounter;
215 while (gba.video.frameCounter == frameCount) {
216 ARMRunLoop(&cpu);
217 }
218 }
219
220 fclose(logfile);
221 free(fifo);
222
223 rom->close(rom);
224 save->close(save);
225
226 return 0;
227}
228
229static void GBAWiiFrame(void) {
230 size_t x, y;
231 uint64_t* texdest = (uint64_t*) texmem;
232 uint64_t* texsrc = (uint64_t*) renderer.outputBuffer;
233 for (y = 0; y < VIDEO_VERTICAL_PIXELS; y += 4) {
234 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS >> 2; ++x) {
235 texdest[0 + x * 4 + y * 64] = texsrc[0 + x + y * 64];
236 texdest[1 + x * 4 + y * 64] = texsrc[64 + x + y * 64];
237 texdest[2 + x * 4 + y * 64] = texsrc[128 + x + y * 64];
238 texdest[3 + x * 4 + y * 64] = texsrc[192 + x + y * 64];
239 }
240 }
241 DCFlushRange(texdest, 256 * 256 * BYTES_PER_PIXEL);
242
243 GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
244 GX_SetColorUpdate(GX_TRUE);
245
246 GX_SetViewport(0, 0, mode->fbWidth, mode->efbHeight, 0, 1);
247 GX_InvalidateTexAll();
248 GX_LoadTexObj(&tex, GX_TEXMAP0);
249
250 GX_Begin(GX_QUADS, GX_VTXFMT0, 4);
251 GX_Position2s16(0, 256);
252 GX_TexCoord2s16(0, 1);
253
254 GX_Position2s16(256, 256);
255 GX_TexCoord2s16(1, 1);
256
257 GX_Position2s16(256, 0);
258 GX_TexCoord2s16(1, 0);
259
260 GX_Position2s16(0, 0);
261 GX_TexCoord2s16(0, 0);
262 GX_End();
263
264 GX_DrawDone();
265
266 whichFb = !whichFb;
267
268 GX_CopyDisp(framebuffer[whichFb], GX_TRUE);
269 VIDEO_SetNextFramebuffer(framebuffer[whichFb]);
270 VIDEO_Flush();
271 VIDEO_WaitVSync();
272}
273
274bool GBAWiiLoadGame(const char* path) {
275 rom = VFileOpen(path, O_RDONLY);
276
277 if (!rom) {
278 return false;
279 }
280 if (!GBAIsROM(rom)) {
281 return false;
282 }
283
284 save = VFileOpen("test.sav", O_RDWR | O_CREAT);
285
286 GBALoadROM(&gba, rom, save, path);
287
288 struct GBACartridgeOverride override;
289 const struct GBACartridge* cart = (const struct GBACartridge*) gba.memory.rom;
290 memcpy(override.id, &cart->id, sizeof(override.id));
291 if (GBAOverrideFind(0, &override)) {
292 GBAOverrideApply(&gba, &override);
293 }
294
295 ARMReset(&cpu);
296 return true;
297}
298
299void GBAWiiLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args) {
300 UNUSED(thread);
301 UNUSED(level);
302 vfprintf(logfile, format, args);
303 fprintf(logfile, "\n");
304 fflush(logfile);
305}
306
307static void _postVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* renderer) {
308 UNUSED(stream);
309 UNUSED(renderer);
310 GBAWiiFrame();
311}
312
313static void _audioDMA(void) {
314 if (!audioBufferSize) {
315 return;
316 }
317 currentAudioBuffer = !currentAudioBuffer;
318 DCFlushRange(audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
319 AUDIO_InitDMA((u32) audioBuffer[currentAudioBuffer], audioBufferSize * sizeof(struct GBAStereoSample));
320 audioBufferSize = 0;
321}