/// <summary>
        /// Add left and right ALU buffers, put the result on the internal data bus, update the Flags accordingly
        /// </summary>
        /// <param name="withInputCarry">If true, add one when the CF flag is set before the operation</param>
        /// <param name="withOutputCarry">If true, update the value of the CF flag after operation</param>
        /// <param name="with16bitsZFlag">If true, update the Z flag according to 16 bits arithmetic semantics</param>
        /// <param name="withSZPFlags">If false, do not update the S, Z, and P/V flags</param>
        /// <param name="withComparisonYXFlags">If true, copy Y and X flags from the right buffer instead of the result</param>
        private void ExecuteArithmeticOperation(ArithmeticOperationType operation, bool withInputCarry, bool withOutputCarry, bool with16bitsZFlag, bool withSZPFlags, bool withComparisonYXFlags)
        {
            // Carry flag (bit 0) is considered only in case of add or sub with carry
            int inputCarry = 0;
            if (withInputCarry)
            {
                inputCarry = F & B00000001;
            }

            // For 16 bits operations performed in 2 stages, check if the previous operation result was Zero
            bool previousOperationResultWasZero = ZF;

            // Perform the operation using our host CPU
            int result = 0;
            switch (operation)
            {
                case ArithmeticOperationType.Addition:
                    result = ALULeftBuffer + ALURightBuffer + inputCarry;
                    break;
                case ArithmeticOperationType.LeftMinusRight:
                    result = ALULeftBuffer - ALURightBuffer - inputCarry;
                    break;
                case ArithmeticOperationType.RightMinusLeft:
                    result = ALURightBuffer - ALULeftBuffer - inputCarry;
                    break;
            }

            // Compute the carries associated with this operation afterwards.
            int carries = (ALULeftBuffer ^ ALURightBuffer ^ result) >> 1;
            // Explanation of the formula above :
            // Consider bit n of the result : if no carry was forwarded from the previous bit, this bit is set if a and b bits are different (0 + 1 = 1 + 0 = 1), reset if a and b bits are the same (0 + 0 = 0, 1 + 1 = 0 + carry for next bit).
            // On the contrary, a carry must have been forwarded from bit n-1 to bit n : if a and b bits are different (a ^ b = 1) and result is reset (result = 0), or if a and b bits are the same (a ^ b = 0) and result is set (result = 1).
            // Formula : carry[n-1 to n] = ((a[n] ^ b[n] == 1 && result[n] == 0) || (a[n] ^ b[n] == 0 & result[n] == 1)).
            // Simplification : carry[n-1 to n] = a[n] ^ b[n] ^ result[n].
            // In the variable "carries" below, when bit n is set  it means that there was a carry from bit n to bit n+1 during the addition.

            // Truncate the result to 8 bits
            byte truncResult = (byte)result;
            // and send it to the internal data bus.
            InternalDataBus = truncResult;

            // Compute flags

            // Prepare a binary mask to preserve the flags which are not affected by the current operation
            byte notAffectedFlagsMask = 0;
            if (!withOutputCarry)
            {
                notAffectedFlagsMask |= B00000001;
            }
            if(!withSZPFlags)
            {
                notAffectedFlagsMask |= B11000100;
            }
            // Preserve all flags which are not affected by the current operation
            F = (byte)(F & notAffectedFlagsMask);

            if (withSZPFlags)
            {
                // Flags bit 7 - SF flag - Set if the 2-complement value is negative. It's simply a copy of the most signifcant bit of the result.
                F |= (byte)(truncResult & B10000000);

                // Flags bit 6 - ZF flag Set if the result is zero.
                if (truncResult == 0 && (!with16bitsZFlag || previousOperationResultWasZero)) F |= B01000000;

                // Flags bit 2 - VF flag 2-compliment signed overflow : set if 2-compliment value doesn't fit in the register.
                F |= (byte)(((carries & B10000000) >> 5) ^ ((carries & B01000000) >> 4));
                // Explanation of the formula above :
                // The idea behind setting the overflow flag is simple. Suppose you sign-extend your 8-bit signed integers to 9 bits, that is, just copy the 7th bit to an extra, 8th bit. An overflow/underflow will occur if the 9-bit sum/difference of these 9-bit signed integers has different values in bits 7 and 8, meaning that the addition/subtraction has lost the result's sign in the 7th bit and used it for the result's magnitude, or, in other words, the 8 bits can't accommodate the sign bit and such a large magnitude.
                // Now, bit 7 of the result can differ from the imaginary sign bit 8 if and only if the carry into bit 7 and the carry into bit 8 (=carry out of bit 7) are different. That's because we start with the addends having bit 7=bit 8 and only different carry-ins into them can affect them in the result in different ways.
                // So overflow flag = carry-out flag XOR carry from bit 6 into bit 7.
            }

            // Undocumented - Flags bits 5 and 3
            if (!withComparisonYXFlags)
            {
                // Flags bit 5 - YF flag - A copy of bit 5 of the result.
                // Flags bit 3 - XF flag - A copy of bit 3 of the result.
                F |= (byte)(truncResult & B00101000);
            }
            else
            {
                // Special case for comparison operation : YF and XF flags are copied from the right operand, not the result.
                F |= (byte)(ALURightBuffer & B00101000);
            }

            // Flags bit 4 - HF flag The half-carry of an addition/subtraction (from bit 3 to 4). Needed for BCD correction with DAA.
            F |= (byte)((carries & B00001000) << 1);

            // Flags bit 1 - NF flag Shows whether the last operation was an addition (0) or a subtraction (1). This information is needed for DAA.
            if (operation != ArithmeticOperationType.Addition)
            {
                F |= B00000010;
            }

            if (withOutputCarry)
            {
                // Flags bit 0 - CF flag The carry flag, set if there was a carry after the most significant bit.
                F |= (byte)((carries & B10000000) >> 7);
            }
        }
Exemple #2
0
 public ArithmeticBinaryOperation(ArithmeticOperationType type, Node lhs, Node rhs)
 {
     this.Type = type;
     this.Lhs  = lhs;
     this.Rhs  = rhs;
 }
        /// <summary>
        /// Add left and right ALU buffers, put the result on the internal data bus, update the Flags accordingly
        /// </summary>
        /// <param name="withInputCarry">If true, add one when the CF flag is set before the operation</param>
        /// <param name="withOutputCarry">If true, update the value of the CF flag after operation</param>
        /// <param name="with16bitsZFlag">If true, update the Z flag according to 16 bits arithmetic semantics</param>
        /// <param name="withSZPFlags">If false, do not update the S, Z, and P/V flags</param>
        /// <param name="withComparisonYXFlags">If true, copy Y and X flags from the right buffer instead of the result</param>
        private void ExecuteArithmeticOperation(ArithmeticOperationType operation, bool withInputCarry, bool withOutputCarry, bool with16bitsZFlag, bool withSZPFlags, bool withComparisonYXFlags)
        {
            // Carry flag (bit 0) is considered only in case of add or sub with carry
            int inputCarry = 0;

            if (withInputCarry)
            {
                inputCarry = F & B00000001;
            }

            // For 16 bits operations performed in 2 stages, check if the previous operation result was Zero
            bool previousOperationResultWasZero = ZF;

            // Perform the operation using our host CPU
            int result = 0;

            switch (operation)
            {
            case ArithmeticOperationType.Addition:
                result = ALULeftBuffer + ALURightBuffer + inputCarry;
                break;

            case ArithmeticOperationType.LeftMinusRight:
                result = ALULeftBuffer - ALURightBuffer - inputCarry;
                break;

            case ArithmeticOperationType.RightMinusLeft:
                result = ALURightBuffer - ALULeftBuffer - inputCarry;
                break;
            }

            // Compute the carries associated with this operation afterwards.
            int carries = (ALULeftBuffer ^ ALURightBuffer ^ result) >> 1;
            // Explanation of the formula above :
            // Consider bit n of the result : if no carry was forwarded from the previous bit, this bit is set if a and b bits are different (0 + 1 = 1 + 0 = 1), reset if a and b bits are the same (0 + 0 = 0, 1 + 1 = 0 + carry for next bit).
            // On the contrary, a carry must have been forwarded from bit n-1 to bit n : if a and b bits are different (a ^ b = 1) and result is reset (result = 0), or if a and b bits are the same (a ^ b = 0) and result is set (result = 1).
            // Formula : carry[n-1 to n] = ((a[n] ^ b[n] == 1 && result[n] == 0) || (a[n] ^ b[n] == 0 & result[n] == 1)).
            // Simplification : carry[n-1 to n] = a[n] ^ b[n] ^ result[n].
            // In the variable "carries" below, when bit n is set  it means that there was a carry from bit n to bit n+1 during the addition.

            // Truncate the result to 8 bits
            byte truncResult = (byte)result;

            // and send it to the internal data bus.
            InternalDataBus = truncResult;

            // Compute flags

            // Prepare a binary mask to preserve the flags which are not affected by the current operation
            byte notAffectedFlagsMask = 0;

            if (!withOutputCarry)
            {
                notAffectedFlagsMask |= B00000001;
            }
            if (!withSZPFlags)
            {
                notAffectedFlagsMask |= B11000100;
            }
            // Preserve all flags which are not affected by the current operation
            F = (byte)(F & notAffectedFlagsMask);

            if (withSZPFlags)
            {
                // Flags bit 7 - SF flag - Set if the 2-complement value is negative. It's simply a copy of the most signifcant bit of the result.
                F |= (byte)(truncResult & B10000000);

                // Flags bit 6 - ZF flag Set if the result is zero.
                if (truncResult == 0 && (!with16bitsZFlag || previousOperationResultWasZero))
                {
                    F |= B01000000;
                }

                // Flags bit 2 - VF flag 2-compliment signed overflow : set if 2-compliment value doesn't fit in the register.
                F |= (byte)(((carries & B10000000) >> 5) ^ ((carries & B01000000) >> 4));
                // Explanation of the formula above :
                // The idea behind setting the overflow flag is simple. Suppose you sign-extend your 8-bit signed integers to 9 bits, that is, just copy the 7th bit to an extra, 8th bit. An overflow/underflow will occur if the 9-bit sum/difference of these 9-bit signed integers has different values in bits 7 and 8, meaning that the addition/subtraction has lost the result's sign in the 7th bit and used it for the result's magnitude, or, in other words, the 8 bits can't accommodate the sign bit and such a large magnitude.
                // Now, bit 7 of the result can differ from the imaginary sign bit 8 if and only if the carry into bit 7 and the carry into bit 8 (=carry out of bit 7) are different. That's because we start with the addends having bit 7=bit 8 and only different carry-ins into them can affect them in the result in different ways.
                // So overflow flag = carry-out flag XOR carry from bit 6 into bit 7.
            }

            // Undocumented - Flags bits 5 and 3
            if (!withComparisonYXFlags)
            {
                // Flags bit 5 - YF flag - A copy of bit 5 of the result.
                // Flags bit 3 - XF flag - A copy of bit 3 of the result.
                F |= (byte)(truncResult & B00101000);
            }
            else
            {
                // Special case for comparison operation : YF and XF flags are copied from the right operand, not the result.
                F |= (byte)(ALURightBuffer & B00101000);
            }

            // Flags bit 4 - HF flag The half-carry of an addition/subtraction (from bit 3 to 4). Needed for BCD correction with DAA.
            F |= (byte)((carries & B00001000) << 1);

            // Flags bit 1 - NF flag Shows whether the last operation was an addition (0) or a subtraction (1). This information is needed for DAA.
            if (operation != ArithmeticOperationType.Addition)
            {
                F |= B00000010;
            }

            if (withOutputCarry)
            {
                // Flags bit 0 - CF flag The carry flag, set if there was a carry after the most significant bit.
                F |= (byte)((carries & B10000000) >> 7);
            }
        }
        private void DecimalAdjust()
        {
            // In BCD representation, each decimal numeral is represented by a four
            // bit pattern : http://en.wikipedia.org/wiki/Binary-coded_decimal.
            // Numbers between 00 and 99 can thus be encoded in one byte = 2 x 4 bits.
            // Bits :               7654        3210
            // Decimal digits :     highDigit   lowDigit
            // For example, decimal number 85 is encoded as :
            // Bits :               1000        0101
            // Decimal digits :     8           5
            // It can simply be written as hexadecimal number 0x85.

            // This instruction is executed after an arithmetic operation,
            // addition (if flag NF = 0) or a subtraction (if flag NF = 1).
            ArithmeticOperationType previousOperation = ArithmeticOperationType.Addition;

            if (NF == true)
            {
                previousOperation = ArithmeticOperationType.LeftMinusRight;
            }

            // This instruction computes the value to add or subtract into ALURightBuffer,
            // to adjust the accumulator after a BCD operation (addition or subtraction).

            // The result of adding idependently each one of the decimal digits
            // of the two initial operands can be found by taking into account
            // the half carry (carry between bits 3 and 4) and the full carry
            // (between bit 7 and the next byte) produced by the operation.
            int  resultAfterOperation        = ALULeftBuffer;
            bool halfCarrySetDuringOperation = HF;
            bool fullCarrySetDuringOperation = CF;

            // If the addition of the first 4 bits gave a result
            // between 0 and 9, then no correction is necessary.
            // Example 1 : 0x4 + 0x5 = 0x9, the result is correct.

            int lowNibbleAfterOperation = resultAfterOperation & B00001111;
            int correction = 0;

            // But if the addition / substraction of the first 4 bits gave a result
            // at least equal to ten (between A and F or with a half carry), then we
            // must translate the number from base 16 (4 bits) to base 10, and produce
            // a half carry to increment the next digit in base 10 if necessary.
            // To do this, we just have to add 6 to the result (2^4 - 10).
            // Example 2 : 0x6 + 0x7 = 0xD, adjust with +6 : 0xD + 6 = 0x13.
            // Example 3 : 0x8 + 0x9 = 0x11, adjust with +6 : 0x11 + 6 = 0x17.
            // After the correction, a new half carry will be propagated to the
            // higher 4 bits only if the value of the low nibble is between
            // 0xA and 0xF.
            if (lowNibbleAfterOperation > 0x09 || halfCarrySetDuringOperation)
            {
                correction = 6;
            }

            // Apply the same kind of correction to the higher digit
            if (resultAfterOperation > 0x99 || fullCarrySetDuringOperation)
            {
                correction += 0x60;
            }

            // Load into ALURightBuffer the correction to add or subtract
            ALURightBuffer = (byte)correction;

            // Execute the operation to adjust the result
            ExecuteArithmeticOperation(previousOperation, false, true, false, true, false);

            // For arithmetic operations, the flag at nit 2 indicates an Overflow condition
            // But with DAA instruction, is is used to indicate the resulting parity is Even
            // The number of 1 bits in a byte are counted.
            // If the total is Odd, ODD parity is flagged (P = 0).
            // If the total is Even, EVEN parity is flagged (P = 1).
            PF = numberOfBitsInByteParityTable[InternalDataBus];

            // Adjust the carry flag to take both operation + correction into account
            CF = resultAfterOperation > 0x99 | fullCarrySetDuringOperation;

            if (TraceMicroInstructions)
            {
                TraceMicroInstruction(new MicroInstruction(Z80MicroInstructionTypes.ArithmeticOperationDecimalAdjust));
            }
        }
Exemple #5
0
 public ArithmeticOperation(int coefficient, ArithmeticOperationType type)
 {
     _coefficient = coefficient;
     _type        = type;
 }