Update to v086r09 release.

byuu says:

A lot more work on the ARMv3 core.
This commit is contained in:
Tim Allen 2012-02-27 11:18:50 +11:00
parent 482b4119f6
commit 3ed42af8a1
8 changed files with 459 additions and 72 deletions

View File

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

View File

@ -38,6 +38,13 @@ namespace nall {
return x | (x + 1);
}
//count number of bits set in a byte
inline unsigned count(unsigned x) {
unsigned count = 0;
do count += x & 1; while(x >>= 1);
return count;
}
//round up to next highest single bit:
//round(15) == 16, round(16) == 16, round(17) == 32
inline unsigned round(unsigned x) {

View File

@ -17,30 +17,61 @@ void ArmDSP::enter() {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
step(16);
step(4); //todo: how many cycles per instruction?
synchronize_cpu();
print(disassemble_opcode(r[15]), "\n", disassemble_registers(), "\n");
instruction = bus_read(r[15]);
r[15] += 8; //pipeline adjust
if((instruction & 0x0e000000) == 0x0a000000) { op_branch(); continue; }
if((instruction & 0x0e000000) == 0x02000000) { op_data_immediate(); r[15] -= 4; continue; }
print("* ARM unknown instruction\n");
while(true) {
step(21477272);
synchronize_cpu();
if(exception) {
print("* ARM unknown instruction\n");
print("\n", disassemble_registers());
print("\n", disassemble_opcode(pipeline.instruction.address), "\n");
while(true) {
step(21477272);
synchronize_cpu();
}
}
if(pipeline.reload) {
pipeline.reload = false;
pipeline.prefetch.address = r[15];
pipeline.prefetch.opcode = bus_read<4>(r[15]);
r[15].step();
}
pipeline.instruction = pipeline.prefetch;
pipeline.prefetch.address = r[15];
pipeline.prefetch.opcode = bus_read<4>(r[15]);
r[15].step();
//print("\n", disassemble_registers(), "\n");
//print(disassemble_opcode(pipeline.instruction.address), "\n");
instruction = pipeline.instruction.opcode;
if((instruction & 0x0fb000f0) == 0x01200000) { op_move_status_register_to_register(); continue; }
if((instruction & 0x0e000010) == 0x00000000) { op_data_immediate_shift(); continue; }
if((instruction & 0x0e000000) == 0x02000000) { op_data_immediate(); continue; }
if((instruction & 0x0e000000) == 0x04000000) { op_move_immediate_offset(); continue; }
if((instruction & 0x0e000000) == 0x08000000) { op_move_multiple(); continue; }
if((instruction & 0x0e000000) == 0x0a000000) { op_branch(); continue; }
exception = true;
}
}
uint8 ArmDSP::mmio_read(unsigned addr) {
//print("* r", hex<4>(addr), "\n");
addr &= 0xffff;
if(addr == 0x3800) return armport[0];
if(addr == 0x3802) return armport[1];
if(addr == 0x3804) return armport[3];
print("* r", hex<4>(addr), "\n");
return 0x00;
}
void ArmDSP::mmio_write(unsigned addr, uint8 data) {
addr &= 0xffff;
if(addr == 0x3802) cpuport[1] = data;
if(addr == 0x3804) cpuport[0] = data;
print("* w", hex<4>(addr), "w = ", hex<2>(data), "\n");
}
@ -59,7 +90,12 @@ void ArmDSP::power() {
void ArmDSP::reset() {
create(ArmDSP::Enter, 21477272);
for(auto &rd : r) rd = 0x00000000;
for(auto &rd : r) rd = 0;
exception = false;
pipeline.reload = true;
r[15].write = [&] { pipeline.reload = true; };
}
}

View File

@ -2,9 +2,13 @@
struct ArmDSP : public Coprocessor {
uint8 programROM[128 * 1024];
uint8 programRAM[16 * 1024];
#include "registers.hpp"
uint8 cpuport[4];
uint8 armport[4];
static void Enter();
void enter();
@ -19,16 +23,21 @@ struct ArmDSP : public Coprocessor {
//opcodes.cpp
bool condition();
void opcode(uint32 &rd, uint32 &rn, uint32 shifter);
void opcode(uint32 data);
void flags(uint32 rd);
void op_branch();
void op_data_immediate();
void op_data_immediate_shift();
void op_move_immediate_offset();
void op_move_status_register_to_register();
void op_move_multiple();
//memory.cpp
uint32 bus_read(uint32 addr);
void bus_write(uint32 addr, uint32 data);
uint8 bus_iread(uint32 addr);
void bus_iwrite(uint32 addr, uint8 data);
template<unsigned size> uint32 bus_read(uint32 addr);
template<unsigned size> void bus_write(uint32 addr, uint32 data);
//disassembler.cpp
string disassemble_opcode(uint32 pc);

View File

@ -1,12 +1,14 @@
#ifdef ARMDSP_CPP
string ArmDSP::disassemble_opcode(uint32 pc) {
static string conditions[] = { "eq", "ne", "cs", "cc", "mi" ,"pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" };
static string conditions[] = { "eq", "ne", "cs", "cc", "mi" ,"pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "" /*al*/, "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" };
string output{hex<8>(pc), " "};
uint32 instruction = bus_read(pc);
uint32 instruction = bus_read<4>(pc);
output.append(hex<8>(instruction), " ");
uint4 condition = instruction >> 28;
@ -15,18 +17,80 @@ string ArmDSP::disassemble_opcode(uint32 pc) {
uint4 rd = instruction >> 12;
uint4 rs = instruction >> 8;
if((instruction & 0x0e000000) == 0x0a000000) {
output.append("b");
if(condition != 14) output.append(conditions[condition]);
output.append(" 0x", hex<8>(pc + 8 + (int24)instruction * 4));
//move status register to register
if((instruction & 0x0fb000f0) == 0x01200000) {
uint1 psr = instruction >> 22;
uint4 field = instruction >> 16;
output.append("msr ", psr ? "spsr:" : "cpsr:",
field & 1 ? "c" : "",
field & 2 ? "x" : "",
field & 4 ? "s" : "",
field & 8 ? "f" : "",
",", registers[(uint4)instruction]);
return output;
}
//data immediate shift
if((instruction & 0x0e000000) == 0x00000000) {
uint4 condition = instruction >> 28;
uint4 opcode = instruction >> 21;
uint1 save = instruction >> 20;
uint4 rd = instruction >> 16;
uint4 rn = instruction >> 12;
uint8 rm = instruction;
uint5 shift = instruction >> 7;
uint2 op = instruction >> 2;
output.append(opcodes[opcode], conditions[condition], save ? "s " : " ");
output.append(registers[rd], ",");
output.append(registers[rn], ",");
output.append(registers[rm], " ");
if(op == 0) output.append("lsl #", shift);
if(op == 1) output.append("lsr #", 1 + shift);
if(op == 2) output.append("asr #", 1 + shift);
if(op == 3 && shift != 0) output.append("ror #", shift);
if(op == 3 && shift == 0) output.append("rrx");
return output;
}
//data immediate
if((instruction & 0x0e000000) == 0x02000000) {
uint5 rotate = 2 * (uint4)(instruction >> 8);
uint32 immediate = (uint8)instruction;
immediate = (immediate >> rotate) | (immediate << (32 - rotate));
output.append(opcodes[opcode], " r", rd, ",#0x", hex<8>(immediate));
output.append(opcodes[opcode], " ", registers[rd], ",#0x", hex<8>(immediate));
return output;
}
//move immediate offset
//todo: support W flag
if((instruction & 0x0e000000) == 0x04000000) {
uint1 u = instruction >> 23;
uint1 load = instruction >> 20;
uint12 immediate = instruction;
output.append(load ? "ldr" : "str", conditions[condition]);
output.append(" ", registers[rd], ",[", registers[rn]);
if(immediate) output.append(u ? "+" : "-", "0x", hex<3>((uint12)instruction));
output.append("]");
return output;
}
//move multiple
if((instruction & 0x0e000000) == 0x08000000) {
output.append(instruction & 0x00100000 ? "ldm" : "stm", conditions[condition], indices[(uint2)(instruction >> 23)]);
output.append(" ", registers[rn], instruction & 0x00200000 ? "!" : "", ",{");
for(unsigned n = 0; n < 16; n++) if(instruction & (1 << n)) output.append(registers[n], ",");
output.rtrim<1>(",");
output.append("}");
return output;
}
//branch
if((instruction & 0x0e000000) == 0x0a000000) {
uint1 l = instruction >> 24;
output.append("b", l ? "l" : "", conditions[condition]);
output.append(" 0x", hex<8>(pc + 8 + (int24)instruction * 4));
return output;
}
@ -39,7 +103,8 @@ string ArmDSP::disassemble_registers() {
"r0:", hex<8>(r[ 0]), " r1:", hex<8>(r[ 1]), " r2:", hex<8>(r[ 2]), " r3:", hex<8>(r[ 3]),
" r4:", hex<8>(r[ 4]), " r5:", hex<8>(r[ 5]), " r6:", hex<8>(r[ 6]), " r7:", hex<8>(r[ 7]), "\n",
"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]), "\n"
" r12:", hex<8>(r[12]), " r13:", hex<8>(r[13]), " r14:", hex<8>(r[14]), " r15:", hex<8>(r[15]), "\n",
"cpsr:", cpsr.n ? "N" : "n", cpsr.z ? "Z" : "z", cpsr.c ? "C" : "c", cpsr.v ? "V" : "v"
};
}

View File

@ -1,18 +1,62 @@
#ifdef ARMDSP_CPP
uint32 ArmDSP::bus_read(uint32 addr) {
uint8 ArmDSP::bus_iread(uint32 addr) {
if(addr >= 0x00000000 && addr <= 0x0001ffff) {
addr &= 0x0001ffff;
return (programROM[addr + 0] << 0)
+ (programROM[addr + 1] << 8)
+ (programROM[addr + 2] << 16)
+ (programROM[addr + 3] << 24);
return programROM[addr & 0x0001ffff];
}
return 0x00000000;
if(addr >= 0x40000020 && addr <= 0x4000002f) {
if(addr == 0x40000020) return cpuport[0];
if(addr == 0x40000024) return cpuport[1];
if(addr == 0x40000028) return cpuport[2];
if(addr == 0x4000002c) return cpuport[3];
}
if(addr >= 0xe0000000 && addr <= 0xe0003fff) {
return programRAM[addr & 0x00003fff];
}
//print("* ARM r", hex<8>(addr), "\n");
return 0x00;
}
void ArmDSP::bus_iwrite(uint32 addr, uint8 data) {
if(addr >= 0x40000020 && addr <= 0x4000002f) {
if(addr == 0x40000020) armport[0] = data;
if(addr == 0x40000024) armport[1] = data;
if(addr == 0x40000028) armport[2] = data;
if(addr == 0x4000002c) armport[3] = data;
if(data) print("* ARM w", hex<8>(addr), " = ", hex<2>(data), "\n");
}
if(addr >= 0xe0000000 && addr <= 0xe0003fff) {
programRAM[addr & 0x00003fff] = data;
return;
}
//print("* ARM w", hex<8>(addr), " = ", hex<2>(data), "\n");
}
template<unsigned size>
uint32 ArmDSP::bus_read(uint32 addr) {
uint32 result = 0;
result |= bus_iread(addr + 0) << 0;
result |= bus_iread(addr + 1) << 8;
result |= bus_iread(addr + 2) << 16;
result |= bus_iread(addr + 3) << 24;
if(size == 1) return result & 0xff;
if(size == 2) return result & 0xffff;
return result;
}
template<unsigned size>
void ArmDSP::bus_write(uint32 addr, uint32 data) {
if(size == 1) data = (bus_read<1>(addr) & 0xffffff00) | (data & 0x000000ff);
if(size == 2) data = (bus_read<2>(addr) & 0xffff0000) | (data & 0x0000ffff);
bus_iwrite(addr + 0, data >> 0);
bus_iwrite(addr + 1, data >> 8);
bus_iwrite(addr + 2, data >> 16);
bus_iwrite(addr + 3, data >> 24);
}
#endif

View File

@ -21,24 +21,52 @@ bool ArmDSP::condition() {
}
}
void ArmDSP::opcode(uint32 &rd, uint32 &rn, uint32 shifter) {
switch((uint4)(instruction >> 21)) {
case 0: rd = rn & shifter; break; //AND (logical and)
case 1: rd = rn & shifter; break; //EOR (logical exclusive or)
case 2: rd = rn - shifter; break; //SUB (subtract)
case 3: rd = shifter - rn; break; //RSB (reverse subtract)
case 4: rd = rn + shifter; break; //ADD (add)
case 5: rd = rn + shifter + cpsr.c; break; //ADC (add with carry)
case 6: rd = rn - shifter - !cpsr.c; break; //SBC (subtract with carry)
case 7: rd = shifter - rn - !cpsr.c; break; //RSC (reverse subtract with carry)
case 8: flags(rn & shifter); break; //TST (test)
case 9: flags(rn ^ shifter); break; //TEQ (test equivalence)
case 10: flags(rn - shifter); break; //CMP (compare)
case 11: flags(rn + shifter); break; //CMN (compare negated)
case 12: rd = rn | shifter; break; //ORR (logical inclusive or)
case 13: rd = shifter; break; //MOV (move)
case 14: rd = rn & ~shifter; break; //BIC (bit clear)
case 15: rd = ~shifter; //MVN (move not)
//rd = target
//rn = source
//rm = modifier
//ri = original target
//ro = modified target
void ArmDSP::opcode(uint32 rm) {
uint4 opcode = instruction >> 21;
uint1 s = instruction >> 20;
auto &rn = r[(uint4)(instruction >> 16)];
auto &rd = r[(uint4)(instruction >> 12)];
uint32 ri = rd, ro;
//comparison opcodes always update flags
if(opcode >= 8 && opcode <= 11) s = 1;
static auto nz = [&](uint32 ro) {
if(!s) return;
cpsr.n = ro & 0x80000000;
cpsr.z = ro == 0;
};
static auto nzcv = [&](uint32 ro) {
if(!s) return;
cpsr.n = ro & 0x80000000;
cpsr.z = ro == 0;
cpsr.c = ro < ri;
cpsr.v = ~(ri ^ rm) & (ri ^ ro) & 0x80000000;
};
switch(opcode) {
case 0: rd = rn & rm; nz(rd); break; //AND (logical and)
case 1: rd = rn ^ rm; nz(rd); break; //EOR (logical exclusive or)
case 2: rd = rn +~rm; nzcv(rd); break; //SUB (subtract)
case 3: rd = rm +~rn; nzcv(rd); break; //RSB (reverse subtract)
case 4: rd = rn + rm; nzcv(rd); break; //ADD (add)
case 5: rd = rn + rm + cpsr.c; nzcv(rd); break; //ADC (add with carry)
case 6: rd = rn +~rm + cpsr.c; nzcv(rd); break; //SBC (subtract with carry)
case 7: rd = rm +~rn + cpsr.c; nzcv(rd); break; //RSC (reverse subtract with carry)
case 8: ro = rn & rm; nz(ro); break; //TST (test)
case 9: ro = rn ^ rm; nz(ro); break; //TEQ (test equivalence)
case 10: ro = rn +~rm; nzcv(ro); break; //CMP (compare)
case 11: ro = rn + rm; nzcv(ro); break; //CMN (compare negated)
case 12: rd = rn | rm; nz(rd); break; //ORR (logical inclusive or)
case 13: rd = rm; nz(rd); break; //MOV (move)
case 14: rd = rn &~rm; nz(rd); break; //BIC (bit clear)
case 15: rd =~rm; nz(rd); break; //MVN (move not)
}
}
@ -54,7 +82,10 @@ void ArmDSP::flags(uint32 rd) {
void ArmDSP::op_branch() {
if(!condition()) return;
int24 displacement = (int24)instruction;
uint1 l = instruction >> 24;
int24 displacement = instruction;
if(l) r[14] = r[15] - 4;
r[15] += displacement * 4;
}
@ -69,28 +100,162 @@ void ArmDSP::op_branch() {
void ArmDSP::op_data_immediate() {
if(!condition()) return;
bool s = (uint1)(instruction >> 20);
uint4 n = (uint4)(instruction >> 16);
uint4 d = (uint4)(instruction >> 12);
uint5 rotate = 2 * (uint4)(instruction >> 8);
uint32 immediate = (uint8)instruction;
immediate = (immediate >> rotate) | (immediate << (32 - rotate));
uint4 rotate = instruction >> 8;
uint8 immediate = instruction >> 0;
opcode(r[d], r[n], immediate);
opcode(
(immediate >> (rotate << 1)) | (immediate << (32 - (rotate << 1)))
);
//todo: set carry for non-add/sub opcodes here
}
//CCCC 010P UBWL NNNN DDDD IIII IIII
//CCCC 000O OOOS NNNN DDDD LLLL LSS0 MMMM
//C = condition
//P = pre/post-indexed addressing
//U = add/sub offset to base
//B = byte/word access
//W = ...
//L = load/save
//O = opcode
//S = update flags
//N = Rn
//D = Rd
//L = shift amount
//S = shift
//M = Rm
void ArmDSP::op_data_immediate_shift() {
if(!condition()) return;
uint5 shift = instruction >> 7;
uint2 op = instruction >> 5;
uint32 rm = r[(uint4)instruction];
if(op == 0) { //LSL (logical shift left)
rm = rm << shift;
}
if(op == 1) { //LSR (logical shift right)
rm = rm >> (1 + shift);
}
if(op == 2) { //ASR (arithmetic shift right)
rm = (int32)rm >> (1 + shift);
}
if(op == 3) { //ROR (rotate right)
if(shift == 0) { //RRX (rorate right with extend)
rm = (cpsr.c << 31) | (rm >> 1);
} else {
rm = (rm >> shift) + (rm << (32 - shift));
}
}
opcode(rm);
//todo: set carry
}
//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 = 32-bit)
//W = writeback
//L = load (0 = save)
//N = Rn
//D = Rd
//I = immediate
void ArmDSP::op_move_immediate_offset() {
if(!condition()) return;
uint1 p = instruction >> 24;
uint1 u = instruction >> 23;
uint1 b = instruction >> 22;
uint1 w = instruction >> 21;
uint1 l = instruction >> 20;
uint4 n = instruction >> 16;
auto &rd = r[(uint4)(instruction >> 12)];
uint12 immediate = instruction;
uint32 rn = r[n];
if(l) {
if(p == 1) rn = u ? rn + immediate : rn - immediate;
if(b) rd = bus_read<1>(rn);
else rd = bus_read<4>(rn);
if(p == 0) rn = u ? rn + immediate : rn - immediate;
} else {
if(p == 1) rn = u ? rn + immediate : rn - immediate;
if(b) bus_write<1>(rn, rd);
else bus_write<4>(rn, rd);
if(p == 0) rn = u ? rn + immediate : rn - immediate;
}
if(p == 1 && w == 1) r[n] = rn;
}
//CCCC 0001 0R10 FFFF ++++ ---- 0000 MMMM
//C = condition
//R = SPSR (0 = CPSR)
//F = field mask
//M = Rm
void ArmDSP::op_move_status_register_to_register() {
if(!condition()) return;
uint1 source = instruction >> 22;
uint4 field = instruction >> 16;
auto &rm = r[(uint4)instruction];
PSR &psr = source ? spsr : cpsr;
if(field & 1) psr.setc(rm);
if(field & 2) psr.setx(rm);
if(field & 4) psr.sets(rm);
if(field & 8) psr.setf(rm);
}
//CCCC 100P USWL NNNN LLLL LLLL LLLL LLLL
//C = condition
//P = pre (0 = post-indexed addressing)
//U = up (add/sub offset to base)
//S = ???
//W = writeback
//L = load (0 = save)
//N = Rn
//L = register list
void ArmDSP::op_move_multiple() {
if(!condition()) return;
uint1 p = instruction >> 24;
uint1 u = instruction >> 23;
uint1 s = instruction >> 22;
uint1 w = instruction >> 21;
uint1 l = instruction >> 20;
uint4 n = instruction >> 16;
uint16 list = instruction;
uint32 rn;
if(p == 0 && u == 1) rn = r[n] + 0; //IA
if(p == 1 && u == 1) rn = r[n] + 4; //IB
if(p == 1 && u == 0) rn = r[n] - bit::count(list) * 4 + 0; //DB
if(p == 0 && u == 0) rn = r[n] - bit::count(list) * 4 + 4; //DA
if(l) {
for(unsigned n = 0; n < 16; n++) {
if(list & (1 << n)) {
r[n] = bus_read<4>(rn);
rn += 4;
}
}
} else {
for(unsigned n = 0; n < 16; n++) {
if(list & (1 << n)) {
bus_write<4>(rn, r[n]);
rn += 4;
}
}
}
if(w) {
if(u == 1) r[n] = r[n] + bit::count(list) * 4; //IA, IB
if(u == 0) r[n] = r[n] - bit::count(list) * 4; //DA, DB
}
}
#endif

View File

@ -7,7 +7,7 @@
//00000018 = IRQ (interrupt)
//0000001c = FIQ (fast interrupt)
struct CPSR {
struct PSR {
bool n;
bool z;
bool c;
@ -16,26 +16,87 @@ struct CPSR {
bool f;
uint5 m;
operator unsigned() const {
return (n << 31) | (z << 30) | (c << 29) | (v << 28) | (i << 7) | (f << 6) | (m << 0);
uint32 getf() const {
return (n << 31) | (z << 30) | (c << 29) | (v << 28);
}
CPSR& operator=(uint32 data) {
uint32 gets() const {
return 0u;
}
uint32 getx() const {
return 0u;
}
uint32 getc() const {
return (i << 7) | (f << 6) | (m << 0);
}
void setf(uint32 data) {
n = data & 0x80000000;
z = data & 0x40000000;
c = data & 0x20000000;
v = data & 0x10000000;
}
void sets(uint32 data) {
}
void setx(uint32 data) {
}
void setc(uint32 data) {
i = data & 0x00000080;
f = data & 0x00000040;
m = data & 0x0000001f;
}
operator uint32() const {
return getf() | gets() | getx() | getc();
}
PSR& operator=(uint32 data) {
setf(data), sets(data), setx(data), setc(data);
return *this;
}
} cpsr;
} cpsr, spsr;
//r13 = SP (stack pointer)
//r14 = LR (link register)
//r15 = PC (program counter)
uint32 r[16];
struct Register {
uint32 data;
function<void ()> write;
operator unsigned() const {
return data;
}
Register& operator=(uint32 n) {
data = n;
if(write) write();
}
Register& operator+=(uint32 n) { return operator=(data + n); }
Register& operator-=(uint32 n) { return operator=(data - n); }
Register& operator&=(uint32 n) { return operator=(data & n); }
Register& operator|=(uint32 n) { return operator=(data | n); }
Register& operator^=(uint32 n) { return operator=(data ^ n); }
void step() {
data += 4;
}
} r[16];
struct Pipeline {
bool reload;
struct Instruction {
uint32 opcode;
uint32 address;
};
Instruction instruction;
Instruction prefetch;
} pipeline;
uint32 instruction;
uint32 prefetch;
bool exception;