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:
Tim Allen 2012-03-31 19:14:31 +11:00
parent c66cc73374
commit ea086fe33f
21 changed files with 528 additions and 139 deletions

View File

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

View File

@ -18,6 +18,15 @@ void CPU::enter() {
}
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();
}
}
@ -47,35 +56,75 @@ void CPU::power() {
for(unsigned n = 0; n < iwram.size; n++) iwram.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.irq_enable = 0;
regs.irq_flag = 0;
regs.postboot = 0;
regs.mode = Registers::Mode::Normal;
bus.mmio[0x130] = this;
bus.mmio[0x200] = this;
bus.mmio[0x202] = this;
bus.mmio[0x208] = this;
for(unsigned n = 0x0b0; n <= 0x0df; n++) bus.mmio[n] = this;
bus.mmio[0x130] = bus.mmio[0x131] = 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 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
for(unsigned n = 0; n < 10; n++) result |= (interface->inputPoll(n) == false) << n;
uint8 result = 0;
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 & 0x30) == 0x30) result &= ~0x30; //left+right cannot be pressed simultaneously
return result;
case 0x40000131:
result |= (interface->inputPoll(8) == false) << 0;
result |= (interface->inputPoll(9) == false) << 1;
return result;
case 0x04000200: //IE
return regs.irq_enable;
//IE
case 0x04000200: return regs.irq_enable >> 0;
case 0x04000201: return regs.irq_enable >> 8;
case 0x04000202: //IF
return regs.irq_flag;
//IF
case 0x04000202: return regs.irq_flag >> 0;
case 0x04000203: return regs.irq_flag >> 8;
case 0x04000208: //IME
return regs.ime;
//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) {
switch(addr & 0x0ffffffc) {
case 0x04000200: //IE
regs.irq_enable = 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;
}
case 0x04000202: //IF
regs.irq_flag = regs.irq_flag & ~word;
if(size == Half) {
addr &= ~1;
write(addr + 0, Byte, word >> 0);
write(addr + 1, Byte, word >> 8);
return;
}
case 0x04000208: //IME
regs.ime = word & 1;
return;
uint8 byte = word;
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;
}
}

View File

@ -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 {
return (
(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);
hblank = source & (1 << 1);
vcoincidence = source & (1 << 2);
@ -32,4 +57,5 @@ CPU::Registers::Interrupt& CPU::Registers::Interrupt::operator=(uint16 source) {
dma3 = source & (1 << 11);
keypad = source & (1 << 12);
cartridge = source & (1 << 13);
return operator uint16();
}

View File

@ -1,6 +1,26 @@
struct Registers {
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 {
bool vblank;
bool hblank;
@ -18,6 +38,10 @@ struct Registers {
bool cartridge;
operator uint16() const;
Interrupt& operator=(uint16 source);
uint16 operator=(uint16 source);
Interrupt& operator=(const Interrupt&) = delete;
} irq_enable, irq_flag;
bool postboot;
enum class Mode : unsigned { Normal, Halt, Stop } mode;
} regs;

View File

@ -12,27 +12,11 @@ struct UnmappedMemory : Memory {
static UnmappedMemory unmappedMemory;
uint32 StaticMemory::read(uint32 addr, uint32 size) {
uint32 word = 0;
switch(size) {
case Word:
addr &= ~3;
word |= 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;
case Word: addr &= ~3; return (data[addr + 0] << 0) | (data[addr + 1] << 8) | (data[addr + 2] << 16) | (data[addr + 3] << 24);
case Half: addr &= ~1; return (data[addr + 0] << 0) | (data[addr + 1] << 8);
case Byte: return (data[addr + 0] << 0);
}
return word;
}
void StaticMemory::write(uint32 addr, uint32 size, uint32 word) {

2
bsnes/gba/ppu/bg.cpp Executable file
View File

@ -0,0 +1,2 @@
void PPU::render_bg() {
}

57
bsnes/gba/ppu/mmio.cpp Executable file
View File

@ -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;
}
}

25
bsnes/gba/ppu/obj.cpp Executable file
View File

@ -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() {
}

View File

@ -12,6 +12,11 @@
namespace GBA {
#include "registers.cpp"
#include "mmio.cpp"
#include "bg.cpp"
#include "obj.cpp"
#include "window.cpp"
PPU ppu;
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 < pram.size; n++) pram.data[n] = 0;
regs.control = 0;
regs.scanline = 0;
bus.mmio[0x006] = this;
for(unsigned n = 0x000; n <= 0x055; n++) bus.mmio[n] = this;
}
void PPU::scanline() {
@ -55,6 +61,8 @@ void PPU::scanline() {
}
if(regs.scanline >= 160) return;
render_bg();
render_obj();
}
void PPU::frame() {
@ -77,18 +85,6 @@ void PPU::frame() {
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() {
vram.data = new uint8[vram.size = 96 * 1024];
oam.data = new uint8[oam.size = 1024];

View File

@ -2,10 +2,7 @@ struct PPU : Thread, Memory {
StaticMemory vram;
StaticMemory oam;
StaticMemory pram;
struct Registers {
unsigned scanline;
} regs;
#include "registers.hpp"
static void Enter();
void enter();
@ -18,6 +15,10 @@ struct PPU : Thread, Memory {
uint32 read(uint32 addr, uint32 size);
void write(uint32 addr, uint32 size, uint32 word);
void render_bg();
void render_obj();
void render_window();
PPU();
};

36
bsnes/gba/ppu/registers.cpp Executable file
View File

@ -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();
}

24
bsnes/gba/ppu/registers.hpp Executable file
View File

@ -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;

2
bsnes/gba/ppu/window.cpp Executable file
View File

@ -0,0 +1,2 @@
void PPU::render_window() {
}

View File

@ -32,6 +32,18 @@ void ARM::exec() {
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) {
auto psr = cpsr();
processor.setMode(mode);

View File

@ -17,6 +17,8 @@ struct ARM {
void power();
void exec();
uint32 read(uint32 addr, uint32 size);
void write(uint32 addr, uint32 size, uint32 word);
void vector(uint32 addr, Processor::Mode mode);
bool condition(uint4 condition);

View File

@ -32,7 +32,7 @@ string ARM::disassemble_arm_instruction(uint32 pc) {
string output{hex<8>(pc), " "};
uint32 instruction = bus_read(pc, Word);
uint32 instruction = read(pc & ~3, Word);
output.append(hex<8>(instruction), " ");
//multiply()
@ -45,7 +45,7 @@ string ARM::disassemble_arm_instruction(uint32 pc) {
uint4 rd = instruction >> 16;
uint4 rn = instruction >> 12;
uint4 rs = instruction >> 8;
uint4 rm = instruction >> 0;
uint4 rm = instruction;
output.append(accumulate ? "mla" : "mul", conditions[condition], save ? "s " : " ");
output.append(registers[rd], ",", registers[rm], ",", registers[rs]);
@ -54,6 +54,25 @@ string ARM::disassemble_arm_instruction(uint32 pc) {
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()
//swp{condition}{b} rd,rm,[rn]
if((instruction & 0x0fb000f0) == 0x01000090) {
@ -115,7 +134,7 @@ string ARM::disassemble_arm_instruction(uint32 pc) {
if(pre == 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;
}
@ -165,8 +184,8 @@ string ARM::disassemble_arm_instruction(uint32 pc) {
if(pre == 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 == 0) output.append(" =0x", hex<2>(bus_read(pc + 8 + (up ? +immediate : -immediate), Byte)));
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>(read(pc + 8 + (up ? +immediate : -immediate), Byte)));
return output;
}
@ -340,7 +359,7 @@ string ARM::disassemble_arm_instruction(uint32 pc) {
if(pre == 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;
}
@ -438,7 +457,7 @@ string ARM::disassemble_thumb_instruction(uint32 pc) {
string output{hex<8>(pc), " "};
uint16 instruction = bus_read(pc, Half);
uint16 instruction = read(pc & ~1, Half);
output.append(hex<4>(instruction), " ");
//adjust_register()
@ -552,7 +571,7 @@ string ARM::disassemble_thumb_instruction(uint32 pc) {
unsigned rm = ((pc + 4) & ~3) + displacement * 4;
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;
}
@ -721,7 +740,7 @@ string ARM::disassemble_thumb_instruction(uint32 pc) {
//bl address
if((instruction & 0xf800) == 0xf000) {
uint11 offsethi = instruction;
instruction = bus_read(pc + 2, Half);
instruction = read((pc & ~1) + 2, Half);
uint11 offsetlo = instruction;
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( "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().i ? "I" : "i", cpsr().f ? "F" : "f", cpsr().t ? "T" : "t");
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("r12:", hex<8>(r(12)), " sp:", hex<8>(r(13)), " lr:", hex<8>(r(14)), " pc:", hex<8>(r(15)), " ");
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().i ? "I" : "i", spsr().f ? "F" : "f", spsr().t ? "T" : "t");
output.append("/", hex<2>(spsr().m));
return output;
}

View File

@ -3,9 +3,10 @@
void ARM::arm_step() {
if(pipeline.reload) {
pipeline.reload = false;
r(15).data &= ~3;
pipeline.fetch.address = r(15);
pipeline.fetch.instruction = bus_read(r(15), Word);
pipeline.fetch.instruction = read(r(15), Word);
pipeline_step();
step(2);
@ -18,6 +19,7 @@ void ARM::arm_step() {
if(trace) {
print(disassemble_registers(), "\n");
print(disassemble_arm_instruction(pipeline.execute.address), "\n");
usleep(100000);
}
if(condition(instruction() >> 28) == false) return;
@ -29,6 +31,7 @@ void ARM::arm_step() {
decode("???? 0001 0010 ++++ ++++ ++++ 0001 ????", branch_exchange_register);
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 ???? ???? ---- 1001 ????", memory_swap);
decode("???? 0001 0?10 ???? ++++ ---- 0000 ????", move_to_status_from_register);
@ -122,19 +125,57 @@ void ARM::arm_move_to_status(uint32 rm) {
//d = rd
//n = rn
//s = rs
//n = rm
//m = rm
void ARM::arm_op_multiply() {
uint1 accumulate = instruction() >> 21;
uint1 save = instruction() >> 20;
uint4 d = instruction() >> 16;
uint4 n = instruction() >> 12;
uint4 s = instruction() >> 8;
uint4 m = instruction() >> 0;
uint4 m = instruction();
r(d) = accumulate ? r(n) : 0u;
step(1);
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]
//cccc 0001 0b00 nnnn dddd ---- 1001 mmmm
//c = condition
@ -148,8 +189,8 @@ void ARM::arm_op_memory_swap() {
uint4 d = instruction() >> 12;
uint4 m = instruction();
uint32 word = bus_read(r(n), byte ? Byte : Word);
bus_write(r(n), byte ? Byte : Word, r(m));
uint32 word = read(r(n), byte ? Byte : Word);
write(r(n), byte ? Byte : Word, r(m));
r(d) = word;
}
@ -177,8 +218,8 @@ void ARM::arm_op_move_half_register() {
uint32 rm = r(m);
if(pre == 1) rn = up ? rn + rm : rn - rm;
if(load == 1) r(d) = bus_read(rn, Half);
if(load == 0) bus_write(rn, Half, r(d));
if(load == 1) r(d) = read(rn, Half);
if(load == 0) write(rn, Half, r(d));
if(pre == 0) rn = up ? rn + rm : rn - rm;
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);
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
if(load == 1) r(d) = bus_read(rn, Half);
if(load == 0) bus_write(rn, Half, r(d));
if(load == 1) r(d) = read(rn, Half);
if(load == 0) write(rn, Half, r(d));
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
if(pre == 0 || writeback == 1) r(n) = rn;
@ -241,7 +282,7 @@ void ARM::arm_op_load_register() {
uint32 rm = r(m);
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;
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);
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;
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
@ -445,11 +486,8 @@ void ARM::arm_op_move_immediate_offset() {
auto &rd = r(d);
if(pre == 1) rn = up ? rn + rm : rn - rm;
if(load) {
rd = bus_read(rn, byte ? Byte : Word);
} else {
bus_write(rn, byte ? Byte : Word, rd);
}
if(load == 1) rd = read(rn, byte ? Byte : Word);
if(load == 0) write(rn, byte ? Byte : Word, rd);
if(pre == 0) rn = up ? rn + rm : rn - rm;
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(pre == 1) rn = up ? rn + rm : rn - rm;
if(load) {
rd = bus_read(rn, byte ? Byte : Word);
} else {
bus_write(rn, byte ? Byte : Word, rd);
}
if(load == 1) rd = read(rn, byte ? Byte : Word);
if(load == 0) write(rn, byte ? Byte : Word, rd);
if(pre == 0) rn = up ? rn + rm : rn - rm;
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++) {
if(list & (1 << n)) {
if(load) r(n) = bus_read(rn, Word);
else bus_write(rn, Word, r(n));
if(load == 1) r(n) = read(rn, Word);
if(load == 0) write(rn, Word, r(n));
rn += 4;
}
}

View File

@ -4,6 +4,7 @@ void arm_opcode(uint32 rm);
void arm_move_to_status(uint32 rm);
void arm_op_multiply();
void arm_op_multiply_long();
void arm_op_memory_swap();
void arm_op_move_half_register();
void arm_op_move_half_immediate();

View File

@ -3,9 +3,10 @@
void ARM::thumb_step() {
if(pipeline.reload) {
pipeline.reload = false;
r(15).data &= ~1;
pipeline.fetch.address = r(15);
pipeline.fetch.instruction = bus_read(r(15), Half);
pipeline.fetch.instruction = read(r(15), Half);
pipeline_step();
step(1);
@ -198,7 +199,7 @@ void ARM::thumb_op_load_literal() {
uint8 displacement = instruction();
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]
@ -214,14 +215,14 @@ void ARM::thumb_op_move_register_offset() {
uint3 d = instruction() >> 0;
switch(opcode) {
case 0: bus_write(r(n) + r(m), Word, r(d)); break; //STR
case 1: bus_write(r(n) + r(m), Half, r(d)); break; //STRH
case 2: bus_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 4: r(d) = bus_read(r(n) + r(m), Word); break; //LDR
case 5: r(d) = bus_read(r(n) + r(m), Half); break; //LDRH
case 6: r(d) = bus_read(r(n) + r(m), Byte); break; //LDRB
case 7: r(d) = (int16)bus_read(r(n) + r(m), Half); break; //LDSH
case 0: write(r(n) + r(m), Word, r(d)); break; //STR
case 1: write(r(n) + r(m), Half, r(d)); break; //STRH
case 2: write(r(n) + r(m), Byte, r(d)); break; //STRB
case 3: r(d) = (int8)read(r(n) + r(m), Byte); break; //LDSB
case 4: r(d) = read(r(n) + r(m), Word); break; //LDR
case 5: r(d) = read(r(n) + r(m), Half); break; //LDRH
case 6: r(d) = read(r(n) + r(m), Byte); break; //LDRB
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 d = instruction() >> 0;
if(load == 1) r(d) = bus_read(r(n) + offset * 4, Word);
if(load == 0) bus_write(r(n) + offset * 4, Word, r(d));
if(load == 1) r(d) = read(r(n) + offset * 4, Word);
if(load == 0) write(r(n) + offset * 4, Word, r(d));
}
//(ldr,str)b rd,[rn,#offset]
@ -253,8 +254,8 @@ void ARM::thumb_op_move_byte_immediate() {
uint3 n = instruction() >> 3;
uint3 d = instruction() >> 0;
if(load == 1) r(d) = bus_read(r(n) + offset, Byte);
if(load == 0) bus_write(r(n) + offset, Byte, r(d));
if(load == 1) r(d) = read(r(n) + offset, Byte);
if(load == 0) write(r(n) + offset, Byte, r(d));
}
//(ldr,str)h rd,[rn,#offset]
@ -269,8 +270,8 @@ void ARM::thumb_op_move_half_immediate() {
uint3 n = instruction() >> 3;
uint3 d = instruction() >> 0;
if(load == 1) r(d) = bus_read(r(n) + offset * 2, Half);
if(load == 0) bus_write(r(n) + offset * 2, Half, r(d));
if(load == 1) r(d) = read(r(n) + offset * 2, Half);
if(load == 0) write(r(n) + offset * 2, Half, r(d));
}
//(ldr,str) rd,[sp,#immediate]
@ -283,8 +284,8 @@ void ARM::thumb_op_move_stack() {
uint3 d = instruction() >> 8;
uint8 immediate = instruction();
if(opcode == 0) bus_write(r(13) + immediate * 4, Word, r(d));
if(opcode == 1) r(d) = bus_read(r(13) + immediate * 4, Word);
if(opcode == 0) write(r(13) + immediate * 4, Word, r(d));
if(opcode == 1) r(d) = read(r(13) + immediate * 4, Word);
}
//add rd,{pc,sp},#immediate
@ -330,16 +331,16 @@ void ARM::thumb_op_stack_multiple() {
for(unsigned l = 0; l < 8; l++) {
if(list & (1 << l)) {
if(load == 1) r(l) = bus_read(sp, Word); //POP
if(load == 0) bus_write(sp, Word, r(l)); //PUSH
if(load == 1) r(l) = read(sp, Word); //POP
if(load == 0) write(sp, Word, r(l)); //PUSH
sp += 4;
}
}
if(branch) {
//note: ARMv5+ POP sets cpsr().t
if(load == 1) r(15) = bus_read(sp, Word); //POP
if(load == 0) bus_write(sp, Word, r(14)); //PUSH
if(load == 1) r(15) = read(sp, Word); //POP
if(load == 0) write(sp, Word, r(14)); //PUSH
sp += 4;
}
@ -359,8 +360,8 @@ void ARM::thumb_op_move_multiple() {
for(unsigned l = 0; l < 8; l++) {
if(list & (1 << l)) {
if(load == 1) r(l) = bus_read(r(n), Word); //LDMIA
if(load == 0) bus_write(r(n), Word, r(l)); //STMIA
if(load == 1) r(l) = read(r(n), Word); //LDMIA
if(load == 0) write(r(n), Word, r(l)); //STMIA
r(n) += 4;
}
}

View File

@ -67,11 +67,11 @@ void ARM::pipeline_step() {
if(cpsr().t == 0) {
r(15).data += 4;
pipeline.fetch.address = r(15);
pipeline.fetch.instruction = bus_read(r(15), Word);
pipeline.fetch.instruction = read(r(15), Word);
} else {
r(15).data += 2;
pipeline.fetch.address = r(15);
pipeline.fetch.instruction = bus_read(r(15), Half);
pipeline.fetch.instruction = read(r(15), Half);
}
}

View File

@ -1,30 +1,19 @@
#ifdef ARMDSP_CPP
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) {
memory += addr & ~3;
uint32 word = 0;
word |= *memory++ << 0;
word |= *memory++ << 8;
word |= *memory++ << 16;
word |= *memory++ << 24;
return adjust(word, addr, size);
return (memory[0] << 0) | (memory[1] << 8) | (memory[2] << 16) | (memory[3] << 24);
};
switch(addr & 0xe0000000) {
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 0x60000000: return adjust(0x40404001, addr, size);
case 0x80000000: return adjust(pipeline.fetch.instruction, addr, size);
case 0x60000000: return 0x40404001;
case 0x80000000: return pipeline.fetch.instruction;
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);
}
@ -33,12 +22,12 @@ uint32 ArmDSP::bus_read(uint32 addr, uint32 size) {
if(addr == 0x40000010) {
if(bridge.cputoarm.ready) {
bridge.cputoarm.ready = false;
return adjust(bridge.cputoarm.data, addr, size);
return bridge.cputoarm.data;
}
}
if(addr == 0x40000020) {
return adjust(bridge.status(), addr, size);
return bridge.status();
}
return 0u;