Update to v086r16 release.

byuu says:

Cydrak, I moved the step from the opcode decoder and opcodes themselves
into bus_(read,write)(byte,word), to minimize code.
If that's not feasible for some reason, please let me know and I'll
change it back to your latest WIP.
This has your carry flag fix, the timer skeleton (doesn't really work
yet), the Booth two-bit steps, and the carry flag clear thing inside
multiply ops.
Also added the aforementioned reset delay and reset bit stuff, and fixed
the steps to 21MHz for instructions and 64KHz for reset pulse.
I wasn't sure about the shifter extra cycles. I only saw it inside one
of the four (or was it three?) opcodes that have shifter functions.
Shouldn't it be in all of them?

The game does indeed appear to be fully playable now, but the AI doesn't
exactly match my real cartridge.
This could be for any number of reasons: ARM CPU bug, timer behavior
bug, oscillator differences between my real hardware and the emulator,
etc.
However ... the AI is 100% predictable every time, both under emulation
and on real hardware.

- For the first step, move 九-1 to 八-1.
- The opponent moves 三-3 to 四-3.
- Now move 七-1 to 六-1.
- The opponent moves 二-2 to 八-8.
  However, on my real SNES, the opponent moves 一-3 to 二-4.
This commit is contained in:
Tim Allen 2012-03-08 00:03:15 +11:00
parent d118b70b30
commit 533aa97011
10 changed files with 97 additions and 76 deletions

View File

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

View File

@ -5,26 +5,34 @@
namespace nall {
template<unsigned bits, typename type_t = unsigned>
constexpr inline type_t uclamp(const type_t x) {
constexpr inline
typename type_if<sizeof(type_t) <= sizeof(unsigned), unsigned, uintmax_t>::type
uclamp(const type_t x) {
enum : type_t { b = 1ull << (bits - 1), y = b * 2 - 1 };
return y + ((x - y) & -(x < y)); //min(x, y);
}
template<unsigned bits, typename type_t = unsigned>
constexpr inline type_t uclip(const type_t x) {
constexpr inline
typename type_if<sizeof(type_t) <= sizeof(unsigned), unsigned, uintmax_t>::type
uclip(const type_t x) {
enum : type_t { b = 1ull << (bits - 1), m = b * 2 - 1 };
return (x & m);
}
template<unsigned bits, typename type_t = signed>
constexpr inline type_t sclamp(const type_t x) {
constexpr inline
typename type_if<sizeof(type_t) <= sizeof(signed), signed, intmax_t>::type
sclamp(const type_t x) {
enum : type_t { b = 1ull << (bits - 1), m = b - 1 };
return (x > m) ? m : (x < -b) ? -b : x;
}
template<unsigned bits, typename type_t = signed>
constexpr inline type_t sclip(const type_t x) {
typedef typename type_if<std::is_same<type_t, signed>::value, unsigned, uintmax_t>::type cast_t;
constexpr inline
typename type_if<sizeof(type_t) <= sizeof(signed), signed, intmax_t>::type
sclip(const type_t x) {
typedef typename type_if<sizeof(type_t) <= sizeof(signed), unsigned, uintmax_t>::type cast_t;
enum : cast_t { b = 1ull << (bits - 1), m = b * 2 - 1 };
return ((x & m) ^ b) - b;
}

View File

@ -1,26 +0,0 @@
#ifndef NALL_STACK_HPP
#define NALL_STACK_HPP
#include <nall/vector.hpp>
namespace nall {
template<typename T> struct stack : public linear_vector<T> {
void push(const T &value) {
linear_vector<T>::append(value);
}
T pull() {
if(linear_vector<T>::size() == 0) throw;
T value = linear_vector<T>::operator[](linear_vector<T>::size() - 1);
linear_vector<T>::remove(linear_vector<T>::size() - 1);
return value;
}
T& operator()() {
if(linear_vector<T>::size() == 0) throw;
return linear_vector<T>::operator[](linear_vector<T>::size() - 1);
}
};
}
#endif

View File

@ -36,12 +36,13 @@ bool quotecopy(char *&t, T *&p) {
string substr(const char *src, unsigned start, unsigned length) {
string dest;
dest.reserve(length + 1);
if(length == ~0u) {
//copy entire string
dest.reserve(strlen(src + start) + 1);
strcpy(dest(), src + start);
} else {
//copy partial string
dest.reserve(length + 1);
strmcpy(dest(), src + start, length + 1);
}
return dest;

View File

@ -66,11 +66,20 @@ namespace nall {
insert(0, data);
}
void remove(unsigned index, unsigned count = 1) {
void remove(unsigned index = ~0u, unsigned count = 1) {
if(index == ~0) index = objectsize ? objectsize - 1 : 0;
for(unsigned n = index; count + n < objectsize; n++) pool[n] = pool[count + n];
objectsize = (count + index >= objectsize) ? index : objectsize - count;
}
T take(unsigned index = ~0u) {
if(index == ~0) index = objectsize ? objectsize - 1 : 0;
if(index >= objectsize) throw exception_out_of_bounds();
T item = pool[index];
remove(index);
return item;
}
void sort() {
nall::sort(pool, objectsize);
}
@ -84,6 +93,16 @@ namespace nall {
return { false, 0u };
}
T& first() {
if(objectsize == 0) throw exception_out_of_bounds();
return pool[0];
}
T& last() {
if(objectsize == 0) throw exception_out_of_bounds();
return pool[objectsize - 1];
}
//access
inline T& operator[](unsigned position) {
if(position >= objectsize) throw exception_out_of_bounds();

View File

@ -16,15 +16,13 @@ void ArmDSP::Enter() { armdsp.enter(); }
void ArmDSP::enter() {
//reset hold delay
while(bridge.reset) {
step(4);
synchronize_cpu();
tick();
continue;
}
//reset sequence delay
if(bridge.ready == false) {
step(4096); //todo: verify cycle count
synchronize_cpu();
tick(65536);
bridge.ready = true;
}
@ -33,17 +31,11 @@ void ArmDSP::enter() {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
step(4); //todo: how many cycles per instruction?
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();
}
while(true) tick(frequency);
}
if(pipeline.reload) {
@ -58,16 +50,17 @@ void ArmDSP::enter() {
pipeline.prefetch.opcode = bus_readword(r[15]);
r[15].step();
pipeline.mdr.address = r[15];
pipeline.mdr.opcode = bus_readword(r[15]);
//todo: bus_readword() calls tick(); so we need to prefetch this as well
//pipeline.mdr.address = r[15];
//pipeline.mdr.opcode = bus_readword(r[15]);
if(pipeline.instruction.address == 0x00000208) trace = 1;
//if(pipeline.instruction.address == 0x00000208) trace = 1;
if(trace) {
print("\n", disassemble_registers(), "\n");
print(disassemble_opcode(pipeline.instruction.address), "\n");
usleep(200000);
}
trace = 0;
//trace = 0;
instruction = pipeline.instruction.opcode;
if(!condition()) continue;
@ -86,6 +79,13 @@ void ArmDSP::enter() {
}
}
void ArmDSP::tick(unsigned clocks) {
if(bridge.timer && --bridge.timer == 0) bridge.busy = false;
step(clocks);
synchronize_cpu();
}
//MMIO: $00-3f|80-bf:3800-38ff
//3800-3807 mirrored throughout
//a0 ignored
@ -100,42 +100,33 @@ uint8 ArmDSP::mmio_read(unsigned addr) {
if(bridge.armtocpu.ready) {
bridge.armtocpu.ready = false;
data = bridge.armtocpu.data;
print("* r3800 = ", hex<2>(data), "\n");
}
}
if(addr == 0x3802) {
bridge.timer = 0;
bridge.busy = false;
}
if(addr == 0x3804) {
data = bridge.status();
}
if(0) {
print("* r", hex<6>(addr), " = ", hex<2>(data), "\n");
usleep(200000);
}
return data;
}
void ArmDSP::mmio_write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessors();
if(0) {
print("* w", hex<6>(addr), " = ", hex<2>(data), "\n");
usleep(200000);
}
addr &= 0xff06;
if(addr == 0x3802) {
bridge.cputoarm.ready = true;
bridge.cputoarm.data = data;
print("* w3802 = ", hex<2>(data), "\n");
}
if(addr == 0x3804) {
data &= 1;
if(!bridge.reset && data) arm_reset();
bridge.reset = data;
}
@ -151,14 +142,6 @@ void ArmDSP::unload() {
}
void ArmDSP::power() {
string filename = { dir(interface->path(Cartridge::Slot::Base, "st018.rom")), "disassembly.txt" };
file fp;
fp.open(filename, file::mode::write);
for(unsigned n = 0; n < 128 * 1024; n += 4) {
fp.print(disassemble_opcode(n), "\n");
}
fp.close();
for(unsigned n = 0; n < 16 * 1024; n++) programRAM[n] = random(0x00);
}
@ -168,9 +151,15 @@ void ArmDSP::reset() {
}
void ArmDSP::arm_reset() {
bridge.ready = false;
create(ArmDSP::Enter, 21477272);
bridge.ready = false;
bridge.timer = 0;
bridge.timerlatch = 0;
bridge.busy = false;
bridge.cputoarm.ready = false;
bridge.armtocpu.ready = false;
for(auto &rd : r) rd = 0;
shiftercarry = 0;
exception = 0;
@ -179,8 +168,8 @@ void ArmDSP::arm_reset() {
}
ArmDSP::ArmDSP() {
firmware = new uint8[160 * 1024];
programRAM = new uint8[16 * 1024];
firmware = new uint8[160 * 1024]();
programRAM = new uint8[16 * 1024]();
programROM = &firmware[0];
dataROM = &firmware[128 * 1024];

View File

@ -10,6 +10,7 @@ struct ArmDSP : public Coprocessor {
static void Enter();
void enter();
void tick(unsigned clocks = 1);
void init();
void load();

View File

@ -42,17 +42,29 @@ void ArmDSP::bus_write(uint32 addr, uint8 data) {
bridge.armtocpu.data = data;
return;
}
if(addr == 0x40000020) bridge.timerlatch = (bridge.timerlatch & 0xffff00) | (data << 0);
if(addr == 0x40000024) bridge.timerlatch = (bridge.timerlatch & 0xff00ff) | (data << 8);
if(addr == 0x40000028) bridge.timerlatch = (bridge.timerlatch & 0x00ffff) | (data << 16);
if(addr == 0x40000028) {
bridge.timer = bridge.timerlatch;
bridge.busy = !bridge.timer;
}
}
uint32 ArmDSP::bus_readbyte(uint32 addr) {
tick();
return bus_read(addr);
}
void ArmDSP::bus_writebyte(uint32 addr, uint32 data) {
tick();
return bus_write(addr, data);
}
uint32 ArmDSP::bus_readword(uint32 addr) {
tick();
addr &= ~3;
return (
(bus_read(addr + 0) << 0)
@ -63,6 +75,7 @@ uint32 ArmDSP::bus_readword(uint32 addr) {
}
void ArmDSP::bus_writeword(uint32 addr, uint32 data) {
tick();
addr &= ~3;
bus_write(addr + 0, data >> 0);
bus_write(addr + 1, data >> 8);

View File

@ -51,10 +51,11 @@ void ArmDSP::opcode(uint32 rm) {
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 = result < source;
cpsr.v = ~(source ^ modify) & (source ^ result) & (1u << 31);
cpsr.c = (1u << 31) & (overflow ^ source ^ modify ^ result);
cpsr.v = (1u << 31) & (overflow);
}
return result;
};
@ -135,11 +136,23 @@ void ArmDSP::op_multiply() {
uint4 s = instruction >> 8;
uint4 m = instruction >> 0;
//Booth's algorithm: two bit steps
uint32 temp = r[s];
while(temp) {
temp >>= 2;
tick();
}
r[d] = r[m] * r[s];
if(accumulate) r[d] += r[n];
if(accumulate) {
tick();
r[d] += r[n];
}
if(save) {
cpsr.n = r[d] >> 31;
cpsr.z = r[d] == 0;
cpsr.c = 0; //undefined
}
}

View File

@ -14,11 +14,14 @@ struct Bridge {
};
Buffer cputoarm;
Buffer armtocpu;
uint32 timer;
uint32 timerlatch;
bool reset;
bool ready;
bool busy;
uint8 status() const {
return (ready << 7) | (cputoarm.ready << 3) | (armtocpu.ready << 0) | 4;
return (ready << 7) | (cputoarm.ready << 3) | (busy << 2) | (armtocpu.ready << 0);
}
} bridge;