mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
c8bb4949b1
commit
79a47d133a
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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];
|
Loading…
Reference in New Issue