Say goodbye to keeping track of the width of signed numbers! Say goodbye to sign extension! Say hello to negabinary!
Negabinary is base -2 (yes, negative two!) -- negative bases indeed exist! Bit 0 of a binary number is the ones digit, bit 1 is the twos digit, bit 2 is the fours digit, bit 3 is the eights digits, and so on. Likewise, a decimal number has a ones digit, a tens digit, a hundreds digit, and so forth. Generalizing this, for base b, there is a b^0 digit, a b^1 digit, a b^2 digit, a b^3 digit, etc. Thus for negabinary, there is a 1 digit, a -2 digit, a 4 digit, a -8 digit, etc. So the first few negabinary numbers are:
Code:
0 0
1 1
10 -2
11 -1
100 4
101 5
110 2
In other words, binary 110 is 1 * 4 + 1 * 2 + 0 * 1 = 6, decimal 110 is 1 * 100 + 1 * 10 + 0 * 1 = 110, and negabinary 110 is 1 * 4 + 1 * -2 + 0 * 1 = 2.
There is only one other thing to be aware of. With binary arithmetic, there are two possible values for a carry: 0 and 1. With negabinary, a carry has three possible values: -1, 0, and 1.
Interestingly, an LSR is a negabinary divide by -2, and the least significant digit (i.e. the even/odd status of negabinary number) will be in the C flag. Similarly, an ASL is a negabinary multiply by -2.
Now there is support for other operations! Four routines are provided: convert negabinary to binary, convert binary to negabinary, add, and subtract. They use only one byte of temporary storage (TMP).
Rather than use a pair of 6502 flags to hold the three possible values of a negabinary carry, in these routines, the Y register is used for the negabinary carry. In fact, the Y register holds a negated carry, since the ADD and SUB routines subtract the negabinary carry rather than add it (as ADC and SBC do for binary arithmetic) -- subtracting the negabinary carry simplified the implementation slightly.
In Y, -1 is represented as $FF, which is, of course, two's complement, not negabinary (where -1 is $03). Hey, nobody promised you artistic integrity!
By the way, after an ASL, the C flag contains the negabinary carry; the corresponding values of Y are 0 (for C flag clear) and -1 (for C flag set). One way to implement this is:
Code:
ASL
LDY #0
BCC L1
DEY
L1
The four negabinary routines are:
NEG2BIN converts an 8-bit negabinary number to a (signed) binary number.
BIN2NEG converts a (signed) 8-bit binary number to a negabinary number.
ADD adds a pair of negabinary numbers: the accumulator and a zero page location (specified by the X register). The formula is A+M-C; again, note that unlike the formula for ADC (A+M+C), the carry is subtracted, not added.
SUB subtracts a pair of negabinary numbers: the accumulator and a zero page location (specified by the X register). The formula is A-M-C.
Because the ADD and SUB return carry (out) in the Y register, they can be used to extend negabinary calculations beyond 8-bits in the same way that the ADC and SBC instructions can extend binary calculations, e.g.:
Code:
LDY #0 ;"clear" negabinary carry
LDA NUMBER1L
LDX #NUMBER2L
JSR ADD
STA NUMBER3L
LDA NUMBER1H
LDX #NUMBER2H ;or INX if NUMBER2L and NUMBER2H are consecutive locations
JSR ADD
STA NUMBER3H
Likewise, multiply and divide by -2 (ASL and LSR) can also be extended.
For more information on negabinary, see:
http://en.wikipedia.org/wiki/NegabinaryCode:
TMP DS 1
; Convert 8-bit negabinary number to 9-bit (two's complement) binary
; number; the high bit (bit 8) is returned in the carry flag
;
; Enter with:
; a = negabinary number = -170 to 85
;
; Returns:
; C flag = 1, A = $56 to $FF when a = -170 to -1
; C flag = 0, A = $00 to $55 when a = 0 to 85
; Z flag = 1 when A = $00, Z flag = 0 otherwise
;
NEG2BIN
PHA
AND #$AA
STA TMP
PLA
AND #$55
SEC
SBC TMP
CMP #$56
ORA #0
RTS
; Convert 8-bit (two's complement) binary number to 8-bit negabinary
; number (accumulator) and carry (Y register)
;
; Enter with:
; a = binary number = -128 to 127
;
; Returns:
; Y = negabinary carry = 0, A = -128 to 85 when a = -128 to 85
; Y = negabinary carry = -1, A = -170 to -129 when a = 86 to 127
;
BIN2NEG
LDY #8
BPL B2 ;always
B1 EOR #$FF
CLC
ADC #1
B2 CMP #$80
ROR
ROR TMP
DEY
BNE B1
TAY
LDA TMP
RTS
; Negabinary add with carry
;
; Enter with:
; a = accumulator in
; y = negabinary carry in = -1 ($FF) to 1
;
; Returns:
; A = a + 0,X - y
; Y = negabinary carry out = -1 to 1
;
ADD
LSR
STA TMP
TYA
LDY #8
A1 EOR #$FF
ADC #1
LSR 0,X
ADC #0
CMP #$80
ROR
ROR TMP
DEY
BNE A1
TAY
LDA TMP
RTS
; Negabinary subtract with carry
;
; Enter with:
; a = accumulator in
; y = negabinary carry in = -1 ($FF) to 1
;
; Returns:
; A = a - 0,X - y
; Y = negabinary carry out = -1 to 1
;
SUB
STA TMP
TYA
LDY #8
LSR 0,X
S1 ADC #0
EOR #$FF
LSR TMP
ADC #1
CMP #$80
ROR
ROR 0,X
DEY
BNE S1
TAY
LDA 0,X
RTS
Incidentally, BIN2NEG can be implemented in terms of SUB as follows:
Code:
BIN2NEG
PHA
AND #$2A
STA 0,X
PLA
AND #$D5
LDY #0
JMP SUB