mirror of https://github.com/bsnes-emu/bsnes.git
Update to v087r12 release.
byuu says: Enough to get through the BIOS and into cartridge ROM. I am a bit annoyed that I was basically told that the GBA PPU wasn't that bad. Sprites are a clusterfuck, easily worse than Mode7, docs don't even begin to explain them in enough detail. This is going to be fun.
This commit is contained in:
parent
c66cc73374
commit
ea086fe33f
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BASE_HPP
|
#ifndef BASE_HPP
|
||||||
#define BASE_HPP
|
#define BASE_HPP
|
||||||
|
|
||||||
static const char Version[] = "087.11";
|
static const char Version[] = "087.12";
|
||||||
|
|
||||||
#include <nall/platform.hpp>
|
#include <nall/platform.hpp>
|
||||||
#include <nall/algorithm.hpp>
|
#include <nall/algorithm.hpp>
|
||||||
|
|
|
@ -18,6 +18,15 @@ void CPU::enter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
processor.irqline = regs.ime && regs.irq_flag;
|
processor.irqline = regs.ime && regs.irq_flag;
|
||||||
|
|
||||||
|
if(regs.mode == Registers::Mode::Halt) {
|
||||||
|
if((regs.irq_enable & regs.irq_flag) == 0) {
|
||||||
|
step(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
regs.mode = Registers::Mode::Normal;
|
||||||
|
}
|
||||||
|
|
||||||
exec();
|
exec();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,35 +56,75 @@ void CPU::power() {
|
||||||
for(unsigned n = 0; n < iwram.size; n++) iwram.data[n] = 0;
|
for(unsigned n = 0; n < iwram.size; n++) iwram.data[n] = 0;
|
||||||
for(unsigned n = 0; n < ewram.size; n++) ewram.data[n] = 0;
|
for(unsigned n = 0; n < ewram.size; n++) ewram.data[n] = 0;
|
||||||
|
|
||||||
|
regs.dma[0].source = regs.dma[1].source = regs.dma[2].source = regs.dma[3].source = 0;
|
||||||
|
regs.dma[0].target = regs.dma[1].target = regs.dma[2].target = regs.dma[3].target = 0;
|
||||||
|
regs.dma[0].length = regs.dma[1].length = regs.dma[2].length = regs.dma[3].length = 0;
|
||||||
|
regs.dma[0].control = regs.dma[1].control = regs.dma[2].control = regs.dma[3].control = 0;
|
||||||
|
|
||||||
regs.ime = 0;
|
regs.ime = 0;
|
||||||
regs.irq_enable = 0;
|
regs.irq_enable = 0;
|
||||||
regs.irq_flag = 0;
|
regs.irq_flag = 0;
|
||||||
|
regs.postboot = 0;
|
||||||
|
regs.mode = Registers::Mode::Normal;
|
||||||
|
|
||||||
bus.mmio[0x130] = this;
|
for(unsigned n = 0x0b0; n <= 0x0df; n++) bus.mmio[n] = this;
|
||||||
bus.mmio[0x200] = this;
|
|
||||||
bus.mmio[0x202] = this;
|
bus.mmio[0x130] = bus.mmio[0x131] = this;
|
||||||
bus.mmio[0x208] = this;
|
bus.mmio[0x200] = bus.mmio[0x201] = this;
|
||||||
|
bus.mmio[0x202] = bus.mmio[0x203] = this;
|
||||||
|
bus.mmio[0x208] = bus.mmio[0x209] = this;
|
||||||
|
bus.mmio[0x300] = bus.mmio[0x301] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 CPU::read(uint32 addr, uint32 size) {
|
uint32 CPU::read(uint32 addr, uint32 size) {
|
||||||
uint32 result = 0;
|
if(size == Word) {
|
||||||
|
addr &= ~3;
|
||||||
|
uint32 word = 0;
|
||||||
|
word |= read(addr + 0, Byte) << 0;
|
||||||
|
word |= read(addr + 1, Byte) << 8;
|
||||||
|
word |= read(addr + 2, Byte) << 16;
|
||||||
|
word |= read(addr + 3, Byte) << 24;
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
||||||
switch(addr & 0x0ffffffc) {
|
if(size == Half) {
|
||||||
|
addr &= ~3;
|
||||||
|
uint32 half = 0;
|
||||||
|
half |= read(addr + 0, Byte) << 0;
|
||||||
|
half |= read(addr + 0, Byte) << 8;
|
||||||
|
return half;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x04000130: //KEYINPUT
|
uint8 result = 0;
|
||||||
for(unsigned n = 0; n < 10; n++) result |= (interface->inputPoll(n) == false) << n;
|
|
||||||
|
switch(addr & 0x0fffffff) {
|
||||||
|
|
||||||
|
//KEYINPUT
|
||||||
|
case 0x04000130:
|
||||||
|
for(unsigned n = 0; n < 8; n++) result |= (interface->inputPoll(n) == false) << n;
|
||||||
if((result & 0xc0) == 0xc0) result &= ~0xc0; //up+down cannot be pressed simultaneously
|
if((result & 0xc0) == 0xc0) result &= ~0xc0; //up+down cannot be pressed simultaneously
|
||||||
if((result & 0x30) == 0x30) result &= ~0x30; //left+right cannot be pressed simultaneously
|
if((result & 0x30) == 0x30) result &= ~0x30; //left+right cannot be pressed simultaneously
|
||||||
return result;
|
return result;
|
||||||
|
case 0x40000131:
|
||||||
|
result |= (interface->inputPoll(8) == false) << 0;
|
||||||
|
result |= (interface->inputPoll(9) == false) << 1;
|
||||||
|
return result;
|
||||||
|
|
||||||
case 0x04000200: //IE
|
//IE
|
||||||
return regs.irq_enable;
|
case 0x04000200: return regs.irq_enable >> 0;
|
||||||
|
case 0x04000201: return regs.irq_enable >> 8;
|
||||||
|
|
||||||
case 0x04000202: //IF
|
//IF
|
||||||
return regs.irq_flag;
|
case 0x04000202: return regs.irq_flag >> 0;
|
||||||
|
case 0x04000203: return regs.irq_flag >> 8;
|
||||||
|
|
||||||
case 0x04000208: //IME
|
//IME
|
||||||
return regs.ime;
|
case 0x04000208: return regs.ime;
|
||||||
|
case 0x04000209: return 0u;
|
||||||
|
|
||||||
|
//POSTFLG + HALTCNT
|
||||||
|
case 0x04000300: return regs.postboot;
|
||||||
|
case 0x04000301: return 0u;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,19 +132,121 @@ uint32 CPU::read(uint32 addr, uint32 size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::write(uint32 addr, uint32 size, uint32 word) {
|
void CPU::write(uint32 addr, uint32 size, uint32 word) {
|
||||||
switch(addr & 0x0ffffffc) {
|
if(size == Word) {
|
||||||
|
addr &= ~3;
|
||||||
case 0x04000200: //IE
|
write(addr + 0, Byte, word >> 0);
|
||||||
regs.irq_enable = word;
|
write(addr + 1, Byte, word >> 8);
|
||||||
|
write(addr + 2, Byte, word >> 16);
|
||||||
|
write(addr + 3, Byte, word >> 24);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x04000202: //IF
|
if(size == Half) {
|
||||||
regs.irq_flag = regs.irq_flag & ~word;
|
addr &= ~1;
|
||||||
|
write(addr + 0, Byte, word >> 0);
|
||||||
|
write(addr + 1, Byte, word >> 8);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x04000208: //IME
|
uint8 byte = word;
|
||||||
regs.ime = word & 1;
|
|
||||||
return;
|
switch(addr & 0x0fffffff) {
|
||||||
|
|
||||||
|
//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;
|
||||||
|
|
||||||
|
//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;
|
||||||
|
|
||||||
|
//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;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,28 @@
|
||||||
|
CPU::Registers::DMA::Control::operator uint16() const {
|
||||||
|
return (
|
||||||
|
(targetmode << 5)
|
||||||
|
|| (sourcemode << 7)
|
||||||
|
|| (repeat << 9)
|
||||||
|
|| (size << 10)
|
||||||
|
|| (drq << 11)
|
||||||
|
|| (timing << 12)
|
||||||
|
|| (irq << 14)
|
||||||
|
|| (enable << 15)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 CPU::Registers::DMA::Control::operator=(uint16 source) {
|
||||||
|
targetmode = source >> 5;
|
||||||
|
sourcemode = source >> 7;
|
||||||
|
repeat = source >> 9;
|
||||||
|
size = source >> 10;
|
||||||
|
drq = source >> 11;
|
||||||
|
timing = source >> 12;
|
||||||
|
irq = source >> 14;
|
||||||
|
enable = source >> 15;
|
||||||
|
return operator uint16();
|
||||||
|
}
|
||||||
|
|
||||||
CPU::Registers::Interrupt::operator uint16() const {
|
CPU::Registers::Interrupt::operator uint16() const {
|
||||||
return (
|
return (
|
||||||
(vblank << 0)
|
(vblank << 0)
|
||||||
|
@ -17,7 +42,7 @@ CPU::Registers::Interrupt::operator uint16() const {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
CPU::Registers::Interrupt& CPU::Registers::Interrupt::operator=(uint16 source) {
|
uint16 CPU::Registers::Interrupt::operator=(uint16 source) {
|
||||||
vblank = source & (1 << 0);
|
vblank = source & (1 << 0);
|
||||||
hblank = source & (1 << 1);
|
hblank = source & (1 << 1);
|
||||||
vcoincidence = source & (1 << 2);
|
vcoincidence = source & (1 << 2);
|
||||||
|
@ -32,4 +57,5 @@ CPU::Registers::Interrupt& CPU::Registers::Interrupt::operator=(uint16 source) {
|
||||||
dma3 = source & (1 << 11);
|
dma3 = source & (1 << 11);
|
||||||
keypad = source & (1 << 12);
|
keypad = source & (1 << 12);
|
||||||
cartridge = source & (1 << 13);
|
cartridge = source & (1 << 13);
|
||||||
|
return operator uint16();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,26 @@
|
||||||
struct Registers {
|
struct Registers {
|
||||||
bool ime;
|
bool ime;
|
||||||
|
|
||||||
|
struct DMA {
|
||||||
|
uint32 source;
|
||||||
|
uint32 target;
|
||||||
|
uint16 length;
|
||||||
|
struct Control {
|
||||||
|
uint2 targetmode;
|
||||||
|
uint2 sourcemode;
|
||||||
|
uint1 repeat;
|
||||||
|
uint1 size;
|
||||||
|
uint1 drq;
|
||||||
|
uint2 timing;
|
||||||
|
uint1 irq;
|
||||||
|
uint1 enable;
|
||||||
|
|
||||||
|
operator uint16() const;
|
||||||
|
uint16 operator=(uint16 source);
|
||||||
|
DMA& operator=(const DMA&) = delete;
|
||||||
|
} control;
|
||||||
|
} dma[4];
|
||||||
|
|
||||||
struct Interrupt {
|
struct Interrupt {
|
||||||
bool vblank;
|
bool vblank;
|
||||||
bool hblank;
|
bool hblank;
|
||||||
|
@ -18,6 +38,10 @@ struct Registers {
|
||||||
bool cartridge;
|
bool cartridge;
|
||||||
|
|
||||||
operator uint16() const;
|
operator uint16() const;
|
||||||
Interrupt& operator=(uint16 source);
|
uint16 operator=(uint16 source);
|
||||||
|
Interrupt& operator=(const Interrupt&) = delete;
|
||||||
} irq_enable, irq_flag;
|
} irq_enable, irq_flag;
|
||||||
|
|
||||||
|
bool postboot;
|
||||||
|
enum class Mode : unsigned { Normal, Halt, Stop } mode;
|
||||||
} regs;
|
} regs;
|
||||||
|
|
|
@ -12,27 +12,11 @@ struct UnmappedMemory : Memory {
|
||||||
static UnmappedMemory unmappedMemory;
|
static UnmappedMemory unmappedMemory;
|
||||||
|
|
||||||
uint32 StaticMemory::read(uint32 addr, uint32 size) {
|
uint32 StaticMemory::read(uint32 addr, uint32 size) {
|
||||||
uint32 word = 0;
|
|
||||||
|
|
||||||
switch(size) {
|
switch(size) {
|
||||||
case Word:
|
case Word: addr &= ~3; return (data[addr + 0] << 0) | (data[addr + 1] << 8) | (data[addr + 2] << 16) | (data[addr + 3] << 24);
|
||||||
addr &= ~3;
|
case Half: addr &= ~1; return (data[addr + 0] << 0) | (data[addr + 1] << 8);
|
||||||
word |= data[addr + 0] << 0;
|
case Byte: return (data[addr + 0] << 0);
|
||||||
word |= data[addr + 1] << 8;
|
|
||||||
word |= data[addr + 2] << 16;
|
|
||||||
word |= data[addr + 3] << 24;
|
|
||||||
break;
|
|
||||||
case Half:
|
|
||||||
addr &= ~1;
|
|
||||||
word |= data[addr + 0] << 0;
|
|
||||||
word |= data[addr + 1] << 8;
|
|
||||||
break;
|
|
||||||
case Byte:
|
|
||||||
word |= data[addr + 0] << 0;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return word;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StaticMemory::write(uint32 addr, uint32 size, uint32 word) {
|
void StaticMemory::write(uint32 addr, uint32 size, uint32 word) {
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
void PPU::render_bg() {
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
uint32 PPU::read(uint32 addr, uint32 size) {
|
||||||
|
if(size == Word) {
|
||||||
|
addr &= ~3;
|
||||||
|
uint32 word = 0;
|
||||||
|
word |= read(addr + 0, Byte) << 0;
|
||||||
|
word |= read(addr + 1, Byte) << 8;
|
||||||
|
word |= read(addr + 2, Byte) << 16;
|
||||||
|
word |= read(addr + 3, Byte) << 24;
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(size == Half) {
|
||||||
|
addr &= ~3;
|
||||||
|
uint32 half = 0;
|
||||||
|
half |= read(addr + 0, Byte) << 0;
|
||||||
|
half |= read(addr + 0, Byte) << 8;
|
||||||
|
return half;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(addr & 0x0fffffff) {
|
||||||
|
|
||||||
|
//VCOUNT
|
||||||
|
case 0x04000006: return regs.scanline >> 0;
|
||||||
|
case 0x04000007: return regs.scanline >> 8;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPU::write(uint32 addr, uint32 size, uint32 word) {
|
||||||
|
if(size == Word) {
|
||||||
|
addr &= ~3;
|
||||||
|
write(addr + 0, Byte, word >> 0);
|
||||||
|
write(addr + 1, Byte, word >> 8);
|
||||||
|
write(addr + 2, Byte, word >> 16);
|
||||||
|
write(addr + 3, Byte, word >> 24);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(size == Half) {
|
||||||
|
addr &= ~1;
|
||||||
|
write(addr + 0, Byte, word >> 0);
|
||||||
|
write(addr + 1, Byte, word >> 8);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 byte = word;
|
||||||
|
|
||||||
|
switch(addr & 0x0fffffff) {
|
||||||
|
|
||||||
|
//DISPCNT
|
||||||
|
case 0x04000000: regs.control = (regs.control & 0xff00) | (byte << 0); return;
|
||||||
|
case 0x04000001: regs.control = (regs.control & 0x00ff) | (byte << 8); return;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
//BG modes 0,1,2 = 0x10000-0x17fff
|
||||||
|
//BG modes 3,4,5 = 0x14000-0x17fff
|
||||||
|
|
||||||
|
//OAM = 1024 bytes (128 entries x 64-bits)
|
||||||
|
|
||||||
|
// 0- 7 = y
|
||||||
|
// 8 = scale
|
||||||
|
// 9 = scaleflag (0 = single-fold, 1 = double-angle)
|
||||||
|
//10-11 = mode (0 = normal, 1 = semi-transparent, 2 = obj window, 3 = prohibited)
|
||||||
|
// 12 = mosaic
|
||||||
|
// 13 = colormode (0 = 16 colors x 16 palettes, 1 = 256 colors x 1 palette)
|
||||||
|
//14-15 = shape (0 = square, 1 = horizontal, 2 = vertical, 3 = prohibited)
|
||||||
|
|
||||||
|
//00-08 = x
|
||||||
|
//09-11 = rotation/scaling parameter
|
||||||
|
// 12 = hflip
|
||||||
|
// 13 = vflip
|
||||||
|
//14-15 = size
|
||||||
|
|
||||||
|
//00-09 = character
|
||||||
|
//10-11 = priority
|
||||||
|
//12-15 = palette (16-color mode)
|
||||||
|
|
||||||
|
void PPU::render_obj() {
|
||||||
|
}
|
|
@ -12,6 +12,11 @@
|
||||||
|
|
||||||
namespace GBA {
|
namespace GBA {
|
||||||
|
|
||||||
|
#include "registers.cpp"
|
||||||
|
#include "mmio.cpp"
|
||||||
|
#include "bg.cpp"
|
||||||
|
#include "obj.cpp"
|
||||||
|
#include "window.cpp"
|
||||||
PPU ppu;
|
PPU ppu;
|
||||||
|
|
||||||
void PPU::Enter() { ppu.enter(); }
|
void PPU::Enter() { ppu.enter(); }
|
||||||
|
@ -35,9 +40,10 @@ void PPU::power() {
|
||||||
for(unsigned n = 0; n < oam.size; n++) oam.data[n] = 0;
|
for(unsigned n = 0; n < oam.size; n++) oam.data[n] = 0;
|
||||||
for(unsigned n = 0; n < pram.size; n++) pram.data[n] = 0;
|
for(unsigned n = 0; n < pram.size; n++) pram.data[n] = 0;
|
||||||
|
|
||||||
|
regs.control = 0;
|
||||||
regs.scanline = 0;
|
regs.scanline = 0;
|
||||||
|
|
||||||
bus.mmio[0x006] = this;
|
for(unsigned n = 0x000; n <= 0x055; n++) bus.mmio[n] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::scanline() {
|
void PPU::scanline() {
|
||||||
|
@ -55,6 +61,8 @@ void PPU::scanline() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(regs.scanline >= 160) return;
|
if(regs.scanline >= 160) return;
|
||||||
|
render_bg();
|
||||||
|
render_obj();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::frame() {
|
void PPU::frame() {
|
||||||
|
@ -77,18 +85,6 @@ void PPU::frame() {
|
||||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 PPU::read(uint32 addr, uint32 size) {
|
|
||||||
if(addr == 0x04000006) {
|
|
||||||
//VCOUNT
|
|
||||||
return regs.scanline;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0u;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PPU::write(uint32 addr, uint32 size, uint32 word) {
|
|
||||||
}
|
|
||||||
|
|
||||||
PPU::PPU() {
|
PPU::PPU() {
|
||||||
vram.data = new uint8[vram.size = 96 * 1024];
|
vram.data = new uint8[vram.size = 96 * 1024];
|
||||||
oam.data = new uint8[oam.size = 1024];
|
oam.data = new uint8[oam.size = 1024];
|
||||||
|
|
|
@ -2,10 +2,7 @@ struct PPU : Thread, Memory {
|
||||||
StaticMemory vram;
|
StaticMemory vram;
|
||||||
StaticMemory oam;
|
StaticMemory oam;
|
||||||
StaticMemory pram;
|
StaticMemory pram;
|
||||||
|
#include "registers.hpp"
|
||||||
struct Registers {
|
|
||||||
unsigned scanline;
|
|
||||||
} regs;
|
|
||||||
|
|
||||||
static void Enter();
|
static void Enter();
|
||||||
void enter();
|
void enter();
|
||||||
|
@ -18,6 +15,10 @@ struct PPU : Thread, Memory {
|
||||||
uint32 read(uint32 addr, uint32 size);
|
uint32 read(uint32 addr, uint32 size);
|
||||||
void write(uint32 addr, uint32 size, uint32 word);
|
void write(uint32 addr, uint32 size, uint32 word);
|
||||||
|
|
||||||
|
void render_bg();
|
||||||
|
void render_obj();
|
||||||
|
void render_window();
|
||||||
|
|
||||||
PPU();
|
PPU();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
PPU::Registers::Control::operator uint16() const {
|
||||||
|
return (
|
||||||
|
(bgmode << 0)
|
||||||
|
|| (cgbmode << 3)
|
||||||
|
|| (frame << 4)
|
||||||
|
|| (hblank << 5)
|
||||||
|
|| (objmap << 6)
|
||||||
|
|| (forceblank << 7)
|
||||||
|
|| (displaybg0 << 8)
|
||||||
|
|| (displaybg1 << 9)
|
||||||
|
|| (displaybg2 << 10)
|
||||||
|
|| (displaybg3 << 11)
|
||||||
|
|| (displayobj << 12)
|
||||||
|
|| (displaybgwindow0 << 13)
|
||||||
|
|| (displaybgwindow1 << 14)
|
||||||
|
|| (displayobjwindow << 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;
|
||||||
|
displaybg0 = source & 0x0100;
|
||||||
|
displaybg1 = source & 0x0200;
|
||||||
|
displaybg2 = source & 0x0400;
|
||||||
|
displaybg3 = source & 0x0800;
|
||||||
|
displayobj = source & 0x1000;
|
||||||
|
displaybgwindow0 = source & 0x2000;
|
||||||
|
displaybgwindow1 = source & 0x4000;
|
||||||
|
displayobjwindow = source & 0x8000;
|
||||||
|
return operator uint16();
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
struct Registers {
|
||||||
|
struct Control {
|
||||||
|
uint3 bgmode;
|
||||||
|
bool cgbmode;
|
||||||
|
bool frame;
|
||||||
|
bool hblank;
|
||||||
|
bool objmap;
|
||||||
|
bool forceblank;
|
||||||
|
bool displaybg0;
|
||||||
|
bool displaybg1;
|
||||||
|
bool displaybg2;
|
||||||
|
bool displaybg3;
|
||||||
|
bool displayobj;
|
||||||
|
bool displaybgwindow0;
|
||||||
|
bool displaybgwindow1;
|
||||||
|
bool displayobjwindow;
|
||||||
|
|
||||||
|
operator uint16() const;
|
||||||
|
uint16 operator=(uint16 source);
|
||||||
|
Control& operator=(const Control&) = delete;
|
||||||
|
} control;
|
||||||
|
|
||||||
|
uint16 scanline;
|
||||||
|
} regs;
|
|
@ -0,0 +1,2 @@
|
||||||
|
void PPU::render_window() {
|
||||||
|
}
|
|
@ -32,6 +32,18 @@ void ARM::exec() {
|
||||||
cpsr().t ? thumb_step() : arm_step();
|
cpsr().t ? thumb_step() : arm_step();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 ARM::read(uint32 addr, uint32 size) {
|
||||||
|
uint32 word = bus_read(addr, size);
|
||||||
|
//uint32 rotate = (addr & 3) << 3;
|
||||||
|
//word = (word >> rotate) | (word << (32 - rotate));
|
||||||
|
//word = word & (~0u >> (32 - size));
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARM::write(uint32 addr, uint32 size, uint32 word) {
|
||||||
|
return bus_write(addr, size, word);
|
||||||
|
}
|
||||||
|
|
||||||
void ARM::vector(uint32 addr, Processor::Mode mode) {
|
void ARM::vector(uint32 addr, Processor::Mode mode) {
|
||||||
auto psr = cpsr();
|
auto psr = cpsr();
|
||||||
processor.setMode(mode);
|
processor.setMode(mode);
|
||||||
|
|
|
@ -17,6 +17,8 @@ struct ARM {
|
||||||
|
|
||||||
void power();
|
void power();
|
||||||
void exec();
|
void exec();
|
||||||
|
uint32 read(uint32 addr, uint32 size);
|
||||||
|
void write(uint32 addr, uint32 size, uint32 word);
|
||||||
void vector(uint32 addr, Processor::Mode mode);
|
void vector(uint32 addr, Processor::Mode mode);
|
||||||
|
|
||||||
bool condition(uint4 condition);
|
bool condition(uint4 condition);
|
||||||
|
|
|
@ -32,7 +32,7 @@ string ARM::disassemble_arm_instruction(uint32 pc) {
|
||||||
|
|
||||||
string output{hex<8>(pc), " "};
|
string output{hex<8>(pc), " "};
|
||||||
|
|
||||||
uint32 instruction = bus_read(pc, Word);
|
uint32 instruction = read(pc & ~3, Word);
|
||||||
output.append(hex<8>(instruction), " ");
|
output.append(hex<8>(instruction), " ");
|
||||||
|
|
||||||
//multiply()
|
//multiply()
|
||||||
|
@ -45,7 +45,7 @@ string ARM::disassemble_arm_instruction(uint32 pc) {
|
||||||
uint4 rd = instruction >> 16;
|
uint4 rd = instruction >> 16;
|
||||||
uint4 rn = instruction >> 12;
|
uint4 rn = instruction >> 12;
|
||||||
uint4 rs = instruction >> 8;
|
uint4 rs = instruction >> 8;
|
||||||
uint4 rm = instruction >> 0;
|
uint4 rm = instruction;
|
||||||
|
|
||||||
output.append(accumulate ? "mla" : "mul", conditions[condition], save ? "s " : " ");
|
output.append(accumulate ? "mla" : "mul", conditions[condition], save ? "s " : " ");
|
||||||
output.append(registers[rd], ",", registers[rm], ",", registers[rs]);
|
output.append(registers[rd], ",", registers[rm], ",", registers[rs]);
|
||||||
|
@ -54,6 +54,25 @@ string ARM::disassemble_arm_instruction(uint32 pc) {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//multiply_long()
|
||||||
|
//(u,s)mull{condition}{s} rdlo,rdhi,rm,rs
|
||||||
|
//(u,s)mlal{condition}{s} rdlo,rdhi,rm,rs
|
||||||
|
if((instruction & 0x0f8000f0) == 0x00800090) {
|
||||||
|
uint4 condition = instruction >> 28;
|
||||||
|
uint1 signextend = instruction >> 22;
|
||||||
|
uint1 accumulate = instruction >> 21;
|
||||||
|
uint1 save = instruction >> 20;
|
||||||
|
uint4 rdhi = instruction >> 16;
|
||||||
|
uint4 rdlo = instruction >> 12;
|
||||||
|
uint4 rs = instruction >> 8;
|
||||||
|
uint4 rm = instruction;
|
||||||
|
|
||||||
|
output.append(signextend ? "s" : "u", accumulate ? "mlal" : "mull", conditions[condition], save ? "s " : " ");
|
||||||
|
output.append(registers[rdlo], ",", registers[rdhi], ",", registers[rm], ",", registers[rs]);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
//memory_swap()
|
//memory_swap()
|
||||||
//swp{condition}{b} rd,rm,[rn]
|
//swp{condition}{b} rd,rm,[rn]
|
||||||
if((instruction & 0x0fb000f0) == 0x01000090) {
|
if((instruction & 0x0fb000f0) == 0x01000090) {
|
||||||
|
@ -115,7 +134,7 @@ string ARM::disassemble_arm_instruction(uint32 pc) {
|
||||||
if(pre == 1) output.append("]");
|
if(pre == 1) output.append("]");
|
||||||
if(pre == 0 || writeback == 1) output.append("!");
|
if(pre == 0 || writeback == 1) output.append("!");
|
||||||
|
|
||||||
if(rn == 15) output.append(" =0x", hex<4>(bus_read(pc + 8 + (up ? +immediate : -immediate), Half)));
|
if(rn == 15) output.append(" =0x", hex<4>(read(pc + 8 + (up ? +immediate : -immediate), Half)));
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,8 +184,8 @@ string ARM::disassemble_arm_instruction(uint32 pc) {
|
||||||
if(pre == 1) output.append("]");
|
if(pre == 1) output.append("]");
|
||||||
if(pre == 0 || writeback == 1) output.append("!");
|
if(pre == 0 || writeback == 1) output.append("!");
|
||||||
|
|
||||||
if(rn == 15 && half == 1) output.append(" =0x", hex<4>(bus_read(pc + 8 + (up ? +immediate : -immediate), Half)));
|
if(rn == 15 && half == 1) output.append(" =0x", hex<4>(read(pc + 8 + (up ? +immediate : -immediate), Half)));
|
||||||
if(rn == 15 && half == 0) output.append(" =0x", hex<2>(bus_read(pc + 8 + (up ? +immediate : -immediate), Byte)));
|
if(rn == 15 && half == 0) output.append(" =0x", hex<2>(read(pc + 8 + (up ? +immediate : -immediate), Byte)));
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,7 +359,7 @@ string ARM::disassemble_arm_instruction(uint32 pc) {
|
||||||
if(pre == 1) output.append("]");
|
if(pre == 1) output.append("]");
|
||||||
if(pre == 0 || writeback == 1) output.append("!");
|
if(pre == 0 || writeback == 1) output.append("!");
|
||||||
|
|
||||||
if(rn == 15) output.append(" =0x", hex<8>(bus_read(pc + 8 + (up ? +immediate : -immediate), byte ? Byte : Word)));
|
if(rn == 15) output.append(" =0x", hex<8>(read(pc + 8 + (up ? +immediate : -immediate), byte ? Byte : Word)));
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,7 +457,7 @@ string ARM::disassemble_thumb_instruction(uint32 pc) {
|
||||||
|
|
||||||
string output{hex<8>(pc), " "};
|
string output{hex<8>(pc), " "};
|
||||||
|
|
||||||
uint16 instruction = bus_read(pc, Half);
|
uint16 instruction = read(pc & ~1, Half);
|
||||||
output.append(hex<4>(instruction), " ");
|
output.append(hex<4>(instruction), " ");
|
||||||
|
|
||||||
//adjust_register()
|
//adjust_register()
|
||||||
|
@ -552,7 +571,7 @@ string ARM::disassemble_thumb_instruction(uint32 pc) {
|
||||||
|
|
||||||
unsigned rm = ((pc + 4) & ~3) + displacement * 4;
|
unsigned rm = ((pc + 4) & ~3) + displacement * 4;
|
||||||
output.append("ldr ", registers[rd], ",[pc,#0x", hex<3>(rm), "]");
|
output.append("ldr ", registers[rd], ",[pc,#0x", hex<3>(rm), "]");
|
||||||
output.append(" =0x", hex<8>(bus_read(rm, Word)));
|
output.append(" =0x", hex<8>(read(rm, Word)));
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
@ -721,7 +740,7 @@ string ARM::disassemble_thumb_instruction(uint32 pc) {
|
||||||
//bl address
|
//bl address
|
||||||
if((instruction & 0xf800) == 0xf000) {
|
if((instruction & 0xf800) == 0xf000) {
|
||||||
uint11 offsethi = instruction;
|
uint11 offsethi = instruction;
|
||||||
instruction = bus_read(pc + 2, Half);
|
instruction = read((pc & ~1) + 2, Half);
|
||||||
uint11 offsetlo = instruction;
|
uint11 offsetlo = instruction;
|
||||||
|
|
||||||
int22 displacement = (offsethi << 11) | (offsetlo << 0);
|
int22 displacement = (offsethi << 11) | (offsetlo << 0);
|
||||||
|
@ -747,12 +766,14 @@ string ARM::disassemble_registers() {
|
||||||
output.append( "r0:", hex<8>(r( 0)), " r1:", hex<8>(r( 1)), " r2:", hex<8>(r( 2)), " r3:", hex<8>(r( 3)), " ");
|
output.append( "r0:", hex<8>(r( 0)), " r1:", hex<8>(r( 1)), " r2:", hex<8>(r( 2)), " r3:", hex<8>(r( 3)), " ");
|
||||||
output.append( "r4:", hex<8>(r( 4)), " r5:", hex<8>(r( 5)), " r6:", hex<8>(r( 6)), " r7:", hex<8>(r( 7)), " ");
|
output.append( "r4:", hex<8>(r( 4)), " r5:", hex<8>(r( 5)), " r6:", hex<8>(r( 6)), " r7:", hex<8>(r( 7)), " ");
|
||||||
output.append("cpsr:", cpsr().n ? "N" : "n", cpsr().z ? "Z" : "z", cpsr().c ? "C" : "c", cpsr().v ? "V" : "v");
|
output.append("cpsr:", cpsr().n ? "N" : "n", cpsr().z ? "Z" : "z", cpsr().c ? "C" : "c", cpsr().v ? "V" : "v");
|
||||||
|
output.append("/", cpsr().i ? "I" : "i", cpsr().f ? "F" : "f", cpsr().t ? "T" : "t");
|
||||||
output.append("/", hex<2>(cpsr().m), "\n");
|
output.append("/", hex<2>(cpsr().m), "\n");
|
||||||
output.append( "r8:", hex<8>(r( 8)), " r9:", hex<8>(r( 9)), " r10:", hex<8>(r(10)), " r11:", hex<8>(r(11)), " ");
|
output.append( "r8:", hex<8>(r( 8)), " r9:", hex<8>(r( 9)), " r10:", hex<8>(r(10)), " r11:", hex<8>(r(11)), " ");
|
||||||
output.append("r12:", hex<8>(r(12)), " sp:", hex<8>(r(13)), " lr:", hex<8>(r(14)), " pc:", hex<8>(r(15)), " ");
|
output.append("r12:", hex<8>(r(12)), " sp:", hex<8>(r(13)), " lr:", hex<8>(r(14)), " pc:", hex<8>(r(15)), " ");
|
||||||
output.append("spsr:");
|
output.append("spsr:");
|
||||||
if(mode() == Processor::Mode::USR || mode() == Processor::Mode::SYS) { output.append("----/--"); return output; }
|
if(mode() == Processor::Mode::USR || mode() == Processor::Mode::SYS) { output.append("----/---/--"); return output; }
|
||||||
output.append( spsr().n ? "N" : "n", spsr().z ? "Z" : "z", spsr().c ? "C" : "c", spsr().v ? "V" : "v");
|
output.append( spsr().n ? "N" : "n", spsr().z ? "Z" : "z", spsr().c ? "C" : "c", spsr().v ? "V" : "v");
|
||||||
|
output.append("/", spsr().i ? "I" : "i", spsr().f ? "F" : "f", spsr().t ? "T" : "t");
|
||||||
output.append("/", hex<2>(spsr().m));
|
output.append("/", hex<2>(spsr().m));
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
void ARM::arm_step() {
|
void ARM::arm_step() {
|
||||||
if(pipeline.reload) {
|
if(pipeline.reload) {
|
||||||
pipeline.reload = false;
|
pipeline.reload = false;
|
||||||
|
r(15).data &= ~3;
|
||||||
|
|
||||||
pipeline.fetch.address = r(15);
|
pipeline.fetch.address = r(15);
|
||||||
pipeline.fetch.instruction = bus_read(r(15), Word);
|
pipeline.fetch.instruction = read(r(15), Word);
|
||||||
|
|
||||||
pipeline_step();
|
pipeline_step();
|
||||||
step(2);
|
step(2);
|
||||||
|
@ -18,6 +19,7 @@ void ARM::arm_step() {
|
||||||
if(trace) {
|
if(trace) {
|
||||||
print(disassemble_registers(), "\n");
|
print(disassemble_registers(), "\n");
|
||||||
print(disassemble_arm_instruction(pipeline.execute.address), "\n");
|
print(disassemble_arm_instruction(pipeline.execute.address), "\n");
|
||||||
|
usleep(100000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(condition(instruction() >> 28) == false) return;
|
if(condition(instruction() >> 28) == false) return;
|
||||||
|
@ -29,6 +31,7 @@ void ARM::arm_step() {
|
||||||
|
|
||||||
decode("???? 0001 0010 ++++ ++++ ++++ 0001 ????", branch_exchange_register);
|
decode("???? 0001 0010 ++++ ++++ ++++ 0001 ????", branch_exchange_register);
|
||||||
decode("???? 0000 00?? ???? ???? ???? 1001 ????", multiply);
|
decode("???? 0000 00?? ???? ???? ???? 1001 ????", multiply);
|
||||||
|
decode("???? 0000 1??? ???? ???? ???? 1001 ????", multiply_long);
|
||||||
decode("???? 0001 0?00 ++++ ???? ---- 0000 ----", move_to_register_from_status);
|
decode("???? 0001 0?00 ++++ ???? ---- 0000 ----", move_to_register_from_status);
|
||||||
decode("???? 0001 0?00 ???? ???? ---- 1001 ????", memory_swap);
|
decode("???? 0001 0?00 ???? ???? ---- 1001 ????", memory_swap);
|
||||||
decode("???? 0001 0?10 ???? ++++ ---- 0000 ????", move_to_status_from_register);
|
decode("???? 0001 0?10 ???? ++++ ---- 0000 ????", move_to_status_from_register);
|
||||||
|
@ -122,19 +125,57 @@ void ARM::arm_move_to_status(uint32 rm) {
|
||||||
//d = rd
|
//d = rd
|
||||||
//n = rn
|
//n = rn
|
||||||
//s = rs
|
//s = rs
|
||||||
//n = rm
|
//m = rm
|
||||||
void ARM::arm_op_multiply() {
|
void ARM::arm_op_multiply() {
|
||||||
uint1 accumulate = instruction() >> 21;
|
uint1 accumulate = instruction() >> 21;
|
||||||
|
uint1 save = instruction() >> 20;
|
||||||
uint4 d = instruction() >> 16;
|
uint4 d = instruction() >> 16;
|
||||||
uint4 n = instruction() >> 12;
|
uint4 n = instruction() >> 12;
|
||||||
uint4 s = instruction() >> 8;
|
uint4 s = instruction() >> 8;
|
||||||
uint4 m = instruction() >> 0;
|
uint4 m = instruction();
|
||||||
|
|
||||||
r(d) = accumulate ? r(n) : 0u;
|
r(d) = accumulate ? r(n) : 0u;
|
||||||
step(1);
|
step(1);
|
||||||
r(d) = mul(r(d), r(m), r(s));
|
r(d) = mul(r(d), r(m), r(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//(u,s)mull{condition}{s} rdlo,rdhi,rm,rs
|
||||||
|
//(u,s)mlal{condition}{s} rdlo,rdhi,rm,rs
|
||||||
|
//cccc 0000 1sas hhhh llll ssss 1001 mmmm
|
||||||
|
//c = condition
|
||||||
|
//s = sign-extend
|
||||||
|
//a = accumulate
|
||||||
|
//s = save flags
|
||||||
|
//h = rdhi
|
||||||
|
//l = rdlo
|
||||||
|
//s = rs
|
||||||
|
//m = rm
|
||||||
|
void ARM::arm_op_multiply_long() {
|
||||||
|
uint1 signextend = instruction() >> 22;
|
||||||
|
uint1 accumulate = instruction() >> 21;
|
||||||
|
uint1 save = instruction() >> 20;
|
||||||
|
uint4 dhi = instruction() >> 16;
|
||||||
|
uint4 dlo = instruction() >> 12;
|
||||||
|
uint4 s = instruction() >> 8;
|
||||||
|
uint4 m = instruction();
|
||||||
|
|
||||||
|
uint64 rm = signextend ? r(m) : (int32)r(m);
|
||||||
|
uint64 rs = signextend ? r(s) : (int32)r(s);
|
||||||
|
|
||||||
|
uint64 rd = rm * rs;
|
||||||
|
if(accumulate) rd += ((uint64)r(dhi) << 32) + ((uint64)r(dlo) << 0);
|
||||||
|
|
||||||
|
r(dhi) = rd >> 32;
|
||||||
|
r(dlo) = rd >> 0;
|
||||||
|
|
||||||
|
if(save) {
|
||||||
|
cpsr().n = r(dhi) >> 31;
|
||||||
|
cpsr().z = r(dhi) == 0 && r(dlo) == 0;
|
||||||
|
cpsr().c = 0;
|
||||||
|
cpsr().v = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//swp{condition}{b} rd,rm,[rn]
|
//swp{condition}{b} rd,rm,[rn]
|
||||||
//cccc 0001 0b00 nnnn dddd ---- 1001 mmmm
|
//cccc 0001 0b00 nnnn dddd ---- 1001 mmmm
|
||||||
//c = condition
|
//c = condition
|
||||||
|
@ -148,8 +189,8 @@ void ARM::arm_op_memory_swap() {
|
||||||
uint4 d = instruction() >> 12;
|
uint4 d = instruction() >> 12;
|
||||||
uint4 m = instruction();
|
uint4 m = instruction();
|
||||||
|
|
||||||
uint32 word = bus_read(r(n), byte ? Byte : Word);
|
uint32 word = read(r(n), byte ? Byte : Word);
|
||||||
bus_write(r(n), byte ? Byte : Word, r(m));
|
write(r(n), byte ? Byte : Word, r(m));
|
||||||
r(d) = word;
|
r(d) = word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,8 +218,8 @@ void ARM::arm_op_move_half_register() {
|
||||||
uint32 rm = r(m);
|
uint32 rm = r(m);
|
||||||
|
|
||||||
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
||||||
if(load == 1) r(d) = bus_read(rn, Half);
|
if(load == 1) r(d) = read(rn, Half);
|
||||||
if(load == 0) bus_write(rn, Half, r(d));
|
if(load == 0) write(rn, Half, r(d));
|
||||||
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
||||||
|
|
||||||
if(pre == 0 || writeback == 1) r(n) = rn;
|
if(pre == 0 || writeback == 1) r(n) = rn;
|
||||||
|
@ -210,8 +251,8 @@ void ARM::arm_op_move_half_immediate() {
|
||||||
uint8 immediate = (ih << 4) + (il << 0);
|
uint8 immediate = (ih << 4) + (il << 0);
|
||||||
|
|
||||||
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
|
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
|
||||||
if(load == 1) r(d) = bus_read(rn, Half);
|
if(load == 1) r(d) = read(rn, Half);
|
||||||
if(load == 0) bus_write(rn, Half, r(d));
|
if(load == 0) write(rn, Half, r(d));
|
||||||
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
|
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
|
||||||
|
|
||||||
if(pre == 0 || writeback == 1) r(n) = rn;
|
if(pre == 0 || writeback == 1) r(n) = rn;
|
||||||
|
@ -241,7 +282,7 @@ void ARM::arm_op_load_register() {
|
||||||
uint32 rm = r(m);
|
uint32 rm = r(m);
|
||||||
|
|
||||||
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
||||||
uint32 word = bus_read(rn, half ? Half : Byte);
|
uint32 word = read(rn, half ? Half : Byte);
|
||||||
r(d) = half ? (int16)word : (int8)word;
|
r(d) = half ? (int16)word : (int8)word;
|
||||||
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
||||||
|
|
||||||
|
@ -274,7 +315,7 @@ void ARM::arm_op_load_immediate() {
|
||||||
uint8 immediate = (ih << 4) + (il << 0);
|
uint8 immediate = (ih << 4) + (il << 0);
|
||||||
|
|
||||||
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
|
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
|
||||||
uint32 word = bus_read(rn, half ? Half : Byte);
|
uint32 word = read(rn, half ? Half : Byte);
|
||||||
r(d) = half ? (int16)word : (int8)word;
|
r(d) = half ? (int16)word : (int8)word;
|
||||||
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
|
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
|
||||||
|
|
||||||
|
@ -445,11 +486,8 @@ void ARM::arm_op_move_immediate_offset() {
|
||||||
auto &rd = r(d);
|
auto &rd = r(d);
|
||||||
|
|
||||||
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
||||||
if(load) {
|
if(load == 1) rd = read(rn, byte ? Byte : Word);
|
||||||
rd = bus_read(rn, byte ? Byte : Word);
|
if(load == 0) write(rn, byte ? Byte : Word, rd);
|
||||||
} else {
|
|
||||||
bus_write(rn, byte ? Byte : Word, rd);
|
|
||||||
}
|
|
||||||
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
||||||
|
|
||||||
if(pre == 0 || writeback == 1) r(n) = rn;
|
if(pre == 0 || writeback == 1) r(n) = rn;
|
||||||
|
@ -493,11 +531,8 @@ void ARM::arm_op_move_register_offset() {
|
||||||
if(mode == 3) rm = rs ? ror(rm, rs) : rrx(rm);
|
if(mode == 3) rm = rs ? ror(rm, rs) : rrx(rm);
|
||||||
|
|
||||||
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
||||||
if(load) {
|
if(load == 1) rd = read(rn, byte ? Byte : Word);
|
||||||
rd = bus_read(rn, byte ? Byte : Word);
|
if(load == 0) write(rn, byte ? Byte : Word, rd);
|
||||||
} else {
|
|
||||||
bus_write(rn, byte ? Byte : Word, rd);
|
|
||||||
}
|
|
||||||
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
||||||
|
|
||||||
if(pre == 0 || writeback == 1) r(n) = rn;
|
if(pre == 0 || writeback == 1) r(n) = rn;
|
||||||
|
@ -537,8 +572,8 @@ void ARM::arm_op_move_multiple() {
|
||||||
|
|
||||||
for(unsigned n = 0; n < 16; n++) {
|
for(unsigned n = 0; n < 16; n++) {
|
||||||
if(list & (1 << n)) {
|
if(list & (1 << n)) {
|
||||||
if(load) r(n) = bus_read(rn, Word);
|
if(load == 1) r(n) = read(rn, Word);
|
||||||
else bus_write(rn, Word, r(n));
|
if(load == 0) write(rn, Word, r(n));
|
||||||
rn += 4;
|
rn += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ void arm_opcode(uint32 rm);
|
||||||
void arm_move_to_status(uint32 rm);
|
void arm_move_to_status(uint32 rm);
|
||||||
|
|
||||||
void arm_op_multiply();
|
void arm_op_multiply();
|
||||||
|
void arm_op_multiply_long();
|
||||||
void arm_op_memory_swap();
|
void arm_op_memory_swap();
|
||||||
void arm_op_move_half_register();
|
void arm_op_move_half_register();
|
||||||
void arm_op_move_half_immediate();
|
void arm_op_move_half_immediate();
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
void ARM::thumb_step() {
|
void ARM::thumb_step() {
|
||||||
if(pipeline.reload) {
|
if(pipeline.reload) {
|
||||||
pipeline.reload = false;
|
pipeline.reload = false;
|
||||||
|
r(15).data &= ~1;
|
||||||
|
|
||||||
pipeline.fetch.address = r(15);
|
pipeline.fetch.address = r(15);
|
||||||
pipeline.fetch.instruction = bus_read(r(15), Half);
|
pipeline.fetch.instruction = read(r(15), Half);
|
||||||
|
|
||||||
pipeline_step();
|
pipeline_step();
|
||||||
step(1);
|
step(1);
|
||||||
|
@ -198,7 +199,7 @@ void ARM::thumb_op_load_literal() {
|
||||||
uint8 displacement = instruction();
|
uint8 displacement = instruction();
|
||||||
|
|
||||||
unsigned rm = (r(15) & ~3) + displacement * 4;
|
unsigned rm = (r(15) & ~3) + displacement * 4;
|
||||||
r(d) = bus_read(rm, Word);
|
r(d) = read(rm, Word);
|
||||||
}
|
}
|
||||||
|
|
||||||
//(ld(r,s),str){b,h} rd,[rn,rm]
|
//(ld(r,s),str){b,h} rd,[rn,rm]
|
||||||
|
@ -214,14 +215,14 @@ void ARM::thumb_op_move_register_offset() {
|
||||||
uint3 d = instruction() >> 0;
|
uint3 d = instruction() >> 0;
|
||||||
|
|
||||||
switch(opcode) {
|
switch(opcode) {
|
||||||
case 0: bus_write(r(n) + r(m), Word, r(d)); break; //STR
|
case 0: write(r(n) + r(m), Word, r(d)); break; //STR
|
||||||
case 1: bus_write(r(n) + r(m), Half, r(d)); break; //STRH
|
case 1: write(r(n) + r(m), Half, r(d)); break; //STRH
|
||||||
case 2: bus_write(r(n) + r(m), Byte, r(d)); break; //STRB
|
case 2: write(r(n) + r(m), Byte, r(d)); break; //STRB
|
||||||
case 3: r(d) = (int8)bus_read(r(n) + r(m), Byte); break; //LDSB
|
case 3: r(d) = (int8)read(r(n) + r(m), Byte); break; //LDSB
|
||||||
case 4: r(d) = bus_read(r(n) + r(m), Word); break; //LDR
|
case 4: r(d) = read(r(n) + r(m), Word); break; //LDR
|
||||||
case 5: r(d) = bus_read(r(n) + r(m), Half); break; //LDRH
|
case 5: r(d) = read(r(n) + r(m), Half); break; //LDRH
|
||||||
case 6: r(d) = bus_read(r(n) + r(m), Byte); break; //LDRB
|
case 6: r(d) = read(r(n) + r(m), Byte); break; //LDRB
|
||||||
case 7: r(d) = (int16)bus_read(r(n) + r(m), Half); break; //LDSH
|
case 7: r(d) = (int16)read(r(n) + r(m), Half); break; //LDSH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,8 +238,8 @@ void ARM::thumb_op_move_word_immediate() {
|
||||||
uint3 n = instruction() >> 3;
|
uint3 n = instruction() >> 3;
|
||||||
uint3 d = instruction() >> 0;
|
uint3 d = instruction() >> 0;
|
||||||
|
|
||||||
if(load == 1) r(d) = bus_read(r(n) + offset * 4, Word);
|
if(load == 1) r(d) = read(r(n) + offset * 4, Word);
|
||||||
if(load == 0) bus_write(r(n) + offset * 4, Word, r(d));
|
if(load == 0) write(r(n) + offset * 4, Word, r(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
//(ldr,str)b rd,[rn,#offset]
|
//(ldr,str)b rd,[rn,#offset]
|
||||||
|
@ -253,8 +254,8 @@ void ARM::thumb_op_move_byte_immediate() {
|
||||||
uint3 n = instruction() >> 3;
|
uint3 n = instruction() >> 3;
|
||||||
uint3 d = instruction() >> 0;
|
uint3 d = instruction() >> 0;
|
||||||
|
|
||||||
if(load == 1) r(d) = bus_read(r(n) + offset, Byte);
|
if(load == 1) r(d) = read(r(n) + offset, Byte);
|
||||||
if(load == 0) bus_write(r(n) + offset, Byte, r(d));
|
if(load == 0) write(r(n) + offset, Byte, r(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
//(ldr,str)h rd,[rn,#offset]
|
//(ldr,str)h rd,[rn,#offset]
|
||||||
|
@ -269,8 +270,8 @@ void ARM::thumb_op_move_half_immediate() {
|
||||||
uint3 n = instruction() >> 3;
|
uint3 n = instruction() >> 3;
|
||||||
uint3 d = instruction() >> 0;
|
uint3 d = instruction() >> 0;
|
||||||
|
|
||||||
if(load == 1) r(d) = bus_read(r(n) + offset * 2, Half);
|
if(load == 1) r(d) = read(r(n) + offset * 2, Half);
|
||||||
if(load == 0) bus_write(r(n) + offset * 2, Half, r(d));
|
if(load == 0) write(r(n) + offset * 2, Half, r(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
//(ldr,str) rd,[sp,#immediate]
|
//(ldr,str) rd,[sp,#immediate]
|
||||||
|
@ -283,8 +284,8 @@ void ARM::thumb_op_move_stack() {
|
||||||
uint3 d = instruction() >> 8;
|
uint3 d = instruction() >> 8;
|
||||||
uint8 immediate = instruction();
|
uint8 immediate = instruction();
|
||||||
|
|
||||||
if(opcode == 0) bus_write(r(13) + immediate * 4, Word, r(d));
|
if(opcode == 0) write(r(13) + immediate * 4, Word, r(d));
|
||||||
if(opcode == 1) r(d) = bus_read(r(13) + immediate * 4, Word);
|
if(opcode == 1) r(d) = read(r(13) + immediate * 4, Word);
|
||||||
}
|
}
|
||||||
|
|
||||||
//add rd,{pc,sp},#immediate
|
//add rd,{pc,sp},#immediate
|
||||||
|
@ -330,16 +331,16 @@ void ARM::thumb_op_stack_multiple() {
|
||||||
|
|
||||||
for(unsigned l = 0; l < 8; l++) {
|
for(unsigned l = 0; l < 8; l++) {
|
||||||
if(list & (1 << l)) {
|
if(list & (1 << l)) {
|
||||||
if(load == 1) r(l) = bus_read(sp, Word); //POP
|
if(load == 1) r(l) = read(sp, Word); //POP
|
||||||
if(load == 0) bus_write(sp, Word, r(l)); //PUSH
|
if(load == 0) write(sp, Word, r(l)); //PUSH
|
||||||
sp += 4;
|
sp += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(branch) {
|
if(branch) {
|
||||||
//note: ARMv5+ POP sets cpsr().t
|
//note: ARMv5+ POP sets cpsr().t
|
||||||
if(load == 1) r(15) = bus_read(sp, Word); //POP
|
if(load == 1) r(15) = read(sp, Word); //POP
|
||||||
if(load == 0) bus_write(sp, Word, r(14)); //PUSH
|
if(load == 0) write(sp, Word, r(14)); //PUSH
|
||||||
sp += 4;
|
sp += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,8 +360,8 @@ void ARM::thumb_op_move_multiple() {
|
||||||
|
|
||||||
for(unsigned l = 0; l < 8; l++) {
|
for(unsigned l = 0; l < 8; l++) {
|
||||||
if(list & (1 << l)) {
|
if(list & (1 << l)) {
|
||||||
if(load == 1) r(l) = bus_read(r(n), Word); //LDMIA
|
if(load == 1) r(l) = read(r(n), Word); //LDMIA
|
||||||
if(load == 0) bus_write(r(n), Word, r(l)); //STMIA
|
if(load == 0) write(r(n), Word, r(l)); //STMIA
|
||||||
r(n) += 4;
|
r(n) += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,11 +67,11 @@ void ARM::pipeline_step() {
|
||||||
if(cpsr().t == 0) {
|
if(cpsr().t == 0) {
|
||||||
r(15).data += 4;
|
r(15).data += 4;
|
||||||
pipeline.fetch.address = r(15);
|
pipeline.fetch.address = r(15);
|
||||||
pipeline.fetch.instruction = bus_read(r(15), Word);
|
pipeline.fetch.instruction = read(r(15), Word);
|
||||||
} else {
|
} else {
|
||||||
r(15).data += 2;
|
r(15).data += 2;
|
||||||
pipeline.fetch.address = r(15);
|
pipeline.fetch.address = r(15);
|
||||||
pipeline.fetch.instruction = bus_read(r(15), Half);
|
pipeline.fetch.instruction = read(r(15), Half);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,30 +1,19 @@
|
||||||
#ifdef ARMDSP_CPP
|
#ifdef ARMDSP_CPP
|
||||||
|
|
||||||
uint32 ArmDSP::bus_read(uint32 addr, uint32 size) {
|
uint32 ArmDSP::bus_read(uint32 addr, uint32 size) {
|
||||||
static auto adjust = [&](uint32 word, uint32 addr, uint32 size) {
|
|
||||||
unsigned rotate = (addr & 3) << 3;
|
|
||||||
word = (word >> rotate) | (word << (32 - rotate));
|
|
||||||
return word & (~0u >> (32 - size));
|
|
||||||
};
|
|
||||||
|
|
||||||
static auto memory = [&](const uint8 *memory, uint32 addr, uint32 size) {
|
static auto memory = [&](const uint8 *memory, uint32 addr, uint32 size) {
|
||||||
memory += addr & ~3;
|
memory += addr & ~3;
|
||||||
uint32 word = 0;
|
return (memory[0] << 0) | (memory[1] << 8) | (memory[2] << 16) | (memory[3] << 24);
|
||||||
word |= *memory++ << 0;
|
|
||||||
word |= *memory++ << 8;
|
|
||||||
word |= *memory++ << 16;
|
|
||||||
word |= *memory++ << 24;
|
|
||||||
return adjust(word, addr, size);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
switch(addr & 0xe0000000) {
|
switch(addr & 0xe0000000) {
|
||||||
case 0x00000000: return memory(programROM, addr & 0x1ffff, size);
|
case 0x00000000: return memory(programROM, addr & 0x1ffff, size);
|
||||||
case 0x20000000: return adjust(pipeline.fetch.instruction, addr, size);
|
case 0x20000000: return pipeline.fetch.instruction;
|
||||||
case 0x40000000: break;
|
case 0x40000000: break;
|
||||||
case 0x60000000: return adjust(0x40404001, addr, size);
|
case 0x60000000: return 0x40404001;
|
||||||
case 0x80000000: return adjust(pipeline.fetch.instruction, addr, size);
|
case 0x80000000: return pipeline.fetch.instruction;
|
||||||
case 0xa0000000: return memory(dataROM, addr & 0x7fff, size);
|
case 0xa0000000: return memory(dataROM, addr & 0x7fff, size);
|
||||||
case 0xc0000000: return adjust(pipeline.fetch.instruction, addr, size);
|
case 0xc0000000: return pipeline.fetch.instruction;
|
||||||
case 0xe0000000: return memory(programRAM, addr & 0x3fff, size);
|
case 0xe0000000: return memory(programRAM, addr & 0x3fff, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,12 +22,12 @@ uint32 ArmDSP::bus_read(uint32 addr, uint32 size) {
|
||||||
if(addr == 0x40000010) {
|
if(addr == 0x40000010) {
|
||||||
if(bridge.cputoarm.ready) {
|
if(bridge.cputoarm.ready) {
|
||||||
bridge.cputoarm.ready = false;
|
bridge.cputoarm.ready = false;
|
||||||
return adjust(bridge.cputoarm.data, addr, size);
|
return bridge.cputoarm.data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(addr == 0x40000020) {
|
if(addr == 0x40000020) {
|
||||||
return adjust(bridge.status(), addr, size);
|
return bridge.status();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0u;
|
return 0u;
|
||||||
|
|
Loading…
Reference in New Issue