Update to v087r15 release.

byuu says:

Added linear (eg non-affine) sprite rendering, 4bpp and 8bpp with hflip
and vflip. Nothing else.
You can now see the Nintendo logo and Gameboy text at the end of the BIOS.
It's a start =)
This commit is contained in:
Tim Allen 2012-04-03 10:47:28 +10:00
parent c8bb4949b1
commit 79a47d133a
15 changed files with 496 additions and 32 deletions

View File

@ -1,7 +1,7 @@
#ifndef BASE_HPP
#define BASE_HPP
static const char Version[] = "087.14";
static const char Version[] = "087.15";
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>

21
bsnes/gba/apu/mmio.cpp Executable file
View File

@ -0,0 +1,21 @@
uint8 APU::read(uint32 addr) {
switch(addr) {
//SOUNDBIAS
case 0x04000088: return regs.bias >> 0;
case 0x04000089: return regs.bias >> 8;
}
return 0u;
}
void APU::write(uint32 addr, uint8 byte) {
switch(addr) {
//SOUNDBIAS
case 0x04000088: regs.bias = (regs.bias & 0xff00) | (byte << 0); return;
case 0x04000089: regs.bias = (regs.bias & 0x00ff) | (byte << 8); return;
}
}

12
bsnes/gba/apu/registers.cpp Executable file
View File

@ -0,0 +1,12 @@
APU::Registers::SoundBias::operator uint16() const {
return (
(level << 0)
| (amplitude << 14)
);
}
uint16 APU::Registers::SoundBias::operator=(uint16 source) {
level = (source >> 0) & 1023;
amplitude = (source >> 14) & 3;
return operator uint16();
}

10
bsnes/gba/apu/registers.hpp Executable file
View File

@ -0,0 +1,10 @@
struct Registers {
struct SoundBias {
uint10 level;
uint2 amplitude;
operator uint16() const;
uint16 operator=(uint16 source);
SoundBias& operator=(const SoundBias&) = delete;
} bias;
} regs;

234
bsnes/gba/cpu/mmio.cpp Executable file
View File

@ -0,0 +1,234 @@
uint8 CPU::read(uint32 addr) {
uint8 result = 0;
switch(addr) {
//TM0CNT_L
case 0x04000100: return regs.timer[0].reload >> 0;
case 0x04000101: return regs.timer[0].reload >> 8;
//TIM0CNT_H
case 0x04000102: return regs.timer[0].control >> 0;
case 0x04000103: return regs.timer[0].control >> 8;
//TM1CNT_L
case 0x04000104: return regs.timer[1].reload >> 0;
case 0x04000105: return regs.timer[1].reload >> 8;
//TM1CNT_H
case 0x04000106: return regs.timer[1].control >> 0;
case 0x04000107: return regs.timer[1].control >> 8;
//TM2CNT_L
case 0x04000108: return regs.timer[2].reload >> 0;
case 0x04000109: return regs.timer[2].reload >> 8;
//TM2CNT_H
case 0x0400010a: return regs.timer[2].control >> 0;
case 0x0400010b: return regs.timer[2].control >> 8;
//TM3CNT_L
case 0x0400010c: return regs.timer[3].reload >> 0;
case 0x0400010d: return regs.timer[3].reload >> 8;
//TM3CNT_H
case 0x0400010e: return regs.timer[3].control >> 0;
case 0x0400010f: return regs.timer[3].control >> 8;
//KEYINPUT
case 0x04000130:
for(unsigned n = 0; n < 8; n++) result |= interface->inputPoll(n) << n;
if((result & 0xc0) == 0xc0) result &= ~0xc0; //up+down cannot be pressed simultaneously
if((result & 0x30) == 0x30) result &= ~0x30; //left+right cannot be pressed simultaneously
return result ^ 0xff;
case 0x04000131:
result |= interface->inputPoll(8) << 0;
result |= interface->inputPoll(9) << 1;
return result ^ 0x03;
//KEYCNT
case 0x04000132: return regs.keypad.control >> 0;
case 0x04000133: return regs.keypad.control >> 8;
//IE
case 0x04000200: return regs.irq.enable >> 0;
case 0x04000201: return regs.irq.enable >> 8;
//IF
case 0x04000202: return regs.irq.flag >> 0;
case 0x04000203: return regs.irq.flag >> 8;
//WAITCNT
case 0x04000204: return regs.wait.control >> 0;
case 0x04000205: return regs.wait.control >> 8;
//IME
case 0x04000208: return regs.ime;
case 0x04000209: return 0u;
//POSTFLG + HALTCNT
case 0x04000300: return regs.postboot;
case 0x04000301: return 0u;
//MEMCNT_L
case 0x04000800: return regs.memory.control >> 0;
case 0x04000801: return regs.memory.control >> 8;
//MEMCNT_H
case 0x04000802: return regs.memory.control >> 16;
case 0x04000803: return regs.memory.control >> 24;
}
return 0u;
}
void CPU::write(uint32 addr, uint8 byte) {
switch(addr) {
//DMA0SAD
case 0x040000b0: regs.dma[0].source = (regs.dma[0].source & 0xffffff00) | (byte << 0); return;
case 0x040000b1: regs.dma[0].source = (regs.dma[0].source & 0xffff00ff) | (byte << 8); return;
case 0x040000b2: regs.dma[0].source = (regs.dma[0].source & 0xff00ffff) | (byte << 16); return;
case 0x040000b3: regs.dma[0].source = (regs.dma[0].source & 0x00ffffff) | (byte << 24); return;
//DMA0DAD
case 0x040000b4: regs.dma[0].target = (regs.dma[0].target & 0xffffff00) | (byte << 0); return;
case 0x040000b5: regs.dma[0].target = (regs.dma[0].target & 0xffff00ff) | (byte << 8); return;
case 0x040000b6: regs.dma[0].target = (regs.dma[0].target & 0xff00ffff) | (byte << 16); return;
case 0x040000b7: regs.dma[0].target = (regs.dma[0].target & 0x00ffffff) | (byte << 24); return;
//DMA0CNT_L
case 0x040000b8: regs.dma[0].length = (regs.dma[0].length & 0xff00) | (byte << 0); return;
case 0x040000b9: regs.dma[0].length = (regs.dma[0].length & 0x00ff) | (byte << 8); return;
//DMA0CNT_H
case 0x040000ba: regs.dma[0].control = (regs.dma[0].control & 0xff00) | (byte << 0); return;
case 0x040000bb: regs.dma[0].control = (regs.dma[0].control & 0x00ff) | (byte << 8); return;
//DMA1SAD
case 0x040000bc: regs.dma[1].source = (regs.dma[1].source & 0xffffff00) | (byte << 0); return;
case 0x040000bd: regs.dma[1].source = (regs.dma[1].source & 0xffff00ff) | (byte << 8); return;
case 0x040000be: regs.dma[1].source = (regs.dma[1].source & 0xff00ffff) | (byte << 16); return;
case 0x040000bf: regs.dma[1].source = (regs.dma[1].source & 0x00ffffff) | (byte << 24); return;
//DMA1DAD
case 0x040000c0: regs.dma[1].target = (regs.dma[1].target & 0xffffff00) | (byte << 0); return;
case 0x040000c1: regs.dma[1].target = (regs.dma[1].target & 0xffff00ff) | (byte << 8); return;
case 0x040000c2: regs.dma[1].target = (regs.dma[1].target & 0xff00ffff) | (byte << 16); return;
case 0x040000c3: regs.dma[1].target = (regs.dma[1].target & 0x00ffffff) | (byte << 24); return;
//DMA1CNT_L
case 0x040000c4: regs.dma[1].length = (regs.dma[1].length & 0xff00) | (byte << 0); return;
case 0x040000c5: regs.dma[1].length = (regs.dma[1].length & 0x00ff) | (byte << 8); return;
//DMA1CNT_H
case 0x040000c6: regs.dma[1].control = (regs.dma[1].control & 0xff00) | (byte << 0); return;
case 0x040000c7: regs.dma[1].control = (regs.dma[1].control & 0x00ff) | (byte << 8); return;
//DMA2SAD
case 0x040000c8: regs.dma[2].source = (regs.dma[2].source & 0xffffff00) | (byte << 0); return;
case 0x040000c9: regs.dma[2].source = (regs.dma[2].source & 0xffff00ff) | (byte << 8); return;
case 0x040000ca: regs.dma[2].source = (regs.dma[2].source & 0xff00ffff) | (byte << 16); return;
case 0x040000cb: regs.dma[2].source = (regs.dma[2].source & 0x00ffffff) | (byte << 24); return;
//DMA2DAD
case 0x040000cc: regs.dma[2].target = (regs.dma[2].target & 0xffffff00) | (byte << 0); return;
case 0x040000cd: regs.dma[2].target = (regs.dma[2].target & 0xffff00ff) | (byte << 8); return;
case 0x040000ce: regs.dma[2].target = (regs.dma[2].target & 0xff00ffff) | (byte << 16); return;
case 0x040000cf: regs.dma[2].target = (regs.dma[2].target & 0x00ffffff) | (byte << 24); return;
//DMA2CNT_L
case 0x040000d0: regs.dma[2].length = (regs.dma[2].length & 0xff00) | (byte << 0); return;
case 0x040000d1: regs.dma[2].length = (regs.dma[2].length & 0x00ff) | (byte << 8); return;
//DMA2CNT_H
case 0x040000d2: regs.dma[2].control = (regs.dma[2].control & 0xff00) | (byte << 0); return;
case 0x040000d3: regs.dma[2].control = (regs.dma[2].control & 0x00ff) | (byte << 8); return;
//DMA3SAD
case 0x040000d4: regs.dma[3].source = (regs.dma[3].source & 0xffffff00) | (byte << 0); return;
case 0x040000d5: regs.dma[3].source = (regs.dma[3].source & 0xffff00ff) | (byte << 8); return;
case 0x040000d6: regs.dma[3].source = (regs.dma[3].source & 0xff00ffff) | (byte << 16); return;
case 0x040000d7: regs.dma[3].source = (regs.dma[3].source & 0x00ffffff) | (byte << 24); return;
//DMA3DAD
case 0x040000d8: regs.dma[3].target = (regs.dma[3].target & 0xffffff00) | (byte << 0); return;
case 0x040000d9: regs.dma[3].target = (regs.dma[3].target & 0xffff00ff) | (byte << 8); return;
case 0x040000da: regs.dma[3].target = (regs.dma[3].target & 0xff00ffff) | (byte << 16); return;
case 0x040000db: regs.dma[3].target = (regs.dma[3].target & 0x00ffffff) | (byte << 24); return;
//DMA3CNT_L
case 0x040000dc: regs.dma[3].length = (regs.dma[3].length & 0xff00) | (byte << 0); return;
case 0x040000dd: regs.dma[3].length = (regs.dma[3].length & 0x00ff) | (byte << 8); return;
//DMA3CNT_H
case 0x040000de: regs.dma[3].control = (regs.dma[3].control & 0xff00) | (byte << 0); return;
case 0x040000df: regs.dma[3].control = (regs.dma[3].control & 0x00ff) | (byte << 8); return;
//TM0CNT_L
case 0x04000100: regs.timer[0].reload = (regs.timer[0].reload & 0xff00) | (byte << 0); return;
case 0x04000101: regs.timer[0].reload = (regs.timer[0].reload & 0x00ff) | (byte << 8); return;
//TM0CNT_H
case 0x04000102: regs.timer[0].control = (regs.timer[0].control & 0xff00) | (byte << 0); return;
case 0x04000103: regs.timer[0].control = (regs.timer[0].control & 0x00ff) | (byte << 8); return;
//TM1CNT_L
case 0x04000104: regs.timer[1].reload = (regs.timer[1].reload & 0xff00) | (byte << 0); return;
case 0x04000105: regs.timer[1].reload = (regs.timer[1].reload & 0x00ff) | (byte << 8); return;
//TM1CNT_H
case 0x04000106: regs.timer[1].control = (regs.timer[1].control & 0xff00) | (byte << 0); return;
case 0x04000107: regs.timer[1].control = (regs.timer[1].control & 0x00ff) | (byte << 8); return;
//TM2CNT_L
case 0x04000108: regs.timer[2].reload = (regs.timer[2].reload & 0xff00) | (byte << 0); return;
case 0x04000109: regs.timer[2].reload = (regs.timer[2].reload & 0x00ff) | (byte << 8); return;
//TM2CNT_H
case 0x0400010a: regs.timer[2].control = (regs.timer[2].control & 0xff00) | (byte << 0); return;
case 0x0400010b: regs.timer[2].control = (regs.timer[2].control & 0x00ff) | (byte << 8); return;
//TM3CNT_L
case 0x0400010c: regs.timer[3].reload = (regs.timer[3].reload & 0xff00) | (byte << 0); return;
case 0x0400010d: regs.timer[3].reload = (regs.timer[3].reload & 0x00ff) | (byte << 8); return;
//TM3CNT_H
case 0x0400010e: regs.timer[3].control = (regs.timer[3].control & 0xff00) | (byte << 0); return;
case 0x0400010f: regs.timer[3].control = (regs.timer[3].control & 0x00ff) | (byte << 8); return;
//KEYCNT
case 0x04000132: regs.keypad.control = (regs.keypad.control & 0xff00) | (byte << 0); return;
case 0x04000133: regs.keypad.control = (regs.keypad.control & 0x00ff) | (byte << 8); return;
//IE
case 0x04000200: regs.irq.enable = (regs.irq.enable & 0xff00) | (byte << 0); return;
case 0x04000201: regs.irq.enable = (regs.irq.enable & 0x00ff) | (byte << 8); return;
//IF
case 0x04000202: regs.irq.flag = regs.irq.flag & ~(byte << 0); return;
case 0x04000203: regs.irq.flag = regs.irq.flag & ~(byte << 8); return;
//WAITCNT
case 0x04000204: regs.wait.control = (regs.wait.control & 0xff00) | ((byte & 0xff) << 0); return;
case 0x04000205: regs.wait.control = (regs.wait.control & 0x00ff) | ((byte & 0x7f) << 8); return;
//IME
case 0x04000208: regs.ime = byte & 1; return;
case 0x04000209: return;
//POSTFLG + HALTCNT
case 0x04000300: regs.postboot = byte & 1; return;
case 0x04000301: regs.mode = byte & 0x80 ? Registers::Mode::Stop : Registers::Mode::Halt; return;
//MEMCNT_L
case 0x04000800: regs.memory.control = (regs.memory.control & 0xffffff00) | (byte << 0); return;
case 0x04000801: regs.memory.control = (regs.memory.control & 0xffff00ff) | (byte << 8); return;
//MEMCNT_H
case 0x04000802: regs.memory.control = (regs.memory.control & 0xff00ffff) | (byte << 16); return;
case 0x04000803: regs.memory.control = (regs.memory.control & 0x00ffffff) | (byte << 24); return;
}
}

View File

@ -12,6 +12,10 @@ struct UnmappedMemory : Memory {
static UnmappedMemory unmappedMemory;
uint8& StaticMemory::operator[](uint32 addr) {
return data[addr];
}
uint32 StaticMemory::read(uint32 addr, uint32 size) {
switch(size) {
case Word: addr &= ~3; return (data[addr + 0] << 0) | (data[addr + 1] << 8) | (data[addr + 2] << 16) | (data[addr + 3] << 24);

View File

@ -7,6 +7,7 @@ struct StaticMemory : Memory {
uint8_t *data;
unsigned size;
uint8& operator[](uint32 addr);
uint32 read(uint32 addr, uint32 size);
void write(uint32 addr, uint32 size, uint32 word);
StaticMemory();

43
bsnes/gba/memory/mmio.cpp Executable file
View File

@ -0,0 +1,43 @@
uint32 MMIO::read(uint32 addr, uint32 size) {
uint32 word = 0;
switch(size) {
case Word:
addr &= ~3;
word |= read(addr + 0) << 0;
word |= read(addr + 1) << 8;
word |= read(addr + 2) << 16;
word |= read(addr + 3) << 24;
break;
case Half:
addr &= ~1;
word |= read(addr + 0) << 0;
word |= read(addr + 1) << 8;
break;
case Byte:
word |= read(addr + 0) << 0;
break;
}
return word;
}
void MMIO::write(uint32 addr, uint32 size, uint32 word) {
switch(size) {
case Word:
addr &= ~3;
write(addr + 0, word >> 0);
write(addr + 1, word >> 8);
write(addr + 2, word >> 16);
write(addr + 3, word >> 24);
break;
case Half:
addr &= ~1;
write(addr + 0, word >> 0);
write(addr + 1, word >> 8);
break;
case Byte:
write(addr + 0, word >> 0);
break;
}
}

81
bsnes/gba/ppu/object.cpp Executable file
View File

@ -0,0 +1,81 @@
void PPU::render_objects() {
for(unsigned n = 0; n < 240; n++) pixel[n].exists = false;
for(unsigned n = 0; n < 128; n++) {
auto &obj = object[n];
uint16 attr0 = oam.read(n * 8 + 0, Half);
uint16 attr1 = oam.read(n * 8 + 2, Half);
uint16 attr2 = oam.read(n * 8 + 4, Half);
obj.y = attr0 >> 0;
obj.affine = attr0 >> 8;
obj.affinesize = attr0 >> 9;
obj.mode = attr0 >> 10;
obj.mosaic = attr0 >> 12;
obj.colors = attr0 >> 13;
obj.shape = attr0 >> 14;
obj.x = attr1 >> 0;
obj.affineparam = attr1 >> 9;
obj.hflip = attr1 >> 12;
obj.vflip = attr1 >> 13;
obj.size = attr1 >> 14;
obj.character = attr2 >> 0;
obj.priority = attr2 >> 10;
obj.palette = attr2 >> 12;
static unsigned widths[] = {
8, 16, 32, 64,
16, 32, 32, 64,
8, 8, 16, 32,
0, 0, 0, 0, //8?
};
static unsigned heights[] = {
8, 16, 32, 64,
8, 8, 16, 32,
16, 32, 32, 64,
0, 0, 0, 0, //8?
};
obj.width = widths [obj.shape * 4 + obj.size];
obj.height = heights[obj.shape * 4 + obj.size];
if(regs.vcounter < obj.y) continue;
if(regs.vcounter >= (obj.y + obj.height) & 255) continue;
if(obj.affine == 0 && obj.affinesize == 1) continue; //invalid mode
if(obj.affine == 0) render_object_linear(obj);
if(obj.affine == 1) render_object_affine(obj);
}
}
void PPU::render_object_linear(Object &obj) {
unsigned py = regs.vcounter - obj.y;
if(obj.vflip) py ^= obj.height - 1;
unsigned rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8;
unsigned baseaddr = 0x10000 + obj.character * 32;
uint9 sx = obj.x;
for(unsigned x = 0; x < obj.width; x++) {
if(sx++ >= 240) continue;
unsigned px = x;
if(obj.hflip) px ^= obj.width - 1;
unsigned offset = (py / 8) * rowsize + (px / 8);
if(obj.colors == 0) offset = baseaddr + offset * 32 + (py & 7) * 4 + (px & 7) / 2;
if(obj.colors == 1) offset = baseaddr + offset * 64 + (py & 7) * 8 + (px & 7);
uint8 color = vram[offset];
if(obj.colors == 0) color = (px & 1) ? color >> 4 : color & 15;
if(color == 0) continue; //transparent
if(obj.colors == 0) pixel[sx] = { true, palette(256 + obj.palette * 16 + color), obj.priority };
if(obj.colors == 1) pixel[sx] = { true, palette(256 + color), obj.priority };
}
}
void PPU::render_object_affine(Object &obj) {
}

View File

@ -13,6 +13,8 @@
namespace GBA {
#include "registers.cpp"
#include "object.cpp"
#include "screen.cpp"
#include "mmio.cpp"
PPU ppu;
@ -89,6 +91,11 @@ void PPU::scanline() {
if(regs.status.vcoincidence) cpu.regs.irq.flag.vcoincidence = 1;
}
if(regs.vcounter < 160) {
render_objects();
render_screen();
}
step(256 * 4);
regs.status.hblank = 1;
if(regs.status.irqhblank) cpu.regs.irq.flag.hblank = 1;

View File

@ -3,6 +3,7 @@ struct PPU : Thread, MMIO {
StaticMemory oam;
StaticMemory pram;
#include "registers.hpp"
#include "state.hpp"
uint16 *output;
static void Enter();
@ -16,6 +17,13 @@ struct PPU : Thread, MMIO {
uint8 read(uint32 addr);
void write(uint32 addr, uint8 byte);
void render_objects();
void render_object_linear(Object&);
void render_object_affine(Object&);
uint15 palette(uint9 index);
void render_screen();
PPU();
~PPU();
};

View File

@ -1,37 +1,37 @@
PPU::Registers::Control::operator uint16() const {
return (
(bgmode << 0)
| (cgbmode << 3)
| (frame << 4)
| (hblank << 5)
| (objmap << 6)
| (forceblank << 7)
| (enablebg[0] << 8)
| (enablebg[1] << 9)
| (enablebg[2] << 10)
| (enablebg[3] << 11)
| (enableobj << 12)
| (enablebgwindow0 << 13)
| (enablebgwindow1 << 14)
| (enableobjwindow << 15)
(bgmode << 0)
| (cgbmode << 3)
| (frame << 4)
| (hblank << 5)
| (objmapping << 6)
| (forceblank << 7)
| (enablebg[0] << 8)
| (enablebg[1] << 9)
| (enablebg[2] << 10)
| (enablebg[3] << 11)
| (enableobj << 12)
| (enablebgwindow[0] << 13)
| (enablebgwindow[1] << 14)
| (enableobjwindow << 15)
);
}
uint16 PPU::Registers::Control::operator=(uint16 source) {
bgmode = source & 0x0007;
cgbmode = source & 0x0008;
frame = source & 0x0010;
hblank = source & 0x0020;
objmap = source & 0x0040;
forceblank = source & 0x0080;
enablebg[0] = source & 0x0100;
enablebg[1] = source & 0x0200;
enablebg[2] = source & 0x0400;
enablebg[3] = source & 0x0800;
enableobj = source & 0x1000;
enablebgwindow0 = source & 0x2000;
enablebgwindow1 = source & 0x4000;
enableobjwindow = source & 0x8000;
bgmode = source & 0x0007;
cgbmode = source & 0x0008;
frame = source & 0x0010;
hblank = source & 0x0020;
objmapping = source & 0x0040;
forceblank = source & 0x0080;
enablebg[0] = source & 0x0100;
enablebg[1] = source & 0x0200;
enablebg[2] = source & 0x0400;
enablebg[3] = source & 0x0800;
enableobj = source & 0x1000;
enablebgwindow[0] = source & 0x2000;
enablebgwindow[1] = source & 0x4000;
enableobjwindow = source & 0x8000;
return operator uint16();
}

View File

@ -4,12 +4,11 @@ struct Registers {
bool cgbmode;
bool frame;
bool hblank;
bool objmap;
bool objmapping;
bool forceblank;
bool enablebg[4];
bool enableobj;
bool enablebgwindow0;
bool enablebgwindow1;
bool enablebgwindow[2];
bool enableobjwindow;
operator uint16() const;

15
bsnes/gba/ppu/screen.cpp Executable file
View File

@ -0,0 +1,15 @@
uint15 PPU::palette(uint9 index) {
uint15 result = 0;
result |= pram[index * 2 + 0] << 0;
result |= pram[index * 2 + 1] << 8;
return result;
}
void PPU::render_screen() {
uint16 *line = output + regs.vcounter * 240;
for(unsigned x = 0; x < 240; x++) {
if(pixel[x].exists) line[x] = pixel[x].color;
else line[x] = palette(0) & 0x7fff;
}
}

29
bsnes/gba/ppu/state.hpp Executable file
View File

@ -0,0 +1,29 @@
struct Pixel {
bool exists;
uint15 color;
uint2 priority;
} pixel[256];
struct Object {
uint8 y;
uint1 affine;
uint1 affinesize;
uint2 mode;
uint1 mosaic;
uint1 colors; //0 = 16, 1 = 256
uint2 shape; //0 = square, 1 = horizontal, 2 = vertical
uint9 x;
uint5 affineparam;
uint1 hflip;
uint1 vflip;
uint2 size;
uint10 character;
uint2 priority;
uint4 palette;
//ancillary data
unsigned width;
unsigned height;
} object[128];