#include "opcode.h"
#include <pthread.h>
#include <ctype.h>
#include <string.h>
#include <limits.h>
#if bench
#include <sys/time.h>
#include <signal.h>
#include <math.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
#define copy64
extern uint8_t kbd_rdy;
extern uint8_t dbg_print_per_inst;
extern WINDOW *scr;
extern WINDOW *regs;
extern WINDOW *inst_win;
extern WINDOW *dbg_win;
#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))
#define ORTHO_1CC(mne, cc) \
mne##_R##cc: case mne##_M##cc
#define ORTHO_1OP(mne) \
mne##_R: case mne##_M
#define ORTHO_2OP(mne) \
mne##_RR: case mne##_RM: case mne##_MR: case mne##_MM
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 print_info(struct sux *cpu, WINDOW *w, uint8_t lines, uint8_t thread);
extern void print_regs(struct sux *cpu, WINDOW *w);
extern void disasm(struct sux *cpu, WINDOW *w, uint8_t lines, uint8_t opcode, uint8_t prefix, uint8_t ext_prefix, uint8_t prefix2, uint8_t *op_type, uint8_t *op_id, uint8_t thread);
#endif
extern int get_key(WINDOW *scr);
extern void io(uint64_t address, uint8_t rw);
extern void init_scr();
static uint8_t get_addrsize(uint8_t prefix, uint8_t addrmode) {
uint8_t id = (prefix & 0x0C) >> 2;
switch (addrmode) {
case ZM:
case ZMX:
case ZMY:
case IND:
case INDX:
case INDY:
switch (id) {
case 2: return 5;
case 3: return 3;
case 1: return 2;
case 0: return 0;
}
break;
case ABS:
case ABSX:
case ABSY:
case AIND:
case AINDX:
case AINDY:
switch (id) {
case 3: return 7;
case 2: return 6;
case 1: return 4;
case 0: return 1;
}
break;
}
return 0xFF;
}
static uint8_t get_ortho_addrsize(uint8_t prefix, uint8_t addrmode) {
uint8_t type = IMM;
switch (addrmode) {
case MEM_ABS :
case MEM_ABSR :
case MEM_AIND :
case MEM_AINDR:
case MEM_ARIND: type = ABS; break;
case MEM_ZM :
case MEM_ZMR :
case MEM_IND :
case MEM_ZINDR:
case MEM_ZRIND: type = ZM; break;
}
return get_addrsize(prefix, type);
}
static int is_1op(uint8_t opcode) {
switch (opcode) {
case ORTHO_1OP(JMP):
case ORTHO_1OP(JSR):
case ORTHO_1OP(PSH):
case ORTHO_1OP(PEA):
case ORTHO_1OP(PUL):
case ORTHO_1OP(SWP):
case ORTHO_1OP(NOT):
case ORTHO_1OP(NEG):
case ORTHO_1OP(DEC):
case ORTHO_1OP(INC):
case ORTHO_1OP(CLZ):
case ORTHO_1OP(CLO): return 1;
}
return 0;
}
static int is_1cc(uint8_t opcode) {
switch (opcode) {
case ORTHO_1CC(SET, NG):
case ORTHO_1CC(SET, PO):
case ORTHO_1CC(SET, CS):
case ORTHO_1CC(SET, CC):
case ORTHO_1CC(SET, EQ):
case ORTHO_1CC(SET, NE):
case ORTHO_1CC(SET, VS):
case ORTHO_1CC(SET, VC): return 1;
}
return 0;
}
static uint8_t isrw(uint8_t opcode, uint8_t ext_prefix) {
if ((ext_prefix & 0xD) == 0xD) {
switch (ext_prefix >> 4) {
case 0x0:
switch (opcode) {
case STA_E :
case STB_E :
case STX_E :
case STY_E :
case STE_AB:
case STE_Z :
case STS_AB:
case STS_Z :
case STS_E :
case STZ_AB:
case STZ_Z :
case STZ_E :
case SNG_E :
case SPO_E :
case SCC_E :
case SCS_E :
case SEQ_E :
case SNE_E :
case INC_E :
case DEC_E :
case NOT_AB:
case NOT_Z :
case NOT_E :
case SWP_AB:
case SWP_Z :
case SWP_E :
case LLM_AB:
case LLM_Z :
case LLM_E :
case LRM_AB:
case LRM_Z :
case LRM_E :
case RLM_AB:
case RLM_Z :
case RLM_E :
case RRM_AB:
case RRM_Z :
case RRM_E :
case ARM_AB:
case ARM_Z :
case ARM_E : return 0;
default : return 1;
}
}
} else {
switch (opcode) {
case STA_AB:
case STA_Z:
case STA_ZX:
case STA_ZY:
case STA_IN:
case STA_IX:
case STA_IY:
case STY_AB:
case STY_Z:
case STY_IN:
case STX_AB:
case STX_Z:
case STX_IN:
case STB_AB:
case STB_Z:
case STB_ZX:
case STB_ZY:
case STB_IN:
case STB_IX:
case STB_IY:
case INC_AB:
case INC_Z:
case DEC_AB:
case DEC_Z:
return 0;
default:
return 1;
}
}
}
static uint8_t isread(uint8_t opcode, uint8_t ext_prefix) {
if ((ext_prefix & 0xD) == 0xD) {
switch (ext_prefix >> 4) {
case 0x0:
switch (opcode) {
case LEA_AB :
case LEA_AX :
case LEA_AY :
case LEA_AI :
case LEA_AIX:
case LEA_AIY:
case LEA_Z :
case LEA_ZX :
case LEA_ZY :
case LEA_IN :
case LEA_IX :
case LEA_IY :
case PEA_AB :
case PEA_AX :
case PEA_AY :
case PEA_AI :
case PEA_AIX:
case PEA_AIY:
case PEA_Z :
case PEA_ZX :
case PEA_ZY :
case PEA_IN :
case PEA_IX :
case PEA_IY :
case LDS_IMM:
case LDS_AB :
case LDS_Z :
case LDS_E :
case LNG_IMM:
case LNG_E :
case LPO_IMM:
case LPO_E :
case LCC_IMM:
case LCC_E :
case LCS_IMM:
case LCS_E :
case LEQ_IMM:
case LEQ_E :
case LNE_IMM:
case LNE_E :
case LDA_E :
case LDB_E :
case LDX_E :
case LDY_E :
case JMP_E :
case JSR_E : return 0;
default : return 1;
}
}
} else {
switch (opcode) {
case LDA_IMM:
case LDA_AB:
case LDA_Z:
case LDA_ZX:
case LDA_ZY:
case LDA_IN:
case LDA_IX:
case LDA_IY:
case LDB_IMM:
case LDB_AB:
case LDB_Z:
case LDB_ZX:
case LDB_ZY:
case LDB_IN:
case LDB_IX:
case LDB_IY:
case LDY_IMM:
case LDY_AB:
case LDY_Z:
case LDY_IN:
case LDX_IMM:
case LDX_AB:
case LDX_Z:
case LDX_IN:
case JMP_AB:
case JMP_Z:
case JMP_IN:
case JSR_IN:
case JSR_AB:
case JSR_Z:
return 0;
default:
return 1;
}
}
}
static int is_ind(uint8_t type) {
switch (type) {
case MEM_AIND :
case MEM_IND :
case MEM_ARIND:
case MEM_ZRIND:
case MEM_AINDR:
case MEM_ZINDR: return 1;
}
return 0;
}
static void *memcopy(void *restrict dst, const void *restrict src, unsigned int n) {
#ifdef copy64
uint64_t *d = dst;
const uint64_t *s = src;
unsigned int r = n % 8;
n /= 8;
#else
uint8_t *d = dst;
const uint8_t *s = src;
#endif
for (; n; *d++ = *s++, n--);
#ifdef copy64
if (r) {
uint64_t mask = (-(uint64_t)1 >> ((8 - r) * 8));
*d = (*d & ~mask) | (*s & mask);
}
#endif
return dst;
}
static uint64_t read_value(struct sux *cpu, uint64_t reg, uint64_t address, uint8_t size, uint8_t inc_clk, uint8_t check_io) {
#if getclk
cpu->clk += inc_clk;
#endif
size = (size > 7) ? 7 : size;
uint64_t mask = (-(uint64_t)1 >> ((7 - size) * 8));
if (address < mem_size) {
#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
#if 1
if (size < 7) {
return (reg & ~mask) | (*(uint64_t *)(addr+address) & mask);
} else {
return *(uint64_t *)(addr+address);
}
#else
#endif
} else {
return (size < 7) ? (reg & ~mask) | (mask) : mask;
}
}
static void write_value(struct sux *cpu, uint64_t value, uint64_t address, uint8_t size, uint8_t inc_clk, uint8_t check_io) {
if (address < mem_size) {
size = (size > 7) ? 7 : size;
#if 1
if (size < 7) {
uint64_t mask = (-(uint64_t)1 >> ((7 - size) * 8));
*(uint64_t *)(addr+address) = (*(uint64_t *)(addr+address) & ~mask) | (value & mask);
} else {
*(uint64_t *)(addr+address) = value;
}
#else
#endif
#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
cpu->clk += inc_clk;
#endif
}
static uint64_t offset_addr(struct sux *cpu, uint64_t offset, uint8_t size, uint8_t inc_clk, uint8_t prefix) {
uint64_t of;
switch (prefix >> 6) {
case 1: of = cpu->sp; break;
case 2: of = cpu->pc; break;
}
#if getclk
cpu->clk += inc_clk;
#endif
switch (size) {
case 0: return of + (int8_t )offset;
case 1: return of + (int16_t)offset;
case 2:
case 3: return of + (int32_t)offset;
case 4:
case 5:
case 6:
case 7: return of + (int64_t)offset;
}
}
static uint64_t imm_addr(struct sux *cpu) {
return cpu->pc;
}
static uint64_t read_addr(struct sux *cpu, uint8_t prefix, uint8_t inc_clk, uint8_t type, uint8_t inc_pc) {
uint64_t address;
uint8_t size = get_addrsize(prefix, type);
if (prefix >> 6) {
address = offset_addr(cpu, read_value(cpu, 0, cpu->pc, size, inc_clk, 0), size, inc_clk, prefix);
} else {
address = read_value(cpu, 0, cpu->pc, size, inc_clk, 0);
}
if (inc_pc) {
cpu->pc += size+1;
}
return address;
}
static uint64_t idx_addr(struct sux *cpu, uint8_t prefix, uint8_t inc_clk, uint8_t type, uint8_t inc_pc, uint64_t idx_reg) {
#if getclk
cpu->clk += inc_clk;
#endif
return read_addr(cpu, prefix, inc_clk, type, inc_pc) + idx_reg;
}
static uint64_t ind_addr(struct sux *cpu, uint8_t prefix, uint8_t inc_clk, uint8_t type, uint8_t inc_pc) {
return read_value(cpu, 0, read_addr(cpu, prefix, inc_clk, type, inc_pc), 7, inc_clk, 0);
}
static uint64_t ind_idx_addr(struct sux *cpu, uint8_t prefix, uint8_t inc_clk, uint8_t type, uint8_t inc_pc, uint64_t idx_reg, uint8_t pre_idx) {
#if getclk
cpu->clk += inc_clk;
#endif
if (pre_idx) {
return read_value(cpu, 0, read_addr(cpu, prefix, inc_clk, type, inc_pc)+idx_reg, 7, inc_clk, 0);
} else {
return ind_addr(cpu, prefix, inc_clk, type, inc_pc) + idx_reg;
}
}
static 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;
uint64_t offset = read_value(cpu, 0, cpu->pc, size, inc_clk, 0);
uint64_t address;
if (inc_pc) {
cpu->pc += (size + 1);
}
#if getclk
cpu->clk += inc_clk;
#endif
switch (rs) {
default: return cpu->pc + (int8_t )offset;
case 1 : return cpu->pc + (int16_t)offset;
case 2 : return cpu->pc + (int32_t)offset;
case 3 : return cpu->pc + (int64_t)offset;
}
}
static uint64_t get_addr(struct sux *cpu, uint8_t opcode, uint8_t prefix, uint8_t ext_prefix, uint8_t inc_pc, uint8_t inc_clk, uint8_t thread) {
uint64_t address = 0;
uint8_t type;
if ((ext_prefix & 0xF) == 0xD) {
switch (ext_prefix >> 4) {
case 0x0: type = ext_optype[opcode]; break;
}
} else {
type = optype[opcode];
}
switch (type) {
case BREG:
case IMPL:
break;
case EIND: address = cpu->e; break;
case IMM:
address = imm_addr(cpu);
switch (opcode) {
case LSL_IMM:
case LSR_IMM:
case ROL_IMM:
case ROR_IMM:
case ASR_IMM:
if ((ext_prefix & 0xD) != 0xD) {
if (inc_pc) {
++cpu->pc;
}
break;
}
default:
if (inc_pc) {
cpu->pc+=(1 << ((prefix >> 4) & 3));
}
break;
}
break;
case ZM : return read_addr(cpu, prefix, inc_clk, ZM, inc_pc);
case ZMX : return idx_addr(cpu, prefix, inc_clk, ZM, inc_pc, cpu->x);
case ZMY : return idx_addr(cpu, prefix, inc_clk, ZM, inc_pc, cpu->y);
case IND : return ind_addr(cpu, prefix, inc_clk, ZM, inc_pc);
case INDX : return ind_idx_addr(cpu, prefix, inc_clk, ZM, inc_pc, cpu->x, 1);
case INDY : return ind_idx_addr(cpu, prefix, inc_clk, ZM, inc_pc, cpu->y, 0);
case ABS : return read_addr(cpu, prefix, inc_clk, ABS, inc_pc);
case ABSX : return idx_addr(cpu, prefix, inc_clk, ABS, inc_pc, cpu->x);
case ABSY : return idx_addr(cpu, prefix, inc_clk, ABS, inc_pc, cpu->y);
case AIND : return ind_addr(cpu, prefix, inc_clk, ABS, inc_pc);
case AINDX: return ind_idx_addr(cpu, prefix, inc_clk, ABS, inc_pc, cpu->x, 1);
case AINDY: return ind_idx_addr(cpu, prefix, inc_clk, ABS, inc_pc, cpu->y, 0);
case REL : return rel_addr(cpu, prefix, inc_clk, inc_pc);
}
return address;
}
static uint64_t ortho_ind_addr(struct sux *cpu, uint8_t prefix, uint64_t address, uint8_t inc_clk) {
return read_value(cpu, 0, address, 7, inc_clk, 0);
}
static uint64_t ortho_ind_idx_addr(struct sux *cpu, uint8_t prefix, uint64_t address, uint8_t inc_clk, uint64_t idx_reg, uint8_t pre_idx) {
if (pre_idx) {
return read_value(cpu, 0, address+idx_reg, 7, inc_clk, 0);
} else {
return ortho_ind_addr(cpu, prefix, address, inc_clk) + idx_reg;
}
}
static uint64_t get_ortho_addr(struct sux *cpu, uint8_t opcode, uint8_t prefix, uint64_t address, operand *op, uint64_t *value, uint8_t *op_type, uint8_t *op_id, uint8_t inc_pc, uint8_t inc_clk, uint8_t thread) {
uint64_t tmp_addr = address;
int num_ops = 1 + !(is_1op(opcode) || is_1cc(opcode));
for (int i = 0; i < num_ops; i++) {
union reg tmp;
tmp.u64 = 0;
op[i].type = op_type[i];
op[i].id = op_id[i];
op[i].value = 0;
op[i].rind[0] = 0xFF;
op[i].rind[1] = 0xFF;
if (op[i].type) {
int inst_size = 0;
int addr_size = get_ortho_addrsize(prefix, op[i].id);
int rs = (1 << (prefix >> 4));
int is_rind = (op[i].id != MEM_ABS && op[i].id != MEM_ZM && op[i].id != MEM_AIND && op[i].id != MEM_IND && op[i].id != MEM_IMM);
if (is_rind) {
inst_size = (op[i].id == MEM_SIB)+1;
tmp.u64 = read_value(cpu, 0, tmp_addr, inst_size-1, 0, 0);
tmp_addr += inst_size;
op[i].rind[0] = (tmp.u8[inst_size-1] & 0x0F);
op[i].rind[1] = (tmp.u8[inst_size-1] >> 4);
if (op[i].rind[1] == op[i].rind[0]) {
op[i].rind[1] = 0xFF;
}
op[i].scale = (inst_size == 2) ? tmp.u8[0] : 0;
}
if (addr_size != 0xFF) {
inst_size = addr_size+1;
op[i].value = read_value(cpu, 0, tmp_addr, inst_size-1, inc_clk, 0);
value[i] = op[i].value;
tmp_addr += inst_size;
}
if (rs && op[i].id == MEM_IMM) {
value[i] = tmp_addr;
tmp_addr += rs;
}
uint64_t tmp_val = 0;
if (is_rind) {
for (int j = 0; j < 2 && op[i].rind[j] != 0xFF; j++) {
int is_index = ((!j && op[i].rind[1] == 0xFF) || j);
uint64_t reg;
switch (op[i].rind[j]) {
case REG_A : reg = cpu->a; break;
case REG_B : reg = cpu->b; break;
case REG_X : reg = cpu->x; break;
case REG_Y : reg = cpu->y; break;
case REG_E : reg = cpu->e; break;
case REG_C : reg = cpu->c; break;
case REG_D : reg = cpu->d; break;
case REG_S : reg = cpu->s; break;
case REG_F : reg = cpu->f; break;
case REG_SP : reg = cpu->sp; break;
case REG_BP : reg = cpu->bp; break;
case REG_R11: reg = cpu->r11; break;
case REG_R12: reg = cpu->r12; break;
case REG_R13: reg = cpu->r13; break;
case REG_R14: reg = cpu->r14; break;
case REG_R15: reg = cpu->r15; break;
}
reg *= (is_index && op[i].id == MEM_SIB) ? op[i].scale+1 : 1;
tmp_val += reg;
}
if (addr_size != 0xFF) {
switch (addr_size) {
case 0: value[i] = (int8_t )value[i]; break;
case 1: value[i] = (int16_t)value[i]; break;
case 2:
case 3: value[i] = (int32_t)value[i]; break;
case 4:
case 5:
case 6:
case 7: value[i] = (int64_t)value[i]; break;
}
}
value[i] += (!is_ind(op[i].id)) ? tmp_val : 0;
#if getclk
cpu->clk += inc_clk;
#endif
}
if (is_ind(op[i].id)) {
uint8_t pre_idx = 0;
uint8_t type = op[i].id;
switch (type) {
case MEM_AIND :
case MEM_IND : value[i] = ortho_ind_addr(cpu, prefix, value[i], inc_clk); break;
case MEM_ARIND:
case MEM_ZRIND: pre_idx = 1;
case MEM_AINDR:
case MEM_ZINDR: value[i] = ortho_ind_idx_addr(cpu, prefix, value[i], inc_clk, tmp_val, pre_idx); break;
}
}
}
}
address = (inc_pc) ? tmp_addr : address;
return address;
}
static uint64_t adc(struct sux *cpu, uint64_t reg, uint64_t value, uint8_t carry, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
uint64_t sum = reg+value+carry;
setflag(sum == 0, Z);
setflag(sum >> (msb-1), N);
setflag(((reg^value) >> (msb-1)) && ((reg^sum) >> (msb-1)), V);
setflag((sum < value), C);
return sum;
}
static uint64_t transfer(struct sux *cpu, uint64_t src, uint64_t value, uint8_t thread) {
setflag(src == 0, Z);
setflag(src >> 63, N);
return src;
}
static void push(struct sux *cpu, uint64_t value, uint8_t size, uint8_t thread) {
write_value(cpu, value, cpu->sp-size, size, 1, 0);
cpu->sp -= size+1;
}
static uint64_t pull(struct sux *cpu, uint8_t size, uint8_t thread) {
uint64_t value = read_value(cpu, 0, cpu->sp+1, size, 1, 0);
cpu->sp += size+1;
return value;
}
static uint64_t and(struct sux *cpu, uint64_t reg, uint64_t value, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
reg &= value;
setflag(reg == 0, Z);
setflag(reg >> (msb-1), N);
return reg;
}
static uint64_t or(struct sux *cpu, uint64_t reg, uint64_t value, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
reg |= value;
setflag(reg == 0, Z);
setflag(reg >> (msb-1), N);
return reg;
}
static uint64_t xor(struct sux *cpu, uint64_t reg, uint64_t value, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
reg ^= value;
setflag(reg == 0, Z);
setflag(reg >> (msb-1), N);
return reg;
}
static uint64_t lsl(struct sux *cpu, uint64_t reg, uint64_t value, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
uint64_t sum = (value < msb) ? reg << value : 0;
setflag(sum == 0, Z);
setflag(sum >> (msb-1), N);
setflag(reg >> (msb-value), C);
return sum;
}
static uint64_t lsr(struct sux *cpu, uint64_t reg, uint64_t value, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
uint64_t sum = (value < msb) ? reg >> value : 0;
setflag(sum == 0, Z);
setflag(sum >> (msb-1), N);
setflag(reg & 1, C);
return sum;
}
static uint64_t asr(struct sux *cpu, uint64_t reg, uint64_t value, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
uint8_t sign = reg >> (msb-1);
uint64_t sum = (value < msb) ? (reg >> value) | ((uint64_t)sign << (msb-1)) : 0;
setflag(sum == 0, Z);
setflag(sum >> (msb-1), N);
setflag(reg & 1, C);
return sum;
}
static uint64_t rol(struct sux *cpu, uint64_t reg, uint64_t value, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
uint64_t sum;
uint64_t c = getflag(C);
switch (value & 0x3F) {
case 0 : return reg;
case 1 : sum = (reg << 1) | (c & 1); break;
default: sum = (reg << value) | (c << (value-1)) | (reg >> ((msb+1)-value)); break;
}
setflag(sum == 0, Z);
setflag(sum >> (msb-1), N);
setflag((reg >> (msb-value)) & 1, C);
return sum;
}
static uint64_t ror(struct sux *cpu, uint64_t reg, uint64_t value, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
uint64_t sum;
uint64_t c = getflag(C);
switch (value & 0x3F) {
case 0 : return reg;
case 1 : sum = (reg >> 1) | (c << (msb-1)); break;
default: sum = (reg >> value) | (c << (msb-value)) | (reg << ((msb+1)-value)); break;
}
setflag(sum == 0, Z);
setflag(sum >> (msb-1), N);
setflag((reg >> (value-1)) & 1, C);
return sum;
}
static uint64_t mul(struct sux *cpu, uint64_t reg, uint64_t value, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
uint64_t sum = reg*value;
setflag(sum == 0, Z);
setflag(sum >> (msb-1), N);
setflag(!((reg^value) >> (msb-1)) && ((reg^sum) >> (msb-1)), V);
return sum;
}
static uint64_t divd(struct sux *cpu, uint64_t reg, uint64_t value, uint64_t *rem, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
uint64_t sum = reg/value;
*rem = reg % value;
setflag(sum == 0, Z);
setflag(sum >> (msb-1), N);
return sum;
}
static 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 uint64_t idr(struct sux *cpu, uint64_t reg, uint8_t inc, uint8_t thread) {
reg += (inc) ? 1 : -1;
setflag(reg == 0, Z);
setflag(reg >> 63, N);
return reg;
}
static uint64_t lbcnt(struct sux *cpu, uint64_t value, uint8_t bit, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
if ((!bit && !value) || (bit && value == -1)) {
return msb;
}
uint64_t j = 0;
for (int i = msb-1; ((value >> i) & 1) == bit; i--, j++);
setflag(j == 0, Z);
return j;
}
static void bit_test(struct sux *cpu, uint64_t reg, uint64_t value, uint8_t thread) {
setflag((value >> 7) & 1, N);
setflag((value >> 6) & 1, V);
setflag((value & reg) == 0, Z);
}
static uint64_t swap(struct sux *cpu, uint64_t reg, uint8_t size, uint8_t thread) {
size = (size > 7) ? 7 : size;
uint8_t half = ((size-1)*8) >> 1;
uint64_t mask = (-(uint64_t)1 >> ((7 - size) * 8));
uint64_t lo_mask = mask >> half;
uint64_t hi_mask = (mask << half) & mask;
return (((reg >> half) & lo_mask) | ((reg << half) & hi_mask));
}
static uint64_t popcnt(struct sux *cpu, uint64_t value, uint8_t thread) {
uint64_t count = 0;
for (; value; count++, value &= value - 1);
return count;
}
static 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;
uint64_t value;
value = read_value(cpu, 0, address, size, 1, 0);
value += (inc) ? 1 : -1;
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 == 0, Z);
setflag(value >> sign, N);
write_value(cpu, value, address, size, 1, 1);
}
static void bitshft_mem(struct sux *cpu, uint8_t shft_type, uint64_t shft_cnt, uint64_t address, uint8_t prefix, uint8_t thread) {
uint8_t size = (1 << ((prefix >> 4) & 3))-1;
uint64_t value = read_value(cpu, 0, address, size, 1, 0);
switch (shft_type) {
case 0: value = lsl(cpu, value, shft_cnt, size+1, thread); break;
case 1: value = lsr(cpu, value, shft_cnt, size+1, thread); break;
case 2: value = rol(cpu, value, shft_cnt, size+1, thread); break;
case 3: value = ror(cpu, value, shft_cnt, size+1, thread); break;
case 4: value = asr(cpu, value, shft_cnt, size+1, thread); break;
}
write_value(cpu, value, address, size, 1, 1);
}
static void not_mem(struct sux *cpu, uint64_t address, uint8_t prefix, uint8_t thread) {
uint8_t size = (1 << ((prefix >> 4) & 3))-1;
write_value(cpu, ~read_value(cpu, 0, address, size, 1, 0), address, size, 1, 1);
}
static void lbcnt_mem(struct sux *cpu, uint64_t address, uint8_t bit, uint8_t size, uint8_t thread) {
uint64_t value = read_value(cpu, 0, address, size, 1, 0);
write_value(cpu, lbcnt(cpu, value, bit, size, thread), address, size, 1, 1);
}
static void swap_mem(struct sux *cpu, uint64_t address, uint8_t size, uint8_t thread) {
uint64_t value = read_value(cpu, 0, address, size, 1, 0);
write_value(cpu, swap(cpu, value, size, thread), address, size, 1, 1);
}
static uint64_t mem_move(struct sux *cpu, uint64_t n, uint64_t dst, uint64_t src, uint8_t rep, uint8_t size, uint8_t thread) {
if (!rep) {
uint64_t value = read_value(cpu, 0, src, size, 1, 1);
write_value(cpu, value, dst, size, 1, 1);
return n-(size+1);
} else {
if (src < mem_size && dst < mem_size) {
memcopy(addr+dst, addr+src, n*(size+1));
}
return 0;
}
}
static uint64_t load(struct sux *cpu, uint64_t reg, uint64_t address, uint8_t size, uint8_t thread) {
uint64_t value = read_value(cpu, reg, address, size, 1, 1);
setflag(value == 0, Z);
setflag(value >> 63, N);
return value;
}
static 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;
write_value(cpu, reg, address, size, 1, 1);
}
static uint64_t mov(struct sux *cpu, uint64_t src, uint64_t size, uint8_t thread) {
size = (size > 7) ? 7 : size;
uint8_t msb = (size+1)*8;
uint64_t dst = 0;
uint64_t mask = (-(uint64_t)1 >> ((7 - size) * 8));
if (size < 7) {
dst = (dst & ~mask) | (src & mask);
} else {
dst = src;
}
setflag(dst == 0, Z);
setflag(dst >> (msb-1), N);
return dst;
}
static uint64_t cmov(struct sux *cpu, uint64_t src, uint64_t size, uint64_t dst, int flag, int *allow_io, uint8_t thread) {
*allow_io = flag;
return (flag) ? mov(cpu, src, size, thread) : dst;
}
static uint64_t set(struct sux *cpu, uint8_t flag, uint8_t thread) {
setflag(flag == 0, Z);
return (flag != 0);
}
static uint64_t inc_dec(struct sux *cpu, uint64_t value, uint8_t size, uint8_t inc, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
value += (inc) ? 1 : -1;
setflag(value == 0, Z);
setflag(value >> (msb-1), N);
return value;
}
static uint64_t imul(struct sux *cpu, uint64_t dst, uint64_t value, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
int64_t sum = dst*value;
setflag(sum == 0, Z);
setflag(sum >> (msb-1), N);
setflag(!((dst^value) >> (msb-1)) && ((dst^sum) >> (msb-1)), V);
return sum;
}
static uint64_t idiv(struct sux *cpu, uint64_t dst, uint64_t value, uint64_t *rem, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
int64_t sum = dst/value;
*rem = dst % value;
setflag(sum == 0, Z);
setflag(sum >> (msb-1), N);
return sum;
}
static uint64_t neg(struct sux *cpu, uint64_t value, uint8_t size, uint8_t thread) {
size = (size > 8) ? 8 : size;
uint8_t msb = size*8;
value = -value;
setflag(value == 0, Z);
setflag(value >> (msb-1), N);
return value;
}
static void exec_ortho_inst(struct sux *cpu, uint8_t opcode, uint8_t prefix, uint8_t size, uint8_t *op_type, uint8_t *op_id, uint8_t thread) {
uint64_t dst = 0;
uint64_t src = 0;
uint64_t rem = 0;
uint64_t address[2] = {0, 0};
int isdiv = 0;
int allow_io[2] = {1, 1};
int is_lea = ((opcode & ~0x18) == LEA_RR);
int is_write = 1;
operand op[2];
cpu->pc = get_ortho_addr(cpu, opcode, prefix, cpu->pc, op, address, op_type, op_id, 1, 1, thread);
for (int i = 0; i < 2; i++) {
if (op[i].type) {
if (is_lea) {
switch (op[i].id) {
case MEM_RIND:
case MEM_ABS :
case MEM_ZM :
case MEM_ABSR:
case MEM_ZMR :
case MEM_SIB :
case MEM_IMM : allow_io[i] = 0; break;
}
} else {
allow_io[i] = (op[i].id != MEM_IMM);
}
}
}
if (op[1].type) {
src = read_value(cpu, 0, address[1], size, 1, allow_io[1]);
} else {
switch (op[1].id) {
case REG_A : src = cpu->a; break;
case REG_B : src = cpu->b; break;
case REG_X : src = cpu->x; break;
case REG_Y : src = cpu->y; break;
case REG_E : src = cpu->e; break;
case REG_C : src = cpu->c; break;
case REG_D : src = cpu->d; break;
case REG_S : src = cpu->s; break;
case REG_F : src = cpu->f; break;
case REG_SP : src = cpu->sp; break;
case REG_BP : src = cpu->bp; break;
case REG_R11: src = cpu->r11; break;
case REG_R12: src = cpu->r12; break;
case REG_R13: src = cpu->r13; break;
case REG_R14: src = cpu->r14; break;
case REG_R15: src = cpu->r15; break;
}
}
if (op[0].type) {
if (op[0].id != MEM_IMM) {
dst = read_value(cpu, 0, address[0], size, 1, allow_io[0]);
}
} else {
switch (op[0].id) {
case REG_A : dst = cpu->a; break;
case REG_B : dst = cpu->b; break;
case REG_X : dst = cpu->x; break;
case REG_Y : dst = cpu->y; break;
case REG_E : dst = cpu->e; break;
case REG_C : dst = cpu->c; break;
case REG_D : dst = cpu->d; break;
case REG_S : dst = cpu->s; break;
case REG_F : dst = cpu->f; break;
case REG_SP : dst = cpu->sp; break;
case REG_BP : dst = cpu->bp; break;
case REG_R11: dst = cpu->r11; break;
case REG_R12: dst = cpu->r12; break;
case REG_R13: dst = cpu->r13; break;
case REG_R14: dst = cpu->r14; break;
case REG_R15: dst = cpu->r15; break;
}
}
switch (opcode) {
case ORTHO_2OP(MNG): dst = cmov(cpu, src, (op[0].type) ? size : 7, dst, getflag(N), &allow_io[0], thread); break;
case ORTHO_2OP(MPO): dst = cmov(cpu, src, (op[0].type) ? size : 7, dst, !getflag(N), &allow_io[0], thread); break;
case ORTHO_2OP(MCS): dst = cmov(cpu, src, (op[0].type) ? size : 7, dst, getflag(C), &allow_io[0], thread); break;
case ORTHO_2OP(MCC): dst = cmov(cpu, src, (op[0].type) ? size : 7, dst, !getflag(C), &allow_io[0], thread); break;
case ORTHO_2OP(MEQ): dst = cmov(cpu, src, (op[0].type) ? size : 7, dst, getflag(Z), &allow_io[0], thread); break;
case ORTHO_2OP(MNE): dst = cmov(cpu, src, (op[0].type) ? size : 7, dst, !getflag(Z), &allow_io[0], thread); break;
case ORTHO_2OP(MVS): dst = cmov(cpu, src, (op[0].type) ? size : 7, dst, getflag(V), &allow_io[0], thread); break;
case ORTHO_2OP(MVC): dst = cmov(cpu, src, (op[0].type) ? size : 7, dst, !getflag(V), &allow_io[0], thread); break;
case ORTHO_2OP(MOV): dst = mov(cpu, src, (op[0].type) ? size : 7, thread); break;
case ORTHO_2OP(ADC): dst = adc(cpu, dst, src, getflag(C), (op[0].type) ? size+1 : 8, thread); break;
case ORTHO_2OP(SBC): dst = adc(cpu, dst, ~src, getflag(C), (op[0].type) ? size+1 : 8, thread); break;
case ORTHO_2OP(ADD): dst = adc(cpu, dst, src, 0, (op[0].type) ? size+1 : 8, thread); break;
case ORTHO_2OP(SUB): dst = adc(cpu, dst, ~src, 1, (op[0].type) ? size+1 : 8, thread); break;
case ORTHO_2OP(AND): dst = and(cpu, dst, src, (op[0].type) ? size+1 : 8, thread); break;
case ORTHO_2OP(OR ): dst = or(cpu, dst, src, (op[0].type) ? size+1 : 8, thread); break;
case ORTHO_2OP(XOR): dst = xor(cpu, dst, src, (op[0].type) ? size+1 : 8, thread); break;
case ORTHO_2OP(LSL): dst = lsl(cpu, dst, src, (op[0].type) ? size+1 : 8, thread); break;
case ORTHO_2OP(LSR): dst = lsr(cpu, dst, src, (op[0].type) ? size+1 : 8, thread); break;
case ORTHO_2OP(ROL): dst = rol(cpu, dst, src, (op[0].type) ? size+1 : 8, thread); break;
case ORTHO_2OP(ROR): dst = ror(cpu, dst, src, (op[0].type) ? size+1 : 8, thread); break;
case ORTHO_2OP(ASR): dst = asr(cpu, dst, src, (op[0].type) ? size+1 : 8, thread); break;
case ORTHO_2OP(MUL): dst = mul(cpu, dst, src, (op[0].type) ? size+1 : 8, thread); break;
case ORTHO_2OP(DIV): dst = divd(cpu, dst, src, &rem, (op[0].type) ? size+1 : 8, thread); isdiv = 1; break;
case ORTHO_2OP(CMP): adc(cpu, dst, ~src, 1, (op[0].type) ? size+1 : 8, thread); is_write = 0; break;
case ORTHO_2OP(PCN): dst = popcnt(cpu, src, thread); break;
case ORTHO_2OP(IML): dst = imul(cpu, dst, src, (op[0].type) ? size+1 : 8, thread); break;
case ORTHO_2OP(IDV): dst = idiv(cpu, dst, src, &rem, (op[0].type) ? size+1 : 8, thread); isdiv = 1; break;
case ORTHO_2OP(LEA):
do {
uint64_t tmp_addr;
uint64_t mask;
if (op[1].type) {
uint8_t addr_size = get_ortho_addrsize(prefix, op[1].id);
size = (!size) ? 7 : size;
tmp_addr = address[1];
} else {
tmp_addr = src;
}
mask = (-(uint64_t)1 >> ((7 - size) * 8));
dst = (tmp_addr & mask);
} while (0);
break;
case ORTHO_1OP(PEA):
do {
uint64_t tmp_addr = (op[0].type) ? address[0] : dst;
push(cpu, tmp_addr, 7, thread);
is_write = 0;
} while (0);
break;
case ORTHO_1OP(JSR): push(cpu, cpu->pc, (size) ? size : 7, thread);
case ORTHO_1OP(JMP): cpu->pc = (op[0].type) ? address[0] : dst; break;
case ORTHO_1OP(INC): dst = inc_dec(cpu, dst, (op[0].type) ? size+1 : 8, 1, thread); break;
case ORTHO_1OP(DEC): dst = inc_dec(cpu, dst, (op[0].type) ? size+1 : 8, 0, thread); break;
case ORTHO_1OP(PSH): push(cpu, dst, size, thread); is_write = 0; break;
case ORTHO_1OP(PUL): dst = pull(cpu, size, thread); break;
case ORTHO_1OP(NOT): dst = ~dst; break;
case ORTHO_1OP(CLZ): dst = lbcnt(cpu, src, 0, size, thread); break;
case ORTHO_1OP(CLO): dst = lbcnt(cpu, src, 1, size, thread); break;
case ORTHO_1OP(SWP): dst = swap(cpu, dst, size, thread); break;
case ORTHO_1OP(NEG): dst = neg(cpu, dst, size, thread); break;
case ORTHO_1CC(SET, NG): dst = set(cpu, getflag(N), thread); break;
case ORTHO_1CC(SET, PO): dst = set(cpu, !getflag(N), thread); break;
case ORTHO_1CC(SET, CS): dst = set(cpu, getflag(C), thread); break;
case ORTHO_1CC(SET, CC): dst = set(cpu, !getflag(C), thread); break;
case ORTHO_1CC(SET, EQ): dst = set(cpu, getflag(Z), thread); break;
case ORTHO_1CC(SET, NE): dst = set(cpu, !getflag(Z), thread); break;
case ORTHO_1CC(SET, VS): dst = set(cpu, getflag(V), thread); break;
case ORTHO_1CC(SET, VC): dst = set(cpu, !getflag(V), thread); break;
}
if (is_write) {
if (op[0].type) {
if (op[0].id != MEM_IMM) {
write_value(cpu, dst, address[0], size, 1, allow_io[0]);
}
} else {
switch (op[0].id) {
case REG_A : cpu->a = dst; break;
case REG_B : cpu->b = dst; break;
case REG_X : cpu->x = dst; break;
case REG_Y : cpu->y = dst; break;
case REG_E : cpu->e = dst; break;
case REG_C : cpu->c = dst; break;
case REG_D : cpu->d = dst; break;
case REG_S : cpu->s = dst; break;
case REG_F : cpu->f = dst; break;
case REG_SP : cpu->sp = dst; break;
case REG_BP : cpu->bp = dst; break;
case REG_R11: cpu->r11 = dst; break;
case REG_R12: cpu->r12 = dst; break;
case REG_R13: cpu->r13 = dst; break;
case REG_R14: cpu->r14 = dst; break;
case REG_R15: cpu->r15 = dst; break;
}
}
}
if (isdiv) {
if (op[1].type) {
if (op[1].id != MEM_IMM) {
write_value(cpu, src, address[1], size, 1, 1);
}
} else {
switch (op[1].id) {
case REG_A : cpu->a = rem; break;
case REG_B : cpu->b = rem; break;
case REG_X : cpu->x = rem; break;
case REG_Y : cpu->y = rem; break;
case REG_E : cpu->e = rem; break;
case REG_C : cpu->c = rem; break;
case REG_D : cpu->d = rem; break;
case REG_S : cpu->s = rem; break;
case REG_F : cpu->f = rem; break;
case REG_SP : cpu->sp = rem; break;
case REG_BP : cpu->bp = rem; break;
case REG_R11: cpu->r11 = rem; break;
case REG_R12: cpu->r12 = rem; break;
case REG_R13: cpu->r13 = rem; break;
case REG_R14: cpu->r14 = rem; break;
case REG_R15: cpu->r15 = rem; break;
}
}
}
}
static void exec_ext_inst(struct sux *cpu, uint8_t opcode, uint8_t prefix, uint64_t value, uint64_t address, uint8_t size, uint8_t thread) {
uint8_t addr_size = get_addrsize(prefix, ext_optype[opcode]);
uint8_t tmp = 0;
switch (opcode) {
case LEA_AB :
case LEA_AX :
case LEA_AY :
case LEA_AI :
case LEA_AIX:
case LEA_AIY:
case LEA_Z :
case LEA_ZX :
case LEA_ZY :
case LEA_IN :
case LEA_IX :
case LEA_IY :
do {
size = (!size) ? addr_size : size;
uint64_t mask = (-(uint64_t)1 >> ((7 - size) * 8));
cpu->e = (address & mask);
} while (0);
break;
case PEA_AB :
case PEA_AX :
case PEA_AY :
case PEA_AI :
case PEA_AIX:
case PEA_AIY:
case PEA_Z :
case PEA_ZX :
case PEA_ZY :
case PEA_IN :
case PEA_IX :
case PEA_IY :
push(cpu, address, 7, thread);
break;
case ADD_IMM:
case ADD_AB :
case ADD_Z :
case ADD_E :
cpu->a = adc(cpu, cpu->a, value, 0, 8, thread);
break;
case SUB_IMM:
case SUB_AB :
case SUB_Z :
case SUB_E :
cpu->a = adc(cpu, cpu->a, ~value, 1, 8, thread);
break;
case ADE_IMM:
case ADE_AB :
case ADE_Z :
cpu->e = adc(cpu, cpu->e, value, 0, 8, thread);
break;
case SBE_IMM:
case SBE_AB :
case SBE_Z :
cpu->e = adc(cpu, cpu->e, ~value, 1, 8, thread);
break;
case ADS_IMM:
case ADS_AB :
case ADS_Z :
case ADS_E :
cpu->sp = adc(cpu, cpu->sp, value, 0, 8, thread);
break;
case SBS_IMM:
case SBS_AB :
case SBS_Z :
case SBS_E :
cpu->sp = adc(cpu, cpu->sp, ~value, 1, 8, thread);
break;
case NOT_A :
cpu->a = ~cpu->a;
break;
case NOT_AB:
case NOT_Z :
case NOT_E :
not_mem(cpu, address, prefix, thread);
break;
case LLM_AB:
case LLM_Z :
case LLM_E :
case LRM_AB:
case LRM_Z :
case LRM_E :
case RLM_AB:
case RLM_Z :
case RLM_E :
case RRM_AB:
case RRM_Z :
case RRM_E :
case ARM_AB:
case ARM_Z :
case ARM_E :
switch (opcode) {
case LLM_AB:
case LLM_Z :
case LLM_E : tmp = 0; break;
case LRM_AB:
case LRM_Z :
case LRM_E : tmp = 1; break;
case RLM_AB:
case RLM_Z :
case RLM_E : tmp = 2; break;
case RRM_AB:
case RRM_Z :
case RRM_E : tmp = 3; break;
case ARM_AB:
case ARM_Z :
case ARM_E : tmp = 4; break;
}
bitshft_mem(cpu, tmp, cpu->b, address, prefix, thread);
break;
case PHE_IMP: push(cpu, cpu->e, size, thread); break;
case PLE_IMP: cpu->e = pull(cpu, size, thread); break;
case CPE_IMM:
case CPE_AB :
case CPE_Z :
adc(cpu, cpu->e, ~value, 1, 8, thread);
break;
case ICE_AB :
case ICE_Z :
case ICE_E :
break;
case LDS_IMM:
case LDS_AB :
case LDS_Z :
case LDS_E :
cpu->sp = load(cpu, cpu->sp, address, size, thread);
break;
case DEE_IMP: cpu->e = idr(cpu, cpu->e, 0, thread); break;
case INE_IMP: cpu->e = idr(cpu, cpu->e, 1, thread); break;
case DES_IMP: cpu->sp = idr(cpu, cpu->sp, 0, thread); break;
case INS_IMP: cpu->sp = idr(cpu, cpu->sp, 1, thread); break;
case STS_AB:
case STS_Z :
case STS_E :
store(cpu, address, cpu->sp, prefix, thread);
break;
case STE_AB:
case STE_Z :
store(cpu, address, cpu->sp, prefix, thread);
break;
case STZ_AB:
case STZ_Z :
case STZ_E :
store(cpu, address, 0, prefix, thread);
break;
case SCO_IMM:
case SCO_AB :
case SCO_Z :
case SCO_E :
break;
case ECO_IMM:
case ECO_AB :
case ECO_Z :
case ECO_E :
break;
case CLZ_AB:
case CLZ_Z :
case CLZ_E :
cpu->a = lbcnt(cpu, value, 0, size, thread);
break;
case CLO_AB:
case CLO_Z :
case CLO_E :
cpu->a = lbcnt(cpu, value, 1, size, thread);
break;
case BIT_AB:
case BIT_Z :
case BIT_E :
bit_test(cpu, cpu->a, value, thread);
break;
case MMV_IMP:
cpu->b = mem_move(cpu, cpu->b, cpu->x, cpu->y, 1, size, thread);
break;
case SWP_A :
cpu->a = swap(cpu, cpu->a, size, thread);
break;
case SWP_AB:
case SWP_Z :
case SWP_E :
swap_mem(cpu, address, size, thread);
break;
case PCN_AB:
case PCN_Z :
case PCN_E :
cpu->a = popcnt(cpu, value, thread);
break;
case REP_REL:
if (cpu->b != 0) {
cpu->b--;
cpu->pc = address;
}
break;
case REQ_REL:
if (cpu->b != 0 && getflag(Z)) {
cpu->b--;
cpu->pc = address;
}
break;
case RNE_REL:
if (cpu->b != 0 && !getflag(Z)) {
cpu->b--;
cpu->pc = address;
}
break;
case LNG_IMM:
case LNG_E :
if (getflag(N)) {
cpu->a = load(cpu, cpu->a, address, size, thread);
}
break;
case LPO_IMM:
case LPO_E :
if (!getflag(N)) {
cpu->a = load(cpu, cpu->a, address, size, thread);
}
break;
case LCS_IMM:
case LCS_E :
if (getflag(C)) {
cpu->a = load(cpu, cpu->a, address, size, thread);
}
break;
case LCC_IMM:
case LCC_E :
if (!getflag(C)) {
cpu->a = load(cpu, cpu->a, address, size, thread);
}
break;
case LEQ_IMM:
case LEQ_E :
if (getflag(Z)) {
cpu->a = load(cpu, cpu->a, address, size, thread);
}
break;
case LNE_IMM:
case LNE_E :
if (!getflag(Z)) {
cpu->a = load(cpu, cpu->a, address, size, thread);
}
break;
case SNG_E :
if (getflag(N)) {
store(cpu, address, cpu->a, prefix, thread);
}
break;
case SPO_E :
if (!getflag(N)) {
store(cpu, address, cpu->a, prefix, thread);
}
break;
case SCS_E :
if (getflag(C)) {
store(cpu, address, cpu->a, prefix, thread);
}
break;
case SCC_E :
if (!getflag(C)) {
store(cpu, address, cpu->a, prefix, thread);
}
break;
case SEQ_E :
if (getflag(Z)) {
store(cpu, address, cpu->a, prefix, thread);
}
break;
case SNE_E :
if (!getflag(Z)) {
store(cpu, address, cpu->a, prefix, thread);
}
break;
}
}
static void exec_base_inst(struct sux *cpu, uint8_t opcode, uint8_t prefix, uint64_t value, uint64_t address, uint8_t size, uint8_t thread) {
uint64_t *rem = 0;
switch (opcode) {
case CPS_IMP:
cpu->ps.u8[thread] = 0;
break;
case ADC_B:
value = cpu->b;
case ADC_IMM:
case ADC_AB:
case ADC_Z:
cpu->a = adc(cpu, cpu->a, value, getflag(C), 8, thread);
break;
case PHP_IMP: push(cpu, cpu->ps.u8[thread], 0, thread); break;
case PHA_IMP: push(cpu, cpu->a , size, thread); break;
case PHB_IMP: push(cpu, cpu->b , size, thread); break;
case PHY_IMP: push(cpu, cpu->y , size, thread); break;
case PHX_IMP: push(cpu, cpu->x , size, thread); break;
case TAY_IMP: cpu->y = transfer(cpu, cpu->a , value, thread); break;
case TAX_IMP: cpu->x = transfer(cpu, cpu->a , value, thread); break;
case TYX_IMP: cpu->x = transfer(cpu, cpu->y , value, thread); break;
case TYA_IMP: cpu->a = transfer(cpu, cpu->y , value, thread); break;
case TXA_IMP: cpu->a = transfer(cpu, cpu->x , value, thread); break;
case TXY_IMP: cpu->y = transfer(cpu, cpu->x , value, thread); break;
case TAB_IMP: cpu->b = transfer(cpu, cpu->a , value, thread); break;
case TSX_IMP: cpu->x = transfer(cpu, cpu->sp, value, thread); break;
case TBA_IMP: cpu->a = transfer(cpu, cpu->b , value, thread); break;
case TXS_IMP: cpu->sp = transfer(cpu, cpu->x , value, thread); break;
case BRA_REL:
case JMP_AB:
case JMP_Z:
case JMP_IN:
cpu->pc = address;
break;
case SBC_B:
value = cpu->b;
case SBC_IMM:
case SBC_AB:
case SBC_Z:
cpu->a = adc(cpu, cpu->a, ~value, getflag(C), 8, thread);
break;
case PLP_IMP: cpu->ps.u8[thread] = pull(cpu, 0, thread); break;
case PLA_IMP: cpu->a = pull(cpu, size, thread); break;
case PLB_IMP: cpu->b = pull(cpu, size, thread); break;
case PLY_IMP: cpu->y = pull(cpu, size, thread); break;
case PLX_IMP: cpu->x = pull(cpu, size, thread); break;
break;
case AND_B:
value = cpu->b;
case AND_IMM:
case AND_AB:
case AND_Z:
cpu->a = and(cpu, cpu->a, value, 8, thread);
break;
case BPO_REL:
if (!getflag(N)) {
cpu->pc = address;
}
break;
case ORA_B:
value = cpu->b;
case ORA_IMM:
case ORA_AB:
case ORA_Z:
cpu->a = or(cpu, cpu->a, value, 8, thread);
break;
case SEI_IMP:
setflag(1, I);
break;
case BNG_REL:
if (getflag(N)) {
cpu->pc = address;
}
break;
case XOR_B:
value = cpu->b;
case XOR_IMM:
case XOR_AB:
case XOR_Z:
cpu->a = xor(cpu, cpu->a, value, 8, thread);
break;
case CLI_IMP:
setflag(0, I);
break;
case BCS_REL:
if (getflag(C)) {
cpu->pc = address;
}
break;
case LSL_B:
value = cpu->b;
case LSL_IMM:
case LSL_AB:
case LSL_Z:
cpu->a = lsl(cpu, cpu->a, value, 8, thread);
break;
case SEC_IMP:
setflag(1, C);
break;
case STA_AB:
case STA_Z:
case STA_ZX:
case STA_ZY:
case STA_IN:
case STA_IX:
case STA_IY:
store(cpu, address, cpu->a, prefix, thread);
break;
case STY_AB:
case STY_Z:
case STY_IN:
store(cpu, address, cpu->y, prefix, thread);
break;
case STX_AB:
case STX_Z:
case STX_IN:
store(cpu, address, cpu->x, prefix, thread);
break;
case STB_AB:
case STB_Z:
case STB_ZX:
case STB_ZY:
case STB_IN:
case STB_IX:
case STB_IY:
store(cpu, address, cpu->b, prefix, thread);
break;
case BCC_REL:
if (!getflag(C)) {
cpu->pc = address;
}
break;
case LSR_B:
value = cpu->b;
case LSR_IMM:
case LSR_AB:
case LSR_Z:
cpu->a = lsr(cpu, cpu->a, value, 8, thread);
break;
case ASR_B:
value = cpu->b;
case ASR_IMM:
case ASR_AB:
case ASR_Z:
cpu->a = asr(cpu, cpu->a, value, 8, thread);
break;
case CLC_IMP:
setflag(0, C);
break;
case LDB_IMM:
case LDB_AB:
case LDB_Z:
case LDB_ZX:
case LDB_ZY:
case LDB_IN:
case LDB_IX:
case LDB_IY:
cpu->b = load(cpu, cpu->b, address, size, thread);
break;
case LDA_IMM:
case LDA_AB:
case LDA_Z:
case LDA_ZX:
case LDA_ZY:
case LDA_IN:
case LDA_IX:
case LDA_IY:
cpu->a = load(cpu, cpu->a, address, size, thread);
break;
case LDY_IMM:
case LDY_AB:
case LDY_Z:
case LDY_IN:
cpu->y = load(cpu, cpu->y, address, size, thread);
break;
case LDX_IMM:
case LDX_AB:
case LDX_Z:
case LDX_IN:
cpu->x = load(cpu, cpu->x, address, size, thread);
break;
case BEQ_REL:
if (getflag(Z)) {
cpu->pc = address;
}
break;
case ROL_B:
value = cpu->b;
case ROL_IMM:
case ROL_AB:
case ROL_Z:
cpu->a = rol(cpu, cpu->a, value, 8, thread);
break;
case BNE_REL:
if (!getflag(Z)) {
cpu->pc = address;
}
break;
case ROR_B:
value = cpu->b;
case ROR_IMM:
case ROR_AB:
case ROR_Z:
cpu->a = ror(cpu, cpu->a, value, 8, thread);
break;
case BVS_REL:
if (getflag(V)) {
cpu->pc = address;
}
break;
case MUL_B:
value = cpu->b;
case MUL_IMM:
case MUL_AB:
case MUL_Z:
cpu->a = mul(cpu, cpu->a, value, 8, thread);
break;
case BVC_REL:
if (!getflag(V)) {
cpu->pc = address;
}
break;
case DIV_B:
case DIV_IMM:
case DIV_AB:
case DIV_Z:
rem = (opcode != DIV_B) ? &cpu->b : &cpu->x;
cpu->a = divd(cpu, cpu->a, value, rem, 8, thread);
break;
case CLV_IMP:
setflag(0, V);
break;
case CPB_IMM:
case CPB_AB:
case CPB_Z:
case CPB_IN:
case CPB_IX:
case CPB_IY:
adc(cpu, cpu->b, ~value, 1, 8, thread);
break;
case CMP_B:
value = cpu->b;
case CMP_IMM:
case CMP_AB:
case CMP_Z:
case CMP_IN:
case CMP_IX:
case CMP_IY:
adc(cpu, cpu->a, ~value, 1, 8, thread);
break;
case CPY_IMM:
case CPY_AB:
case CPY_Z:
adc(cpu, cpu->y, ~value, 1, 8, thread);
break;
case CPX_IMM:
case CPX_AB:
case CPX_Z:
adc(cpu, cpu->x, ~value, 1, 8, thread);
break;
case INC_IMP: cpu->a = idr(cpu, cpu->a, 1, thread); break;
case INB_IMP: cpu->b = idr(cpu, cpu->b, 1, thread); break;
case INY_IMP: cpu->y = idr(cpu, cpu->y, 1, thread); break;
case INX_IMP: cpu->x = idr(cpu, cpu->x, 1, thread); break;
case DEC_IMP: cpu->a = idr(cpu, cpu->a, 0, thread); break;
case DEB_IMP: cpu->b = idr(cpu, cpu->b, 0, thread); break;
case DEY_IMP: cpu->y = idr(cpu, cpu->y, 0, thread); break;
case DEX_IMP: cpu->x = idr(cpu, cpu->x, 0, thread); break;
case JSR_IN:
case JSR_AB:
case JSR_Z:
push(cpu, cpu->pc, (size) ? size : 7, thread);
cpu->pc = address;
break;
case INC_AB:
case INC_Z:
idm(cpu, address, prefix, 1, thread);
break;
case NOP_IMP:
break;
case RTI_IMP:
cpu->ps.u8[thread] = pull(cpu, 0, thread);
size = 0;
case RTS_IMP:
cpu->pc = pull(cpu, (size) ? size : 7, thread);
break;
case DEC_AB:
case DEC_Z:
idm(cpu, address, prefix, 0, thread);
break;
case BRK_IMP:
case WAI_IMP:
if (opcode == WAI_IMP) {
pthread_mutex_lock(&main_mutex);
pthread_cond_signal(&main_cond);
pthread_mutex_unlock(&main_mutex);
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
}
push(cpu, cpu->pc, 7, thread);
push(cpu, cpu->ps.u8[thread], 0, thread);
setflag(1, I);
value = read_value(cpu, 0, (opcode == BRK) ? 0xFFE0 : 0xFFA0, 7, 1, 0);
if (opcode == WAI_IMP) {
kbd_rdy &= (uint8_t)~(1 << thread);
}
cpu->pc = value;
default:
break;
}
}
#if 0
#else
#define t(w8, w7, w6, w5, w4, w3, w2, w1, w0) \
if ((const uint32_t [9]){w0, w1, w2, w3, w4, w5, w6, w7, w8}[o8] & o8m)
#endif
#define inst(op, ext) \
static void inst_##op(struct sux *cpu, uint8_t prefix, uint8_t size, int inc_pc, int inc_clk, uint8_t thread) { \
const unsigned int o8 = 0x##op / 32, o8m = 1 << (0x##op % 32); \
const uint8_t rs = 1 << ((prefix >> 4) & 3); \
uint64_t addr = 0, idx = 0, tmp = 0, tmp2 = 0; \
uint64_t dummy = 0, *reg = &dummy; \
int pre_idx = 0, mem_type = ZM; \
\
t(1, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000) { addr = 0xFFA0; } \
t(2, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000) { addr = 0xFFC0; } \
t(4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000) { addr = 0xFFE0; } \
t(0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000200, 0x00000000, 0x00000000, 0x00000000) { addr = 0xFFF0; } \
t(7, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000200, 0x00000000, 0x00000000, 0x00000000) { prefix = 0x0F; } \
t(7, 0x00060000, 0x00060000, 0x00160000, 0x00160000, 0x01160200, 0x01160000, 0x01170000, 0x01170000) { mem_type = ABS; } \
t(0, 0x01000100, 0x01000100, 0x04000400, 0x00000000, 0x00000000, 0x40004000, 0x40004000, 0x00000000) { idx = cpu->x; } \
t(0, 0x06400200, 0x06400400, 0x00000000, 0x00000000, 0x00000000, 0x10000000, 0x10000000, 0x00000000) { idx = cpu->y; } \
t(7, 0x00460160, 0x00460160, 0x00160160, 0x00160160, 0x01161360, 0x11165161, 0x11175161, 0x01171160) { addr = read_addr(cpu, prefix, inc_clk, mem_type, inc_pc) + idx; } \
t(0, 0x01000000, 0x01000000, 0x04000400, 0x00000000, 0x00000000, 0x40000000, 0x40000000, 0x00000000) { pre_idx = 1; } \
t(0, 0x07101210, 0x17101410, 0x14001400, 0x10001000, 0x10000000, 0x40000000, 0x40000000, 0x00000000) { addr = ind_idx_addr(cpu, prefix, inc_clk, ZM, inc_pc, idx, pre_idx); } \
\
t(0, 0x02220022, 0x03760576, 0x00220432, 0x00661076, 0x00660066, 0x00660466, 0x51665466, 0x00660466) { reg = &cpu->a; } \
t(0, 0x05540354, 0x04000000, 0x04000200, 0x02000000, 0x10000000, 0x51005000, 0x00000000, 0x04100110) { reg = &cpu->b; } \
t(0, 0x00000000, 0x00001200, 0x12000000, 0x00001000, 0x05101110, 0x04000000, 0x00100110, 0x00000000) { reg = &cpu->x; } \
t(0, 0x00000000, 0x00000000, 0x00041044, 0x10001200, 0x02000400, 0x00100110, 0x04000000, 0x01001000) { reg = &cpu->y; } \
t(0, 0x00000000, 0x00000000, 0x00000000, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000) { reg = &cpu->sp; } \
t(7, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00004200, 0x00000200, 0x02000200, 0x02000201) { reg = &cpu->ps[thread]; } \
t(0, 0x07561370, 0x17561570, 0x14161560, 0x10161160, 0x11161160, 0x51165161, 0x51175161, 0x01171160) { tmp = read_value(cpu, 0, addr, rs, inc_clk, 1); } \
t(0, 0x00000006, 0x00000006, 0x00000006, 0x00000006, 0x00000016, 0x00000016, 0x00000016, 0x00000016) { tmp = read_value(cpu, 0, cpu->pc, rs, inc_clk, 0); cpu->pc += rs; } \
\
t(0, 0x00000000, 0x00000200, 0x02000210, 0x02000210, 0x02000000, 0x00000000, 0x00000000, 0x00000000) { tmp = *reg; } \
t(0, 0x00000000, 0x00000200, 0x00100310, 0x00000200, 0x00000000, 0x00000000, 0x00000000, 0x00000000) { ++tmp; } \
t(0, 0x00000000, 0x00000000, 0x02000000, 0x02100110, 0x02000000, 0x00000000, 0x00000000, 0x00000000) { --tmp; } \
}
#undef ORTHO_1CC
#undef ORTHO_1OP
#undef ORTHO_2OP