diff --git a/src/cpu.js b/src/cpu.js index b3a210d..16588fb 100644 --- a/src/cpu.js +++ b/src/cpu.js @@ -152,55 +152,21 @@ module.exports = class CPU { add_lit: (lit) => { this.dbg.currentMnemonic = 'ADD lit'; - // Calculate sum - let sum = this.acc + lit; - if (sum > 255) { - this.flags.C = true; - sum = (sum % 255) - 1; - } else { - this.flags.C = false; - } - // Calculate overflow flag status - let bitSixCarry = 0; - if ((this.acc & 64) && (lit & 64)) { bitSixCarry = 1; } - // let overflow = bitSixCarry ^ (this.flags & 8); - // FIXME - re-implement overflow - // I'm on a plane and can't remember how this works - let overflow = 0; - if (overflow) { - this.flags.O = true; - } else { - this.flags.O = false; - } + const [sum, carry, overflow] = sumCarryOverflow(this.acc, lit); this.acc = sum; + this.flags.C = carry; + this.flags.O = overflow; this._updateFlagNegative(); this._updateFlagZero(); this._incrementIP(2); }, add_addr: (addr) => { - this.dbg.currentMnemonic = 'ADD addr'; - // Calculate sum - let sum = this.acc + this.memory[addr]; - if (sum > 255) { - this.flags.C = true; - sum = (sum % 255) - 1; - } else { - this.flags.C = false; - } - // Calculate overflow flag status - let bitSixCarry = 0; - if ((this.acc & 64) && (addr & 64)) { bitSixCarry = 1; } - // let overflow = bitSixCarry ^ (this.flags & 8); - // FIXME - re-implement overflow - // I'm on a plane and can't remember how this works - let overflow = 0; - if (overflow) { - this.flags.O = true; - } else { - this.flags.O = false; - } + this.dbg.currentMnemonic = `ADD addr; @ addr: $${num2hex(this.memory[addr])}`; + const [sum, carry, overflow] = sumCarryOverflow(this.acc, this.memory[addr]); this.acc = sum; + this.flags.C = carry; + this.flags.O = overflow; this._updateFlagNegative(); this._updateFlagZero(); this._incrementIP(2); @@ -208,55 +174,21 @@ module.exports = class CPU { sub_lit: (lit) => { this.dbg.currentMnemonic = 'SUB lit'; - // Calculate sum - let sum = this.acc - lit; - if (sum < 0) { - this.flags.C = true; - sum = sum + 256; - } else { - this.flags.C = false; - } - // Calculate overflow flag status - let bitSixCarry = 0; - if ((this.acc & 64) && (lit & 64)) { bitSixCarry = 1; } - // let overflow = bitSixCarry ^ (this.flags & 8); - // FIXME - re-implement overflow - // I'm on a plane and can't remember how this works - let overflow = 0; - if (overflow) { - this.flags.O = true; - } else { - this.flags.O = false; - } - this.acc = sum; + const [difference, carry, overflow] = differenceCarryOverflow(this.acc, lit); + this.acc = difference; + this.flags.C = carry; + this.flags.O = overflow; this._updateFlagNegative(); this._updateFlagZero(); this._incrementIP(2); }, sub_addr: (addr) => { - this.dbg.currentMnemonic = 'SUB addr'; - // Calculate sum - let sum = this.acc - this.memory[addr]; - if (sum < 0) { - this.flags.C = true; - sum = sum + 256; - } else { - this.flags.C = false; - } - // Calculate overflow flag status - let bitSixCarry = 0; - if ((this.acc & 64) && (addr & 64)) { bitSixCarry = 1; } - // let overflow = bitSixCarry ^ (this.flags & 8); - // FIXME - re-implement overflow - // I'm on a plane and can't remember how this works - let overflow = 0; - if (overflow) { - this.flags.O = true; - } else { - this.flags.O = false; - } - this.acc = sum; + this.dbg.currentMnemonic = `SUB addr; @ addr: $${num2hex(this.memory[addr])}`; + const [difference, carry, overflow] = differenceCarryOverflow(this.acc, this.memory[addr]); + this.acc = difference; + this.flags.C = carry; + this.flags.O = overflow; this._updateFlagNegative(); this._updateFlagZero(); this._incrementIP(2); @@ -350,3 +282,39 @@ module.exports = class CPU { process.exit(); } } + +/** + * @arg {number} n1 + * @arg {number} n2 + * @returns {[number, boolean, boolean]} [sum, carry, overflow] + **/ +function sumCarryOverflow(n1, n2) { + let sum = n1 + n2; + let carry = false; + if (sum > 255) { + carry = true; + sum = (sum % 255) - 1; + } + + let n1_bit6 = (n1 & 64) === 64; // Bit 6 is the 64s place + let n2_bit6 = (n2 & 64) === 64; // 64 & n == 64 where n >= 64 + let carryIntoLastBit = n1_bit6 && n2_bit6; + console.log('c_in', carryIntoLastBit, 'c_out', carry); + let overflow = carryIntoLastBit != carry; + + return [sum, carry, overflow]; +} + +/** + * @arg {number} n1 + * @arg {number} n2 + * @returns {[number, boolean, boolean]} [sum, carry, overflow] + **/ +function differenceCarryOverflow(n1, n2) { + // https://www.righto.com/2012/12/the-6502-overflow-flag-explained.html + // > SBC simply takes the ones complement of the second value and then performs an ADC. + // + // https://stackoverflow.com/a/8966863 + // > The signed overflow flag value, however, must be the same for both A-B and A+(-B) because it depends on whether or not the result has the correct sign bit and in both cases the sign bit will be the same. + return sumCarryOverflow(n1, -n2); +} \ No newline at end of file diff --git a/test-programs/flag-overflow-2.asm b/test-programs/flag-overflow-2.asm new file mode 100644 index 0000000..80a8605 --- /dev/null +++ b/test-programs/flag-overflow-2.asm @@ -0,0 +1,63 @@ +;; Test behaviour of flags during addition and subtraction +;; with a focus on the Overflow flag +2023-08-29 + +; http://teaching.idallen.com/dat2343/11w/notes/040_overflow.txt: +; +; > 1. If the sum of two numbers with the sign bits off yields a result number + ; with the sign bit on, the "overflow" flag is turned on. +; > +; > 0100 + 0100 = 1000 (overflow flag is turned on) +; > +; > 2. If the sum of two numbers with the sign bits on yields a result number +; > with the sign bit off, the "overflow" flag is turned on. +; > +; > 1000 + 1000 = 0000 (overflow flag is turned on) +; > +; > Otherwise, the overflow flag is turned off. +; > * 0100 + 0001 = 0101 (overflow flag is turned off) +; > * 0110 + 1001 = 1111 (overflow flag is turned off) +; > * 1000 + 0001 = 1001 (overflow flag is turned off) +; > * 1100 + 1100 = 1000 (overflow flag is turned off) + +;; Check simple addition and subtraction +; LDA 1 +; STO 0 +; LDA 1 +; ADD 1 +; LDA 1 +; ADD (0) +; LDA 3 +; SUB 1 +; LDA 3 +; SUB (0) + +;; Check zero flag, negative flag +; LDA 0 +; LDA 255 + +;; Check overflow flag + +LDA 0b01000000 +ADD 0b01000000 ; 10000000 ; Overflow flag is on + +LDA 0b10000000 +ADD 0b10000000 ; 00000000 ; Overflow flag is on + + +; > * 0100 + 0001 = 0101 (overflow flag is turned off) +; > * 0110 + 1001 = 1111 (overflow flag is turned off) +; > * 1000 + 0001 = 1001 (overflow flag is turned off) +; > * 1100 + 1100 = 1000 (overflow flag is turned off) + +LDA 0b01000000 +ADD 0b00010000 ; 01010000 ; overflow off + +LDA 0b01100000 +ADD 0b10010000 ; 11110000 ; overflow off + +LDA 0b10000000 +ADD 0b00010000 ; 10010000 ; overflow off + +LDA 0b11000000 +ADD 0b11000000 ; 10000000 ; overflow off \ No newline at end of file