#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;
uint64_t a[8];
uint64_t y[8];
uint64_t x[8];
uint8_t crt;
uint64_t pc[8];
uint64_t ps;
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));
}
int push(uint64_t value, uint8_t size) {
for (uint8_t i = 0; i < size; i++) {
addr[STK_STADDR+sp] = value >> 8*i;
sp--;
}
return 1;
}
uint64_t pull(uint8_t size) {
uint64_t value;
for (uint8_t i = 0; i < size; i++) {
value = addr[STK_STADDR+sp] << 8*i;
sp++;
}
return value;
}
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] >> 63;
a[thread] = sum;
break;
case SRA:
sum = (value < 64) ? a[thread] >> value : 0;
c = a[thread] & 1;
a[thread] = sum;
break;
case ROL:
a[thread] = rol(a[thread], value);
break;
case ROR:
a[thread] = ror(a[thread], value);
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:
push(pc[thread], 8);
pc[thread] = value;
break;
case RTS:
pc[thread] = pull(8);
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];
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:
push(ps, value);
break;
case PHA:
case PAY:
case PAX:
push(a[thread], value);
if (opcode == PAY)
push(y[thread], value);
if (opcode == PAX)
push(x[thread], value);
break;
case PHY:
push(y[thread], value);
break;
case PHX:
push(x[thread], value);
break;
case PLP:
ps = pull(value);
break;
case PLA:
case PYA:
case PXA:
a[thread] = pull(value);
if (opcode == PAY)
y[thread] = pull(value);
if (opcode == PAX)
x[thread] = pull(value);
break;
case PLY:
y[thread] = pull(value);
break;
case PLX:
x[thread] = pull(value);
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] = 0xE1;
addr[0x8002] = 0x01;
addr[0x8003] = 0x51;
addr[0x8004] = 0x01;
addr[0x8005] = 0xF1;
addr[0x8006] = 0x00;
addr[0x8007] = 0x40;
addr[0x8008] = 0x50;
addr[0x8009] = 0x01;
addr[0x800A] = 0x80;
addr[0x800B] = 0x10;
addr[0x800C] = 0x03;
addr[0x800D] = 0x80;
addr[0x8010] = 0xFF;
addr[0xFFFC] = 0x00;
addr[0xFFFD] = 0x80;
pc[0] = addr[0xFFFC] | (addr[0xFFFD] << 8);
uint64_t value = 0;
uint8_t end = 0;
uint8_t opcode = 0;
uint8_t size;
printf("\033[2J");
while (!end) {
printf("pc: 0x%04llx, a: 0x%016llx, carry: %u, inst: %s, value: 0x%04llx, $%04llx: %02x\n", pc[0], a[0], ps & 1, opname[opcode], value, value, addr[value]);
opcode = addr[pc[0]];
if (opcode == STA || opcode == JMP || opcode == BCS)
size = 2;
else if (opcode == CPS || opcode == NOP)
size = 0;
else if (opcode == STP)
end = 1, size = 0;
else
size = 1;
if(update_pc)
pc[0]++;
else
update_pc = 1;
if (size) {
int i = 0;
while (i < size) {
if (!i)
value = (addr[pc[0]]);
else
value |= (addr[pc[0]] << (8*i));
printf("pc: 0x%04llx, a: 0x%016llx, carry: %u, inst: %s, value: 0x%04llx, $%04llx: %02x\n", pc[0], a[0], ps & 1, opname[opcode], value, value, addr[value]);
if (update_pc)
pc[0]++;
else
update_pc = 1;
i++;
}
}
if (opcode == SLA || opcode == ADC)
alu(opcode, value, 0);
if (opcode == JMP || opcode == BCS)
branch(opcode, value, 0);
memory(opcode, value, 0);
printf("pc: 0x%04llx, a: 0x%016llx, carry: %u, inst: %s, value: 0x%04llx, $%04llx: %02x\n", pc[0], a[0], ps & 1, opname[opcode], value, value, addr[value]);
}
free(addr);
return 0;
}