#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;
#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 esc = 0;
uint8_t *addr;
uint8_t kbd_rdy;
WINDOW *scr;
struct suxthr {
struct sux sx;
uint8_t th;
};
#if bench
double ipc;
struct timeval str[THREADS], en[THREADS];
#endif
inline uint8_t get_addrsize(uint8_t prefix, uint8_t addrmode) {
uint8_t id = (prefix & 0x0C) >> 2;
switch (addrmode) {
case ZM:
switch (id) {
case 2: return 5;
case 3: return 3;
case 1: return 2;
case 0: return 0;
}
break;
case ABS:
switch (id) {
case 3: return 7;
case 2: return 6;
case 1: return 4;
case 0: return 1;
}
break;
}
return 0;
}
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 opcode = 0;
union reg address;
union reg value;
#if getclk
uint64_t iclk = 0;
#endif
#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
gettimeofday(&str[thread], 0);
#endif
for (;;) {
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
for (uint8_t i = (24*thread)+2; i <= 24*(thread+1); i++) {
wmove(scr, i, 0);
waddch(scr, (i == lines) ? '>' : ' ');
}
wmove(scr, lines, 1);
wclrtoeol(scr);
wprintw(scr, "pc: $%04"PRIX64 , cpu->pc);
wprintw(scr, ", a: $%016"PRIX64, cpu->a);
wprintw(scr, ", b: $%016"PRIX64, cpu->b);
wprintw(scr, ", x: $%016"PRIX64, cpu->x);
wprintw(scr, ", y: $%016"PRIX64, cpu->y);
wprintw(scr, ", sp: $%04X", cpu->sp);
wprintw(scr, ", ps: $%02X", cpu->ps.u8[thread]);
wprintw(scr, ", inst: ");
#if keypoll
pthread_mutex_unlock(&mutex);
#endif
#endif
prefix = addr[cpu->pc];
if ((prefix & 0x03) != 0x03) {
prefix = 0;
}
cpu->pc += ((prefix & 0x03) == 0x03);
opcode = addr[cpu->pc];
address.u64 = cpu->pc;
++cpu->pc;
#if getclk
++iclk;
#endif
if (optype[opcode] != IMPL) {
address.u64 = get_addr(cpu, &tmpaddr, opcode, prefix, thread);
if (address.u64 > mem_size-1) {
addr[STEP_ADDR] = 1;
step = 1;
}
setreg_sw(value.u8, 0, addr, address.u64, prefix, 0, RS);
if (optype[opcode] == REL) {
switch ((prefix >> 4) & 3) {
default: address.u64 = cpu->pc + (int8_t )value.u8[0] ; break;
case 1 : address.u64 = cpu->pc + (int16_t)value.u16[0]; break;
case 2 : address.u64 = cpu->pc + (int32_t)value.u32[0]; break;
case 3 : address.u64 = cpu->pc + (int64_t)value.u64 ; break;
}
}
#if getclk
++iclk;
#endif
}
#if debug && !bench
#if keypoll
pthread_mutex_lock(&mutex);
#endif
uint64_t operands[3];
operands[0] = value.u64;
operands[1] = address.u64;
operands[2] = tmpaddr;
disasm(cpu, operands, lines, opcode, prefix, thread);
lines+=1;
#if keypoll
pthread_mutex_unlock(&mutex);
#endif
#endif
uint8_t size = (1 << ((prefix >> 4) & 3))-1;
switch(opcode) {
case CPS_IMP:
cpu->ps.u64 = 0;
break;
case ADC_B:
value.u64 = cpu->b;
case ADC_IMM:
case ADC_AB:
case ADC_Z:
adc(cpu, value.u64, 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:
case TAX_IMP:
case TYX_IMP:
case TYA_IMP:
case TXA_IMP:
case TXY_IMP:
case TAB_IMP:
case TSX_IMP:
case TBA_IMP:
case TXS_IMM:
transfer(cpu, value.u64, opcode, prefix, thread);
break;
case BRA_REL:
case JMP_AB:
case JMP_Z:
case JMP_IN:
cpu->pc = address.u64;
break;
case SBC_B:
value.u64 = cpu->b;
case SBC_IMM:
case SBC_AB:
case SBC_Z:
sbc(cpu, value.u64, 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.u64 = cpu->b;
case AND_IMM:
case AND_AB:
case AND_Z:
and(cpu, value.u64, thread);
break;
case BPO_REL:
if (!getflag(N)) {
cpu->pc = address.u64;
}
break;
case ORA_B:
value.u64 = cpu->b;
case ORA_IMM:
case ORA_AB:
case ORA_Z:
or(cpu, value.u64, thread);
break;
case SEI_IMP:
setflag(1, I);
break;
case BNG_REL:
if (getflag(N)) {
cpu->pc = address.u64;
}
break;
case XOR_B:
value.u64 = cpu->b;
case XOR_IMM:
case XOR_AB:
case XOR_Z:
xor(cpu, value.u64, thread);
break;
case CLI_IMP:
setflag(0, I);
break;
case BCS_REL:
if (getflag(C)) {
cpu->pc = address.u64;
}
break;
case LSL_B:
value.u64 = cpu->b;
case LSL_IMM:
case LSL_AB:
case LSL_Z:
lsl(cpu, value.u64, 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.u64, cpu->a, prefix, thread);
break;
case STY_AB:
case STY_Z:
case STY_IN:
store(cpu, address.u64, cpu->y, prefix, thread);
break;
case STX_AB:
case STX_Z:
case STX_IN:
store(cpu, address.u64, 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.u64, cpu->b, prefix, thread);
break;
case BCC_REL:
if (!getflag(C)) {
cpu->pc = address.u64;
}
break;
case LSR_B:
value.u64 = cpu->b;
case LSR_IMM:
case LSR_AB:
case LSR_Z:
lsr(cpu, value.u64, thread);
break;
case ASR_B:
value.u64 = cpu->b;
case ASR_IMM:
case ASR_AB:
case ASR_Z:
asr(cpu, value.u64, 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, address.u64, cpu->b, prefix, 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, address.u64, cpu->a, prefix, thread);
break;
case LDY_IMM:
case LDY_AB:
case LDY_Z:
case LDY_IN:
cpu->y = load(cpu, address.u64, cpu->y, prefix, thread);
break;
case LDX_IMM:
case LDX_AB:
case LDX_Z:
case LDX_IN:
cpu->x = load(cpu, address.u64, cpu->x, prefix, thread);
break;
case BEQ_REL:
if (getflag(Z)) {
cpu->pc = address.u64;
}
break;
case ROL_B:
value.u64 = cpu->b;
case ROL_IMM:
case ROL_AB:
case ROL_Z:
rol(cpu, value.u64, thread);
break;
case BNE_REL:
if (!getflag(Z)) {
cpu->pc = address.u64;
}
break;
case ROR_B:
value.u64 = cpu->b;
case ROR_IMM:
case ROR_AB:
case ROR_Z:
ror(cpu, value.u64, thread);
break;
case BVS_REL:
if (getflag(V)) {
cpu->pc = address.u64;
}
break;
case MUL_B:
value.u64 = cpu->b;
case MUL_IMM:
case MUL_AB:
case MUL_Z:
mul(cpu, value.u64, thread);
break;
case BVC_REL:
if (!getflag(V)) {
cpu->pc = address.u64;
}
break;
case DIV_IMM:
case DIV_B:
case DIV_AB:
case DIV_Z:
divd(cpu, value.u64, opcode, 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:
cmp(cpu, value.u64, cpu->b, thread);
break;
case CMP_B:
value.u64 = cpu->b;
case CMP_IMM:
case CMP_AB:
case CMP_Z:
case CMP_IN:
case CMP_IX:
case CMP_IY:
cmp(cpu, value.u64, cpu->a, thread);
break;
case CPY_IMM:
case CPY_AB:
case CPY_Z:
cmp(cpu, value.u64, cpu->y, thread);
break;
case CPX_IMM:
case CPX_AB:
case CPX_Z:
cmp(cpu, value.u64, cpu->x, 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, 7, thread);
cpu->pc = address.u64;
break;
case INC_AB:
case INC_Z:
idm(cpu, address.u64, prefix, 1, thread);
break;
case NOP_IMP:
break;
case RTI_IMP:
cpu->ps.u64 = pull(cpu, 0, thread);
case RTS_IMP:
cpu->pc = pull(cpu, 7, thread);
break;
case DEC_AB:
case DEC_Z:
idm(cpu, address.u64, 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);
setreg(value.u8, +, 0, addr, +, (opcode == BRK) ? 0xFFE0 : 0xFFA0, 7);
if (opcode == WAI_IMP) {
kbd_rdy &= (uint8_t)~(1 << thread);
}
cpu->pc = value.u64;
default:
break;
}
#if !IO
ins++;
#endif
#if !bench
if (step) {
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);
#if debug
wrefresh(scr);
#endif
}
#endif
#if debug && !bench
#if keypoll
pthread_mutex_lock(&mutex);
#endif
wmove(scr, (6*thread)+1, 0);
wprintw(scr, "Instructions executed: %"PRIu64, ins);
#if getclk
wprintw(scr, ", Clock cycles: %"PRIu64, iclk);
#endif
if (step && !subdbg) {
wrefresh(scr);
}
#if keypoll
pthread_mutex_unlock(&mutex);
#endif
#elif bench
if (ins >= BENCH_INST) {
pthread_mutex_lock(&main_mutex);
threads_done++;
inst[thread] = ins;
#if getclk
clk[thread] = iclk;
#endif
pthread_cond_signal(&main_cond);
pthread_mutex_unlock(&main_mutex);
gettimeofday(&en[thread], 0);
break;
}
#endif
}
return NULL;
}
void init_scr() {
if (!scr) {
scr = initscr();
}
nodelay(scr, 0);
wtimeout(scr, 8);
crmode();
noecho();
nl();
curs_set(1);
scrollok(scr, 1);
start_color();
use_default_colors();
init_pair(1, COLOR_WHITE, -1);
attron(COLOR_PAIR(1) | A_BOLD);
}
int main(int argc, char **argv) {
struct suxthr thr[THREADS];
char *tmp = malloc(2048);
addr = malloc(mem_size);
memset(key, 0, sizeof(key));
#if bench
inss = 0;
#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;
}
}
sprintf(tmp, "\033[2J\033[H");
fwrite(tmp, sizeof(char), strlen(tmp), stdout);
fflush(stdout);
init_scr();
werase(scr);
wmove(scr, 0, 0);
wrefresh(scr);
pthread_t therads[THREADS];
int result;
uint16_t vec = 0xFFC0;
uint8_t offset;
for (int i = 0; i < THREADS; i++) {
thr[i].sx.sp = 0xFFFF;
thr[i].sx.stk_st = i+1;
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.pc = (uint64_t)addr[vec+0+offset]
| (uint64_t)addr[vec+1+offset] << 8
| (uint64_t)addr[vec+2+offset] << 16
| (uint64_t)addr[vec+3+offset] << 24
| (uint64_t)addr[vec+4+offset] << 32
| (uint64_t)addr[vec+5+offset] << 40
| (uint64_t)addr[vec+6+offset] << 48
| (uint64_t)addr[vec+7+offset] << 56;
thr[i].th = i;
#if !IO
inst[i] = 0;
#endif
result = pthread_create(&therads[i], NULL, run, &thr[i]);
assert(!result);
}
int c = 0;
uint8_t step_key = 0;
uint8_t end = 0;
werase(scr);
while (threads_done < THREADS && !end) {
#if !bench
int x, y;
if ((step_key && step && !kbd_rdy) || !step || kbd_rdy) {
if ((c != EOF && c !=-1)) {
#if !keypoll
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
#endif
pthread_mutex_lock(&main_mutex);
curs_set(0);
pthread_cond_wait(&main_cond, &main_mutex);
pthread_mutex_unlock(&main_mutex);
curs_set(1);
c = 0;
step_key = 0;
addr[CTRL_ADDR] = 0;
wrefresh(scr);
}
}
#if keypoll
pthread_mutex_lock(&mutex);
#endif
c = get_key(scr);
if (c == 19) {
if (kbd_rdy) {
c = get_key(scr);
}
step = 1;
} else if (c == 0x11) {
end = 1;
continue;
}
if (step) {
if (c != 19 && c != 18 && c != 0x11 && !isalnum(c)) {
int keycode = get_keycode(key);
switch (keycode) {
case KEY_F(1):
endwin();
puts("Starting asmmon()");
asmmon("stdin");
puts("Reinitializing screen.");
init_scr();
wrefresh(scr);
break;
}
#if debug && !bench
wmove(scr, getmaxy(scr)-1, 0);
wclrtoeol(scr);
wprintw(scr, "c: %i", c);
wmove(scr, y, x);
#endif
}
}
if (kbd_rdy) {
switch (c) {
case ERR:
addr[CTRL_ADDR] = 0;
break;
case '\0': break;
default:
if (kbd_rdy && c < 0x100) {
addr[RX_ADDR] = (uint8_t)c;
addr[CTRL_ADDR] = 1;
#if debug && !bench
wmove(scr, getmaxy(scr)-1, 0);
wclrtoeol(scr);
wprintw(scr, "c: %i ", c);
wprintw(scr, "key: ");
for (int i = 0; key[i] != '\0'; i++) {
wprintw(scr, "$%02X%s", key[i], (key[i+1] != '\0') ? ", " : "");
}
wmove(scr, y, x);
#endif
}
break;
}
} else {
if (step) {
step = !(c == 18);
step_key = (c == 19);
}
}
addr[STEP_ADDR] = step;
#if keypoll
pthread_mutex_unlock(&mutex);
#endif
#else
pthread_mutex_lock(&main_mutex);
pthread_cond_wait(&main_cond, &main_mutex);
pthread_mutex_unlock(&main_mutex);
#endif
}
endwin();
#if bench
if (threads_done == THREADS) {
double tm_sec, tm_usec, tm[THREADS], ttm;
#if getclk
double clkspd;
double mhz;
#endif
double ips[THREADS];
double ipst;
for (int i = 0; i < THREADS; i++) {
tm_sec = (en[i].tv_sec - str[i].tv_sec);
tm_usec = (en[i].tv_usec-str[i].tv_usec);
tm[i] = (tm_sec*1000000)+(tm_usec);
ips[i] = inst[i]/tm[i];
if (i) {
inss += inst[i];
ttm += tm[i];
ipst += ips[i];
#if getclk
tclk += clk[i];
#endif
} else {
inss = inst[i];
ttm = tm[i];
ipst = ips[i];
#if getclk
tclk = clk[i];
#endif
}
#if getclk
clkspd = (tm[i]/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, tm: %f\n", i, inst[i], i, ips[i], tm[i]/1000000);
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 = (ttm/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", ttm/1000000);
fwrite(tmp, sizeof(char), strlen(tmp), stdout);
fflush(stdout);
free(tmp);
}
#endif
free(addr);
return 0;
}