Binary coded decimal: Difference between revisions

Content added Content deleted
(→‎{{header|PL/M}}: Show the same 12-digit arithmetic as the Algol 68 and Algol W samples)
m (syntax highlighting fixup automation)
Line 18: Line 18:
The 6502 is a bit different in that it has a special operating mode where all addition and subtraction is handled as binary-coded decimal. Like the 68000, this must be invoked ahead of time, rather than using the Intel method of doing the math normally and then correcting it after the fact. (This special operating mode won't work on the aforementioned Ricoh 2A03, which performs math in "normal" mode even if the decimal flag is set.)
The 6502 is a bit different in that it has a special operating mode where all addition and subtraction is handled as binary-coded decimal. Like the 68000, this must be invoked ahead of time, rather than using the Intel method of doing the math normally and then correcting it after the fact. (This special operating mode won't work on the aforementioned Ricoh 2A03, which performs math in "normal" mode even if the decimal flag is set.)


<lang 6502asm>sed ;set decimal flag; now all math is BCD
<syntaxhighlight lang=6502asm>sed ;set decimal flag; now all math is BCD
lda #$19
lda #$19
clc
clc
Line 46: Line 46:
jsr PrintHex
jsr PrintHex
jsr NewLine
jsr NewLine
rts ;return to basic</lang>
rts ;return to basic</syntaxhighlight>
{{out}}
{{out}}
<pre>20
<pre>20
Line 53: Line 53:
=={{header|68000 Assembly}}==
=={{header|68000 Assembly}}==
The 68000 has special mathematics commands for binary-coded decimal. However, they only work at byte length, and cannot use immediate operands. Even adding by 1 this way requires you to load 1 into a register first.
The 68000 has special mathematics commands for binary-coded decimal. However, they only work at byte length, and cannot use immediate operands. Even adding by 1 this way requires you to load 1 into a register first.
<lang 68000devpac> MOVEQ #$19,D0
<syntaxhighlight lang=68000devpac> MOVEQ #$19,D0
MOVEQ #1,D1
MOVEQ #1,D1
MOVEQ #0,D2
MOVEQ #0,D2
Line 77: Line 77:
JSR PrintHex
JSR PrintHex


jmp *</lang>
jmp *</syntaxhighlight>
{{out}}
{{out}}
<pre>20
<pre>20
Line 84: Line 84:
=={{header|ALGOL 68}}==
=={{header|ALGOL 68}}==
Algol 68 does not have BCD as standard. This sample implements 2-digit unsigned packed decimal numbers, similar to the [[#PL/M|PL/M]] sample. The 2-digit numbers are then used to provide addition/subtraction of larger numbers.
Algol 68 does not have BCD as standard. This sample implements 2-digit unsigned packed decimal numbers, similar to the [[#PL/M|PL/M]] sample. The 2-digit numbers are then used to provide addition/subtraction of larger numbers.
<lang algol68>BEGIN # implements packed BCD arithmetic #
<syntaxhighlight lang=algol68>BEGIN # implements packed BCD arithmetic #
INT x99 = ( 9 * 16 ) + 9; # maximum unsigned 2-digit BCD value #
INT x99 = ( 9 * 16 ) + 9; # maximum unsigned 2-digit BCD value #
# structure to hold BCD values #
# structure to hold BCD values #
Line 204: Line 204:
OD
OD


END</lang>
END</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 235: Line 235:
=={{header|ALGOL W}}==
=={{header|ALGOL W}}==
{{Trans|ALGOL 68}}
{{Trans|ALGOL 68}}
<lang pascal>begin % implements packed BCD arithmetic %
<syntaxhighlight lang=pascal>begin % implements packed BCD arithmetic %
integer X99; % maximum unsigned 2-digit BCD value %
integer X99; % maximum unsigned 2-digit BCD value %
% structure to hold BCD values %
% structure to hold BCD values %
Line 370: Line 370:
end
end


end.</lang>
end.</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 401: Line 401:
=={{header|Forth}}==
=={{header|Forth}}==
This code implements direct BCD arithmetic using notes from Douglas Jones from the University of Iowa: https://homepage.cs.uiowa.edu/~jones/bcd/bcd.html#packed
This code implements direct BCD arithmetic using notes from Douglas Jones from the University of Iowa: https://homepage.cs.uiowa.edu/~jones/bcd/bcd.html#packed
<lang Forth>
<syntaxhighlight lang=Forth>
\ add two 15 digit bcd numbers
\ add two 15 digit bcd numbers
\
\
Line 419: Line 419:


: bcd- bcdneg bcd+ ;
: bcd- bcdneg bcd+ ;
</syntaxhighlight>
</lang>
{{Out}}
{{Out}}
<pre>
<pre>
Line 435: Line 435:
Here, we represent hexadecimal numbers using J's constant notation, and to demonstrate bcd we generate results in that representation:
Here, we represent hexadecimal numbers using J's constant notation, and to demonstrate bcd we generate results in that representation:


<lang J> bcd=: &.((10 #. 16 #.inv ". ::]) :. ('16b',16 hfd@#. 10 #.inv ]))
<syntaxhighlight lang=J> bcd=: &.((10 #. 16 #.inv ". ::]) :. ('16b',16 hfd@#. 10 #.inv ]))
16b19 +bcd 1
16b19 +bcd 1
16b20
16b20
Line 443: Line 443:
16b100
16b100
(16b99 +bcd 1) -bcd 1
(16b99 +bcd 1) -bcd 1
16b99</lang>
16b99</syntaxhighlight>


Note that we're actually using a hex representation as an intermediate result here. Technically, though, sticking with built in arithmetic and formatting as decimal, but gluing the '16b' prefix onto the formatted result would have been more efficient. And that says a lot about bcd representation. (The value of bcd is not efficiency, but how it handles edge cases. Consider the [https://en.wikipedia.org/wiki/IEEE_754#Decimal decimal IEEE 754] format as an example where this might be considered significant. There are other ways to achieve those edge cases -- bcd happens to be relevant when building the mechanisms into hardware.)
Note that we're actually using a hex representation as an intermediate result here. Technically, though, sticking with built in arithmetic and formatting as decimal, but gluing the '16b' prefix onto the formatted result would have been more efficient. And that says a lot about bcd representation. (The value of bcd is not efficiency, but how it handles edge cases. Consider the [https://en.wikipedia.org/wiki/IEEE_754#Decimal decimal IEEE 754] format as an example where this might be considered significant. There are other ways to achieve those edge cases -- bcd happens to be relevant when building the mechanisms into hardware.)
Line 449: Line 449:
For reference, here are decimal and binary representations of the above numbers:
For reference, here are decimal and binary representations of the above numbers:


<lang J> (":,_16{.' '-.~'2b',":@#:) 16b19
<syntaxhighlight lang=J> (":,_16{.' '-.~'2b',":@#:) 16b19
25 2b11001
25 2b11001
(":,_16{.' '-.~'2b',":@#:) 16b20
(":,_16{.' '-.~'2b',":@#:) 16b20
Line 463: Line 463:
2b11001
2b11001
25
25
NB. ...</lang>
NB. ...</syntaxhighlight>


=={{header|Julia}}==
=={{header|Julia}}==
Handles negative and floating point numbers (but avoid BigFloats due to very long decimal places from binary to decimal conversion).
Handles negative and floating point numbers (but avoid BigFloats due to very long decimal places from binary to decimal conversion).
<lang ruby>const nibs = [0b0, 0b1, 0b10, 0b11, 0b100, 0b101, 0b110, 0b111, 0b1000, 0b1001]
<syntaxhighlight lang=ruby>const nibs = [0b0, 0b1, 0b10, 0b11, 0b100, 0b101, 0b110, 0b111, 0b1000, 0b1001]


"""
"""
Line 561: Line 561:
println("BCD 99 ($(bcd_encode(99)[1])) + BCD 1 ($(bcd_encode(1))[1]) = BCD 100 " *
println("BCD 99 ($(bcd_encode(99)[1])) + BCD 1 ($(bcd_encode(1))[1]) = BCD 100 " *
"($(bcd_encode(bcd_decode(bcd_encode(99)...) + bcd_decode(bcd_encode(1)...))))")
"($(bcd_encode(bcd_decode(bcd_encode(99)...) + bcd_decode(bcd_encode(1)...))))")
</lang>{{out}}
</syntaxhighlight>{{out}}
<pre>
<pre>
1 encoded is (UInt8[0x01], 1), decoded is 1
1 encoded is (UInt8[0x01], 1), decoded is 1
Line 581: Line 581:
==={{header|Free Pascal}}===
==={{header|Free Pascal}}===
There exist a special unit for BCD, even with fractions.Obvious for Delphi compatibility.
There exist a special unit for BCD, even with fractions.Obvious for Delphi compatibility.
<lang pascal>program CheckBCD;
<syntaxhighlight lang=pascal>program CheckBCD;
// See https://wiki.freepascal.org/BcdUnit
// See https://wiki.freepascal.org/BcdUnit
{$IFDEF FPC} {$MODE objFPC}{$ELSE} {$APPTYPE CONSOLE} {$ENDIF}
{$IFDEF FPC} {$MODE objFPC}{$ELSE} {$APPTYPE CONSOLE} {$ENDIF}
Line 611: Line 611:
BcdMultiply(Bcd0,Bcd0,BcdOut);
BcdMultiply(Bcd0,Bcd0,BcdOut);
writeln(BcdToStr(Bcd0),'*',BcdToStr(Bcd0),' =',BcdToStr(BcdOut));
writeln(BcdToStr(Bcd0),'*',BcdToStr(Bcd0),' =',BcdToStr(BcdOut));
end.</lang>
end.</syntaxhighlight>
{{out}}
{{out}}
<pre>19+1 =20
<pre>19+1 =20
Line 623: Line 623:
The FPU maths is all as normal (decimal), it is only the load and store that convert from/to BCD.<br>
The FPU maths is all as normal (decimal), it is only the load and store that convert from/to BCD.<br>
While I supply everything in decimal, you could easily return and pass around the likes of acc and res.
While I supply everything in decimal, you could easily return and pass around the likes of acc and res.
<!--<lang Phix>-->
<!--<syntaxhighlight lang=Phix>-->
<span style="color: #008080;">without</span> <span style="color: #008080;">javascript_semantics</span> <span style="color: #000080;font-style:italic;">-- (not a chance!)</span>
<span style="color: #008080;">without</span> <span style="color: #008080;">javascript_semantics</span> <span style="color: #000080;font-style:italic;">-- (not a chance!)</span>
<span style="color: #7060A8;">requires</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"1.0.2"</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- #ilASM{fbld, fbstp} added</span>
<span style="color: #7060A8;">requires</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"1.0.2"</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- #ilASM{fbld, fbstp} added</span>
Line 666: Line 666:
<span style="color: #000000;">test</span><span style="color: #0000FF;">(</span><span style="color: #000000;">30</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">test</span><span style="color: #0000FF;">(</span><span style="color: #000000;">30</span><span style="color: #0000FF;">,-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">test</span><span style="color: #0000FF;">(</span><span style="color: #000000;">99</span><span style="color: #0000FF;">,+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">test</span><span style="color: #0000FF;">(</span><span style="color: #000000;">99</span><span style="color: #0000FF;">,+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<!--</lang>-->
<!--</syntaxhighlight>-->
{{out}}
{{out}}
<pre>
<pre>
Line 678: Line 678:
The aaa, aas, aam, and aad instructions are also available.
The aaa, aas, aam, and aad instructions are also available.
Same output as above, of course
Same output as above, of course
<!--<lang Phix>-->
<!--<syntaxhighlight lang=Phix>-->
<span style="color: #008080;">without</span> <span style="color: #008080;">javascript_semantics</span> <span style="color: #000080;font-style:italic;">-- (not a chance!)</span>
<span style="color: #008080;">without</span> <span style="color: #008080;">javascript_semantics</span> <span style="color: #000080;font-style:italic;">-- (not a chance!)</span>
<span style="color: #7060A8;">requires</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"1.0.2"</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- #ilASM{aaa, etc} added</span>
<span style="color: #7060A8;">requires</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"1.0.2"</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- #ilASM{aaa, etc} added</span>
Line 705: Line 705:
<span style="color: #000000;">test2</span><span style="color: #0000FF;">(</span><span style="color: #000000;">#30</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'-'</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">test2</span><span style="color: #0000FF;">(</span><span style="color: #000000;">#30</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'-'</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">test2</span><span style="color: #0000FF;">(</span><span style="color: #000000;">#99</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'+'</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">test2</span><span style="color: #0000FF;">(</span><span style="color: #000000;">#99</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'+'</span><span style="color: #0000FF;">)</span>
<!--</lang>-->
<!--</syntaxhighlight>-->


=== hll bit fiddling ===
=== hll bit fiddling ===
With routines to convert between decimal and bcd, same output as above, of course.
With routines to convert between decimal and bcd, same output as above, of course.
No attempt has been made to support fractions or negative numbers...
No attempt has been made to support fractions or negative numbers...
<!--<lang Phix>(phixonline)-->
<!--<syntaxhighlight lang=Phix>(phixonline)-->
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> <span style="color: #000080;font-style:italic;">-- (no requires() needed here)</span>
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> <span style="color: #000080;font-style:italic;">-- (no requires() needed here)</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">bcd_decode</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">bcd</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">bcd_decode</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">bcd</span><span style="color: #0000FF;">)</span>
Line 766: Line 766:
<span style="color: #000000;">test3</span><span style="color: #0000FF;">(</span><span style="color: #000000;">30</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'-'</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">test3</span><span style="color: #0000FF;">(</span><span style="color: #000000;">30</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'-'</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">test3</span><span style="color: #0000FF;">(</span><span style="color: #000000;">99</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'+'</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">test3</span><span style="color: #0000FF;">(</span><span style="color: #000000;">99</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'+'</span><span style="color: #0000FF;">)</span>
<!--</lang>-->
<!--</syntaxhighlight>-->


=={{header|PL/M}}==
=={{header|PL/M}}==
Line 772: Line 772:
The 8080 PL/M compiler supports packed BCD by wrapping the 8080/Z80 DAA instruction with the DEC built in function, demonstrated here. Unfortunately, I couldn't get the first use of DEC to yeild the correct result without first doing a shift operation. Not sure if this is a bug in the program, the compiler or the 8080 emulator or that I'm misunderstanding something...
The 8080 PL/M compiler supports packed BCD by wrapping the 8080/Z80 DAA instruction with the DEC built in function, demonstrated here. Unfortunately, I couldn't get the first use of DEC to yeild the correct result without first doing a shift operation. Not sure if this is a bug in the program, the compiler or the 8080 emulator or that I'm misunderstanding something...
This is basically {{Trans|Z80 Assembly}}
This is basically {{Trans|Z80 Assembly}}
<lang pli>100H: /* DEMONSTRATE PL/M'S BCD HANDLING */
<syntaxhighlight lang=pli>100H: /* DEMONSTRATE PL/M'S BCD HANDLING */


BDOS: PROCEDURE( FN, ARG ); /* CP/M BDOS SYSTEM CALL */
BDOS: PROCEDURE( FN, ARG ); /* CP/M BDOS SYSTEM CALL */
Line 804: Line 804:
CALL PR$BCD( B ); CALL PR$BCD( A ); CALL PR$NL;
CALL PR$BCD( B ); CALL PR$BCD( A ); CALL PR$NL;


EOF</lang>
EOF</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 813: Line 813:


A more complex example, showing how the DEC function can be used to perform unsigned BCD addition and subtraction on arbitrary length BCD numbers.
A more complex example, showing how the DEC function can be used to perform unsigned BCD addition and subtraction on arbitrary length BCD numbers.
<lang pli>100H: /* DEMONSTRATE PL/M'S BCD HANDLING */
<syntaxhighlight lang=pli>100H: /* DEMONSTRATE PL/M'S BCD HANDLING */


BDOS: PROCEDURE( FN, ARG ); /* CP/M BDOS SYSTEM CALL */
BDOS: PROCEDURE( FN, ARG ); /* CP/M BDOS SYSTEM CALL */
Line 888: Line 888:


EOF
EOF
</syntaxhighlight>
</lang>
{{out}}
{{out}}
<pre>
<pre>
Line 915: Line 915:
=={{header|Rust}}==
=={{header|Rust}}==
Based on the Forth implementation re: how to implement BCD arithmetic in software. Uses operator overloading for new BCD type.
Based on the Forth implementation re: how to implement BCD arithmetic in software. Uses operator overloading for new BCD type.
<lang Rust>
<syntaxhighlight lang=Rust>
#[derive(Copy, Clone)]
#[derive(Copy, Clone)]
pub struct Bcd64 {
pub struct Bcd64 {
Line 961: Line 961:
assert_eq!((Bcd64{ bits: 0x99 } + one).bits, 0x100);
assert_eq!((Bcd64{ bits: 0x99 } + one).bits, 0x100);
}
}
</syntaxhighlight>
</lang>
{{Out}}
{{Out}}
For the output, use "cargo test" to run the unit test for this module.
For the output, use "cargo test" to run the unit test for this module.
Line 983: Line 983:


In what follows, the hex prefix '0x' is simply a way of representing BCD literals and has nothing to do with hexadecimal as such.
In what follows, the hex prefix '0x' is simply a way of representing BCD literals and has nothing to do with hexadecimal as such.
<lang ecmascript>import "./check" for Check
<syntaxhighlight lang=ecmascript>import "./check" for Check
import "./math" for Int
import "./math" for Int
import "./str" for Str
import "./str" for Str
Line 1,061: Line 1,061:
}
}
if (packed) System.print()
if (packed) System.print()
}</lang>
}</syntaxhighlight>


{{out}}
{{out}}
Line 1,077: Line 1,077:
The <code>DAA</code> function will convert an 8-bit hexadecimal value to BCD after an addition or subtraction is performed. The algorithm used is actually quite complex, but the Z80's dedicated hardware for it makes it all happen in 4 clock cycles, tied with the fastest instructions the CPU can perform.
The <code>DAA</code> function will convert an 8-bit hexadecimal value to BCD after an addition or subtraction is performed. The algorithm used is actually quite complex, but the Z80's dedicated hardware for it makes it all happen in 4 clock cycles, tied with the fastest instructions the CPU can perform.


<lang z80>
<syntaxhighlight lang=z80>
PrintChar equ &BB5A ;Amstrad CPC kernel's print routine
PrintChar equ &BB5A ;Amstrad CPC kernel's print routine
org &1000
org &1000
Line 1,127: Line 1,127:
add a,&F0
add a,&F0
adc a,&40
adc a,&40
jp PrintChar</lang>
jp PrintChar</syntaxhighlight>
{{out}}
{{out}}
<pre>20
<pre>20