#include "sux.h"
#include <assert.h>
#if getclk
uint64_t clk[THREADS];
uint64_t tclk;
#endif
const uint16_t tv = 0xFF50;
#if !IO
uint64_t inst[THREADS];
#endif
#if bench
uint64_t inss;
uint8_t time_done = 0;
#endif
#if debug
uint8_t subdbg;
#endif
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t main_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_cond_t main_cond = PTHREAD_COND_INITIALIZER;
uint8_t threads_done = 0;
uint8_t step = 0;
uint8_t *addr;
uint8_t kbd_rdy;
uint8_t end = 0;
WINDOW *scr;
WINDOW *regs;
WINDOW *inst_win;
WINDOW *dbg_win;
struct suxthr {
struct sux sx;
uint8_t th;
};
#if bench
double ipc;
void stop_timer() {
time_done = 1;
}
void start_timer(int sec, int usec) {
struct itimerval it_val;
for (; usec > 1000000; sec++, usec -= 1000000);
it_val.it_value.tv_sec = sec;
it_val.it_value.tv_usec = usec;
it_val.it_interval.tv_sec = 0;
it_val.it_interval.tv_usec = 0;
if (signal(SIGALRM, stop_timer) == SIG_ERR) {
perror("Unable to catch SIGALRM.");
exit(1);
}
if (setitimer(ITIMER_REAL, &it_val, NULL) == -1) {
perror("Error calling setitimer().");
exit(1);
}
}
#endif
uint8_t is_extop(uint8_t opcode, uint8_t dbg) {
switch (opcode) {
case ADC_E:
case SBC_E:
case AND_E:
case ORA_E:
case XOR_E:
case LSL_E:
case LSR_E:
case ROL_E:
case ROR_E:
case MUL_E:
case DIV_E:
case ASR_E:
case CMP_E:
case LDY_E:
case LDA_E:
case LDB_E:
case CPB_E:
case CPX_E:
case CPY_E:
case LDX_E:
case DEC_E:
case INC_E:
case STY_E:
case STA_E:
case STB_E:
case STX_E:
case JMP_E:
case JSR_E: return 0;
}
return 1;
}
void *run(void *args) {
struct suxthr *thr = (void *)args;
struct sux *cpu = &thr->sx;
uint8_t thread = thr->th;
uint8_t prefix = 0;
uint8_t ext_prefix = 0;
uint8_t prefix2 = 0;
uint8_t op_id = 0;
uint8_t opcode = 0;
union reg address;
union reg value;
cpu->clk = 0;
uint64_t *rem = NULL;
#if !IO
uint64_t ins = 0;
#endif
#if !bench
uint8_t lines = (6*thread)+2;
#endif
#if debug && !bench
if (!subdbg) {
addr[STEP_ADDR] = 1;
step = 1;
}
#if keypoll
pthread_mutex_lock(&mutex);
#endif
werase(scr);
#if keypoll
pthread_mutex_unlock(&mutex);
#endif
#endif
uint64_t tmpaddr = 0;
#if bench
start_timer(1, 0);
#endif
for (;;) {
#if !bench
if (end) {
pthread_mutex_lock(&main_mutex);
pthread_cond_signal(&main_cond);
pthread_mutex_unlock(&main_mutex);
return NULL;
}
#endif
address.u64 = 0;
value.u64 = 0;
#if debug && !bench
if (lines > 24*(thread+1)) {
lines = (24*thread)+2;
}
#if keypoll
pthread_mutex_lock(&mutex);
#endif
print_info(cpu, inst_win, lines, thread);
print_regs(cpu, regs);
#if keypoll
pthread_mutex_unlock(&mutex);
#endif
#endif
uint32_t inst = read_value(cpu, 0, cpu->pc, 4, 1, 0);
uint8_t *tmp_inst = (uint8_t *)&inst;
uint8_t inst_len = 0;
prefix = ((inst & 3) == 3) ? tmp_inst[inst_len++] : 0;
ext_prefix = ((tmp_inst[inst_len] & 0xF) == 0xD) ? tmp_inst[inst_len++] : 0;
opcode = tmp_inst[inst_len++];
op_id = (ext_prefix == 0x1D) ? tmp_inst[inst_len++] : 0;
cpu->pc += inst_len;
address.u64 = cpu->pc;
uint8_t operand_type[2];
uint8_t am;
uint8_t ortho_id[2];
uint8_t ext_id = 0;
uint8_t tmp_opcode = opcode;
uint8_t tmp_ext_prefix = ext_prefix;
int is_ortho = 0;
if (ext_prefix) {
ext_id = (ext_prefix >> 4);
switch (ext_id) {
case 0x0:
am = ext_optype[opcode];
if (!is_extop(opcode, 0)) {
tmp_ext_prefix = 0;
switch (opcode) {
case ADC_E: tmp_opcode = ADC_IMM; break;
case SBC_E: tmp_opcode = SBC_IMM; break;
case AND_E: tmp_opcode = AND_IMM; break;
case ORA_E: tmp_opcode = ORA_IMM; break;
case XOR_E: tmp_opcode = XOR_IMM; break;
case LSL_E: tmp_opcode = LSL_IMM; break;
case LSR_E: tmp_opcode = LSR_IMM; break;
case ROL_E: tmp_opcode = ROL_IMM; break;
case ROR_E: tmp_opcode = ROR_IMM; break;
case MUL_E: tmp_opcode = MUL_IMM; break;
case DIV_E: tmp_opcode = DIV_IMM; break;
case ASR_E: tmp_opcode = ASR_IMM; break;
case CMP_E: tmp_opcode = CMP_IMM; break;
case LDY_E: tmp_opcode = LDY_IMM; break;
case LDA_E: tmp_opcode = LDA_IMM; break;
case LDB_E: tmp_opcode = LDB_IMM; break;
case CPB_E: tmp_opcode = CPB_IMM; break;
case CPX_E: tmp_opcode = CPX_IMM; break;
case CPY_E: tmp_opcode = CPY_IMM; break;
case LDX_E: tmp_opcode = LDX_IMM; break;
case DEC_E: tmp_opcode = DEC_Z; break;
case INC_E: tmp_opcode = INC_Z; break;
case STY_E: tmp_opcode = STY_Z; break;
case STA_E: tmp_opcode = STA_Z; break;
case STB_E: tmp_opcode = STB_Z; break;
case STX_E: tmp_opcode = STX_Z; break;
case JMP_E: tmp_opcode = JMP_Z; break;
case JSR_E: tmp_opcode = JSR_Z; break;
}
}
break;
case 0x1:
operand_type[0] = ((opcode & 0x10) >> 4);
operand_type[1] = ((opcode & 0x08) >> 3);
ortho_id[0] = op_id >> 4;
ortho_id[1] = op_id & 0x0F;
am = IMPL;
is_ortho = 1;
break;
}
} else {
am = optype[opcode];
}
uint8_t rs = (prefix >> 4) & 3;
uint8_t size = (1 << rs) - 1;
uint8_t check_io = (am != IMM);
#if debug && !bench
#if keypoll
pthread_mutex_lock(&mutex);
#endif
disasm(cpu, inst_win, lines, opcode, prefix, ext_prefix, prefix2, operand_type, ortho_id, thread);
lines+=1;
#if keypoll
pthread_mutex_unlock(&mutex);
#endif
#endif
if (am != IMPL && am != BREG) {
address.u64 = get_addr(cpu, opcode, prefix, ext_prefix, 1, 1, thread);
if (isrw(opcode, ext_prefix) && am != REL && isread(opcode, ext_prefix)) {
value.u64 = read_value(cpu, 0, address.u64, size, 1, check_io);
}
}
ext_prefix = tmp_ext_prefix;
opcode = tmp_opcode;
if (ext_prefix) {
uint8_t tmp = 0;
switch (ext_id) {
case 0x0: exec_ext_inst(cpu, opcode, prefix, value.u64, address.u64, size, thread); break;
case 0x1: exec_ortho_inst(cpu, opcode, prefix, size, operand_type, ortho_id, thread); break;
}
} else {
exec_base_inst(cpu, opcode, prefix, value.u64, address.u64, size, thread);
}
#if !IO
ins++;
#endif
#if !bench
if (step) {
int c = 0;
#if debug
wrefresh(scr);
wrefresh(regs);
wrefresh(inst_win);
#endif
for (; step && c != 19 && !end; c = get_key(scr));
#if debug
wrefresh(scr);
wrefresh(regs);
wrefresh(inst_win);
#endif
}
#endif
#if debug && !bench
#if keypoll
pthread_mutex_lock(&mutex);
#endif
wmove(inst_win, (6*thread)+1, 0);
wprintw(inst_win, "Instructions executed: %"PRIu64, ins);
#if getclk
wprintw(inst_win, ", Clock cycles: %"PRIu64, cpu->clk);
#endif
if (step || !subdbg) {
wrefresh(scr);
wrefresh(regs);
wrefresh(inst_win);
wrefresh(dbg_win);
}
#if keypoll
pthread_mutex_unlock(&mutex);
#endif
#elif bench
if (time_done) {
pthread_mutex_lock(&main_mutex);
threads_done++;
inst[thread] = ins;
#if getclk
clk[thread] = cpu->clk;
#endif
pthread_cond_signal(&main_cond);
pthread_mutex_unlock(&main_mutex);
break;
}
#endif
}
return NULL;
}
void init_scr() {
if (!scr) {
scr = initscr();
}
nodelay(scr, 0);
keypad(scr, 1);
crmode();
noecho();
nl();
curs_set(1);
scrollok(scr, 1);
start_color();
use_default_colors();
init_pair(1, COLOR_WHITE, -1);
wattron(scr, COLOR_PAIR(1) | A_BOLD);
#if debug
int x;
int y;
getmaxyx(scr, y, x);
if (!regs) {
regs = newwin(24, 80, 1, (x/2));
}
nodelay(regs, 0);
keypad(regs, 1);
scrollok(regs, 1);
wattron(regs, COLOR_PAIR(1) | A_BOLD);
if (!inst_win) {
inst_win = newwin(28, (x/2)-20, 0, 0);
}
nodelay(inst_win, 0);
keypad(inst_win, 1);
scrollok(inst_win, 1);
wattron(inst_win, COLOR_PAIR(1) | A_BOLD);
if (!dbg_win) {
dbg_win = newwin(33, x, y-33, 0);
}
nodelay(dbg_win, 0);
keypad(dbg_win, 1);
scrollok(dbg_win, 1);
wattron(dbg_win, COLOR_PAIR(1) | A_BOLD);
#endif
}
int main(int argc, char **argv) {
struct suxthr thr[THREADS];
char *tmp = malloc(2048);
addr = malloc(mem_size);
#if bench
inss = 0;
struct timeval str, en;
#endif
int v = 0;
if (argc != 2) {
if (asmmon("stdin") == 2) {
return 0;
}
} else {
#if debug
subdbg = !strcmp(argv[1], "programs/sub-suite/subsuite.s");
#endif
if (asmmon(argv[1]) == 2) {
return 0;
}
}
init_scr();
werase(scr);
wmove(scr, 0, 0);
wrefresh(scr);
#if debug
werase(regs);
wmove(regs, 0, 0);
wrefresh(regs);
werase(inst_win);
wmove(inst_win, 0, 0);
wrefresh(inst_win);
werase(dbg_win);
wmove(dbg_win, 0, 0);
wrefresh(dbg_win);
#endif
pthread_t therads[THREADS];
int result;
uint16_t vec = 0xFFC0;
uint8_t offset;
for (int i = 0; i < THREADS; i++) {
thr[i].sx.sp = (i << 16) | 0xFFFF;
offset = (i) ? ((i-1) << 3) : 0;
vec = (i) ? 0xFF50 : 0xFFC0;
thr[i].sx.a = 0, thr[i].sx.b = 0;
thr[i].sx.x = 0, thr[i].sx.y = 0;
thr[i].sx.e = 0, thr[i].sx.c = 0;
thr[i].sx.d = 0, thr[i].sx.s = 0;
thr[i].sx.f = 0, thr[i].sx.bp = 0;
thr[i].sx.r11 = 0, thr[i].sx.r12 = 0;
thr[i].sx.r12 = 0, thr[i].sx.r13 = 0;
thr[i].sx.r14 = 0, thr[i].sx.r15 = 0;
thr[i].sx.pc = read_value(&thr[i].sx, 0, vec+offset, 7, 0, 0);
thr[i].th = i;
#if !IO
inst[i] = 0;
#endif
result = pthread_create(&therads[i], NULL, run, &thr[i]);
assert(!result);
}
werase(scr);
#if bench
endwin();
gettimeofday(&str, 0);
double t = 0;
double dt = 0;
double t2 = 0;
#endif
while (threads_done < THREADS && !end) {
#if !bench
pthread_mutex_lock(&main_mutex);
pthread_cond_wait(&main_cond, &main_mutex);
pthread_mutex_unlock(&main_mutex);
#else
pthread_mutex_lock(&main_mutex);
pthread_cond_wait(&main_cond, &main_mutex);
pthread_mutex_unlock(&main_mutex);
#endif
}
#if !bench
endwin();
#endif
#if bench
gettimeofday(&en, 0);
if (threads_done == THREADS) {
double tm_sec, tm_usec, tm;
#if getclk
double clkspd;
double mhz;
#endif
double ips[THREADS];
double ipst;
tm_sec = (en.tv_sec - str.tv_sec);
tm_usec = (en.tv_usec-str.tv_usec);
tm = (tm_sec*1000000)+(tm_usec);
for (int i = 0; i < THREADS; i++) {
ips[i] = inst[i]/tm;
if (i) {
inss += inst[i];
ipst += ips[i];
#if getclk
tclk += clk[i];
#endif
} else {
inss = inst[i];
ipst = ips[i];
#if getclk
tclk = clk[i];
#endif
}
#if getclk
clkspd = (tm/1000000)*1000000/clk[i];
mhz = 1000000.0/clkspd/1000000;
#endif
sprintf(tmp, "Instructions executed for thread %i: %"PRIu64", Instructions per Second for thread %i in MIPS: %f\n", i, inst[i], i, ips[i]);
fwrite(tmp, sizeof(char), strlen(tmp), stdout);
}
sprintf(tmp, "Total Instructions executed: %"PRIu64", Total Instructions per Second in MIPS: %f", inss, ipst);
fwrite(tmp, sizeof(char), strlen(tmp), stdout);
#if getclk
clkspd = (tm/1000000)*1000000/tclk;
mhz = 1000000.0/clkspd/1000000;
sprintf(tmp, ", Clock cycles: %"PRIu64", Clock Speed in MHz: %f", tclk, mhz);
fwrite(tmp, sizeof(char), strlen(tmp), stdout);
#endif
sprintf(tmp, ", tm: %f\n", tm/1000000);
fwrite(tmp, sizeof(char), strlen(tmp), stdout);
fflush(stdout);
free(tmp);
}
#endif
free(addr);
return 0;
}