Feature (simulator): Implement Negative, Zero, and Overflow flags
This commit is contained in:
parent
b1dc2a7c7e
commit
3af5d66626
94
simulator.js
94
simulator.js
|
|
@ -2,12 +2,6 @@ const { INITIAL_IP_ADDRESS, CYCLE_LIMIT } = require('./machine.config');
|
||||||
const { num2hex, num2bin_4bit } = require('./logging.js');
|
const { num2hex, num2bin_4bit } = require('./logging.js');
|
||||||
const display = require('./display.js');
|
const display = require('./display.js');
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// implement setting and clearing of:
|
|
||||||
// - [ ] overflow flag
|
|
||||||
// - [ ] zero flag
|
|
||||||
// - [ ] negative flag
|
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
const CPU = {
|
const CPU = {
|
||||||
running: false,
|
running: false,
|
||||||
|
|
@ -36,6 +30,17 @@ const CPU = {
|
||||||
unsetFlagZero: () => { CPU.FLAGS &= ~4 },
|
unsetFlagZero: () => { CPU.FLAGS &= ~4 },
|
||||||
unsetFlagOverflow: () => { CPU.FLAGS &= ~2 },
|
unsetFlagOverflow: () => { CPU.FLAGS &= ~2 },
|
||||||
unsetFlagCarry: () => { CPU.FLAGS &= ~1 },
|
unsetFlagCarry: () => { CPU.FLAGS &= ~1 },
|
||||||
|
|
||||||
|
updateFlagZero: () => {
|
||||||
|
if (CPU.Acc === 0) {
|
||||||
|
CPU.setFlagZero();
|
||||||
|
} else {
|
||||||
|
CPU.unsetFlagZero();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateFlagNegative: () => {
|
||||||
|
CPU.Acc & 128 ? CPU.setFlagNegative : CPU.unsetFlagNegative },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -62,68 +67,116 @@ const Instructions = {
|
||||||
load_lit: (lit) => {
|
load_lit: (lit) => {
|
||||||
CPU.currentInstruction.mnemonic = 'LDA lit';
|
CPU.currentInstruction.mnemonic = 'LDA lit';
|
||||||
CPU.Acc = lit;
|
CPU.Acc = lit;
|
||||||
|
CPU.updateFlagNegative();
|
||||||
|
CPU.updateFlagZero();
|
||||||
CPU.IP = CPU.IP += 2;
|
CPU.IP = CPU.IP += 2;
|
||||||
},
|
},
|
||||||
|
|
||||||
load_addr: (addr) => {
|
load_addr: (addr) => {
|
||||||
CPU.currentInstruction.mnemonic = `LDA addr; @ addr: ${num2hex(CPU.memory[addr])}`;
|
CPU.currentInstruction.mnemonic = `LDA addr; @ addr: ${num2hex(CPU.memory[addr])}`;
|
||||||
CPU.Acc = CPU.memory[addr];
|
CPU.Acc = CPU.memory[addr];
|
||||||
|
CPU.updateFlagNegative();
|
||||||
|
CPU.updateFlagZero();
|
||||||
CPU.IP = CPU.IP += 2;
|
CPU.IP = CPU.IP += 2;
|
||||||
},
|
},
|
||||||
|
|
||||||
add_lit: (lit) => {
|
add_lit: (lit) => {
|
||||||
CPU.currentInstruction.mnemonic = 'ADD lit';
|
CPU.currentInstruction.mnemonic = 'ADD lit';
|
||||||
|
// Calculate sum
|
||||||
let sum = CPU.Acc + lit;
|
let sum = CPU.Acc + lit;
|
||||||
// TODO: implement NZO flags
|
|
||||||
if (sum > 255) {
|
if (sum > 255) {
|
||||||
CPU.setFlagCarry();
|
CPU.setFlagCarry();
|
||||||
CPU.Acc = (sum % 255) - 1;
|
sum = (sum % 255) - 1;
|
||||||
} else {
|
} else {
|
||||||
CPU.unsetFlagCarry();
|
CPU.unsetFlagCarry();
|
||||||
CPU.Acc = sum;
|
|
||||||
}
|
}
|
||||||
|
// Calculate overflow flag status
|
||||||
|
let bitSixCarry = 0;
|
||||||
|
if ((CPU.Acc & 64) && (lit & 64)) { bitSixCarry = 1; }
|
||||||
|
let overflow = bitSixCarry ^ (CPU.FLAGS & 1)
|
||||||
|
if (overflow) {
|
||||||
|
CPU.setFlagOverflow();
|
||||||
|
} else {
|
||||||
|
CPU.unsetFlagOverflow();
|
||||||
|
}
|
||||||
|
CPU.Acc = sum;
|
||||||
|
CPU.updateFlagNegative();
|
||||||
|
CPU.updateFlagZero();
|
||||||
CPU.IP = CPU.IP += 2;
|
CPU.IP = CPU.IP += 2;
|
||||||
},
|
},
|
||||||
|
|
||||||
add_addr: (addr) => {
|
add_addr: (addr) => {
|
||||||
CPU.currentInstruction.mnemonic = 'ADD addr';
|
CPU.currentInstruction.mnemonic = 'ADD addr';
|
||||||
|
// Calculate sum
|
||||||
let sum = CPU.Acc + CPU.memory[addr];
|
let sum = CPU.Acc + CPU.memory[addr];
|
||||||
// TODO: implement NZO flags
|
|
||||||
if (sum > 255) {
|
if (sum > 255) {
|
||||||
CPU.setFlagCarry();
|
CPU.setFlagCarry();
|
||||||
CPU.Acc = (sum % 255) - 1;
|
sum = (sum % 255) - 1;
|
||||||
} else {
|
} else {
|
||||||
CPU.unsetFlagCarry();
|
CPU.unsetFlagCarry();
|
||||||
CPU.Acc = sum;
|
|
||||||
}
|
}
|
||||||
|
// Calculate overflow flag status
|
||||||
|
let bitSixCarry = 0;
|
||||||
|
if ((CPU.Acc & 64) && (addr & 64)) { bitSixCarry = 1; }
|
||||||
|
let overflow = bitSixCarry ^ (CPU.FLAGS & 1)
|
||||||
|
if (overflow) {
|
||||||
|
CPU.setFlagOverflow();
|
||||||
|
} else {
|
||||||
|
CPU.unsetFlagOverflow();
|
||||||
|
}
|
||||||
|
CPU.Acc = sum;
|
||||||
|
CPU.updateFlagNegative();
|
||||||
|
CPU.updateFlagZero();
|
||||||
CPU.IP = CPU.IP += 2;
|
CPU.IP = CPU.IP += 2;
|
||||||
},
|
},
|
||||||
|
|
||||||
sub_lit: (lit) => {
|
sub_lit: (lit) => {
|
||||||
CPU.currentInstruction.mnemonic = 'SUB lit';
|
CPU.currentInstruction.mnemonic = 'SUB lit';
|
||||||
|
// Calculate sum
|
||||||
let sum = CPU.Acc - lit;
|
let sum = CPU.Acc - lit;
|
||||||
// TODO: implement NZO flags
|
|
||||||
if (sum < 0) {
|
if (sum < 0) {
|
||||||
CPU.setFlagCarry();
|
CPU.setFlagCarry();
|
||||||
CPU.Acc = 255 + sum + 1;
|
sum = sum + 256;
|
||||||
} else {
|
} else {
|
||||||
CPU.unsetFlagCarry();
|
CPU.unsetFlagCarry();
|
||||||
CPU.Acc = sum;
|
|
||||||
}
|
}
|
||||||
|
// Calculate overflow flag status
|
||||||
|
let bitSixCarry = 0;
|
||||||
|
if ((CPU.Acc & 64) && (lit & 64)) { bitSixCarry = 1; }
|
||||||
|
let overflow = bitSixCarry ^ (CPU.FLAGS & 1)
|
||||||
|
if (overflow) {
|
||||||
|
CPU.setFlagOverflow();
|
||||||
|
} else {
|
||||||
|
CPU.unsetFlagOverflow();
|
||||||
|
}
|
||||||
|
CPU.Acc = sum;
|
||||||
|
CPU.updateFlagNegative();
|
||||||
|
CPU.updateFlagZero();
|
||||||
CPU.IP = CPU.IP += 2;
|
CPU.IP = CPU.IP += 2;
|
||||||
},
|
},
|
||||||
|
|
||||||
sub_addr: (addr) => {
|
sub_addr: (addr) => {
|
||||||
CPU.currentInstruction.mnemonic = 'SUB addr';
|
CPU.currentInstruction.mnemonic = 'SUB addr';
|
||||||
|
// Calculate sum
|
||||||
let sum = CPU.Acc - CPU.memory[addr];
|
let sum = CPU.Acc - CPU.memory[addr];
|
||||||
// TODO: implement NZO flags
|
|
||||||
if (sum < 0) {
|
if (sum < 0) {
|
||||||
CPU.setFlagCarry();
|
CPU.setFlagCarry();
|
||||||
CPU.Acc = 255 + sum + 1;
|
sum = sum + 256;
|
||||||
} else {
|
} else {
|
||||||
CPU.unsetFlagCarry();
|
CPU.unsetFlagCarry();
|
||||||
CPU.Acc = sum;
|
|
||||||
}
|
}
|
||||||
|
// Calculate overflow flag status
|
||||||
|
let bitSixCarry = 0;
|
||||||
|
if ((CPU.Acc & 64) && (addr & 64)) { bitSixCarry = 1; }
|
||||||
|
let overflow = bitSixCarry ^ (CPU.FLAGS & 1)
|
||||||
|
if (overflow) {
|
||||||
|
CPU.setFlagOverflow();
|
||||||
|
} else {
|
||||||
|
CPU.unsetFlagOverflow();
|
||||||
|
}
|
||||||
|
CPU.Acc = sum;
|
||||||
|
CPU.updateFlagNegative();
|
||||||
|
CPU.updateFlagZero();
|
||||||
CPU.IP = CPU.IP += 2;
|
CPU.IP = CPU.IP += 2;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -163,21 +216,18 @@ const Instructions = {
|
||||||
if (flagNum === 2) { mask = 4; }
|
if (flagNum === 2) { mask = 4; }
|
||||||
if (flagNum === 3) { mask = 8; }
|
if (flagNum === 3) { mask = 8; }
|
||||||
if (mask === null) { throw new Error('Invalid flag number'); }
|
if (mask === null) { throw new Error('Invalid flag number'); }
|
||||||
console.log('flag mask:', mask);
|
|
||||||
CPU.FLAGS = CPU.FLAGS ^= mask;
|
CPU.FLAGS = CPU.FLAGS ^= mask;
|
||||||
CPU.IP += 2;
|
CPU.IP += 2;
|
||||||
},
|
},
|
||||||
|
|
||||||
flag_hop: (flagNum) => {
|
flag_hop: (flagNum) => {
|
||||||
CPU.currentInstruction.mnemonic = `FHP; IP+2: ${CPU.memory[CPU.IP+2]}, IP+3: ${CPU.memory[CPU.IP+3]}`;
|
CPU.currentInstruction.mnemonic = `FHP; IP+2: ${CPU.memory[CPU.IP+2]}, IP+3: ${CPU.memory[CPU.IP+3]}`;
|
||||||
console.log('flag number:', flagNum);
|
|
||||||
let mask = null;
|
let mask = null;
|
||||||
if (flagNum === 0) { mask = 1; }
|
if (flagNum === 0) { mask = 1; }
|
||||||
if (flagNum === 1) { mask = 2; }
|
if (flagNum === 1) { mask = 2; }
|
||||||
if (flagNum === 2) { mask = 4; }
|
if (flagNum === 2) { mask = 4; }
|
||||||
if (flagNum === 3) { mask = 8; }
|
if (flagNum === 3) { mask = 8; }
|
||||||
if (mask === null) { throw new Error('Invalid flag number'); }
|
if (mask === null) { throw new Error('Invalid flag number'); }
|
||||||
console.log('flag mask:', mask);
|
|
||||||
if (CPU.FLAGS & mask) {
|
if (CPU.FLAGS & mask) {
|
||||||
CPU.IP += 4;
|
CPU.IP += 4;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -264,7 +314,7 @@ async function logCPUState(debug = false) {
|
||||||
console.log('Mnemonic:', CPU.currentInstruction.mnemonic);
|
console.log('Mnemonic:', CPU.currentInstruction.mnemonic);
|
||||||
console.log(`Machine: $${num2hex(CPU.currentInstruction.opcode)} $${num2hex(CPU.currentInstruction.operand)}`);
|
console.log(`Machine: $${num2hex(CPU.currentInstruction.opcode)} $${num2hex(CPU.currentInstruction.operand)}`);
|
||||||
console.log();
|
console.log();
|
||||||
console.log(`IP: $${num2hex(CPU.IP)} Acc: $${num2hex(CPU.Acc)} FLAGS: ${num2bin_4bit(CPU.FLAGS)} ${CPU.running ? "running" : "halted" }`);
|
console.log(`IP: $${num2hex(CPU.IP)} Acc: $${num2hex(CPU.Acc)} NVOC: ${num2bin_4bit(CPU.FLAGS)} ${CPU.running ? "running" : "halted" }`);
|
||||||
console.log();
|
console.log();
|
||||||
// Pause to show animated display:
|
// Pause to show animated display:
|
||||||
if (!debug) await new Promise(resolve => setTimeout(resolve, 75));
|
if (!debug) await new Promise(resolve => setTimeout(resolve, 75));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
;; test overflow flag
|
||||||
|
; https://www.righto.com/2012/12/the-6502-overflow-flag-explained.html
|
||||||
|
|
||||||
|
LDA $50
|
||||||
|
ADD $10 ; CF, OF: 0 0
|
||||||
|
LDA $50
|
||||||
|
ADD $50 ; CF, OF: 0 1
|
||||||
|
LDA $50
|
||||||
|
ADD $90 ; CF, OF: 0 0
|
||||||
|
LDA $50
|
||||||
|
ADD $D0 ; CF, OF: 1 0
|
||||||
|
LDA $D0
|
||||||
|
ADD $10 ; CF, OF: 0 0
|
||||||
|
LDA $D0
|
||||||
|
ADD $50 ; CF, OF: 1 0
|
||||||
|
LDA $D0
|
||||||
|
ADD $90 ; CF, OF: 1 1
|
||||||
|
LDA $D0
|
||||||
|
ADD $D0 ; CF, OF: 1 0
|
||||||
|
|
||||||
|
LDA $50
|
||||||
|
SUB $F0 ; CF, OF: 1 0
|
||||||
|
LDA $50
|
||||||
|
SUB $B0 ; CF, OF: 1 1
|
||||||
|
LDA $50
|
||||||
|
SUB $70 ; CF, OF: 1 0
|
||||||
|
LDA $50
|
||||||
|
SUB $30 ; CF, OF: 0 0
|
||||||
|
LDA $D0
|
||||||
|
SUB $F0 ; CF, OF: 1 0
|
||||||
|
LDA $D0
|
||||||
|
SUB $B0 ; CF, OF: 0 0
|
||||||
|
LDA $D0
|
||||||
|
SUB $70 ; CF, OF: 0 1
|
||||||
|
LDA $D0
|
||||||
|
SUB $30 ; CF, OF: 0 0
|
||||||
Loading…
Reference in New Issue