Oscar/Mike:
Let's try this again. I meant to post a response to this thread a few days ago but work intervened. I wanted to offer up the model that I used to validate the BCD operation of my 65C02 cores. It is in Verilog, but should be easily translated into C/C++.
The model supports BCD addition/subtraction and generates the digit adjustments as needed to pass Klaus' BCD tests. I've posted the arithmetic unit model below, and I'll provide some commentary below the source in the hope that you may be able to use its algorithm in your project:
Code:
////////////////////////////////////////////////////////////////////////////////
//
// Test Adder Unit (AU)
//
// Task AU_Model provides a logical model of the binary/decimal mode adder that
// is implemented in the UUT. Its purpose is not to functionally represent
// the implementation in the UUT, but to logically compute the expected
// outputs (sum and status) for the various operations that the UUT adder
// is expected to perform.
task AU_Model;
input iOp;
input [7:0] iA, iB;
input [7:0] iPSW;
input [7:0] iMsk;
output [7:0] oSum;
output [7:0] oPSW;
reg D, Ci;
reg [4:0] LSN, MSN;
reg [7:0] A, B, ALU;
reg C7, C3;
reg N, V, Z, C;
begin
D = iPSW[3];
Ci = iPSW[0];
A = iA;
B = ((iOp) ? ~iB : iB);
if(D) begin // Decimal Mode Operations
if(iOp) begin // Decimal Mode Subtraction
LSN[4:0] = A[3:0] + B[3:0] + Ci;
C3 = LSN[4] & ~(LSN[3] & (LSN[2] | LSN[1]));
ALU[3:0] = ((C3) ? (LSN[3:0] + 0) : (LSN[3:0] + 10));
MSN[4:0] = A[7:4] + B[7:4] + C3;
C7 = MSN[4] & ~(MSN[3] & (MSN[2] | MSN[1]));
ALU[7:4] = ((C7) ? (MSN[3:0] + 0) : (MSN[3:0] + 10));
end else begin // Decimal Mode Addition
LSN[4:0] = A[3:0] + B[3:0] + Ci;
C3 = LSN[4] | (LSN[3] & (LSN[2] | LSN[1]));
ALU[3:0] = ((C3) ? (LSN[3:0] + 6) : (LSN[3:0] + 0));
MSN[4:0] = A[7:4] + B[7:4] + C3;
C7 = MSN[4] | (MSN[3] & (MSN[2] | MSN[1]));
ALU[7:4] = ((C7) ? (MSN[3:0] + 6) : (MSN[3:0] + 0));
end
N = ALU[7]; V = ((Op) ? ~C7 : C7); Z = ~|ALU; C = C7;
end else begin // Binary Mode Operations
LSN[4:0] = A[3:0] + B[3:0] + Ci;
C3 = LSN[4];
ALU[3:0] = LSN[3:0];
MSN[3:0] = A[6:4] + B[6:4] + C3;
MSN[4] = (MSN[3] & (A[7] ^ B[7]) | (A[7] & B[7]));
C7 = MSN[4];
ALU[7:4] = {A[7] ^ B[7] ^ MSN[3], MSN[2:0]};
N = ALU[7]; V = (MSN[4] ^ MSN[3]); Z = ~|ALU; C = C7;
end
oSum = ALU;
oPSW = ((iPSW & ~iMsk) | ({N, V, 4'b0, Z, C} & iMsk));
end
endtask;
A Verilog task is equivalent to a Pascal procedure. Verilog's syntax is a bit primitive, so the input/output statements at the top provide an ordered list of the task's parameter list. Following that is a declaration of a number of local variables.
The operation of the module is controlled by the iOp input: 1 - subtraction; 0 - addition. The left input operand, iA, is assigned to the local variable A, and the right input operand, iB, is assigned to B. iB is directly assigned for addition operations and the complement of iB is assigned to B when a subtraction is to take place. The D flag and the C flag are extracted from the input PSW. The D flag is used to select one of two groups of equations: the BCD group and the binary group. The C flag will be added into the sum/difference.
A five bit sum is performed for the lower and upper nibbles. They are performed in sequence in order to generate and preserve the nibble carries, C3 (low nibble) and C7 (high nibble). The unadjusted sums are maintained in the variables LSN (least significant nibble) and MSN (most significant nibble).
The key to the implementation are the generation of C3 and C7. These signals are formed by the binary mode carries (LSN[4] and MSN[4]), plus an equation that determines if the nibble value is greater than 9. Notice that for SBC the adjustment factor applied to each nibble, if required, is 10, and that for ADC, the adjustment factor applied to each nibble, ir required, is 6. Also notice in that the application of a nibble adjustment is complementary between subtraction and addition.
Don't pay any mind to the two values that assigned the N and V flags for BCD mode operations. From my recollection, Klaus' test programs doesn't try to test and enforce any specific behavior on these two flags for BCD mode operations. The C and Z flags are set appropriately, but the values that I assign to N and V are just those that I considered appropriate. N is set according the value of the most significant bit as it would in a two's complement binary mode operation. V is set in BCD mode if there is a carry out, which means that it is essentially the same as the C flag.