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 display = require('./display.js');
|
||||
|
||||
// TODO:
|
||||
// implement setting and clearing of:
|
||||
// - [ ] overflow flag
|
||||
// - [ ] zero flag
|
||||
// - [ ] negative flag
|
||||
|
||||
// STATE
|
||||
const CPU = {
|
||||
running: false,
|
||||
|
|
@ -36,6 +30,17 @@ const CPU = {
|
|||
unsetFlagZero: () => { CPU.FLAGS &= ~4 },
|
||||
unsetFlagOverflow: () => { CPU.FLAGS &= ~2 },
|
||||
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) => {
|
||||
CPU.currentInstruction.mnemonic = 'LDA lit';
|
||||
CPU.Acc = lit;
|
||||
CPU.updateFlagNegative();
|
||||
CPU.updateFlagZero();
|
||||
CPU.IP = CPU.IP += 2;
|
||||
},
|
||||
|
||||
load_addr: (addr) => {
|
||||
CPU.currentInstruction.mnemonic = `LDA addr; @ addr: ${num2hex(CPU.memory[addr])}`;
|
||||
CPU.Acc = CPU.memory[addr];
|
||||
CPU.updateFlagNegative();
|
||||
CPU.updateFlagZero();
|
||||
CPU.IP = CPU.IP += 2;
|
||||
},
|
||||
|
||||
add_lit: (lit) => {
|
||||
CPU.currentInstruction.mnemonic = 'ADD lit';
|
||||
// Calculate sum
|
||||
let sum = CPU.Acc + lit;
|
||||
// TODO: implement NZO flags
|
||||
if (sum > 255) {
|
||||
CPU.setFlagCarry();
|
||||
CPU.Acc = (sum % 255) - 1;
|
||||
sum = (sum % 255) - 1;
|
||||
} else {
|
||||
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;
|
||||
},
|
||||
|
||||
add_addr: (addr) => {
|
||||
CPU.currentInstruction.mnemonic = 'ADD addr';
|
||||
// Calculate sum
|
||||
let sum = CPU.Acc + CPU.memory[addr];
|
||||
// TODO: implement NZO flags
|
||||
if (sum > 255) {
|
||||
CPU.setFlagCarry();
|
||||
CPU.Acc = (sum % 255) - 1;
|
||||
sum = (sum % 255) - 1;
|
||||
} else {
|
||||
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;
|
||||
},
|
||||
|
||||
sub_lit: (lit) => {
|
||||
CPU.currentInstruction.mnemonic = 'SUB lit';
|
||||
// Calculate sum
|
||||
let sum = CPU.Acc - lit;
|
||||
// TODO: implement NZO flags
|
||||
if (sum < 0) {
|
||||
CPU.setFlagCarry();
|
||||
CPU.Acc = 255 + sum + 1;
|
||||
sum = sum + 256;
|
||||
} else {
|
||||
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;
|
||||
},
|
||||
|
||||
sub_addr: (addr) => {
|
||||
CPU.currentInstruction.mnemonic = 'SUB addr';
|
||||
// Calculate sum
|
||||
let sum = CPU.Acc - CPU.memory[addr];
|
||||
// TODO: implement NZO flags
|
||||
if (sum < 0) {
|
||||
CPU.setFlagCarry();
|
||||
CPU.Acc = 255 + sum + 1;
|
||||
sum = sum + 256;
|
||||
} else {
|
||||
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;
|
||||
},
|
||||
|
||||
|
|
@ -163,21 +216,18 @@ const Instructions = {
|
|||
if (flagNum === 2) { mask = 4; }
|
||||
if (flagNum === 3) { mask = 8; }
|
||||
if (mask === null) { throw new Error('Invalid flag number'); }
|
||||
console.log('flag mask:', mask);
|
||||
CPU.FLAGS = CPU.FLAGS ^= mask;
|
||||
CPU.IP += 2;
|
||||
},
|
||||
|
||||
flag_hop: (flagNum) => {
|
||||
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;
|
||||
if (flagNum === 0) { mask = 1; }
|
||||
if (flagNum === 1) { mask = 2; }
|
||||
if (flagNum === 2) { mask = 4; }
|
||||
if (flagNum === 3) { mask = 8; }
|
||||
if (mask === null) { throw new Error('Invalid flag number'); }
|
||||
console.log('flag mask:', mask);
|
||||
if (CPU.FLAGS & mask) {
|
||||
CPU.IP += 4;
|
||||
} else {
|
||||
|
|
@ -264,7 +314,7 @@ async function logCPUState(debug = false) {
|
|||
console.log('Mnemonic:', CPU.currentInstruction.mnemonic);
|
||||
console.log(`Machine: $${num2hex(CPU.currentInstruction.opcode)} $${num2hex(CPU.currentInstruction.operand)}`);
|
||||
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();
|
||||
// Pause to show animated display:
|
||||
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