#include "opcode.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define C (1 << 0)
#define Z (1 << 1)
#define I (1 << 2)
#define S (1 << 3)
#define V (1 << 6)
#define N (1 << 7)
#define STK_STADDR 0x010000
uint8_t *addr;
uint8_t update_pc = 1;
uint16_t sp = 0xFFFF;
uint64_t a[8];
uint64_t y[8];
uint64_t x[8];
uint8_t crt;
uint64_t pc[8];
uint64_t ps;
int optype(uint8_t opcode) {
switch (opcode) {
case ADC:
case SBC:
case MUL:
case DIV:
case AND:
case AAY:
case AAX:
case ANY:
case ANX:
case ORA:
case OAY:
case OAX:
case ORY:
case ORX:
case XOR:
case XAY:
case XAX:
case XRY:
case XRX:
case SLA:
case SRA:
case ROL:
case ROR:
case INC:
case IAY:
case IAX:
case INY:
case INX:
case DEC:
case DAY:
case DAX:
case DEY:
case DEX:
return ALU;
case STT:
case ENT:
return THREAD;
case JMP:
case JSR:
case RTS:
case BPO:
case BNG:
case BEQ:
case BNE:
case BCS:
case BCC:
case BVS:
case BVC:
case CMP:
case CAY:
case CAX:
case CPY:
case CPX:
return BRANCH;
case CPS:
case SEI:
case CLI:
case SEC:
case CLC:
case SSP:
case CSP:
case SEV:
case CLV:
return FLAG;
case LDA:
case LDY:
case LAY:
case LDX:
case LAX:
case 0x59:
case 0x52:
case 0x53:
case 0x54:
case 0x55:
case STA:
case SAY:
case SAX:
case STY:
case STX:
case PHP:
case PHA:
case PAY:
case PAX:
case PHY:
case PHX:
case PLP:
case PLA:
case PYA:
case PXA:
case PLY:
case PLX:
return MEMORY;
case NOP:
case BRK:
return MISC;
default:
return -1;
}
}
uint64_t rol(uint64_t a, uint64_t value) {
const uint64_t mask = 8 * sizeof(a) - 1;
value &= mask;
return (a << value) | (a >> (-value & mask));
}
uint64_t ror(uint64_t a, uint64_t value) {
const uint64_t mask = 8 * sizeof(a) - 1;
value &= mask;
return (a >> value) | (a << (-value & mask));
}
void push(uint8_t value) {
addr[STK_STADDR+sp] = value;
sp--;
}
uint8_t pull() {
sp++;
return addr[STK_STADDR+sp];
}
int alu(uint8_t opcode, uint64_t value, uint8_t thread) {
uint64_t sum;
uint8_t c = 0;
uint8_t v = 0;
uint8_t z = 0;
uint8_t n = 0;
switch(opcode) {
case ADC:
c = (ps & (1 << thread)) >> thread;
sum = a[thread]+value+c;
v = !((a[thread]^value) & 0x8000000000000000) && ((a[thread]^sum) & 0x8000000000000000);
c = (sum < value);
a[thread] = sum;
break;
case SBC:
c = !(ps & (1 << thread));
sum = a[thread]-value-c;
v = ((a[thread]^value) & 0x8000000000000000) && ((a[thread]^sum) & 0x8000000000000000);
c = (sum > value);
a[thread] = sum;
break;
case MUL:
c = (ps & (1 << thread)) >> thread;
sum = a[thread]*value+c;
v = !((a[thread]^value) & 0x8000000000000000) && ((a[thread]^sum) & 0x8000000000000000);
c = (!((a[thread]^sum) && (a[thread]^value)) || (a[thread] >= ((uint64_t)1 << 32) && value >= ((uint64_t)1 << 32)));
a[thread] = sum;
break;
case DIV:
sum = a[thread]/value;
v = ((a[thread]^value) & 0x8000000000000000) && ((a[thread]^sum) & 0x8000000000000000);
a[thread] = sum;
break;
case AND:
case AAY:
case AAX:
a[thread] &= value;
if (opcode == AAY)
y[thread] &= value;
if (opcode == AAX)
x[thread] &= value;
break;
case ANY:
y[thread] &= value;
break;
case ANX:
x[thread] &= value;
break;
case ORA:
case OAY:
case OAX:
a[thread] |= value;
if (opcode == OAY)
y[thread] |= value;
if (opcode == OAX)
x[thread] |= value;
break;
case ORY:
y[thread] |= value;
break;
case ORX:
x[thread] |= value;
break;
case XOR:
case XAY:
case XAX:
a[thread] ^= value;
if (opcode == XAY)
y[thread] ^= value;
if (opcode == XAX)
x[thread] ^= value;
break;
case XRY:
y[thread] ^= value;
break;
case XRX:
x[thread] ^= value;
break;
case SLA:
sum = (value < 64) ? a[thread] << value : 0;
c = a[thread] >> 64-value;
a[thread] = sum;
break;
case SRA:
sum = (value < 64) ? a[thread] >> value : 0;
c = a[thread] & 1;
a[thread] = sum;
break;
case ROL:
c = (ps & (1 << thread)) >> thread;
sum = a[thread] << value;
sum |= c;
c = a[thread] >> (uint64_t)64-value;
a[thread] = sum;
break;
case ROR:
c = (ps & (1 << thread)) >> thread;
sum = a[thread] >> value;
sum |= (uint64_t)c << (uint64_t)64-value;
c = a[thread] & 1;
a[thread] = sum;
break;
case INC:
case IAY:
case IAX:
a[thread]++;
if (opcode == IAY)
y[thread]++;
if (opcode == IAX)
x[thread]++;
break;
case INY:
y[thread]++;
break;
case INX:
x[thread]++;
break;
case DEC:
case DAY:
case DAX:
a[thread]--;
if (opcode == DAY)
y[thread]--;
if (opcode == DAX)
x[thread]--;
break;
case DEY:
y[thread]--;
break;
case DEX:
x[thread]--;
break;
default:
break;
}
z = (a[thread] == 0);
n = (a[thread] >> 63);
ps = (n) ? (ps | (uint64_t)N << 8*thread) : (ps & ~((uint64_t)N << 8*thread));
ps = (v) ? (ps | (uint64_t)V << 8*thread) : (ps & ~((uint64_t)V << 8*thread));
ps = (z) ? (ps | (uint64_t)Z << 8*thread) : (ps & ~((uint64_t)Z << 8*thread));
ps = (c) ? (ps | (uint64_t)C << 8*thread) : (ps & ~((uint64_t)C << 8*thread));
return 1;
}
int threads(uint8_t opcode, uint64_t value) {
uint8_t t = value & 0xFF;
switch(opcode) {
case STT:
crt |= t;
for (uint8_t i = 0; i < 7; i++)
if ((t >> i) & 1)
pc[i] = value >> 8;
break;
case ENT:
crt &= ~t;
for (uint8_t i = 0; i < 7; i++)
if ((t >> i) & 1)
pc[i] = 0;
break;
}
return 1;
}
int branch(uint8_t opcode, uint64_t value, uint8_t thread) {
uint64_t dif;
switch (opcode) {
case JMP:
pc[thread] = value;
break;
case JSR:
pc[thread] = value;
break;
case RTS:
break;
case BPO:
if (!(ps & ((uint64_t)N << 8*thread)))
pc[thread] = value;
else
pc[thread]+=0;
break;
case BNG:
if (ps & ((uint64_t)N << 8*thread))
pc[thread] = value;
else
pc[thread]+=0;
break;
case BEQ:
if (ps & ((uint64_t)Z << 8*thread))
pc[thread] = value;
else
pc[thread]+=0;
break;
case BNE:
if (!(ps & ((uint64_t)Z << 8*thread)))
pc[thread] = value;
else
pc[thread]+=0;
break;
case BCS:
if (ps & ((uint64_t)C << 8*thread))
pc[thread] = value;
else
pc[thread]+=0;
break;
case BCC:
if (!(ps & ((uint64_t)C << 8*thread)))
pc[thread] = value;
else
pc[thread]+=0;
break;
case BVS:
if (ps & ((uint64_t)V << 8*thread))
pc[thread] = value;
else
pc[thread]+=0;
break;
case BVC:
if (!(ps & ((uint64_t)V << 8*thread)))
pc[thread] = value;
else
pc[thread]+=0;
break;
case CMP:
case CAY:
case CAX:
if (opcode == CAY || opcode == CAX) {
dif = (opcode == CAY) ? a[thread]-y[thread] : a[thread]-x[thread];
value = a[thread];
} else {
dif = value-a[thread];
}
ps = (dif & 0x8000000000000000) ? (ps | (uint64_t)N << 8*thread) : (ps & ~((uint64_t)N << 8*thread));
ps = (dif == 0) ? (ps | (uint64_t)Z << 8*thread) : (ps & ~((uint64_t)Z << 8*thread));
ps = (dif > value) ? (ps | (uint64_t)C << 8*thread) : (ps & ~((uint64_t)C << 8*thread));
break;
case CPY:
dif = value-y[thread];
ps = (dif & 0x8000000000000000) ? (ps | (uint64_t)N << 8*thread) : (ps & ~((uint64_t)N << 8*thread));
ps = (dif == 0) ? (ps | (uint64_t)Z << 8*thread) : (ps & ~((uint64_t)Z << 8*thread));
ps = (dif > value) ? (ps | (uint64_t)C << 8*thread) : (ps & ~((uint64_t)C << 8*thread));
break;
case CPX:
dif = value-x[thread];
ps = (dif & 0x8000000000000000) ? (ps | (uint64_t)N << 8*thread) : (ps & ~((uint64_t)N << 8*thread));
ps = (dif == 0) ? (ps | (uint64_t)Z << 8*thread) : (ps & ~((uint64_t)Z << 8*thread));
ps = (dif > value) ? (ps | (uint64_t)C << 8*thread) : (ps & ~((uint64_t)C << 8*thread));
break;
}
return 1;
}
int setflags(uint8_t opcode, uint8_t thread) {
switch (opcode) {
case CPS:
ps &= 0;
break;
case SEI:
ps |= ((uint64_t)I << 8*thread);
break;
case CLI:
ps &= ~((uint64_t)I << 8*thread);
break;
case SEC:
ps |= ((uint64_t)C << 8*thread);
break;
case CLC:
ps &= ~((uint64_t)C << 8*thread);
break;
case SSP:
ps |= ((uint64_t)S << 8*thread);
break;
case CSP:
ps &= ~((uint64_t)S << 8*thread);
break;
case SEV:
ps |= ((uint64_t)V << 8*thread);
break;
case CLV:
ps &= ~((uint64_t)V << 8*thread);
break;
}
return 1;
}
int memory(uint8_t opcode, uint64_t value, uint8_t thread) {
switch (opcode) {
case LDA:
case LDY:
case LAY:
case LDX:
case LAX:
case 0x95:
case 0x25:
case 0x35:
case 0x45:
case 0x55:
if (opcode == LDA || opcode == LAY || opcode == LAX)
a[thread] = value;
else if (opcode == 0x95 || opcode == 0x35 || opcode == 0x55)
a[thread] = addr[value];
if (opcode == LAY || opcode == LDY)
y[thread] = value;
else if (opcode == 0x35)
y[thread] = addr[value];
if (opcode == LAX)
x[thread] = value;
else if (opcode == 0x55)
x[thread] = addr[value];
break;
case STA:
case SAY:
case SAX:
addr[value] = a[thread];
if (opcode == SAY)
addr[value] = y[thread];
if (opcode == SAX)
addr[value] = x[thread];
break;
case STY:
addr[value] = y[thread];
break;
case STX:
addr[value] = x[thread];
break;
case PHP:
break;
case PHA:
case PAY:
case PAX:
if (value > 7)
value = 7;
for (int8_t i = 8*value; i > 0; i-=8) {
push(a[thread] >> i);
}
push(a[thread] & 0xFF);
if (opcode == PAY) {
if (value > 7)
value = 7;
for (int8_t i = 8*value; i > 0; i-=8) {
push(y[thread] >> i);
}
push(y[thread] & 0xFF);
}
if (opcode == PAX) {
if (value > 7)
value = 7;
for (int8_t i = 8*value; i > 0; i-=8) {
push(x[thread] >> i);
}
push(x[thread] & 0xFF);
}
break;
case PHY:
if (value > 7)
value = 7;
for (int8_t i = 8*value; i > 0; i-=8) {
push(y[thread] >> i);
}
push(y[thread] & 0xFF);
break;
case PHX:
if (value > 7)
value = 7;
for (int8_t i = 8*value; i > 0; i-=8) {
push(x[thread] >> i);
}
push(x[thread] & 0xFF);
break;
case PLP:
break;
case PLA:
case PYA:
case PXA:
if (value > 7)
value = 7;
for (uint8_t i = 0; i <= 8*value; i+=8) {
if (!i)
a[thread] = (uint64_t)pull();
else
a[thread] += (uint64_t)pull() << i;
}
if (opcode == PAY) {
if (value > 7)
value = 7;
for (uint8_t i = 0; i <= 8*value; i+=8) {
if (!i)
y[thread] = (uint64_t)pull();
else
y[thread] += (uint64_t)pull() << i;
}
}
if (opcode == PAX) {
if (value > 7)
value = 7;
for (uint8_t i = 0; i <= 8*value; i+=8) {
if (!i)
x[thread] = (uint64_t)pull();
else
x[thread] += (uint64_t)pull() << i;
}
}
break;
case PLY:
if (value > 7)
value = 7;
for (uint8_t i = 0; i <= 8*value; i+=8) {
if (!i)
y[thread] = (uint64_t)pull();
else
y[thread] += (uint64_t)pull() << i;
}
break;
case PLX:
if (value > 7)
value = 7;
for (uint8_t i = 0; i <= 8*value; i+=8) {
if (!i)
x[thread] = (uint64_t)pull();
else
x[thread] += (uint64_t)pull() << i;
}
break;
}
return 1;
}
int misc(uint8_t opcode, uint8_t thread) {
switch (opcode) {
case NOP:
break;
TODO
case BRK:
break;
}
return 1;
}
int main(int argc, char **argv) {
a[0] = 0;
x[0] = 0;
y[0] = 0;
addr = malloc(8*0x04000000);
int v = 0;
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
addr[0x8000] = 0x00;
addr[0x8001] = 0xC1;
addr[0x8002] = 0x09;
addr[0x8003] = 0x08;
addr[0x8004] = 0x1C;
addr[0x8005] = 0x08;
addr[0x8006] = 0x10;
addr[0x8007] = 0x01;
addr[0x8008] = 0x80;
addr[0x8009] = 0x00;
addr[0x800A] = 0x00;
addr[0x800B] = 0x00;
addr[0xFFFC] = 0x00;
addr[0xFFFD] = 0x80;
pc[0] = addr[0xFFFC] | (addr[0xFFFD] << 8);
uint64_t value = 0;
uint64_t inst = 0;
uint8_t lines = 2;
uint8_t end = 0;
uint8_t opcode = 0;
uint8_t size;
printf("\033[2J");
while (!end) {
opcode = addr[pc[0]];
if (opcode == STA || opcode == JMP || opcode == BCS)
size = 2;
else if (opcode == CPS || opcode == NOP || opcode == INC || opcode == IAX || opcode == CAX)
size = 0;
else if (opcode == STP)
end = 1, size = 0;
else
size = 1;
pc[0]++;
if (size) {
int i = 0;
while (i < size) {
if (!i)
value = (addr[pc[0]]);
else
value |= (addr[pc[0]] << (8*i));
pc[0]++;
i++;
}
}
if (optype(opcode) == ALU)
alu(opcode, value, 0);
if (optype(opcode) == THREAD)
threads(opcode, value);
if (optype(opcode) == BRANCH)
branch(opcode, value, 0);
if (optype(opcode) == MEMORY)
memory(opcode, value, 0);
if (optype(opcode) == FLAG)
setflags(opcode, 0);
if (optype(opcode) == MISC)
misc(opcode, 0);
if (lines > 50)
lines = 2;
printf("\033[%uHpc: 0x%04llx, a: 0x%016llx, x: 0x%016llx, y: 0x%016llx, sp: 0x%04lx, carry: %u, inst: %s, value: 0x%04llx, $%04llx: %02x\n", lines, pc[0], a[0], x[0], y[0], sp, ps & 1, opname[opcode], value, value, addr[value]);
lines++;
printf("\033[1HInstructions executed: %llu\n", inst++);
}
free(addr);
return 0;
}