Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libmeteor/source/graphics/screen.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/graphics/screen.hpp"
18
#include "../globals.hpp"
19
#include "../debug.hpp"
20
#include "ameteor.hpp"
21
22
#include <cstring>
23
24
namespace AMeteor
25
{
26
namespace Graphics
27
{
28
BgLayer Screen::* const Screen::BgLayers [4] =
29
{ &Screen::m_bgLayer0, &Screen::m_bgLayer1,
30
&Screen::m_bgLayer2, &Screen::m_bgLayer3 };
31
32
// TODO there is no more need to pass theses references
33
Screen::Screen (Memory& memory, Io& io) :
34
m_io(io),
35
m_surface(new uint16_t[WIDTH*HEIGHT]),
36
m_renderer(m_surface),
37
m_frameskip(0),
38
m_curframe(0),
39
m_dispcnt(0),
40
m_refX2(0), m_refY2(0), m_refX3(0), m_refY3(0),
41
m_pPalette((uint16_t*)memory.GetRealAddress(0x05000000)),
42
m_bgLayer0(0, memory, io, m_pPalette),
43
m_bgLayer1(1, memory, io, m_pPalette),
44
m_bgLayer2(2, memory, io, m_pPalette),
45
m_bgLayer3(3, memory, io, m_pPalette),
46
m_objs(memory, io, m_pPalette + 256)
47
{
48
}
49
50
Screen::~Screen ()
51
{
52
delete [] m_surface;
53
}
54
55
void Screen::DrawLine (uint8_t line)
56
{
57
if (m_curframe < m_frameskip)
58
{
59
// we skip this frame
60
// VBlank
61
if (line == 159)
62
// we don't update screen since we haven't drawn anything on it, it
63
// would show up a buffer with the previous image or maybe only
64
// garbage (we use double buffering)
65
m_curframe = (m_curframe + 1) % FRMSKIP_TOTAL;
66
return;
67
}
68
69
uint16_t* lineBg = new uint16_t[4*WIDTH];
70
// layers may draw transparent pixels, so we need to clear them
71
memset(lineBg, 0, 4*WIDTH*sizeof(uint16_t));
72
uint32_t* lineObj = new uint32_t[WIDTH];
73
for (uint32_t* p = lineObj + WIDTH - 1; p >= lineObj; --p)
74
*p = 0x00030000;
75
uint8_t prio[4];
76
prio[0] = m_bgLayer0.GetPriority();
77
prio[1] = m_bgLayer1.GetPriority();
78
prio[2] = m_bgLayer2.GetPriority();
79
prio[3] = m_bgLayer3.GetPriority();
80
uint8_t layersOn = (m_dispcnt >> 8) & 0x1F;
81
82
switch (m_dispcnt & 0x7)
83
{
84
case 0: // all in mode 0
85
// if the bg is enabled draw it
86
if (layersOn & (0x1 )) m_bgLayer0.DrawLine0(line, lineBg);
87
if (layersOn & (0x1 << 1)) m_bgLayer1.DrawLine0(line, lineBg+WIDTH);
88
if (layersOn & (0x1 << 2)) m_bgLayer2.DrawLine0(line, lineBg+2*WIDTH);
89
if (layersOn & (0x1 << 3)) m_bgLayer3.DrawLine0(line, lineBg+3*WIDTH);
90
// if objects are enabled draw them
91
if (layersOn & (0x1 << 4)) m_objs.DrawLine(line, lineObj);
92
break;
93
case 1: // bg0 and bg1 in mode 0 and bg2 in mode 2, no bg3
94
// disable layer 3
95
layersOn &= 0xF7;
96
// if the bg is enabled draw it
97
if (layersOn & (0x1 )) m_bgLayer0.DrawLine0(line, lineBg);
98
if (layersOn & (0x1 << 1)) m_bgLayer1.DrawLine0(line, lineBg+WIDTH);
99
if (layersOn & (0x1 << 2))
100
m_bgLayer2.DrawLine2(lineBg+2*WIDTH,
101
m_refX2, m_refY2,
102
m_io.DRead16(Io::BG2PA),
103
m_io.DRead16(Io::BG2PC));
104
// if objects are enabled draw them
105
if (layersOn & (0x1 << 4)) m_objs.DrawLine(line, lineObj);
106
break;
107
case 2: // bg2 and bg3 in mode 2, no bg0 and bg1
108
// disable layers 0 and 1
109
layersOn &= 0xFC;
110
// if the bg is enabled draw it
111
if (layersOn & (0x1 << 2))
112
m_bgLayer2.DrawLine2(lineBg+2*WIDTH,
113
m_refX2, m_refY2,
114
m_io.DRead16(Io::BG2PA),
115
m_io.DRead16(Io::BG2PC));
116
if (layersOn & (0x1 << 3))
117
m_bgLayer3.DrawLine2(lineBg+3*WIDTH,
118
m_refX3, m_refY3,
119
m_io.DRead16(Io::BG3PA),
120
m_io.DRead16(Io::BG3PC));
121
// if objects are enabled draw them
122
if (layersOn & (0x1 << 4)) m_objs.DrawLine(line, lineObj);
123
break;
124
case 3: // bg2 only 15 bit direct color 240x160
125
layersOn &= 0xF4;
126
if (layersOn & (0x1 << 2))
127
m_bgLayer2.DrawLine3(lineBg+2*WIDTH,
128
m_refX2, m_refY2,
129
m_io.DRead16(Io::BG2PA),
130
m_io.DRead16(Io::BG2PC));
131
if (layersOn & (0x1 << 4))
132
m_objs.DrawLineHighOnly(line, lineObj);
133
break;
134
// TODO (remember, HIGH ONLY for objs, don't make shitty copy paste)
135
case 4: // bg2 only in mode 4 (bitmap 256)
136
layersOn &= 0xF4;
137
// if bg2 is enabled
138
if (layersOn & (0x1 << 2))
139
// draw it
140
m_bgLayer2.DrawLine4(
141
line,
142
lineBg+2*WIDTH,
143
m_refX2, m_refY2,
144
m_io.DRead16(Io::BG2PA),
145
m_io.DRead16(Io::BG2PB),
146
m_io.DRead16(Io::BG2PC),
147
m_io.DRead16(Io::BG2PD),
148
m_dispcnt & (0x1 << 4));
149
// if objs are enabled
150
if (layersOn & (0x1 << 4))
151
// all objs with the current priority
152
m_objs.DrawLineHighOnly(line, lineObj);
153
break;
154
case 5: // bg2 only 15 bit direct color 160x128 2 frames
155
layersOn &= 0xF4;
156
if (layersOn & (0x1 << 2))
157
m_bgLayer2.DrawLine5(lineBg+2*WIDTH,
158
m_refX2, m_refY2,
159
m_io.DRead16(Io::BG2PA),
160
m_io.DRead16(Io::BG2PC),
161
m_dispcnt & (0x1 << 4));
162
if (layersOn & (0x1 << 4))
163
m_objs.DrawLineHighOnly(line, lineObj);
164
break;
165
default :
166
met_abort("not supported : " << (m_dispcnt & 0x7));
167
break;
168
}
169
170
// windows
171
/* I got very little information for this, it may not be accurate. All
172
* the sources don't say the same thing */
173
uint8_t* window = NULL;
174
if (m_dispcnt >> 13)
175
{
176
window = new uint8_t[WIDTH];
177
// Outside window
178
memset(window, m_io.DRead16(Io::WINOUT) & 0x3F, WIDTH*sizeof(uint8_t));
179
// OBJ window
180
if (m_dispcnt & (0x1 << 15))
181
m_objs.DrawWindow(line, window);
182
// Window 1
183
if (m_dispcnt & (0x1 << 14))
184
DrawWindow(line, window,
185
m_io.DRead16(Io::WIN1V), m_io.DRead16(Io::WIN1H),
186
(m_io.DRead16(Io::WININ) >> 8) & 0x3F);
187
// Window 0
188
if (m_dispcnt & (0x1 << 13))
189
DrawWindow(line, window,
190
m_io.DRead16(Io::WIN0V), m_io.DRead16(Io::WIN0H),
191
m_io.DRead16(Io::WININ) & 0x3F);
192
}
193
194
// color effects
195
uint16_t bldcnt = m_io.DRead16(Io::BLDCNT);
196
uint8_t colorEffect = (bldcnt >> 6) & 0x3;
197
uint8_t eva = std::min(m_io.DRead8(Io::BLDALPHA) & 0x1F, 16);
198
uint8_t evb = std::min(m_io.DRead8(Io::BLDALPHA+1) & 0x1F, 16);
199
uint8_t evy = std::min(m_io.DRead8(Io::BLDY) & 0x1F, 16);
200
201
// blending
202
uint16_t* surface = m_surface + line*WIDTH;
203
uint16_t out, bout;
204
// top and back are formated as follow :
205
// 4 bits | 4 bits
206
// priority | layer
207
uint8_t top, back;
208
uint8_t curprio;
209
uint32_t* pObj = lineObj;
210
uint16_t* pBg = lineBg;
211
uint8_t* pWin = window;
212
uint8_t winmask;
213
// if window are disabled, we draw everything which is enabled by
214
// layersOn
215
if (!window)
216
winmask = 0xFF;
217
for (uint8_t x = 0; x < WIDTH; ++x, ++pBg, ++pObj, ++pWin)
218
{
219
if (window)
220
winmask = *pWin;
221
222
// backdrop
223
bout = out = m_pPalette[0];
224
back = top = 0xF5;
225
226
// for each layer
227
for (uint8_t l = 0; l < 4; ++l)
228
// if layer is enabled and
229
if ((layersOn & (0x1 << l)) &&
230
// pixel to draw is not transparent
231
(pBg[l*WIDTH] & 0x8000))
232
{
233
curprio = ((prio[l] << 4) | l);
234
235
if (curprio < back && curprio > top)
236
{
237
bout = pBg[l*WIDTH];
238
back = curprio;
239
}
240
else if (
241
// priority is lower than current top pixel and
242
curprio < top &&
243
// this layer should be drawn in current window
244
(winmask & (0x1 << l)))
245
{
246
bout = out;
247
out = pBg[l*WIDTH];
248
back = top;
249
top = curprio;
250
}
251
}
252
253
// now objects
254
// if objects are enabled
255
if ((layersOn & (0x1 << 4)) &&
256
// pixel to draw is not transparent
257
(*pObj & 0x8000))
258
{
259
curprio = ((*pObj >> (16 - 4)) & (0x3 << 4));
260
261
if (curprio <= (back & 0xF0) && curprio > (top & 0xF0))
262
{
263
bout = *pObj;
264
back = curprio | 4;
265
}
266
else if (// priority is lower than current top pixel and
267
// NOTE : objects are OVER bg with same priority
268
curprio <= (top & 0xF0) &&
269
// objects should be drawn in current window
270
(winmask & (0x1 << 4)))
271
{
272
bout = out;
273
out = *pObj;
274
back = top;
275
top = curprio | 4;
276
}
277
}
278
279
// if we have an object on top and it has semi transparency
280
if ((top & 0xF) == 4 && (*pObj & (0x1 << 18)))
281
{
282
// if second target is just behind
283
if (bldcnt & ((0x1 << 8) << (back & 0xF)))
284
// apply alpha blend
285
out =
286
std::min(((bout & 0x001F) * evb +
287
(out & 0x001F) * eva) / 16, 0x001F) |
288
std::min((((bout & 0x03E0) * evb +
289
(out & 0x03E0) * eva) / 16) & 0xFFE0, 0x03E0) |
290
// no need to take care of over flow since u16 & s32 = s32
291
std::min((((bout & 0x7C00) * evb +
292
(out & 0x7C00) * eva) / 16) & 0xFC00, 0x7C00);
293
}
294
// else if no window or window and effects are enabled in window
295
// and we have a first target on top
296
else if ((!window || (*pWin & (0x1 << 5)))
297
&& (bldcnt & (0x1 << (top & 0xF))))
298
switch (colorEffect)
299
{
300
case 1: // alpha blend
301
// if second target is just behind
302
// TODO optimization : special cases for eva = 0 or evb = 0
303
if (bldcnt & ((0x1 << 8) << (back & 0xF)))
304
// apply alpha blend
305
out =
306
std::min(((bout & 0x001F) * evb +
307
(out & 0x001F) * eva) / 16, 0x001F) |
308
std::min((((bout & 0x03E0) * evb +
309
(out & 0x03E0) * eva) / 16) & 0xFFE0, 0x03E0) |
310
// no need to take care of over flow since u16 & s32 = s32
311
std::min((((bout & 0x7C00) * evb +
312
(out & 0x7C00) * eva) / 16) & 0xFC00, 0x7C00);
313
break;
314
case 2: // brightness increase
315
// we don't need saturation since the formula makes it so it never
316
// goes above 0x1F
317
out =
318
(((out & 0x001F) +
319
((0x001F - (out & 0x001F)) * evy) / 16) & 0x001F) |
320
(((out & 0x03E0) +
321
((0x03E0 - (out & 0x03E0)) * evy) / 16) & 0x03E0) |
322
(((out & 0x7C00) +
323
((0x7C00 - (out & 0x7C00)) * evy) / 16) & 0x7C00);
324
break;
325
case 3: // brightness decrease
326
// we don't need saturation since the formula makes it so it never
327
// goes below 0
328
out =
329
((((out & 0x001F) * (16-evy)) / 16) & 0x001F) |
330
((((out & 0x03E0) * (16-evy)) / 16) & 0x03E0) |
331
((((out & 0x7C00) * (16-evy)) / 16) & 0x7C00);
332
break;
333
}
334
335
*surface = out;
336
++surface;
337
}
338
339
m_refX2 += (int16_t)m_io.DRead16(Io::BG2PB);
340
m_refY2 += (int16_t)m_io.DRead16(Io::BG2PD);
341
m_refX3 += (int16_t)m_io.DRead16(Io::BG3PB);
342
m_refY3 += (int16_t)m_io.DRead16(Io::BG3PD);
343
344
if (window)
345
delete [] window;
346
delete [] lineBg;
347
delete [] lineObj;
348
349
// VBlank
350
if (line == 159)
351
{
352
m_curframe = (m_curframe + 1) % FRMSKIP_TOTAL;
353
m_renderer.VBlank();
354
}
355
}
356
357
void Screen::DrawWindow (uint8_t line, uint8_t* surface,
358
uint16_t win0v, uint16_t win0h, uint8_t mask)
359
{
360
// the variables are called win0, but this function works also for win1
361
uint8_t win0t = win0v >> 8, win0b = win0v & 0xFF;
362
// VBA says that if t == b and they are greater than 228, we are in the
363
// window
364
// This is from Tonc documentation
365
if (win0t >= 227)
366
return;
367
else if (win0b > win0t && line >= win0t && line < win0b
368
// the above is the normal behaviour
369
|| win0b < win0t && (line >= win0t || line < win0b)
370
// the above is the "inverted" behaviour
371
)
372
{
373
uint8_t win0l, win0r;
374
uint8_t* ptr;
375
win0l = win0h >> 8;
376
win0r = win0h & 0xFF;
377
// this seems wrong
378
//if (win0l > 240)
379
// win0l = 240;
380
//if (win0r > 240)
381
// win0r = 240;
382
383
// if this is the normal behaviour
384
if (win0l <= win0r)
385
{
386
ptr = surface + win0l;
387
for (uint8_t i = win0l; i < win0r && i < 240; ++i, ++ptr)
388
*ptr = mask;
389
}
390
// else, this is the inverted behaviour
391
else
392
{
393
ptr = surface;
394
for (uint8_t i = 0; i < win0r && i < 240; ++i, ++ptr)
395
*ptr = mask;
396
ptr = surface + win0l;
397
for (uint8_t i = win0l; i < 240; ++i, ++ptr)
398
*ptr = mask;
399
}
400
}
401
}
402
403
bool Screen::SaveState (std::ostream& stream)
404
{
405
SS_WRITE_VAR(m_refX2);
406
SS_WRITE_VAR(m_refY2);
407
SS_WRITE_VAR(m_refX3);
408
SS_WRITE_VAR(m_refY3);
409
410
return true;
411
}
412
413
bool Screen::LoadState (std::istream& stream)
414
{
415
SS_READ_VAR(m_refX2);
416
SS_READ_VAR(m_refY2);
417
SS_READ_VAR(m_refX3);
418
SS_READ_VAR(m_refY3);
419
420
return true;
421
}
422
}
423
}
424
425