From 3af5d66626b2a0cef0e45deee71d2ee60948dda2 Mon Sep 17 00:00:00 2001 From: n loewen Date: Tue, 15 Aug 2023 15:22:21 +0100 Subject: [PATCH] Feature (simulator): Implement Negative, Zero, and Overflow flags --- simulator.js | 94 +++++++++++++++++++++++++-------- test-programs/flag-overflow.asm | 36 +++++++++++++ 2 files changed, 108 insertions(+), 22 deletions(-) create mode 100644 test-programs/flag-overflow.asm diff --git a/simulator.js b/simulator.js index 1cde7c5..819a378 100644 --- a/simulator.js +++ b/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)); diff --git a/test-programs/flag-overflow.asm b/test-programs/flag-overflow.asm new file mode 100644 index 0000000..86dabb4 --- /dev/null +++ b/test-programs/flag-overflow.asm @@ -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 \ No newline at end of file