diff --git a/bsnes/base/base.hpp b/bsnes/base/base.hpp index aa168cdc..cb1edb92 100755 --- a/bsnes/base/base.hpp +++ b/bsnes/base/base.hpp @@ -1,7 +1,7 @@ #ifndef BASE_HPP #define BASE_HPP -static const char Version[] = "087.15"; +static const char Version[] = "087.16"; #include #include diff --git a/bsnes/gba/cpu/cpu.cpp b/bsnes/gba/cpu/cpu.cpp index ecd212f7..909bdeac 100755 --- a/bsnes/gba/cpu/cpu.cpp +++ b/bsnes/gba/cpu/cpu.cpp @@ -4,6 +4,7 @@ namespace GBA { #include "registers.cpp" #include "mmio.cpp" +#include "dma.cpp" CPU cpu; void CPU::Enter() { cpu.enter(); } @@ -28,6 +29,7 @@ void CPU::enter() { regs.mode = Registers::Mode::Normal; } + dma_run(); exec(); } } @@ -72,6 +74,10 @@ void CPU::power() { regs.mode = Registers::Mode::Normal; regs.memory.control = 0x0d000020; + pending.dma.vblank = 0; + pending.dma.hblank = 0; + pending.dma.hdma = 0; + for(unsigned n = 0x0b0; n <= 0x0df; n++) bus.mmio[n] = this; //DMA for(unsigned n = 0x100; n <= 0x10f; n++) bus.mmio[n] = this; //Timers for(unsigned n = 0x130; n <= 0x133; n++) bus.mmio[n] = this; //Keypad diff --git a/bsnes/gba/cpu/cpu.hpp b/bsnes/gba/cpu/cpu.hpp index 3fb53966..83fb0968 100755 --- a/bsnes/gba/cpu/cpu.hpp +++ b/bsnes/gba/cpu/cpu.hpp @@ -2,6 +2,7 @@ struct CPU : Processor::ARM, Thread, MMIO { StaticMemory iwram; StaticMemory ewram; #include "registers.hpp" + #include "state.hpp" static void Enter(); void enter(); @@ -14,6 +15,9 @@ struct CPU : Processor::ARM, Thread, MMIO { uint8 read(uint32 addr); void write(uint32 addr, uint8 byte); + void dma_run(); + void dma_transfer(uint2 channel); + CPU(); }; diff --git a/bsnes/gba/cpu/dma.cpp b/bsnes/gba/cpu/dma.cpp new file mode 100755 index 00000000..d4b5b1f3 --- /dev/null +++ b/bsnes/gba/cpu/dma.cpp @@ -0,0 +1,45 @@ +void CPU::dma_run() { + for(unsigned n = 0; n < 4; n++) { + if(regs.dma[n].control.enable == false) continue; + switch(regs.dma[n].control.timingmode) { + case 0: break; + case 1: if(pending.dma.vblank == false) continue; break; + case 2: if(pending.dma.hblank == false) continue; break; + case 3: if(pending.dma.hdma == false || n != 3) continue; break; + } + dma_transfer(n); + } + + pending.dma.vblank = false; + pending.dma.hblank = false; + pending.dma.hdma = false; +} + +void CPU::dma_transfer(uint2 n) { + auto &channel = regs.dma[n]; + + unsigned size = channel.control.size ? Word : Half; + unsigned seek = channel.control.size ? 4 : 2; + uint16 length = channel.length; + + channel.basetarget = channel.target; + do { + uint32 word = bus.read(channel.source, size); + bus.write(channel.target, size, word); + + switch(channel.control.sourcemode) { + case 0: channel.source += seek; break; + case 1: channel.source -= seek; break; + } + + switch(channel.control.targetmode) { + case 0: channel.target += seek; break; + case 1: channel.target -= seek; break; + case 3: channel.target += seek; break; + } + } while(--length); + if(channel.control.targetmode == 3) channel.target = channel.basetarget; + + channel.control.enable = false; + if(channel.control.irq) regs.irq.flag.dma[n] = 1; +} diff --git a/bsnes/gba/cpu/registers.cpp b/bsnes/gba/cpu/registers.cpp index e37aa269..40150c01 100755 --- a/bsnes/gba/cpu/registers.cpp +++ b/bsnes/gba/cpu/registers.cpp @@ -5,7 +5,7 @@ CPU::Registers::DMA::Control::operator uint16() const { | (repeat << 9) | (size << 10) | (drq << 11) - | (timing << 12) + | (timingmode << 12) | (irq << 14) | (enable << 15) ); @@ -17,7 +17,7 @@ uint16 CPU::Registers::DMA::Control::operator=(uint16 source) { repeat = source >> 9; size = source >> 10; drq = source >> 11; - timing = source >> 12; + timingmode = source >> 12; irq = source >> 14; enable = source >> 15; return operator uint16(); @@ -78,15 +78,15 @@ CPU::Registers::Interrupt::operator uint16() const { (vblank << 0) | (hblank << 1) | (vcoincidence << 2) - | (timer0 << 3) - | (timer1 << 4) - | (timer2 << 5) - | (timer3 << 6) + | (timer[0] << 3) + | (timer[1] << 4) + | (timer[2] << 5) + | (timer[3] << 6) | (serial << 7) - | (dma0 << 8) - | (dma1 << 9) - | (dma2 << 10) - | (dma3 << 11) + | (dma[0] << 8) + | (dma[1] << 9) + | (dma[2] << 10) + | (dma[3] << 11) | (keypad << 12) | (cartridge << 13) ); @@ -96,15 +96,15 @@ uint16 CPU::Registers::Interrupt::operator=(uint16 source) { vblank = source & (1 << 0); hblank = source & (1 << 1); vcoincidence = source & (1 << 2); - timer0 = source & (1 << 3); - timer1 = source & (1 << 4); - timer2 = source & (1 << 5); - timer3 = source & (1 << 6); + timer[0] = source & (1 << 3); + timer[1] = source & (1 << 4); + timer[2] = source & (1 << 5); + timer[3] = source & (1 << 6); serial = source & (1 << 7); - dma0 = source & (1 << 8); - dma1 = source & (1 << 9); - dma2 = source & (1 << 10); - dma3 = source & (1 << 11); + dma[0] = source & (1 << 8); + dma[1] = source & (1 << 9); + dma[2] = source & (1 << 10); + dma[3] = source & (1 << 11); keypad = source & (1 << 12); cartridge = source & (1 << 13); return operator uint16(); diff --git a/bsnes/gba/cpu/registers.hpp b/bsnes/gba/cpu/registers.hpp index eb5db46d..21f3a7ba 100755 --- a/bsnes/gba/cpu/registers.hpp +++ b/bsnes/gba/cpu/registers.hpp @@ -9,7 +9,7 @@ struct Registers { uint1 repeat; uint1 size; uint1 drq; - uint2 timing; + uint2 timingmode; uint1 irq; uint1 enable; @@ -17,6 +17,9 @@ struct Registers { uint16 operator=(uint16 source); DMA& operator=(const DMA&) = delete; } control; + + //internal + uint32 basetarget; } dma[4]; struct TimerControl { @@ -64,15 +67,9 @@ struct Registers { bool vblank; bool hblank; bool vcoincidence; - bool timer0; - bool timer1; - bool timer2; - bool timer3; + bool timer[4]; bool serial; - bool dma0; - bool dma1; - bool dma2; - bool dma3; + bool dma[4]; bool keypad; bool cartridge; diff --git a/bsnes/gba/cpu/state.hpp b/bsnes/gba/cpu/state.hpp new file mode 100755 index 00000000..4926a3d8 --- /dev/null +++ b/bsnes/gba/cpu/state.hpp @@ -0,0 +1,7 @@ +struct Pending { + struct DMA { + bool vblank; + bool hblank; + bool hdma; + } dma; +} pending; diff --git a/bsnes/gba/gba.hpp b/bsnes/gba/gba.hpp index 36788a54..77f5db26 100755 --- a/bsnes/gba/gba.hpp +++ b/bsnes/gba/gba.hpp @@ -13,7 +13,7 @@ namespace GBA { /* bgba - Game Boy Advance emulator - author: byuu + authors: byuu, Cydrak license: GPLv3 project started: 2012-03-19 */ diff --git a/bsnes/gba/ppu/background.cpp b/bsnes/gba/ppu/background.cpp new file mode 100755 index 00000000..3a8af01f --- /dev/null +++ b/bsnes/gba/ppu/background.cpp @@ -0,0 +1,72 @@ +void PPU::render_backgrounds() { + if(regs.control.bgmode == 0) { + render_background_linear(0); + render_background_linear(1); + render_background_linear(2); + render_background_linear(3); + } + + if(regs.control.bgmode == 1) { + render_background_linear(0); + render_background_linear(1); + //render_background_affine(2); + } + + if(regs.control.bgmode == 2) { + //render_background_affine(2); + //render_background_affine(3); + } +} + +void PPU::render_background_linear(unsigned bgnumber) { + for(unsigned n = 0; n < 240; n++) pixel[bgnumber][n].exists = false; + if(regs.control.enablebg[bgnumber] == false) return; + + auto &bg = regs.bg[bgnumber]; + uint9 voffset = regs.vcounter + bg.voffset; + uint9 hoffset = bg.hoffset; + + unsigned basemap = bg.control.screenbaseblock << 11; + unsigned basechr = bg.control.characterbaseblock << 14; + unsigned px = hoffset & 7, py = voffset & 7; + + Tile tile; + uint8 data[8]; + + for(unsigned x = 0; x < 240; x++) { + if(x == 0 || px & 8) { + px &= 7; + + unsigned tx = hoffset / 8, ty = voffset / 8; + unsigned offset = (ty & 31) * 32 + (tx & 31); + if(bg.control.screensize & 1) if(tx & 32) offset += 32 * 32; + if(bg.control.screensize & 2) if(ty & 32) offset += 32 * 32 * (1 + (bg.control.screensize & 1)); + offset = basemap + offset * 2; + uint16 mapdata = vram.read(offset, Half); + + tile.character = mapdata >> 0; + tile.hflip = mapdata >> 10; + tile.vflip = mapdata >> 11; + tile.palette = mapdata >> 12; + + if(bg.control.colormode == 0) { + offset = basechr + tile.character * 32 + (py ^ (tile.vflip ? 7 : 0)) * 4; + uint32 word = vram.read(offset, Word); + for(unsigned n = 0; n < 8; n++) data[n] = (word >> (n * 4)) & 15; + } else { + offset = basechr + tile.character * 64 + (py ^ (tile.vflip ? 7 : 0)) * 8; + uint32 wordlo = vram.read(offset + 0, Word); + uint32 wordhi = vram.read(offset + 4, Word); + for(unsigned n = 0; n < 4; n++) data[0 + n] = (wordlo >> (n * 8)) & 255; + for(unsigned n = 0; n < 4; n++) data[4 + n] = (wordhi >> (n * 8)) & 255; + } + } + + hoffset++; + uint8 color = data[px++ ^ (tile.hflip ? 7 : 0)]; + if(color == 0) continue; //transparent + + if(bg.control.colormode == 0) pixel[bgnumber][x] = { true, palette(tile.palette * 16 + color), bg.control.priority }; + if(bg.control.colormode == 1) pixel[bgnumber][x] = { true, palette(color), bg.control.priority }; + } +} diff --git a/bsnes/gba/ppu/object.cpp b/bsnes/gba/ppu/object.cpp index 71493a48..3164f005 100755 --- a/bsnes/gba/ppu/object.cpp +++ b/bsnes/gba/ppu/object.cpp @@ -1,5 +1,6 @@ void PPU::render_objects() { - for(unsigned n = 0; n < 240; n++) pixel[n].exists = false; + for(unsigned n = 0; n < 240; n++) pixel[4][n].exists = false; + if(regs.control.enableobj == false) return; for(unsigned n = 0; n < 128; n++) { auto &obj = object[n]; @@ -72,8 +73,8 @@ void PPU::render_object_linear(Object &obj) { 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 }; + if(obj.colors == 0) pixel[4][sx] = { true, palette(256 + obj.palette * 16 + color), obj.priority }; + if(obj.colors == 1) pixel[4][sx] = { true, palette(256 + color), obj.priority }; } } diff --git a/bsnes/gba/ppu/ppu.cpp b/bsnes/gba/ppu/ppu.cpp index 1ad26b58..585445ef 100755 --- a/bsnes/gba/ppu/ppu.cpp +++ b/bsnes/gba/ppu/ppu.cpp @@ -13,6 +13,7 @@ namespace GBA { #include "registers.cpp" +#include "background.cpp" #include "object.cpp" #include "screen.cpp" #include "mmio.cpp" @@ -85,6 +86,7 @@ void PPU::scanline() { if(regs.vcounter == 160) { if(regs.status.irqvblank) cpu.regs.irq.flag.vblank = 1; + cpu.pending.dma.vblank = true; } if(regs.status.irqvcoincidence) { @@ -92,17 +94,21 @@ void PPU::scanline() { } if(regs.vcounter < 160) { + render_backgrounds(); render_objects(); render_screen(); } - step(256 * 4); + step(1024); regs.status.hblank = 1; if(regs.status.irqhblank) cpu.regs.irq.flag.hblank = 1; + cpu.pending.dma.hblank = true; - step( 52 * 4); + step(200); regs.status.hblank = 0; + cpu.pending.dma.hdma = true; + step(8); if(++regs.vcounter == 228) regs.vcounter = 0; } diff --git a/bsnes/gba/ppu/ppu.hpp b/bsnes/gba/ppu/ppu.hpp index 36f1c18e..6dd6bc26 100755 --- a/bsnes/gba/ppu/ppu.hpp +++ b/bsnes/gba/ppu/ppu.hpp @@ -17,6 +17,9 @@ struct PPU : Thread, MMIO { uint8 read(uint32 addr); void write(uint32 addr, uint8 byte); + void render_backgrounds(); + void render_background_linear(unsigned bgnumber); + void render_objects(); void render_object_linear(Object&); void render_object_affine(Object&); diff --git a/bsnes/gba/ppu/screen.cpp b/bsnes/gba/ppu/screen.cpp index 1a51b64c..80543e3d 100755 --- a/bsnes/gba/ppu/screen.cpp +++ b/bsnes/gba/ppu/screen.cpp @@ -9,7 +9,14 @@ 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; + uint15 color = palette(0) & 0x7fff; + for(signed p = 3; p >= 0; p--) { + if(pixel[3][x].exists && pixel[3][x].priority == p) color = pixel[3][x].color; + if(pixel[2][x].exists && pixel[2][x].priority == p) color = pixel[2][x].color; + if(pixel[1][x].exists && pixel[1][x].priority == p) color = pixel[1][x].color; + if(pixel[0][x].exists && pixel[0][x].priority == p) color = pixel[0][x].color; + if(pixel[4][x].exists && pixel[4][x].priority == p) color = pixel[4][x].color; + } + line[x] = color; } } diff --git a/bsnes/gba/ppu/state.hpp b/bsnes/gba/ppu/state.hpp index 1234accb..37257320 100755 --- a/bsnes/gba/ppu/state.hpp +++ b/bsnes/gba/ppu/state.hpp @@ -2,7 +2,7 @@ struct Pixel { bool exists; uint15 color; uint2 priority; -} pixel[256]; +} pixel[5][256]; struct Object { uint8 y; @@ -27,3 +27,10 @@ struct Object { unsigned width; unsigned height; } object[128]; + +struct Tile { + uint10 character; + uint1 hflip; + uint1 vflip; + uint4 palette; +}; diff --git a/bsnes/processor/arm/arm.cpp b/bsnes/processor/arm/arm.cpp index 49844bcd..1a9a35f1 100755 --- a/bsnes/processor/arm/arm.cpp +++ b/bsnes/processor/arm/arm.cpp @@ -16,7 +16,6 @@ void ARM::power() { crash = false; r(15).modify = [&] { pipeline.reload = true; - r(15).data &= cpsr().t ? ~1 : ~3; }; trace = false; diff --git a/bsnes/processor/arm/instructions-arm.cpp b/bsnes/processor/arm/instructions-arm.cpp index 0d31db6a..9807dda2 100755 --- a/bsnes/processor/arm/instructions-arm.cpp +++ b/bsnes/processor/arm/instructions-arm.cpp @@ -5,8 +5,8 @@ void ARM::arm_step() { pipeline.reload = false; r(15).data &= ~3; - pipeline.fetch.address = r(15); - pipeline.fetch.instruction = read(r(15), Word); + pipeline.fetch.address = r(15) & ~3; + pipeline.fetch.instruction = read(pipeline.fetch.address, Word); pipeline_step(); step(2); diff --git a/bsnes/processor/arm/instructions-thumb.cpp b/bsnes/processor/arm/instructions-thumb.cpp index c7320a4a..db9e4f89 100755 --- a/bsnes/processor/arm/instructions-thumb.cpp +++ b/bsnes/processor/arm/instructions-thumb.cpp @@ -5,8 +5,8 @@ void ARM::thumb_step() { pipeline.reload = false; r(15).data &= ~1; - pipeline.fetch.address = r(15); - pipeline.fetch.instruction = read(r(15), Half); + pipeline.fetch.address = r(15) & ~1; + pipeline.fetch.instruction = read(pipeline.fetch.address, Half); pipeline_step(); step(1); diff --git a/bsnes/processor/arm/registers.cpp b/bsnes/processor/arm/registers.cpp index 183a77ef..9292be3f 100755 --- a/bsnes/processor/arm/registers.cpp +++ b/bsnes/processor/arm/registers.cpp @@ -66,12 +66,12 @@ void ARM::pipeline_step() { if(cpsr().t == 0) { r(15).data += 4; - pipeline.fetch.address = r(15); - pipeline.fetch.instruction = read(r(15), Word); + pipeline.fetch.address = r(15) & ~3; + pipeline.fetch.instruction = read(pipeline.fetch.address, Word); } else { r(15).data += 2; - pipeline.fetch.address = r(15); - pipeline.fetch.instruction = read(r(15), Half); + pipeline.fetch.address = r(15) & ~1; + pipeline.fetch.instruction = read(pipeline.fetch.address, Half); } }