There is no construct for BCD in C, so how would compiler use that instruction? You can still make an assembly section and use the instruction manually.
Compilers also don't support things like enabling/disabling interrupts. All that stuff is supported by the intrinsic functions, that are defined though inline assembly.
EDIT: Also, I don't think there are special BCD instructions in AVR. There is a half-carry flag in the status register, which can be used to accelerate BCD math. But there is nothing compiler can do with that flag.
carry_out = carry;
if (half_carry || (sum & 0x0F) > 0x09) sum += 0x06;
if (carry || sum > 0x9F){sum += 0x60; carry_out |= carry;}
reg BCDadd(reg a, reg b){
reg sum = a + b;
reg sum_c = sum + 0x6666666666666666;
reg carries = ((a ^ b ^ sum_c) >> 4) & 0x1111111111111111; // internal carries
carries |= (reg)(sum_c < a) << 60; // carry from MSB
return sum + carries * 6;
}
There is no construct for BCD in C, so how would compiler use that instruction? You can still make an assembly section and use the instruction manually.
It makes sense to have hardware half-carry if it can feed into a dedicated and fast DAA instruction, but very weird to have it without that! Half carry is I think very easy to synthesize (A ^ B ^ (A+B)) & 0x10 if I haven't screwed up, but actually using it would seem to require a complete matrix of all four cases of whether carry and/or half-carry is set to decide whether to do nothing or add 0x06, 0x60, or 0x66 to get the correct result.
...
It turns out that if you have bigger registers you can do BCD adds quite efficiently without any hardware support at all. For example for a 64 bit machine working with 16 decimal digits in BCD (and assuming that's enough so you don't need carry-in or carry-out):
Some code patterns can be recognized so as the compiler can use BCD instructions, but not necessarily trivial. I have currently no example in mind of a target for which GCC would do this, for instance.
That sort of thing must do wonders for reliability and proper operation.That's why there are optimization flags. Want readability - disable optimization. Optimized code is hard to read sometimes anyway without clever use of instructions. And obviously compiler authors won't do it just for fun, there must be a performance reason.
That sort of thing must do wonders for reliability and proper operation.That's why there are optimization flags. Want readability - disable optimization. Optimized code is hard to read sometimes anyway without clever use of instructions. And obviously compiler authors won't do it just for fun, there must be a performance reason.
Misread "reliability". How that would affect reliability? It is on the compiler to ensure that the final code does what C source says. Who cares how it is achieved?
Misread "reliability". How that would affect reliability? It is on the compiler to ensure that the final code does what C source says. Who cares how it is achieved?
But I do think stateful flags like carry should be retained and that might extend to half-carry which is used to support BCD.
I guess the point of David Hess was not really about the IS having BCD instructions, but implementing calculations that would require being done in BCD for correctness, whereas there has been some projects in which they were just implemented using for instance FP, which is obviously completely wrong. The culprit of course was using FP. BCD can always be implemented "by hand" with pure binary operations. (But with reduced performance compared to FP of course...)
Why do you say that using FP "is obviously completely wrong"?Not SiliconWizard, but: decimals. Exact in BCD, inexact in floating point (unless your radix is a power of 10).
As to AVR, I don't know the IS well enough to tell. I guess a half-carry flag would be better than nothing to implement BCD operations slightly more efficiently, but yes, without some kind of DAA instruction, the benefit would be limited.
;***************************************************************************
;*
;* "BCDadd" - 2-digit packed BCD addition
;*
;* This subroutine adds the two unsigned 2-digit BCD numbers
;* "BCD1" and "BCD2". The result is returned in "BCD1", and the overflow
;* carry in "BCD2".
;*
;* Number of words :21
;* Number of cycles :23/25 (Min/Max)
;* Low registers used :None
;* High registers used :3 (BCD1,BCD2,tmpadd)
;*
;***************************************************************************
;***** Subroutine Register Variables
.def BCD1 =r16 ;BCD input value #1
.def BCD2 =r17 ;BCD input value #2
.def tmpadd =r18 ;temporary register
;***** Code
BCDadd:
ldi tmpadd,6 ;value to be added later
add BCD1,BCD2 ;add the numbers binary
clr BCD2 ;clear BCD carry
brcc add_0 ;if carry not clear
ldi BCD2,1 ; set BCD carry
add_0:
brhs add_1 ;if half carry not set
add BCD1,tmpadd ; add 6 to LSD
brhs add_2 ; if half carry not set (LSD <= 9)
subi BCD1,6 ; restore value
rjmp add_2 ;else
add_1:
add BCD1,tmpadd ; add 6 to LSD
add_2:
brcc add_2a
ldi BCD2,1
add_2a:
swap tmpadd
add BCD1,tmpadd ;add 6 to MSD
brcs add_4 ;if carry not set (MSD <= 9)
sbrs BCD2,0 ; if previous carry not set
subi BCD1,$60 ; restore value
add_3:
ret ;else
add_4:
ldi BCD2,1 ; set BCD carry
ret
adc BCD1,BCD2
daa BDC1
Why do you say that using FP "is obviously completely wrong"?Not SiliconWizard, but: decimals. Exact in BCD, inexact in floating point (unless your radix is a power of 10).
You don't use decimals. You use integers, just as BCD is fundamentally integer, and insert a "." character only when printing.Then, your abstract decimal data type needs to have a separate field for the base power of ten -- which literally makes it a radix-10 floating point.
Why do you say that using FP "is obviously completely wrong"?Not SiliconWizard, but: decimals. Exact in BCD, inexact in floating point (unless your radix is a power of 10).
You don't use decimals. You use integers, just as BCD is fundamentally integer, and insert a "." character only when printing.
It *might* be easier to verify correctness of financial calculations with BCD compared to binaryOne interesting thing would be to poison-values-after-use with an invalid BCD pattern (each nibble ≥10). It is easy to detect (the same as per-digit carry check after addition) before they are used, and the detection will work even if the value was only partially overwritten. (The runtime would hide these, only providing an error (C) or exception (C++) if such are ever detected. Kinda-sorta like IEEE-754 floating-point operations with NANs, for example.)
You don't use decimals. You use integers, just as BCD is fundamentally integer, and insert a "." character only when printing.Then, your abstract decimal data type needs to have a separate field for the base power of ten -- which literally makes it a radix-10 floating point.
AFAIK, financial software uses 2- and 4-decimal numeric types. Mathematically, there is no difference between using an integer type that represents the numeric value in units of 1/100 or 1/10,000 in binary or in BCD, but verification for correctness may be easier when the underlying datatype uses BCD. I would not use BCD at the application programming level, but in an interpreter or runtime I might. (Apologies for the conditionals, but I just don't have experience with financial software to know how important additional verification would be.
OTOH, I don't see the point of using FP if you're strictly using FP as integers. It would require proper care from the developers side to begin with, and wouldn't make much sense unless you target CPUs with much faster FPU than their integer ALU. It's just asking for shooting yourself in the foot, and there's a very large probability this would lead to bugs due to improper use.
Just use integers. OK you were trying to make a point with FP, but frankly this doesn't make much sense in this context IMHO. Developers tempted to use FP for financial applications will usually do this to make their life easier, and we have a few examples of this leading to very bad software. If they have to use FP with even more caution than if they were using integers, that's pretty twisted.
Nice of you to not include the next sentence in that paragraph, "You can obviously have radix-10 fixed point types as well.", so you can say you used radix-10 fixed-point instead; i.e. integer values in units of 10-n of the base currency unit.No.You don't use decimals. You use integers, just as BCD is fundamentally integer, and insert a "." character only when printing.Then, your abstract decimal data type needs to have a separate field for the base power of ten -- which literally makes it a radix-10 floating point.
I absolutely did use FP values to represent money in all that software running on 68020+68882 and up to PowerPC. And, rarely, x86.Explains a lot about the state of our financial system. (Kidding!)