mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
d118b70b30
commit
533aa97011
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -10,6 +10,7 @@ struct ArmDSP : public Coprocessor {
|
|||
|
||||
static void Enter();
|
||||
void enter();
|
||||
void tick(unsigned clocks = 1);
|
||||
|
||||
void init();
|
||||
void load();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue