Update to v087r05 release.

byuu says:

Implemented all of the ARMv3 instructions, and the bx rm instruction as
well. Already hit THUMB mode right at the start of the BIOS, sigh.
Implemented the first THUMB instruction to get that rolling. Also tried
to support the S flag to LDM/STM, but not sure how successful I was.
This commit is contained in:
Tim Allen 2012-03-21 22:08:16 +11:00
parent 6701403745
commit 04087a74b0
17 changed files with 987 additions and 36 deletions

View File

@ -93,6 +93,7 @@ sync:
rm -r phoenix/test
archive-all:
tar -cjf bsnes.tar.bz2 base data gb gba libco nall nes obj out phoenix ruby snes target-debugger target-libsnes target-ui Makefile cc.bat purge.bat
if [ -f bsnes.tar.bz2 ]; then rm bsnes.tar.bz2; fi
tar -cjf bsnes.tar.bz2 `ls`
help:;

View File

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

490
bsnes/gba/cpu/core/arm.cpp Executable file
View File

@ -0,0 +1,490 @@
void ARM::arm_step() {
if(pipeline.reload) {
pipeline.reload = false;
pipeline.fetch.address = r(15);
pipeline.fetch.instruction = bus.read(r(15), Word);
r(15).data += 4;
pipeline.decode = pipeline.fetch;
pipeline.fetch.address = r(15);
pipeline.fetch.instruction = bus.read(r(15), Word);
step(2);
}
r(15).data += 4;
pipeline.execute = pipeline.decode;
pipeline.decode = pipeline.fetch;
pipeline.fetch.address = r(15);
pipeline.fetch.instruction = bus.read(r(15), Word);
step(2);
print(disassemble_arm_opcode(pipeline.execute.address), "\n");
if(arm_condition() == false) return;
if((instruction() & 0x0fc000f0) == 0x00000090) { arm_op_multiply(); return; }
if((instruction() & 0x0fb000f0) == 0x01000000) { arm_op_move_to_register_from_status(); return; }
if((instruction() & 0x0fb000f0) == 0x01200000) { arm_op_move_to_status_from_register(); return; }
if((instruction() & 0x0ff000f0) == 0x01200010) { arm_op_branch_exchange_register(); return; }
if((instruction() & 0x0e000010) == 0x00000000) { arm_op_data_immediate_shift(); return; }
if((instruction() & 0x0e000090) == 0x00000010) { arm_op_data_register_shift(); return; }
if((instruction() & 0x0e000000) == 0x02000000) { arm_op_data_immediate(); return; }
if((instruction() & 0x0e000000) == 0x04000000) { arm_op_move_immediate_offset(); return; }
if((instruction() & 0x0e000010) == 0x06000000) { arm_op_move_register_offset(); return; }
if((instruction() & 0x0e000000) == 0x08000000) { arm_op_move_multiple(); return; }
if((instruction() & 0x0e000000) == 0x0a000000) { arm_op_branch(); return; }
exception = true;
}
bool ARM::arm_condition() {
uint4 condition = instruction() >> 28;
switch(condition) {
case 0: return cpsr().z == 1; //EQ (equal)
case 1: return cpsr().z == 0; //NE (not equal)
case 2: return cpsr().c == 1; //CS (carry set)
case 3: return cpsr().c == 0; //CC (carry clear)
case 4: return cpsr().n == 1; //MI (negative)
case 5: return cpsr().n == 0; //PL (positive)
case 6: return cpsr().v == 1; //VS (overflow)
case 7: return cpsr().v == 0; //VC (no overflow)
case 8: return cpsr().c == 1 && cpsr().z == 0; //HI (unsigned higher)
case 9: return cpsr().c == 0 || cpsr().z == 1; //LS (unsigned lower or same)
case 10: return cpsr().n == cpsr().v; //GE (signed greater than or equal)
case 11: return cpsr().n != cpsr().v; //LT (signed less than)
case 12: return cpsr().z == 0 && cpsr().n == cpsr().v; //GT (signed greater than)
case 13: return cpsr().z == 1 || cpsr().n != cpsr().v; //LE (signed less than or equal)
case 14: return true; //AL (always)
case 15: return false; //NV (never)
}
}
void ARM::arm_opcode(uint32 rm) {
uint4 opcode = instruction() >> 21;
uint1 save = instruction() >> 20;
uint4 n = instruction() >> 16;
uint4 d = instruction() >> 12;
uint32 rn = r(n);
auto test = [&](uint32 result) {
if(save) {
cpsr().n = result >> 31;
cpsr().z = result == 0;
cpsr().c = carryout();
}
return result;
};
auto math = [&](uint32 source, uint32 modify, bool carry) {
uint32 result = source + modify + carry;
if(save) {
uint32 overflow = ~(source ^ modify) & (source ^ result);
cpsr().n = result >> 31;
cpsr().z = result == 0;
cpsr().c = (1u << 31) & (overflow ^ source ^ modify ^ result);
cpsr().v = (1u << 31) & (overflow);
}
return result;
};
switch(opcode) {
case 0: r(d) = test(rn & rm); break; //AND
case 1: r(d) = test(rn ^ rm); break; //EOR
case 2: r(d) = math(rn, ~rm, 1); break; //SUB
case 3: r(d) = math(rm, ~rn, 1); break; //RSB
case 4: r(d) = math(rn, rm, 0); break; //ADD
case 5: r(d) = math(rn, rm, cpsr().c); break; //ADC
case 6: r(d) = math(rn, ~rm, cpsr().c); break; //SBC
case 7: r(d) = math(rm, ~rn, cpsr().c); break; //RSC
case 8: test(rn & rm); break; //TST
case 9: test(rn ^ rm); break; //TEQ
case 10: math(rn, ~rm, 1); break; //CMP
case 11: math(rn, rm, 0); break; //CMN
case 12: r(d) = test(rn | rm); break; //ORR
case 13: r(d) = test(rm); break; //MOV
case 14: r(d) = test(rn &~rm); break; //BIC
case 15: r(d) = test(~rm); break; //MVN
}
}
//logical shift left
void ARM::lsl(bool &c, uint32 &rm, uint32 rs) {
while(rs--) {
c = rm >> 31;
rm <<= 1;
}
}
//logical shift right
void ARM::lsr(bool &c, uint32 &rm, uint32 rs) {
while(rs--) {
c = rm & 1;
rm >>= 1;
}
}
//arithmetic shift right
void ARM::asr(bool &c, uint32 &rm, uint32 rs) {
while(rs--) {
c = rm & 1;
rm = (int32)rm >> 1;
}
}
//rotate right
void ARM::ror(bool &c, uint32 &rm, uint32 rs) {
while(rs--) {
c = rm & 1;
rm = (rm << 31) | (rm >> 1);
}
}
//rotate right with extend
void ARM::rrx(bool &c, uint32 &rm) {
bool carry = c;
c = rm & 1;
rm = (carry << 31) | (rm >> 1);
}
//mul{condition}{s} rd,rm,rs
//mla{condition}{s} rd,rm,rs,rn
//cccc 0000 00as dddd nnnn ssss 1001 mmmm
//c = condition
//a = accumulate
//s = save flags
//d = rd
//n = rn
//s = rs
//n = 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;
//Booth's algorithm: two bit steps
uint32 temp = r(s);
while(temp) {
temp >>= 2;
step(1);
}
r(d) = r(m) * r(s);
if(accumulate) {
step(1);
r(d) += r(n);
}
if(save) {
cpsr().n = r(d) >> 31;
cpsr().z = r(d) == 0;
cpsr().c = 0; //undefined
}
}
//mrs{condition} rd,(c,s)psr
//cccc 0001 0r00 ++++ dddd ---- 0000 ----
//c = condition
//r = SPSR (0 = CPSR)
//d = rd
void ARM::arm_op_move_to_register_from_status() {
uint1 source = instruction() >> 22;
uint4 d = instruction() >> 12;
if(source) {
if(mode() == Processor::Mode::USR) return;
if(mode() == Processor::Mode::SYS) return;
}
r(d) = source ? spsr() : cpsr();
}
//msr{condition} (c,s)psr:{fields},rm
//cccc 0001 0r10 ffff ++++ ---- 0000 mmmm
//c = condition
//r = SPSR (0 = CPSR)
//f = field mask
//m = rm
void ARM::arm_op_move_to_status_from_register() {
uint1 source = instruction() >> 22;
uint4 field = instruction() >> 16;
uint4 m = instruction();
if(source) {
if(mode() == Processor::Mode::USR) return;
if(mode() == Processor::Mode::SYS) return;
}
PSR &psr = source ? spsr() : cpsr();
uint32 rm = r(m);
if(field & 1) {
psr.i = rm & 0x00000080;
psr.f = rm & 0x00000040;
psr.t = rm & 0x00000020;
psr.m = rm & 0x0000001f;
if(source == 0) {
processor.setMode((Processor::Mode)psr.m);
}
}
if(field & 8) {
psr.n = rm & 0x80000000;
psr.z = rm & 0x40000000;
psr.c = rm & 0x20000000;
psr.v = rm & 0x10000000;
}
}
//bx{condition} rm
//cccc 0001 0010 ++++ ++++ ++++ 0001 mmmm
//c = condition
//m = rm
void ARM::arm_op_branch_exchange_register() {
uint4 m = instruction();
r(15) = r(m);
cpsr().t = r(m) & 1;
}
//{opcode}{condition}{s} rd,rm {shift} #immediate
//{opcode}{condition} rn,rm {shift} #immediate
//{opcode}{condition}{s} rd,rn,rm {shift} #immediate
//cccc 000o ooos nnnn dddd llll lss0 mmmm
//c = condition
//o = opcode
//s = save flags
//n = rn
//d = rd
//l = shift immediate
//s = shift
//m = rm
void ARM::arm_op_data_immediate_shift() {
uint1 save = instruction() >> 20;
uint5 shift = instruction() >> 7;
uint2 mode = instruction() >> 5;
uint4 m = instruction();
uint32 rs = shift;
uint32 rm = r(m);
bool c = cpsr().c;
if(mode == 0) lsl(c, rm, rs);
if(mode == 1) lsr(c, rm, rs ? rs : 32);
if(mode == 2) asr(c, rm, rs ? rs : 32);
if(mode == 3) rs ? ror(c, rm, rs) : rrx(c, rm);
carryout() = c;
arm_opcode(rm);
}
//{opcode}{condition}{s} rd,rm {shift} rs
//{opcode}{condition} rn,rm {shift} rs
//{opcode}{condition}{s} rd,rn,rm {shift} rs
//cccc 000o ooos nnnn dddd ssss 0ss1 mmmm
//c = condition
//o = opcode
//s = save flags
//n = rn
//d = rd
//s = rs
//s = shift
//m = rm
void ARM::arm_op_data_register_shift() {
uint1 save = instruction() >> 20;
uint4 s = instruction() >> 8;
uint2 mode = instruction() >> 5;
uint4 m = instruction();
uint8 rs = r(s);
uint32 rm = r(m);
bool c = cpsr().c;
if(mode == 0) lsl(c, rm, rs < 33 ? rs : 33);
if(mode == 1) lsr(c, rm, rs < 33 ? rs : 33);
if(mode == 2) asr(c, rm, rs < 32 ? rs : 32);
if(mode == 3 && rs) ror(c, rm, rs & 31 == 0 ? 32 : rs & 31);
carryout() = c;
arm_opcode(rm);
}
//{opcode}{condition}{s} rd,#immediate
//{opcode}{condition} rn,#immediate
//{opcode}{condition}{s} rd,rn,#immediate
//cccc 001o ooos nnnn dddd ssss iiii iiii
//c = condition
//o = opcode
//s = save flags
//n = rn
//d = rd
//s = shift immediate
//i = immediate
void ARM::arm_op_data_immediate() {
uint1 save = instruction() >> 20;
uint4 shift = instruction() >> 8;
uint8 immediate = instruction();
uint32 rs = shift << 1;
uint32 rm = (immediate >> rs) | (immediate << (32 - rs));
if(rs) carryout() = immediate >> 31;
arm_opcode(rm);
}
//(ldr,str){condition}{b} rd,[rn{,+/-offset}]{!}
//(ldr,str){condition}{b} rd,[rn]{,+/-offset}
//cccc 010p ubwl nnnn dddd iiii iiii iiii
//c = condition
//p = pre (0 = post-indexed addressing)
//u = up (add/sub offset to base)
//b = byte (1 = word)
//w = writeback
//l = load (0 = save)
//n = rn
//d = rd
//i = immediate
void ARM::arm_op_move_immediate_offset() {
uint1 pre = instruction() >> 24;
uint1 up = instruction() >> 23;
uint1 byte = instruction() >> 22;
uint1 writeback = instruction() >> 21;
uint1 load = instruction() >> 20;
uint4 n = instruction() >> 16;
uint4 d = instruction() >> 12;
uint12 rm = instruction();
uint32 rn = r(n);
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(pre == 0) rn = up ? rn + rm : rn - rm;
if(pre == 0 || writeback == 1) r(n) = rn;
}
//(ldr,str){condition}{b} rd,[rn,rm {mode} #immediate]{1}
//(ldr,str){condition}{b} rd,[rn],rm {mode} #immediate
//cccc 011p ubwl nnnn dddd llll lss0 mmmm
//c = condition
//p = pre (0 = post-indexed addressing)
//u = up
//b = byte (1 = word)
//w = writeback
//l = load (0 = save)
//n = rn
//d = rd
//l = shift immediate
//s = shift mode
//m = rm
void ARM::arm_op_move_register_offset() {
uint1 pre = instruction() >> 24;
uint1 up = instruction() >> 23;
uint1 byte = instruction() >> 22;
uint1 writeback = instruction() >> 21;
uint1 load = instruction() >> 20;
uint4 n = instruction() >> 16;
uint4 d = instruction() >> 12;
uint5 immediate = instruction() >> 7;
uint2 mode = instruction() >> 5;
uint4 m = instruction();
uint32 rn = r(n);
auto &rd = r(d);
uint32 rs = immediate;
uint32 rm = r(m);
bool c = cpsr().c;
if(mode == 0) lsl(c, rm, rs);
if(mode == 1) lsr(c, rm, rs ? rs : 32);
if(mode == 2) asr(c, rm, rs ? rs : 32);
if(mode == 3) rs ? ror(c, rm, rs) : rrx(c, 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(pre == 0) rn = up ? rn + rm : rn - rm;
if(pre == 0 || writeback == 1) r(n) = rn;
}
//(ldm,stm){condition}{mode} rn{!},{r...}
//cccc 100p uswl nnnn llll llll llll llll
//c = condition
//p = pre (0 = post-indexed addressing)
//u = up (add/sub offset to base)
//s = spsr copy -or- usr register copy
//w = writeback
//l = load (0 = save)
//n = rn
//l = register list
void ARM::arm_op_move_multiple() {
uint1 pre = instruction() >> 24;
uint1 up = instruction() >> 23;
uint1 s = instruction() >> 22;
uint1 writeback = instruction() >> 21;
uint1 load = instruction() >> 20;
uint4 n = instruction() >> 16;
uint16 list = instruction();
uint32 rn = r(n);
if(pre == 0 && up == 1) rn = rn + 0; //IA
if(pre == 1 && up == 1) rn = rn + 4; //IB
if(pre == 1 && up == 0) rn = rn - bit::count(list) * 4 + 0; //DB
if(pre == 0 && up == 0) rn = rn - bit::count(list) * 4 + 4; //DA
Processor::Mode pmode = mode();
bool usr = false;
if(s && load == 1 && (list & 0x8000) == 0) usr = true;
if(s && load == 0) usr = true;
if(usr) processor.setMode(Processor::Mode::USR);
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));
rn += 4;
}
}
if(usr) processor.setMode(pmode);
if(load == 1 && s && (list & 0x8000)) {
if(mode() != Processor::Mode::USR && mode() != Processor::Mode::SYS) {
cpsr() = spsr();
processor.setMode((Processor::Mode)cpsr().m);
}
}
if(writeback) {
if(up == 1) r(n) = r(n) + bit::count(list) * 4; //IA, IB
if(up == 0) r(n) = r(n) - bit::count(list) * 4; //DA, DB
}
}
//b{l}{condition} address
//cccc 101l dddd dddd dddd dddd dddd dddd
//c = condition
//l = link
//d = displacement (24-bit signed)
void ARM::arm_op_branch() {
uint1 link = instruction() >> 24;
int24 displacement = instruction();
if(link) r(14) = r(15) - 4;
r(15) += displacement * 4;
}

22
bsnes/gba/cpu/core/arm.hpp Executable file
View File

@ -0,0 +1,22 @@
void arm_step();
bool arm_condition();
void arm_opcode(uint32 rm);
void lsl(bool &c, uint32 &rm, uint32 rs);
void lsr(bool &c, uint32 &rm, uint32 rs);
void asr(bool &c, uint32 &rm, uint32 rs);
void ror(bool &c, uint32 &rm, uint32 rs);
void rrx(bool &c, uint32 &rm);
void arm_op_multiply();
void arm_op_move_to_register_from_status();
void arm_op_move_to_status_from_register();
void arm_op_branch_exchange_register();
void arm_op_data_immediate_shift();
void arm_op_data_register_shift();
void arm_op_data_immediate();
void arm_op_move_immediate_offset();
void arm_op_move_register_offset();
void arm_op_move_multiple();
void arm_op_branch();

View File

@ -1,8 +1,14 @@
#include "registers.cpp"
#include "arm.cpp"
#include "thumb.cpp"
#include "disassembler.cpp"
void ARM::power() {
processor.power();
pipeline.reload = true;
exception = false;
r(15).modify = [&] { pipeline.reload = true; };
r(15).modify = [&] {
pipeline.reload = true;
r(15).data &= cpsr().t ? ~1 : ~3;
};
}

View File

@ -1,4 +1,9 @@
struct ARM {
#include "registers.hpp"
#include "arm.hpp"
#include "thumb.hpp"
#include "disassembler.hpp"
virtual void step(unsigned clocks) = 0;
void power();
};

View File

@ -0,0 +1,314 @@
string ARM::disassemble_arm_opcode(uint32 pc) {
static string conditions[] = {
"eq", "ne", "cs", "cc",
"mi", "pl", "vs", "vc",
"hi", "ls", "ge", "lt",
"gt", "le", "", "nv",
};
static string opcodes[] = {
"and", "eor", "sub", "rsb",
"add", "adc", "sbc", "rsc",
"tst", "teq", "cmp", "cmn",
"orr", "mov", "bic", "mvn",
};
static string registers[] = {
"r0", "r1", "r2", "r3",
"r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11",
"r12", "sp", "lr", "pc",
};
static string indices[] = {
"da", "ia", "db", "ib",
};
static auto is_move = [](uint4 opcode) { return opcode == 13 || opcode == 15; };
static auto is_comp = [](uint4 opcode) { return opcode >= 8 && opcode <= 11; };
static auto is_math = [](uint4 opcode) { return opcode < 8 || opcode == 12 || opcode == 14; };
string output{hex<8>(pc), " "};
uint32 instruction = bus.read(pc, Word);
output.append(hex<8>(instruction), " ");
//multiply()
//mul{condition}{s} rd,rm,rs
//mla{condition}{s} rd,rm,rs,rn
if((instruction & 0x0fc000f0) == 0x00000090) {
uint4 condition = instruction >> 28;
uint1 accumulate = instruction >> 21;
uint1 save = instruction >> 20;
uint4 rd = instruction >> 16;
uint4 rn = instruction >> 12;
uint4 rs = instruction >> 8;
uint4 rm = instruction >> 0;
output.append(accumulate ? "mla" : "mul", conditions[condition], save ? "s " : " ");
output.append(registers[rd], ",", registers[rm], ",", registers[rs]);
if(accumulate) output.append(",", registers[rn]);
return output;
}
//move_to_register_from_status()
//mrs{condition} rd,(c,s)psr
if((instruction & 0x0fb000f0) == 0x01000000) {
uint4 condition = instruction >> 28;
uint1 psr = instruction >> 22;
uint4 rd = instruction >> 12;
output.append("mrs", conditions[condition], " ");
output.append(registers[rd], ",", psr ? "spsr" : "cpsr");
return output;
}
//move_to_status_from_register()
//msr{condition} (c,s)psr:{fields},rm
if((instruction & 0x0fb000f0) == 0x01200000) {
uint4 condition = instruction >> 28;
uint1 psr = instruction >> 22;
uint4 field = instruction >> 16;
uint4 rm = instruction;
output.append("msr", conditions[condition], " ");
output.append(psr ? "spsr:" : "cpsr:");
output.append(
field & 1 ? "c" : "",
field & 2 ? "x" : "",
field & 4 ? "s" : "",
field & 8 ? "f" : ""
);
output.append(",", registers[rm]);
return output;
}
//branch_exchange_register()
//bx{condition} rm
if((instruction & 0x0ff000f0) == 0x01200010) {
uint4 condition = instruction >> 28;
uint4 rm = instruction;
output.append("bx", conditions[condition], " ");
output.append(registers[rm]);
return output;
}
//data_immediate_shift()
//{opcode}{condition}{s} rd,rm {shift} #immediate
//{opcode}{condition} rn,rm {shift} #immediate
//{opcode}{condition}{s} rd,rn,rm {shift} #immediate
if((instruction & 0x0e000010) == 0x00000000) {
uint4 condition = instruction >> 28;
uint4 opcode = instruction >> 21;
uint1 save = instruction >> 20;
uint4 rn = instruction >> 16;
uint4 rd = instruction >> 12;
uint5 shift = instruction >> 7;
uint2 op = instruction >> 5;
uint4 rm = instruction;
output.append(opcodes[opcode], conditions[condition]);
if(is_move(opcode)) output.append(save ? "s " : " ", registers[rd]);
if(is_comp(opcode)) output.append(" ", registers[rn]);
if(is_math(opcode)) output.append(save ? "s " : " ", registers[rd], ",", registers[rn]);
output.append(",", registers[rm]);
if(op == 0 && shift != 0) output.append(" lsl #", shift);
if(op == 1) output.append(" lsr #", shift == 0 ? 32u : (unsigned)shift);
if(op == 2) output.append(" asr #", shift == 0 ? 32u : (unsigned)shift);
if(op == 3 && shift != 0) output.append(" ror #", shift);
if(op == 3 && shift == 0) output.append(" rrx");
return output;
}
//data_register_shift()
//{opcode}{condition}{s} rd,rm {shift} rs
//{opcode}{condition} rn,rm {shift} rs
//{opcode}{condition}{s} rd,rn,rm {shift} rs
if((instruction & 0x0e000090) == 0x00000010) {
uint4 condition = instruction >> 28;
uint4 opcode = instruction >> 21;
uint1 save = instruction >> 20;
uint4 rn = instruction >> 16;
uint4 rd = instruction >> 12;
uint4 rs = instruction >> 8;
uint2 mode = instruction >> 5;
uint4 rm = instruction;
output.append(opcodes[opcode], conditions[condition]);
if(is_move(opcode)) output.append(save ? "s " : " ", registers[rd], ",");
if(is_comp(opcode)) output.append(registers[rn], ",");
if(is_math(opcode)) output.append(save ? "s " : " ", registers[rd], ",", registers[rn], ",");
output.append(registers[rm]);
if(mode == 0) output.append(" lsl ");
if(mode == 1) output.append(" lsr ");
if(mode == 2) output.append(" asr ");
if(mode == 3) output.append(" ror ");
output.append(registers[rs]);
return output;
}
//data_immediate()
//{opcode}{condition}{s} rd,#immediate
//{opcode}{condition} rn,#immediate
//{opcode}{condition}{s} rd,rn,#immediate
if((instruction & 0x0e000000) == 0x02000000) {
uint4 condition = instruction >> 28;
uint4 opcode = instruction >> 21;
uint1 save = instruction >> 20;
uint4 rn = instruction >> 16;
uint4 rd = instruction >> 12;
uint4 rotate = instruction >> 8;
uint8 immediate = instruction;
uint32 rm = (immediate >> (rotate << 1)) | (immediate << (32 - (rotate << 1)));
output.append(opcodes[opcode], conditions[condition]);
if(is_move(opcode)) output.append(save ? "s " : " ", registers[rd]);
if(is_comp(opcode)) output.append(" ", registers[rn]);
if(is_math(opcode)) output.append(save ? "s " : " ", registers[rd], ",", registers[rn]);
output.append(",#0x", hex<8>(rm));
return output;
}
//move_immediate_offset()
//(ldr,str){condition}{b} rd,[rn{,+/-offset}]{!}
//(ldr,str){condition}{b} rd,[rn]{,+/-offset}
if((instruction & 0x0e000000) == 0x04000000) {
uint4 condition = instruction >> 28;
uint1 pre = instruction >> 24;
uint1 up = instruction >> 23;
uint1 byte = instruction >> 22;
uint1 writeback = instruction >> 21;
uint1 load = instruction >> 20;
uint4 rn = instruction >> 16;
uint4 rd = instruction >> 12;
uint12 immediate = instruction;
output.append(load ? "ldr" : "str", conditions[condition], byte ? "b " : " ");
output.append(registers[rd], ",[", registers[rn]);
if(pre == 0) output.append("]");
if(immediate) output.append(",", up ? "+" : "-", "0x", hex<3>(immediate));
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)));
return output;
}
//move_register_offset()
//(ldr,str){condition}{b} rd,[rn,rm {mode} #immediate]{1}
//(ldr,str){condition}{b} rd,[rn],rm {mode} #immediate
if((instruction & 0x0e000010) == 0x06000000) {
uint4 condition = instruction >> 28;
uint1 pre = instruction >> 24;
uint1 up = instruction >> 23;
uint1 byte = instruction >> 22;
uint1 writeback = instruction >> 21;
uint1 load = instruction >> 20;
uint4 rn = instruction >> 16;
uint4 rd = instruction >> 12;
uint5 shift = instruction >> 7;
uint2 mode = instruction >> 5;
uint4 rm = instruction;
output.append(load ? "ldr" : "str", conditions[condition], byte ? "b " : " ");
output.append(registers[rd], ",[", registers[rn]);
if(pre == 0) output.append("]");
output.append(",", up ? "+" : "-", registers[rm]);
if(mode == 0 && shift != 0) output.append(" lsl #", shift);
if(mode == 1) output.append(" lsr #", shift == 0 ? 32u : (unsigned)shift);
if(mode == 2) output.append(" asr #", shift == 0 ? 32u : (unsigned)shift);
if(mode == 3 && shift != 0) output.append(" ror #", shift);
if(mode == 3 && shift == 0) output.append(" rrx");
if(pre == 1) output.append("]");
if(pre == 0 || writeback == 1) output.append("!");
return output;
}
//move_multiple()
//(ldm,stm) {condition}{mode} rn{!},{r...}{^}
if((instruction & 0x0e000000) == 0x08000000) {
uint4 condition = instruction >> 28;
uint2 index = instruction >> 23;
uint1 s = instruction >> 22;
uint1 writeback = instruction >> 21;
uint1 load = instruction >> 20;
uint4 rn = instruction >> 16;
uint16 list = instruction;
output.append(load ? "ldm" : "stm", conditions[condition], indices[index], " ");
output.append(registers[rn], writeback ? "!" : "", ",{");
for(unsigned n = 0; n < 16; n++) if(list & (1 << n)) output.append(registers[n], ",");
output.rtrim<1>(",");
output.append("}", s ? "^" : "");
return output;
}
//branch()
//b{l}{condition} address
if((instruction & 0x0e000000) == 0x0a000000) {
uint4 condition = instruction >> 28;
uint1 link = instruction >> 24;
output.append("b", link ? "l" : "", conditions[condition], " ");
output.append("0x", hex<8>(pc + 8 + (int24)instruction * 4));
return output;
}
output.append("???");
return output;
}
string ARM::disassemble_thumb_opcode(uint32 pc) {
static string opcodes[] = { "mov", "cmp", "add", "sub" };
static string registers[] = {
"r0", "r1", "r2", "r3",
"r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11",
"r12", "sp", "lr", "pc",
};
string output{hex<8>(pc), " "};
uint16 instruction = bus.read(pc, Half);
output.append(hex<4>(instruction), " ");
//immediate()
//(mov,cmp,add,sub) (rd,rn),#immediate
if((instruction & 0xe000) == 0x2000) {
uint2 opcode = instruction >> 11;
uint3 rd = instruction >> 8;
uint8 immediate = instruction;
output.append(opcodes[opcode], " ", registers[rd], ",#0x", hex<2>(immediate));
return output;
}
output.append("???");
return output;
}
string ARM::disassemble_registers() {
string output;
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", "\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; }
output.append( spsr().n ? "N" : "n", spsr().z ? "Z" : "z", spsr().c ? "C" : "c", spsr().v ? "V" : "v");
return output;
}

View File

@ -0,0 +1,3 @@
string disassemble_arm_opcode(uint32 pc);
string disassemble_thumb_opcode(uint32 pc);
string disassemble_registers();

View File

@ -4,6 +4,7 @@ struct GPR {
inline operator uint32() const { return data; }
inline GPR& operator=(uint32 n) { data = n; if(modify) modify(); return *this; }
inline GPR& operator=(const GPR& source) { return operator=(source.data); }
inline GPR& operator &=(uint32 n) { return operator=(data & n); }
inline GPR& operator |=(uint32 n) { return operator=(data | n); }
@ -47,10 +48,12 @@ struct PSR {
struct Pipeline {
bool reload;
struct Instruction {
uint32 opcode;
uint32 address;
uint32 instruction;
};
Instruction execute;
Instruction decode;
Instruction fetch;
@ -100,6 +103,7 @@ struct Processor {
GPR pc;
PSR cpsr;
bool carryout;
GPR *r[16];
PSR *spsr;
@ -115,3 +119,6 @@ bool exception;
alwaysinline GPR& r(unsigned n) { return *processor.r[n]; }
alwaysinline PSR& cpsr() { return processor.cpsr; }
alwaysinline PSR& spsr() { return *processor.spsr; }
alwaysinline bool& carryout() { return processor.carryout; }
alwaysinline uint32 instruction() { return pipeline.execute.instruction; }
alwaysinline Processor::Mode mode() { return (Processor::Mode)processor.cpsr.m; }

80
bsnes/gba/cpu/core/thumb.cpp Executable file
View File

@ -0,0 +1,80 @@
void ARM::thumb_step() {
if(pipeline.reload) {
pipeline.reload = false;
pipeline.fetch.address = r(15);
pipeline.fetch.instruction = bus.read(r(15), Half);
r(15).data += 2;
pipeline.decode = pipeline.fetch;
pipeline.fetch.address = r(15);
pipeline.fetch.instruction = bus.read(r(15), Half);
step(1);
}
r(15).data += 2;
pipeline.execute = pipeline.decode;
pipeline.decode = pipeline.fetch;
pipeline.fetch.address = r(15);
pipeline.fetch.instruction = bus.read(r(15), Half);
step(1);
print(disassemble_thumb_opcode(pipeline.execute.address), "\n");
if((instruction() & 0xe000) == 0x2000) { thumb_op_immediate(); return; }
exception = true;
}
void ARM::thumb_mov(GPR &source, uint32 modify) {
cpsr().n = modify >> 31;
cpsr().z = modify == 0;
source = modify;
}
void ARM::thumb_cmp(GPR &source, uint32 modify) {
uint32 result = source - modify;
uint32 overflow = ~(source ^ modify) & (source ^ result);
cpsr().n = result >> 31;
cpsr().z = result == 0;
cpsr().c = (1u << 31) & (overflow ^ source ^ modify ^ result);
cpsr().v = (1u << 31) & (overflow);
}
void ARM::thumb_add(GPR &source, uint32 modify) {
uint32 result = source + modify;
uint32 overflow = ~(source ^ modify) & (source ^ result);
cpsr().n = result >> 31;
cpsr().z = result == 0;
cpsr().c = (1u << 31) & (overflow ^ source ^ modify ^ result);
cpsr().v = (1u << 31) & (overflow);
source = result;
}
void ARM::thumb_sub(GPR &source, uint32 modify) {
uint32 result = source - modify;
uint32 overflow = ~(source ^ modify) & (source ^ result);
cpsr().n = result >> 31;
cpsr().z = result == 0;
cpsr().c = (1u << 31) & (overflow ^ source ^ modify ^ result);
cpsr().v = (1u << 31) & (overflow);
source = result;
}
//(mov,cmp,add,sub) (rd,rn),#immediate
//001o orrr iiii iiii
//o = opcode
//r = (rd,rn)
//i = immediate
void ARM::thumb_op_immediate() {
uint2 opcode = instruction() >> 11;
uint3 d = instruction() >> 8;
uint8 immediate = instruction();
switch(opcode) {
case 0: thumb_mov(r(d), immediate); break;
case 1: thumb_cmp(r(d), immediate); break;
case 2: thumb_add(r(d), immediate); break;
case 3: thumb_sub(r(d), immediate); break;
}
}

8
bsnes/gba/cpu/core/thumb.hpp Executable file
View File

@ -0,0 +1,8 @@
void thumb_step();
void thumb_mov(GPR &source, uint32 modify);
void thumb_cmp(GPR &source, uint32 modify);
void thumb_add(GPR &source, uint32 modify);
void thumb_sub(GPR &source, uint32 modify);
void thumb_op_immediate();

View File

@ -9,7 +9,11 @@ void CPU::Enter() { cpu.enter(); }
void CPU::enter() {
while(true) {
step(2);
if(exception) {
print(disassemble_registers(), "\n");
while(true) step(frequency);
}
cpsr().t ? thumb_step() : arm_step();
}
}

View File

@ -5,42 +5,42 @@ namespace GBA {
Bus bus;
uint32 StaticMemory::read(uint32 addr, uint32 size) {
unsigned bits = addr & 3;
addr &= ~3;
uint32 word = 0;
switch(size) {
case Word: word |= data[addr + 3] << 24;
word |= data[addr + 2] << 16;
case Half: word |= data[addr + 1] << 8;
case Byte: word |= data[addr + 0] << 0;
}
if(bits) {
unsigned rotate = bits << 3;
word = (word >> rotate) | (word << (32 - rotate));
}
switch(size) {
case Word: return word;
case Half: return word & 0xffff;
case Byte: return word & 0xff;
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;
}
return word;
}
void StaticMemory::write(uint32 addr, uint32 size, uint32 word) {
switch(size) {
case Word:
addr &= ~3;
data[addr + 3] = word >> 24;
data[addr + 2] = word >> 16;
data[addr + 1] = word >> 8;
data[addr + 0] = word >> 0;
data[addr + 1] = word >> 8;
data[addr + 2] = word >> 16;
data[addr + 3] = word >> 24;
break;
case Half:
addr &= ~1;
data[addr + 1] = word >> 8;
data[addr + 0] = word >> 0;
data[addr + 1] = word >> 8;
break;
case Byte:
data[addr + 0] = word >> 0;

View File

@ -1,5 +1,15 @@
#include <gba/gba.hpp>
//pixel: 4 cycles
//hdraw: 240 pixels ( 960 cycles)
//hblank: 68 pixels ( 272 cycles)
//scanline: 308 pixels (1232 cycles)
//vdraw: 160 scanlines (197120 cycles)
//vblank: 68 scanlines ( 83776 cycles)
//frame: 208 scanlines (280896 cycles)
namespace GBA {
PPU ppu;
@ -9,7 +19,7 @@ void PPU::Enter() { ppu.enter(); }
void PPU::enter() {
while(true) {
frame();
step(279620);
step(280896);
}
}

View File

@ -26,7 +26,7 @@ string ArmDSP::disassemble_opcode(uint32 pc) {
uint4 rs = instruction >> 8;
uint4 rm = instruction >> 0;
output.append(accumulate ? "mla" : "mul", condition[conditions], save ? "s " : " ");
output.append(accumulate ? "mla" : "mul", conditions[condition], save ? "s " : " ");
output.append(registers[rd], ",", registers[rm], ",", registers[rs]);
if(accumulate) output.append(",", registers[rn]);
@ -40,7 +40,7 @@ string ArmDSP::disassemble_opcode(uint32 pc) {
uint1 psr = instruction >> 22;
uint4 rd = instruction >> 12;
output.append("mrs", condition[conditions], " ");
output.append("mrs", conditions[condition], " ");
output.append(registers[rd], ",", psr ? "spsr" : "cpsr");
return output;
@ -96,7 +96,7 @@ string ArmDSP::disassemble_opcode(uint32 pc) {
//{opcode}{condition}{s} rd,rm {shift} rs
//{opcode}{condition} rn,rm {shift} rs
//{opcode}{condition}{s} rd,rn,rm {shift} rs
if((instruction & 0x0e000090) == 0x00000010) {
if((instruction & 0x0e000090) == 0x00000010) {
uint4 condition = instruction >> 28;
uint4 opcode = instruction >> 21;
uint1 save = instruction >> 20;
@ -162,7 +162,7 @@ string ArmDSP::disassemble_opcode(uint32 pc) {
if(p == 0) output.append("]");
if(immediate) output.append(",", u ? "+" : "-", "0x", hex<3>(immediate));
if(p == 1) output.append("]");
if(p == 1 && w == 1) output.append("!");
if(p == 0 || w == 1) output.append("!");
if(rn == 15) output.append(" =0x", hex<8>(bus_readword(pc + 8 + (u ? +immediate : -immediate))));
@ -195,7 +195,7 @@ string ArmDSP::disassemble_opcode(uint32 pc) {
if(mode == 3 && shift != 0) output.append(" ror #", shift);
if(mode == 3 && shift == 0) output.append(" rrx");
if(p == 1) output.append("]");
if(p == 1 && w == 1) output.append("!");
if(p == 0 || w == 1) output.append("!");
return output;
}
@ -239,7 +239,6 @@ string ArmDSP::disassemble_registers() {
"r8:", hex<8>(r[ 8]), " r9:", hex<8>(r[ 9]), " r10:", hex<8>(r[10]), " r11:", hex<8>(r[11]),
" r12:", hex<8>(r[12]), " r13:", hex<8>(r[13]), " r14:", hex<8>(r[14]), " r15:", hex<8>(r[15]), " ",
"spsr:", spsr.n ? "N" : "n", spsr.z ? "Z" : "z", spsr.c ? "C" : "c", spsr.v ? "V" : "v"
};
}

View File

@ -233,7 +233,7 @@ void ArmDSP::op_data_register_shift() {
uint1 save = instruction >> 20;
uint4 s = instruction >> 8;
uint2 mode = instruction >> 5;
uint4 m = instruction >> 0;
uint4 m = instruction;
uint8 rs = r[s];
uint32 rm = r[m];
@ -309,8 +309,8 @@ void ArmDSP::op_move_immediate_offset() {
if(p == 0 || w == 1) r[n] = rn;
}
//(ldr)(str){condition}{b} rd,[rn,rm {mode} #immediate]{!}
//(ldr)(str){condition}{b} rd,[rn],rm {mode} #immediate
//(ldr,str){condition}{b} rd,[rn,rm {mode} #immediate]{!}
//(ldr,str){condition}{b} rd,[rn],rm {mode} #immediate
//cccc 011p ubwl nnnn dddd llll lss0 mmmm
//c = condition
//p = pre (0 = post-indexed addressing)

View File

@ -86,6 +86,8 @@ struct Register {
if(write) write();
}
Register& operator=(const Register &source) { return operator=(source.data); }
Register& operator+=(uint32 n) { return operator=(data + n); }
Register& operator-=(uint32 n) { return operator=(data - n); }
Register& operator&=(uint32 n) { return operator=(data & n); }