#include "opcode.h"
#include <pthread.h>
#include <ctype.h>
#include <string.h>
#include <limits.h>
#if bench
#include <sys/time.h>
#endif
#include <curses.h>
#define THREADS 1
#define BENCH_INST 100000000 << (THREADS-1)
#define CTRL_ADDR 0x100
#define TX_ADDR 0x101
#define RX_ADDR 0x102
#define STEP_ADDR 0x110
#define CURSES_BACKSPACE 0x7F
extern uint8_t kbd_rdy;
extern WINDOW *scr;
#if debug
extern uint8_t subdbg;
#endif
static const uint64_t mem_size = 0x04000000;
extern uint8_t step;
extern uint8_t end;
#define setflag(flag, bit) ((flag)) ? (cpu->ps.u8[thread] |= bit) : (cpu->ps.u8[thread] &= ~bit)
#define getflag(bit) (cpu->ps.u8[thread] & bit)
extern pthread_mutex_t mutex;
extern pthread_mutex_t main_mutex;
extern pthread_cond_t cond;
extern pthread_cond_t main_cond;
#if debug
extern void disasm(struct sux *cpu, uint8_t lines, uint8_t opcode, uint8_t prefix, uint8_t thread);
#endif
extern int get_key(WINDOW *scr);
extern void io(uint64_t address, uint8_t rw);
extern void init_scr();
static inline union reg read_value(struct sux *cpu, uint64_t address, uint8_t size, uint8_t inc_clk, uint8_t check_io) {
union reg value;
value.u64 = 0;
#if (IO || debug) && !branch
#if keypoll
pthread_mutex_lock(&mutex);
#endif
if (check_io) {
io(address, 1);
}
#if keypoll
pthread_mutex_unlock(&mutex);
#endif
#endif
switch (size) {
case 7: value.u8[7] = addr[address+7];
case 6: value.u8[6] = addr[address+6];
case 5: value.u8[5] = addr[address+5];
case 4: value.u8[4] = addr[address+4];
case 3: value.u8[3] = addr[address+3];
case 2: value.u8[2] = addr[address+2];
case 1: value.u8[1] = addr[address+1];
case 0: value.u8[0] = addr[address+0];
}
#if getclk
if (inc_clk) {
++cpu->clk;
}
#endif
return value;
}
static inline void write_value(struct sux *cpu, union reg value, uint64_t address, uint8_t size, uint8_t inc_clk, uint8_t check_io) {
switch (size) {
case 7: addr[address+7] = value.u8[7];
case 6: addr[address+6] = value.u8[6];
case 5: addr[address+5] = value.u8[5];
case 4: addr[address+4] = value.u8[4];
case 3: addr[address+3] = value.u8[3];
case 2: addr[address+2] = value.u8[2];
case 1: addr[address+1] = value.u8[1];
case 0: addr[address+0] = value.u8[0];
}
#if (IO || debug) && !branch
#if keypoll
pthread_mutex_lock(&mutex);
#endif
if (check_io) {
io(address, 0);
}
#if keypoll
pthread_mutex_unlock(&mutex);
#endif
#endif
#if getclk
if (inc_clk) {
++cpu->clk;
}
#endif
}
static inline uint64_t offset_addr(struct sux *cpu, union reg offset, uint8_t size, uint8_t inc_clk, uint8_t prefix) {
uint64_t of;
switch (prefix >> 6) {
case 1: of = ((cpu->stk_st << 16) | cpu->sp); break;
case 2: of = cpu->pc ; break;
}
#if getclk
if (inc_clk) {
++cpu->clk;
}
#endif
switch (size) {
case 0: return of + (int8_t )offset.u8 [0];
case 1: return of + (int16_t)offset.u16[0];
case 2:
case 3: return of + (int32_t)offset.u32[0];
case 4:
case 5:
case 6:
case 7: return of + (int64_t)offset.u64 ;
}
}
static inline uint64_t imm_addr(struct sux *cpu) {
return cpu->pc;
}
static inline uint64_t zm_addr(struct sux *cpu, uint8_t prefix, uint8_t inc_clk, uint8_t inc_pc) {
union reg address = read_value(cpu, cpu->pc, get_addrsize(prefix, ZM), inc_clk, 0);
if (prefix >> 6) {
address.u64 = offset_addr(cpu, address, get_addrsize(prefix, ZM), inc_clk, prefix);
}
if (inc_pc) {
cpu->pc += get_addrsize(prefix, ZM)+1;
}
return address.u64;
}
static inline uint64_t zmx_addr(struct sux *cpu, uint8_t prefix, uint8_t inc_clk, uint8_t inc_pc) {
#if getclk
if (inc_clk) {
++cpu->clk;
}
#endif
return zm_addr(cpu, prefix, inc_clk, inc_pc) + cpu->x;
}
static inline uint64_t zmy_addr(struct sux *cpu, uint8_t prefix, uint8_t inc_clk, uint8_t inc_pc) {
#if getclk
if (inc_clk) {
++cpu->clk;
}
#endif
return zm_addr(cpu, prefix, inc_clk, inc_pc) + cpu->y;
}
static inline uint64_t abs_addr(struct sux *cpu, uint8_t prefix, uint8_t inc_clk, uint8_t inc_pc) {
union reg address = read_value(cpu, cpu->pc, get_addrsize(prefix, ABS), inc_clk, 0);
if (prefix >> 6) {
address.u64 = offset_addr(cpu, address, get_addrsize(prefix, ABS), inc_clk, prefix);
}
if (inc_pc) {
cpu->pc += get_addrsize(prefix, ABS)+1;
}
return address.u64;
}
static inline uint64_t ind_addr(struct sux *cpu, uint8_t prefix, uint8_t inc_clk, uint8_t inc_pc) {
union reg address = read_value(cpu, zm_addr(cpu, prefix, inc_clk, inc_pc), 7, inc_clk, 0);
return address.u64;
}
static inline uint64_t indx_addr(struct sux *cpu, uint8_t prefix, uint8_t inc_clk, uint8_t inc_pc) {
union reg address = read_value(cpu, zm_addr(cpu, prefix, inc_clk, inc_pc)+cpu->x, 7, inc_clk, 0);
#if getclk
if (inc_clk) {
++cpu->clk;
}
#endif
return address.u64;
}
static inline uint64_t indy_addr(struct sux *cpu, uint8_t prefix, uint8_t inc_clk, uint8_t inc_pc) {
#if getclk
if (inc_clk) {
++cpu->clk;
}
#endif
return ind_addr(cpu, prefix, inc_clk, inc_pc) + cpu->y;
}
static inline uint64_t rel_addr(struct sux *cpu, uint8_t prefix, uint8_t inc_clk, uint8_t inc_pc) {
uint8_t rs = (prefix >> 4) & 3;
uint8_t size = (1 << rs) - 1;
union reg offset = read_value(cpu, cpu->pc, size, inc_clk, 0);
uint64_t address;
if (inc_pc) {
cpu->pc += (size + 1);
}
switch (rs) {
default: address = cpu->pc + (int8_t )offset.u8 [0]; break;
case 1 : address = cpu->pc + (int16_t)offset.u16[0]; break;
case 2 : address = cpu->pc + (int32_t)offset.u32[0]; break;
case 3 : address = cpu->pc + (int64_t)offset.u64 ; break;
}
#if getclk
if (inc_clk) {
++cpu->clk;
}
#endif
return address;
}
static inline uint64_t get_addr(struct sux *cpu, uint8_t opcode, uint8_t prefix, uint8_t inc_pc, uint8_t inc_clk, uint8_t thread) {
union reg address;
union reg value;
uint8_t tmp = 0;
uint64_t tmp2 = 0;
union reg saveaddr;
address.u64 = 0;
value.u64 = 0;
switch (optype[opcode]) {
case BREG:
case IMPL:
break;
case IMM:
address.u64 = imm_addr(cpu);
switch (opcode) {
case LSL_IMM:
case LSR_IMM:
case ROL_IMM:
case ROR_IMM:
case ASR_IMM:
if (inc_pc) {
++cpu->pc;
}
break;
default:
if (inc_pc) {
cpu->pc+=(1 << ((prefix >> 4) & 3));
}
case TXS_IMM: break;
}
break;
case ZM : address.u64 = zm_addr(cpu, prefix, inc_clk, inc_pc); break;
case ZMX : address.u64 = zmx_addr(cpu, prefix, inc_clk, inc_pc); break;
case ZMY : address.u64 = zmy_addr(cpu, prefix, inc_clk, inc_pc); break;
case IND : address.u64 = ind_addr(cpu, prefix, inc_clk, inc_pc); break;
case INDX: address.u64 = indx_addr(cpu, prefix, inc_clk, inc_pc); break;
case INDY: address.u64 = indy_addr(cpu, prefix, inc_clk, inc_pc); break;
case ABS : address.u64 = abs_addr(cpu, prefix, inc_clk, inc_pc); break;
case REL : address.u64 = rel_addr(cpu, prefix, inc_clk, inc_pc); break;
}
return address.u64;
}
static inline void adc(struct sux *cpu, uint64_t value, uint8_t thread) {
uint64_t sum = cpu->a+value+getflag(C);
setflag(sum == 0, Z);
setflag((sum >> 63), N);
setflag(((cpu->a^value) >> 63) && ((cpu->a^sum) >> 63), V);
setflag((sum < value), C);
cpu->a = sum;
}
static inline void sbc(struct sux *cpu, uint64_t value, uint8_t thread) {
uint64_t sum = cpu->a-value-!getflag(C);
setflag(sum == 0, Z);
setflag(sum >> 63, N);
setflag(((cpu->a^value) >> 63) && ((cpu->a^sum) >> 63), V);
setflag((sum > value), C);
cpu->a = sum;
}
static inline void transfer(struct sux *cpu, uint64_t value, uint8_t opcode, uint8_t prefix, uint8_t thread) {
uint64_t reg;
switch (opcode) {
case TBA_IMP: cpu->a = cpu->b; reg = cpu->a; break;
case TXA_IMP: cpu->a = cpu->x; reg = cpu->a; break;
case TYA_IMP: cpu->a = cpu->y; reg = cpu->a; break;
case TAB_IMP: cpu->b = cpu->a; reg = cpu->b; break;
case TAY_IMP: cpu->y = cpu->a; reg = cpu->y; break;
case TXY_IMP: cpu->y = cpu->x; reg = cpu->y; break;
case TAX_IMP: cpu->x = cpu->a; reg = cpu->x; break;
case TYX_IMP: cpu->x = cpu->y; reg = cpu->x; break;
case TSX_IMP: cpu->x = cpu->sp & 0xFFFF; cpu->x = cpu->stk_st << 16; break;
case TXS_IMM: cpu->sp = cpu->x;
if (prefix == 0x13 && (value == thread+1 || value > 8)) {
cpu->stk_st = value & 0xFF;
cpu->stk_st += value << 16;
cpu->pc+=2;
}
break;
}
setflag(reg == 0, Z);
setflag(reg >> 63, N);
}
static inline void push(struct sux *cpu, uint64_t value, uint8_t size, uint8_t thread) {
union reg reg;
reg.u64 = value;
uint64_t sbr = (cpu->stk_st << 16);
write_value(cpu, reg, (sbr+cpu->sp)-size, size, 1, 0);
cpu->sp -= size+1;
}
static inline uint64_t pull(struct sux *cpu, uint8_t size, uint8_t thread) {
uint64_t sbr = (cpu->stk_st << 16);
union reg reg = read_value(cpu, sbr+cpu->sp+1, size, 1, 0);
cpu->sp += size+1;
return reg.u64;
}
static inline void and(struct sux *cpu, uint64_t value, uint8_t thread) {
cpu->a &= value;
setflag(cpu->a == 0, Z);
setflag(cpu->a >> 63, N);
}
static inline void or(struct sux *cpu, uint64_t value, uint8_t thread) {
cpu->a |= value;
setflag(cpu->a == 0, Z);
setflag(cpu->a >> 63, N);
}
static inline void xor(struct sux *cpu, uint64_t value, uint8_t thread) {
cpu->a ^= value;
setflag(cpu->a == 0, Z);
setflag(cpu->a >> 63, N);
}
static inline void lsl(struct sux *cpu, uint64_t value, uint8_t thread) {
uint64_t sum = (value < 64) ? cpu->a << value : 0;
setflag(sum == 0, Z);
setflag(sum >> 63, N);
setflag(cpu->a >> (64-value), C);
cpu->a = sum;
}
static inline void lsr(struct sux *cpu, uint64_t value, uint8_t thread) {
uint64_t sum = (value < 64) ? cpu->a >> value : 0;
setflag(sum == 0, Z);
setflag(sum >> 63, N);
setflag(cpu->a & 1, C);
cpu->a = sum;
}
static inline void asr(struct sux *cpu, uint64_t value, uint8_t thread) {
uint8_t sign = cpu->a >> 63;
uint64_t sum = (value < 64) ? (cpu->a >> value) | ((uint64_t)sign << 63) : 0;
setflag(sum == 0, Z);
setflag(sum >> 63, N);
setflag(cpu->a & 1, C);
cpu->a = sum;
}
static inline void rol(struct sux *cpu, uint64_t value, uint8_t thread) {
uint64_t sum = cpu->a << value;
sum |= getflag(C);
setflag(sum == 0, Z);
setflag(sum >> 63, N);
setflag(cpu->a >> (uint64_t)(64-value), C);
cpu->a = sum;
}
static inline void ror(struct sux *cpu, uint64_t value, uint8_t thread) {
uint64_t sum = cpu->a >> value;
sum |= (uint64_t)getflag(C) << (uint64_t)(64-value);
setflag(sum == 0, Z);
setflag(sum >> 63, N);
setflag(cpu->a & 1, C);
cpu->a = sum;
}
static inline void mul(struct sux *cpu, uint64_t value, uint8_t thread) {
uint64_t sum = cpu->a*value;
cpu->a = sum;
setflag(sum == 0, Z);
setflag(sum >> 63, N);
setflag(!((cpu->a^value) >> 63) && ((cpu->a^sum) >> 63), V);
}
static inline void divd(struct sux *cpu, uint64_t value, uint8_t opcode, uint8_t thread) {
uint64_t sum = cpu->a/value;
if (opcode != DIV_B) {
cpu->b = cpu->a % value;
} else {
value = cpu->b;
cpu->x = cpu->a % value;
}
cpu->a = sum;
setflag(sum == 0, Z);
setflag((sum >> 63), N);
}
static inline void cmp(struct sux *cpu, uint64_t value, uint64_t reg, uint8_t thread) {
uint64_t sum = reg-value;
setflag(sum >> 63, N);
setflag(((reg^value) >> 63) && ((reg^sum) >> 63), V);
setflag(sum == 0, Z);
setflag(reg >= value, C);
}
static inline uint64_t idr(struct sux *cpu, uint64_t reg, uint8_t inc, uint8_t thread) {
if (inc) {
reg++;
} else {
reg--;
}
setflag(reg == 0, Z);
setflag(reg >> 63, N);
return reg;
}
static inline void idm(struct sux *cpu, uint64_t address, uint8_t prefix, uint8_t inc, uint8_t thread) {
uint8_t size = (1 << ((prefix >> 4) & 3))-1;
union reg value = read_value(cpu, address, size, 1, 0);
if (inc) {
value.u64++;
} else {
value.u64--;
}
uint8_t sign = 0;
switch ((prefix >> 4) & 3) {
default: sign = 7; break;
case 1: sign = 15; break;
case 2: sign = 31; break;
case 3: sign = 63; break;
}
setflag(value.u64 == 0, Z);
setflag(value.u64 >> sign, N);
write_value(cpu, value, address, size, 1, 1);
}
static inline uint64_t load(struct sux *cpu, uint64_t value, uint8_t thread) {
setflag(value == 0, Z);
setflag(value >> 63, N);
return value;
}
static inline void store(struct sux *cpu, uint64_t address, uint64_t reg, uint8_t prefix, uint8_t thread) {
uint8_t size = (1 << ((prefix >> 4) & 3))-1;
union reg value;
value.u64 = reg;
write_value(cpu, value, address, size, 1, 1);
}