Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

BCD math on 6502

1,087 views
Skip to first unread message

Joseph Rose

unread,
Mar 25, 2000, 3:00:00 AM3/25/00
to
How do the C64's processor and if possible the 65C02 do this? What
instructions can work with BCD? I need the information for my NewCPU
emulator and c64 BCD math conversions.


Gizbern

unread,
Mar 26, 2000, 3:00:00 AM3/26/00
to

Joseph Rose <jor...@pop.gis.net> wrote in message
news:38DD6E2A...@pop.gis.net...

> How do the C64's processor and if possible the 65C02 do this? What
> instructions can work with BCD? I need the information for my NewCPU
> emulator and c64 BCD math conversions.
>

Instructions that use decimal code are: ADC and SBC. To switch to decimal
mode use SED (SEt Decimal) and to back to hexadecimal use CLD (CLear
Decimal).
For example:
In hexadecimal: In decimal:
$09+$01= $0A $10
$04+$08= $0C $12

Decimal is rather useless mode...

Gizbern


Joseph Rose

unread,
Mar 26, 2000, 3:00:00 AM3/26/00
to

Gizbern wrote:

> Joseph Rose <jor...@pop.gis.net> wrote in message
> news:38DD6E2A...@pop.gis.net...
> > How do the C64's processor and if possible the 65C02 do this? What
> > instructions can work with BCD? I need the information for my NewCPU
> > emulator and c64 BCD math conversions.
> >
>
> Instructions that use decimal code are: ADC and SBC. To switch to decimal
> mode use SED (SEt Decimal) and to back to hexadecimal use CLD (CLear
> Decimal).

That's all? I thought there would be more. How is it done in the hardware?
How do I multiply?

>
> For example:
> In hexadecimal: In decimal:
> $09+$01= $0A $10
> $04+$08= $0C $12
>
> Decimal is rather useless mode...
>

No wonder it's not used often.... Of course, binary has more functionality
(256 possibilities per byte, instead of 100) and is easier for the hardware.

>
> Gizbern


Cameron Kaiser

unread,
Mar 26, 2000, 3:00:00 AM3/26/00
to
"Gizbern" <bod...@poczta.onet.pl> writes:

>Decimal is rather useless mode...

Quite the contrary. Decimal mode is very useful for easily dealing with
floating point (in fact, on most microcomputer BASICs with the exception
of the Commodores, BCD is how they're handled) and for prepping figures
for display. You can take each nybble and add 48, and print directly.

--
Cameron Kaiser * cka...@stockholm.ptloma.edu * posting with a Commodore 128
personal page: http://www.armory.com/~spectre/
** Computer Workshops: games, productivity software and more for C64/128! **
** http://www.armory.com/~spectre/cwi/ **

Matthew Montchalin

unread,
Mar 26, 2000, 3:00:00 AM3/26/00
to
On 26 Mar 2000, Cameron Kaiser wrote:
|"Gizbern" <bod...@poczta.onet.pl> writes:
|
|>Decimal is rather useless mode...
|
|Quite the contrary. Decimal mode is very useful for easily dealing with
|floating point (in fact, on most microcomputer BASICs with the exception
|of the Commodores, BCD is how they're handled) and for prepping figures
|for display. You can take each nybble and add 48, and print directly.

Um, yes, or you could ORA #$30 and store each nybble directly into screen
memory. :) Of course, for the desirability of zero suppression, you'll
still want to check for that sort of thing.

For instance, if you have a four byte value all in BCD, not only can you
represent any number from 0 to 99,999,999 but you might also want to set
aside a flag for those times when a comma ($2c) needs to be stored into
screen memory.


Joseph Rose

unread,
Mar 26, 2000, 3:00:00 AM3/26/00
to

Cameron Kaiser wrote:

> "Gizbern" <bod...@poczta.onet.pl> writes:
>
> >Decimal is rather useless mode...
>
> Quite the contrary. Decimal mode is very useful for easily dealing with
> floating point (in fact, on most microcomputer BASICs with the exception
> of the Commodores, BCD is how they're handled) and for prepping figures
> for display. You can take each nybble and add 48, and print directly.
>

I take that back. So, how do I multiply? I can multiply binary integers by
shifting and adding the first number (name?!) to the answer at 1-bit positions in
the multiplier, but how do I handle the shifts and single-digit multiplications
(i.e. 6*7)in BCD?

Cameron Kaiser

unread,
Mar 26, 2000, 3:00:00 AM3/26/00
to
Joseph Rose <jor...@pop.gis.net> writes:

>I take that back. So, how do I multiply? I can multiply binary integers by
>shifting and adding the first number (name?!) to the answer at 1-bit
>positions in
>the multiplier, but how do I handle the shifts and single-digit
>multiplications (i.e. 6*7)in BCD?

Call me old-fashioned, but I would just do repetitive additions. :-)

But, you can do multiplications by ten simply by shifting left four (and
division by ten the same way). That is unfortunately the only optimisation
I can think off the top of my head, but I'm sure John Iannetta has been
reading this and can think of many others. :-)

Cameron Kaiser

unread,
Mar 27, 2000, 3:00:00 AM3/27/00
to
Cameron Kaiser <cka...@stockholm.ptloma.edu> writes:

>But, you can do multiplications by ten simply by shifting left four (and
>division by ten the same way).

Obviously, division is shifting right, not left ... ;-)

Jim Butterfield

unread,
Mar 27, 2000, 3:00:00 AM3/27/00
to
Joseph Rose (jor...@pop.gis.net) wrote:


: Gizbern wrote:

: > Joseph Rose <jor...@pop.gis.net> wrote in message
: > news:38DD6E2A...@pop.gis.net...
: > > How do the C64's processor and if possible the 65C02 do this? What
: > > instructions can work with BCD? I need the information for my NewCPU
: > > emulator and c64 BCD math conversions.
: > >
: >
: > Instructions that use decimal code are: ADC and SBC. To switch to decimal
: > mode use SED (SEt Decimal) and to back to hexadecimal use CLD (CLear
: > Decimal).

: That's all? I thought there would be more. How is it done in the hardware?
: How do I multiply?

Part of the hardware is called an "ALU" (arithmetic logic unit) .. this
does all the arithmetic, as well as logical operations. The ALU is
internally structured to do addition either way: binary or BCD; it knows
which to do by means of the decimal flag. It's all internal within the
CPU chip.

In binary or in BCD, you multiply by working through a loop of some
sort. With binary, it's most commonly done by shifting each bit of the
multiplier out and adding if appropriate. You can do almost exactly the
same thing if you're multiplying a BCD number by a binary one .. shift
the binary, add the BCD. To multiply BCD by BCD, you either convert one
of the numbers, or extract each BCD digit from the multiplier and process
things that way. Serious BCD math coding often breaks the number into
one-nybble-per-byte before doing the math, and then repacks the finished
number.

: >
: > For example:


: > In hexadecimal: In decimal:
: > $09+$01= $0A $10
: > $04+$08= $0C $12

: >
: > Decimal is rather useless mode...

: No wonder it's not used often.... Of course, binary has more functionality


: (256 possibilities per byte, instead of 100) and is easier for the hardware.

When you have numbers that exist principally for display or printing,
keeping them in BCD mode can save you a lot of converting. Typically,
high scores on games and other simple counts fit well into BCD mode.

There's also a secret bonus: BCD mode can be quite useful in doing
conversions from binary to BCD. I won't give the code here (it's well
known to most 6502 coders), but the trick is this: adding a number to
itself is the same as a left-shift in binary .. but in decimal mode, it
will fit a binary bit neatly into a decimal value.

It's worth noting that some kinds of computation are more "natural" if
performed in BCD. This is common in fiscal calculations: say, you have
a dollars-and-cents value, and you need to take a percentage. Although
there are work-arounds in binary, dollars-and-cents and percentages don't
fit comfortably - 1/100 is an endless fraction in binary notation, as
contrasted to an exact value in BCD.

--Jim


Rene van Belzen

unread,
Mar 28, 2000, 3:00:00 AM3/28/00
to
On Sun, 26 Mar 2000 15:41:55 -0500, Joseph Rose <jor...@pop.gis.net>
wrote:

>
>Gizbern wrote:
>
>> Joseph Rose <jor...@pop.gis.net> wrote in message
>> news:38DD6E2A...@pop.gis.net...
>> > How do the C64's processor and if possible the 65C02 do this? What
>> > instructions can work with BCD? I need the information for my NewCPU
>> > emulator and c64 BCD math conversions.
>> >
>>
>> Instructions that use decimal code are: ADC and SBC. To switch to decimal
>> mode use SED (SEt Decimal) and to back to hexadecimal use CLD (CLear
>> Decimal).
>
>That's all? I thought there would be more. How is it done in the hardware?
>How do I multiply?
>

Yes, it is done in hardware.

You are probably confused because the processor you are used to, uses
a kind of "decimal adjust accumulator" instruction, which is common in
Intel CPUs (clones). However in Rockwell and CMOS CPUs no microcode is
used, only hardwiring. This is why the clock frequency of 65xx CPUs is
so much lower than the 808x and Z80 CPUs. The drawback is less
instructions and less internal registers.

Before you can multiply, you have to define your BCD format. Remember,
BCD is not a fixed length number format, like integer
(16-bit/32-bit/64-bit) and real (4-byte/5-byte/8-byte/16-byte) are.
Furthermore, you have to define a method to note the decimal place and
the sign. For scientific notation, you could even introduce an
exponent (with sign, of course). And preferably use "packed BCD",
which uses both the upper and lower 4 bits of a byte, instead of just
the lower 4 bits (lower nibble), in normal BCD.

If you use whole number packed BCD (wihout decimal point or exponent),
your BCD format could look something like this:
nibble 0: (length - 1) -> 2
nibble 1: sign -> 0 (0=plus, other values minus)
nibble 2: digit 3 -> 1
nibble 4: digit 2 -> 6
nibble 5: digit 1 -> 9

This represents the value +169. It can be stored in 3 bytes. -25 can
also be stored in 3 bytes, but uses only 5 nibbles (0-4); the sixth
nibble is undefined.

To multiply, first determine the sign, then use repetitive additions
in decimal mode (switched on by SED, and switched off by CLD), just as
you do in normal multiplications (only shift by 4-bits, instead of by
1-bit). You load the appropriate nibble of the multiplicator, and add
the multiplicand to the result just as many times as the value of the
previously loaded nibble. Don't forget to correct the length of the
result.

If you want, I can device an algorithm and some code. However, this
would take some time, because I have never done this before.

Bye,

Rene.


Rene van Belzen

unread,
Mar 28, 2000, 3:00:00 AM3/28/00
to
On 27 Mar 2000 08:33:11 -0600, Cameron Kaiser
<cka...@stockholm.ptloma.edu> wrote:

>Cameron Kaiser <cka...@stockholm.ptloma.edu> writes:
>
>>But, you can do multiplications by ten simply by shifting left four (and
>>division by ten the same way).
>
>Obviously, division is shifting right, not left ... ;-)
>

Shifting left the multiplicator is possible too, you only have to
shift left the result as well.

123
123 x
------
123 (1 times 123)
.246 (2 times 123), result and multiplicator shifted left
..369 (3 times 123), result and multiplicator shifted left
------
15129

Rene.

John Iannetta

unread,
Mar 28, 2000, 3:00:00 AM3/28/00
to
Joseph Rose <jor...@pop.gis.net> said:

"I take that back. So, how do I multiply? I can multiply binary integers by
shifting and adding the first number (name?!) to the answer at 1-bit positions
in the multiplier, but how do I handle the shifts and single-digit

multiplications (i.e. 6*7)in BCD?".

Multiplication requires a multiplication table; it is rather simple for
binary:

0 X 0 = 0
0 X 1 = 0
1 X 0 = 0
1 X 1 = 1

If the mutiplier bit is "1", you can just sum the multiplicand into the product
register. But a decimal multiplication table (what we called the "timesies"
when I was in school) has 100 entries. Decimal multiplication can be done
using such a table and decimal mode. But I think that Cameron's idea of
repeated addition is a better choice. Here's a routine for the C-64 that will
display the product of two positive integers; the calculation uses decimal
mode. The maximum number of integers in the multiplier is 16; same for the
multiplicand. Each of those is assumed to be in low memory in 8 consecutive
addresses, from 680 to 687 ($02A8 to $02AF) and from 688 to 695 ($02B0 to
$02B7). The digits are packed two to a byte. The most significant digit is in
the high nybble of the byte in 680 and in 688. The least significant digit is
in the byte in 687 and in 695. The product goes into addresses 697 to 712.

$C000 SEI ;
$C001 SED ;
$C002 LDA #$07 ;
$C004 STA $FC ;
$C006 LDX #$09 ;
$C008 LDA #$00 ;
$C00A STA $02B7,X ;
$C00D DEX ;
$C00E BNE $C00A ;
$C010 LDY $FC ;
$C012 LDA $02B0,Y ;
$C015 STA $FB ;
$C017 AND #$0F ;
$C019 TAX ;
$C01A BEQ $C033 ;
$C01C LDY #$07 ;
$C01E CLC ;
$C01F LDA $02A8,Y ;
$C022 ADC $02B9,Y ;
$C025 STA $02B9,Y ;
$C028 DEY ;
$C029 BPL $C01F ;
$C02B BCC $C030 ;
$C02D INC $02B8 ;
$C030 DEX ;
$C031 BNE $C01C ;
$C033 LDY #$04 ;
$C035 LDX #$EF ;
$C037 CLC ;
$C038 ROR $01C9,X ;
$C03B INX ;
$C03C BNE $C038 ;
$C03E DEY ;
$C03F BNE $C035 ;
$C041 LDA $FB ;
$C043 LSR ;
$C044 LSR ;
$C045 LSR ;
$C046 LSR ;
$C047 TAX ;
$C048 BEQ $C061 ;
$C04A LDY #$07 ;
$C04C CLC ;
$C04D LDA $02A8,Y ;
$C050 ADC $02B9,Y ;
$C053 STA $02B9,Y ;
$C056 DEY ;
$C057 BPL $C04D ;
$C059 BCC $C05E ;
$C05B INC $02B8 ;
$C05E DEX ;
$C05F BNE $C04A ;
$C061 LDY #$04 ;
$C063 LDX #$EF ;
$C065 CLC ;
$C066 ROR $01C9,X ;
$C069 INX ;
$C06A BNE $C066 ;
$C06C DEY ;
$C06D BNE $C063 ;
$C06F DEC $FC ;
$C071 BPL $C010 ;
$C073 CLD ;
$C074 CLI ;
$C075 LDX #$F0 ;
$C077 LDY $01C9,X ;
$C07A TYA ;
$C07B LSR ;
$C07C LSR ;
$C07D LSR ;
$C07E LSR ;
$C07F ORA #$30 ;
$C081 JSR $FFD2 ;
$C084 TYA ;
$C085 AND #$0F ;
$C087 ORA #$30 ;
$C089 JSR $FFD2 ;
$C08C INX ;
$C08D BNE $C077 ;
$C08F RTS ;
$C090 BRK ;

The following C-64 BASIC program will write the ML routine to memory, and
then prompt you for two integers. They will be written to the appropriate
addresses, and the ML routine will be called.

>> begin program
10 poke55,.:poke56,160:clr:z=49152
15 s$="000000000000000":k=48
20 fori=ztoi+143:ready:c=c+y:pokei,y
25 next:printchr$(147);:ifc=19317then35
30 print"data statement error":end
35 a$="":print:print"enter multiplicand (16 digits max.)."
40 print:inputa$:iflen(a$)>16then35
45 ifa$=""thenend
50 b$="":print:print"enter multiplier (16 digits max.)."
55 print:inputb$:iflen(b$)>16then50
60 ifb$=""thenend
65 print:a$=left$(s$,16-len(a$))+a$
70 b$=left$(s$,16-len(b$))+b$
75 fori=.to7:h=asc(mid$(a$,2*i+1))-k
80 l=asc(mid$(a$,2*i+2))-k
85 p=asc(mid$(b$,2*i+1))-k
90 q=asc(mid$(b$,2*i+2))-k
95 poke680+i,16*h+l:poke688+i,16*p+q
100 next:print"press <return> to call routine."
105 geta$:ifa$<>chr$(13)then105
110 print:sysz:print:print:goto35
200 data120,248,169,7,133,252,162
210 data9,169,0,157,183,2,202,208
220 data250,164,252,185,176,2,133
230 data251,41,15,170,240,23,160
240 data7,24,185,168,2,121,185,2
250 data153,185,2,136,16,244,144
260 data3,238,184,2,202,208,233,160
270 data4,162,239,24,126,201,1,232
280 data208,250,136,208,244,165,251
290 data74,74,74,74,170,240,23,160
300 data7,24,185,168,2,121,185,2
310 data153,185,2,136,16,244,144
320 data3,238,184,2,202,208,233,160
330 data4,162,239,24,126,201,1,232
340 data208,250,136,208,244,198,252
350 data16,157,216,88,162,240,188
360 data201,1,152,74,74,74,74,9,48
370 data32,210,255,152,41,15,9,48
380 data32,210,255,232,208,232,96
>> end program
-----------------------------------------------------------------------------

--
123 456
789 *0#

If you see a telephone keypad above, you're probably using a Commodore 64.

Joseph Rose

unread,
Mar 28, 2000, 3:00:00 AM3/28/00
to
All I have to do is shift left by 4 and add digit x to itself y times. There
must be a faster way to do the single-digit multiplication, i.e.
(6*7)=(6*4)+(6*2)+(6*1). I forgot to mention divide. Now how do I emulate BCD
math operations?

Pekka "Pihti" Takala

unread,
Mar 31, 2000, 3:00:00 AM3/31/00
to

In this program, the pokes to locations 55 and 56 are not needed since the
program itself is at $c000-$c090. Also, the clr command is not needed.

Instead, change the line 10 as like this:

10 z=49152:ifpeek(49155)=7then35

This line prevents multiple data pokeing to memory, it just checks that if
the code is already there (it assumes that the code is written correctly.
If the program is in memory, the location says 7 when peeked. If not, then
it is poked to memory.

The 55 and 56 pokes are not needed because in standard 64 they are already
correct.

--
-------------------------------------
--- Pekka "Pihti" Takala ---
--- pekka....@pp.inet.fi ---
--- pi...@pp.inet.fi ---
-------------------------------------

Joseph Rose

unread,
Mar 31, 2000, 3:00:00 AM3/31/00
to
How does the 6502 work with BCD numbers? In other words, how do I
emulate BCD calculations?

Matthew Montchalin

unread,
Mar 31, 2000, 3:00:00 AM3/31/00
to
On Fri, 31 Mar 2000, Joseph Rose wrote:

|How does the 6502 work with BCD numbers? In other words, how do I
|emulate BCD calculations?

The processor status register has a "decimal" mode that can be entered
simply by executing the "Set Decimal Mode" instruction:

sed
...
lda #5 sed clc adc #6
...
cld

And at this point the accumulator has #$11 in it.


Matthew Montchalin

unread,
Mar 31, 2000, 3:00:00 AM3/31/00
to
On Sat, 1 Apr 2000, Rene van Belzen wrote:

|And you would have to prevent any maskable interrupts during decimal
|mode, using SEI, and allow them in non-decimal mode, using CLI. You
|don't want the IRQ to use decimal mode as well, do you?

And so long as we are beating dead horses, who amongst us has forgotten
that the 6502 microprocessor upon RESET doesn't have a known state for
the decimal flag? For which reason we find it desirable to clear the
decimal flag early on just for good measure?

Now, next time we decide to write a replacement EPROM for our C-64's,
perhaps we could incorporate this bit into the seed for our random
number generators? ;)

Hmmm.... Come to think of it, maybe I should save the state of the
decimal flag on my 1541 replacement operating system?

For instance,

Reset ; arriving here from the vector at ($fffc)
php pla sta Random_seed
cld
...

Of course, only one bit (um, I think) in the Random_seed would be
useful in this particular situation ...


Matthew Montchalin

unread,
Mar 31, 2000, 3:00:00 AM3/31/00
to

|The processor status register has a "decimal" mode that can be entered
|simply by executing the "Set Decimal Mode" instruction:
|
| sed
| ...
| lda #5 sed clc adc #6
| ...
| cld

That ought to read:

sed
...
lda #5 clc adc #6
...
cld

Lousy editing inserted too many sed's into the previous example.


White Flame (aka David Holz)

unread,
Mar 31, 2000, 3:00:00 AM3/31/00
to
Matthew Montchalin wrote:
> Hmmm.... Come to think of it, maybe I should save the state of the
> decimal flag on my 1541 replacement operating system?

What do you need random numbers for in the 1541? Are you doing SSL
encryption between the 1541 and the C64? You never know exactly where
Big Brother(tm) is hiding... ;)

--
White Flame (aka David Holz)
http://fly.to/theflame

Rene van Belzen

unread,
Apr 1, 2000, 3:00:00 AM4/1/00
to
On Fri, 31 Mar 2000 17:47:27 -0800, Matthew Montchalin
<mmon...@OregonVOS.net> wrote:

>On Fri, 31 Mar 2000, Joseph Rose wrote:
>
>|How does the 6502 work with BCD numbers? In other words, how do I
>|emulate BCD calculations?
>

>The processor status register has a "decimal" mode that can be entered
>simply by executing the "Set Decimal Mode" instruction:
>
> sed
> ...
> lda #5 sed clc adc #6
> ...
> cld
>

>And at this point the accumulator has #$11 in it.
>

And you would have to prevent any maskable interrupts during decimal
mode, using SEI, and allow them in non-decimal mode, using CLI. You
don't want the IRQ to use decimal mode as well, do you?

Rene.

John Iannetta

unread,
Apr 1, 2000, 3:00:00 AM4/1/00
to
Pekka \"Pihti\" Takala <pekka....@pp.inet.fi> said:

"...
...
...


> 10 poke55,.:poke56,160:clr:z=49152

...
...
...


In this program, the pokes to locations 55 and 56 are not needed since the
program itself is at $c000-$c090. Also, the clr command is not needed.

Instead, change the line 10 as like this:

10 z=49152:ifpeek(49155)=7then35

This line prevents multiple data pokeing to memory, it just checks that if
the code is already there (it assumes that the code is written correctly.
If the program is in memory, the location says 7 when peeked. If not, then

it is poked to memory.".

Many C-64 BASIC programs change the numbers in addresses 55 and 56. For
example, a program for displaying a DOODLE! file would probably write 0 to 55
and 92 to 56. That would set the pointer to 23552, to protect the DOODLE!
data. If after using such a program, the user tries to load a program that
extends past that address, he will get an OUT OF MEMORY error when he uses the
first variable. So I start all of my C-64 BASIC programs by setting the
pointer to 40960; the CLR is required to set 51/52 to 55/56. That is my
contribution to my friends in the wonderful world of the Commodore-64.

I think that your suggestion for line 10 could lead to a crash. If the
program is run for the first time, there's a probability of 1/256 that a "7" is
already in address 49155. So the SYS would call a routine that isn't there.
Here's what I sometimes do for a very long ML routine, to avoid writing the
data to memory every time that the program is run:

10 z=1:ifa=.thena=1:fori=49152toi+2000:ready:pokei,y:next
20 poke2053,65:poke55,.:poke56,160:clr

Cameron Kaiser

unread,
Apr 1, 2000, 3:00:00 AM4/1/00
to
Pekka \"Pihti\" Takala <pekka....@pp.inet.fi> writes:

>In this program, the pokes to locations 55 and 56 are not needed since the
>program itself is at $c000-$c090. Also, the clr command is not needed.

Recalling from John's programming practises, they're there in case some
other program has fudged around with BASIC's end-of-memory pointers. Or
you could reset the computer and use your method. Either way ... :-)

Matthew Montchalin

unread,
Apr 1, 2000, 3:00:00 AM4/1/00
to
On Fri, 31 Mar 2000, White Flame (aka David Holz) wrote:
|Matthew Montchalin wrote:
|> Hmmm.... Come to think of it, maybe I should save the state of the
|> decimal flag on my 1541 replacement operating system?
|
|What do you need random numbers for in the 1541? Are you doing SSL
|encryption between the 1541 and the C64? You never know exactly where
|Big Brother(tm) is hiding... ;)

Do you think it would be a waste of space to devote some of the EEPROM
addressing space to code that will support a command like:

10 dim i(256) : rem or maybe it should be dim i(255)
20 open15,8,15
30 open2,8,2,"#0"
40 print#15,"u:rnd" : rem fill up buffer with 256 random bytes
50 for i = 0 to 255
60 get#2,chr$(i)
70 i(i)=asc(chr$(i)) : rem now get them into the i(n) array
80 next
90 close 2
100 close 15

Mike Gregory

unread,
Apr 2, 2000, 4:00:00 AM4/2/00
to
Hi

Why not? I don't think the IRQ uses ADC or SBC. Does it?
They are relatively uncommon instructions.

Mike G


On Sat, 01 Apr 2000 06:58:54 GMT, hur...@xs4all.nl (Rene van Belzen)
wrote:

Rene van Belzen

unread,
Apr 2, 2000, 4:00:00 AM4/2/00
to
Hi,

Sorry, I was mistaken. I should have written:
"We don't want the IRQ to clear our decimal mode, do we?"

In, at least, the C128 the instruction CLD is one of the first
instructions of the IRQ routine. It is located at $FA65 in ROM.

Uncommon or not, IF ADC or SBC are used in (customized) IRQ, things
could go wrong.

Rene.

On Sun, 02 Apr 2000 00:25:52 GMT, mike...@tig.com.au (Mike Gregory)
wrote:

John Iannetta

unread,
Apr 2, 2000, 4:00:00 AM4/2/00
to
mike...@tig.com.au (Mike Gregory) said:

"Why not? I don't think the IRQ uses ADC or SBC. Does it?

They are relatively uncommon instructions.".

The C-64 system interrupt routine doesn't use ADC. But it uses SBC each
jiffy when it updates the jiffy clock (TI$ and TI). After the clock is
incremented, the number $4F1A01 (5184001 jiffies, or 24 hours + 1 jiffy) is
subtracted from the 24-bit count. If carry is set, the count is set to zero.
Which of course means that the jiffy clock loses 1/60 of a second each day.
The consequences of this are obvious. After 591 years, the C-64's jiffy clock
will be one hour early.

But because the subtraction is used to check the carry flag, being in
decimal mode doesn't make any difference. So the interrupt routine doesn't
care whether the 6510 is in binary or in decimal mode. But the CHRGET routine
DOES care. It is called for each byte in a BASIC line, and does two
subtractions each time. When in decimal mode, the routine returns a different
byte from the one in memory.

John

Joseph Rose

unread,
Apr 2, 2000, 4:00:00 AM4/2/00
to
John Iannetta wrote:

> mike...@tig.com.au (Mike Gregory) said:
>
> "Why not? I don't think the IRQ uses ADC or SBC. Does it?
> They are relatively uncommon instructions.".
>
> The C-64 system interrupt routine doesn't use ADC. But it uses SBC each
> jiffy when it updates the jiffy clock (TI$ and TI). After the clock is
> incremented, the number $4F1A01 (5184001 jiffies, or 24 hours + 1 jiffy) is
> subtracted from the 24-bit count. If carry is set, the count is set to zero.
> Which of course means that the jiffy clock loses 1/60 of a second each day.
> The consequences of this are obvious. After 591 years, the C-64's jiffy clock
> will be one hour early.
>

That doesn't matter! Every time you turn on the C64, the clock is set to 0. The
Commodore 64 can't be safely kept on for even four hours. I once broke my
original C64 by keeping it on for extended periods of time. Besides that, the
Commodore 64 didn't even last for 20 years, let alone the 591 years mentioned
above.

>
> But because the subtraction is used to check the carry flag, being in
> decimal mode doesn't make any difference. So the interrupt routine doesn't
> care whether the 6510 is in binary or in decimal mode. But the CHRGET routine
> DOES care. It is called for each byte in a BASIC line, and does two
> subtractions each time. When in decimal mode, the routine returns a different
> byte from the one in memory.
>

So turn it off when you return to BASIC. :)

Mike Gregory

unread,
Apr 3, 2000, 3:00:00 AM4/3/00
to
On 2 Apr 2000 08:23:58 GMT, John Iannetta <73510...@CompuServe.COM>
wrote:


>Which of course means that the jiffy clock loses 1/60 of a second each day.
>The consequences of this are obvious. After 591 years, the C-64's jiffy clock
>will be one hour early.

So it's not as good as a stopped clock which is right twice a day???


> But the CHRGET routine
>DOES care. It is called for each byte in a BASIC line, and does two
>subtractions each time. When in decimal mode, the routine returns a different
>byte from the one in memory.
>

Are we likely to be running these routines and Basic at the same time?

IIRC whenever I've used decimal mode, which would not be frequently, I
have never worried about the IRQ and have never had any probs.!!!

Mike G

J. Robertson

unread,
Apr 3, 2000, 3:00:00 AM4/3/00
to
On Sun, 02 Apr 2000 23:47:09 -0400, Joseph Rose <jor...@pop.gis.net>
wrote:

>John Iannetta wrote:
>
>> mike...@tig.com.au (Mike Gregory) said:
>>
>> "Why not? I don't think the IRQ uses ADC or SBC. Does it?
>> They are relatively uncommon instructions.".
>>
>> The C-64 system interrupt routine doesn't use ADC. But it uses SBC each
>> jiffy when it updates the jiffy clock (TI$ and TI). After the clock is
>> incremented, the number $4F1A01 (5184001 jiffies, or 24 hours + 1 jiffy) is
>> subtracted from the 24-bit count. If carry is set, the count is set to zero.

>> Which of course means that the jiffy clock loses 1/60 of a second each day.
>> The consequences of this are obvious. After 591 years, the C-64's jiffy clock
>> will be one hour early.
>>
>

>That doesn't matter! Every time you turn on the C64, the clock is set to 0. The
>Commodore 64 can't be safely kept on for even four hours. I once broke my

Actually it can...*if* you use a heavy duty power supply (not the
brick one it comes with originally). Otherwise, using a C64 as a BBS
as many have done wouldn't have been possible.

Jason

>original C64 by keeping it on for extended periods of time. Besides that, the
>Commodore 64 didn't even last for 20 years, let alone the 591 years mentioned
>above.

>
>>
>> But because the subtraction is used to check the carry flag, being in
>> decimal mode doesn't make any difference. So the interrupt routine doesn't

>> care whether the 6510 is in binary or in decimal mode. But the CHRGET routine


>> DOES care. It is called for each byte in a BASIC line, and does two
>> subtractions each time. When in decimal mode, the routine returns a different
>> byte from the one in memory.
>>
>

Rene van Belzen

unread,
Apr 3, 2000, 3:00:00 AM4/3/00
to
On Mon, 03 Apr 2000 01:46:53 GMT, mike...@tig.com.au (Mike Gregory)
wrote:

>IIRC whenever I've used decimal mode, which would not be frequently, I


>have never worried about the IRQ and have never had any probs.!!!

That means you have never programmed IRQ on a C128, which switches off
decimal mode in normal IRQ. You would have to rewrite the entire C128
IRQ to avoid the CLD instruction, or simple prevent IRQ by SEI.

Rene.

Marko Mäkelä

unread,
Apr 3, 2000, 3:00:00 AM4/3/00
to
>>>>> "Rene" == Rene van Belzen <hur...@xs4all.nl> writes:

Rene> That means you have never programmed IRQ on a C128, which
Rene> switches off decimal mode in normal IRQ. You would have to
Rene> rewrite the entire C128 IRQ to avoid the CLD instruction, or
Rene> simple prevent IRQ by SEI.

Excuse me, but what are you talking about? The RTI instruction at the
end of the interrupt handler pulls the status register from the stack,
restoring the value that the D flag had prior to the interrupt.

It is the other way around: the interrupt routine must clear the D
flag if it assumes binary mode and the main program does some
arithmetics in decimal mode.

Marko

Mike Gregory

unread,
Apr 3, 2000, 3:00:00 AM4/3/00
to

Yes you are right, the 128 always seemed to me to be a stop-gap
computer. Never did much on it at all. Just shows how fascinating the
64 was.

Mike G

On Mon, 03 Apr 2000 04:57:13 GMT, hur...@xs4all.nl (Rene van Belzen)
wrote:

>That means you have never programmed IRQ on a C128, which switches off
>decimal mode in normal IRQ. You would have to rewrite the entire C128
>IRQ to avoid the CLD instruction, or simple prevent IRQ by SEI.

John Iannetta

unread,
Apr 3, 2000, 3:00:00 AM4/3/00
to
Joseph Rose <jor...@pop.gis.net> said:

"> Which of course means that the jiffy clock loses 1/60 of a second each day.
> The consequences of this are obvious. After 591 years, the C-64's jiffy
clock will be one hour early.


"That doesn't matter! Every time you turn on the C64, the clock is set to 0.
The Commodore 64 can't be safely kept on for even four hours. I once broke

my original C64 by keeping it on for extended periods of time. Besides that,


the Commodore 64 didn't even last for 20 years, let alone the 591 years
mentioned above."

I keep my C-64 (Serial # 00005609) on 24 hours/day, 7 days/week. When I
have a math problem to solve, I switch on the monitor, and see "READY.". If I
use my imagination, I can almost see something like:

"Hi! I'm your faithful friend, ready to do your bidding. Why turn on that
PC, and wait for the POST to complete? I've served you all those years, and
I have no intentions of letting you down now."

Then I can either load a program, do something quickly in direct mode, or
write a short BASIC program if an iterative calculation is required. Don't
get me wrong, my PC has its uses. I can't think of any right now, but I do
find myself using it now and then.

The C-64 did indeed not last for 20 years, nor did anything else on this
planet that was manufactured in 1982. Are you sure that the reason for the
demise of your C-64 was its being left on for long periods of time? It's not
very easy to pinpoint the cause of a failure. Keeping a piece of electronic
equipment powered-up continuously has its pros and cons. Thermal cycling is
eliminated, thereby increasing reliability. But the average temperature is
increased, which decreases reliability.

"> But because the subtraction is used to check the carry flag, being in
> decimal mode doesn't make any difference. So the interrupt routine doesn't
> care whether the 6510 is in binary or in decimal mode. But the CHRGET
> routine DOES care. It is called for each byte in a BASIC line, and does two
> subtractions each time. When in decimal mode, the routine returns a
> different byte from the one in memory.

"So turn it off when you return to BASIC. :)".

Good idea! I want to correct what I said about CHRGET. It IS called for
each byte of the line, but the two subtractions are done only if the byte value
is less than 58. First 48 is subtracted; then 208 is subtracted. That returns
the original value, but the carry flag is defined. Carry is clear for a digit
and set for all else. And the zero flag is set only on 0 and 58 (colon). But
doing the subtractions in decimal mode, the zero at end-of-line is returned as
a 64 (with Z flag clear and C flag clear). That messes things up.

Joseph Rose

unread,
Apr 3, 2000, 3:00:00 AM4/3/00
to
Matthew Montchalin wrote:

> On Fri, 31 Mar 2000, Joseph Rose wrote:
>
> |How does the 6502 work with BCD numbers? In other words, how do I
> |emulate BCD calculations?
>
> The processor status register has a "decimal" mode that can be entered
> simply by executing the "Set Decimal Mode" instruction:
>
> sed
> ...
> lda #5 sed clc adc #6
> ...
> cld
>
> And at this point the accumulator has #$11 in it.

I downloaded a web page with information on the 6502 series processors.
It gives the basic information on opcodes, addressing modes, cycles,
undocumented opcodes and bugs, It also describes some side-effects of
Decimal mode math (adding or subtracting illegal numbers, i.e. $0F+$01).
However, I don't understand how to copy this in my emulator. Will the DAA
and DAS instructions after emulated ADC and SBC do the same? Does Decimal
mode require the same number of cycles? I want to emulate BCD exactly.

BTW, How do I multiply or divide a BCD byte or digit by 2? If I can't use
ASL and LSR, how do I perform the BCD equivalent? I don't want to perform
7 adds to multiply by 7 or 9 adds to multiply by 9. I also don't want to
use tables in a ROM routine, or in any math the computer can easily do
without tables. I'll use tables to speed up math, not do math.


White Flame (aka David Holz)

unread,
Apr 3, 2000, 3:00:00 AM4/3/00
to
Matthew Montchalin wrote:
> Do you think it would be a waste of space to devote some of the EEPROM
> addressing space to code that will support a command like:

Calculating on the 1541 is only a few percentage points faster than
calculating on the C64 (them bad lines, IRQs, etc), plus you have the
overhead of the IEC communication. It would be a lot slower than
calculating on the 64, and random # functions don't take up that much in
memory.

If you used self-modifying code for your random key in a multitasking
system on the 64 without semaphore blocking, you'd get _very_ random
numbers.


--

White Flame (aka David Holz)

http://fly.to/theflame

John Iannetta

unread,
Apr 4, 2000, 3:00:00 AM4/4/00
to
Joseph Rose <jor...@pop.gis.net> said:

"Does Decimal mode require the same number of cycles? I want to emulate BCD

exactly.".

Yes, the same number of clock cycles as binary mode.

"BTW, How do I multiply or divide a BCD byte or digit by 2? If I can't use
ASL and LSR, how do I perform the BCD equivalent? I don't want to perform
7 adds to multiply by 7 or 9 adds to multiply by 9. I also don't want to
use tables in a ROM routine, or in any math the computer can easily do

without tables. I'll use tables to speed up math, not do math.".

To multiply a number (from 0 to 99) in .A by 2 in decimal mode, do:

SED
CLC
STA $02
ADC $02
CLD
RTS

The product is in .A. To divide a number in .A by 2 in decimal mode, do:

SED
LSR
TAX
AND #$08
BEQ Sam
TXA
SEC
SBC #$03
TAX
Sam TXA
CLD
RTS

The quotient is in .A. You should resign yourself to the fact that the 6502
won't do multiply and divide. Without using tables, in binary mode, you
multiply and divide by adding, subtracting, shifting, and rotating. In decimal
mode, you do the same thing. But you shift/rotate 4 bits at a time. And if
a multiplier nybble is 9, you will have to sum into the product register 9
times. Except for special cases (like multipliers and divisors of 10, 100,
etc.), that's the way it is done. Did you read my reply containing the
routine for multiplying two 16-digit integers in decimal mode?

Marko Mäkelä

unread,
Apr 4, 2000, 3:00:00 AM4/4/00
to
>>>>> "John" == John Iannetta <73510...@CompuServe.COM> writes:

John> Yes, the same number of clock cycles as binary mode.

As the subject says "6502" in it, I'd add that on Rockwell 65C02, BCD
addition and subtraction take one more cycle, because they set the
Negative and oVerflow flags "correctly" after the operation.

John> The product is in .A. To divide a number in .A by 2 in decimal
John> mode, do:

Hmm, your routine seems a bit inefficient. You're using two
registers, although one would suffice. It's a pity that the NMOS 6502
doesn't allow immediate addressing mode for the BIT instruction. But
what you could do is to initialize a zero page location with the
constant 8:

SED
LSR
BIT zp8 ; zero page location initialized with 8
BEQ Sam
SEC
SBC #3
Sam: CLD
RTS

If you use a non-zero page location, this solution takes one more
cycle and byte. I'm assuming that your algorithm is correct; I didn't
check it. And I don't remember if the BIT instruction affects the
Carry flag. If it does, the SEC instruction can be eliminated.

Marko

John Iannetta

unread,
Apr 15, 2000, 3:00:00 AM4/15/00
to
"White Flame (aka David Holz)" said:

"Calculating on the 1541 is only a few percentage points faster than
calculating on the C64 (them bad lines, IRQs, etc), plus you have the
overhead of the IEC communication. It would be a lot slower than
calculating on the 64, and random # functions don't take up that much in
memory.".

Compared to an NTSC C-64 (at 1.022727 MHz), a 1541 drive (at 1.000000 MHz)
is slower, if you blank the screen. Even with the computer's system interrupt
enabled, so long as you don't press any keys. The 1541 doesn't have to worry
about badlines, but there are normally two sources of IRQ interrupts. One is
the timer ticks for doing the job in the job queue. When the read/write head
moves across the tracks, it moves a half-track on each tick. The other is the
-ATN line going low, indicating that the IEC (serial) bus should be serviced.
The NMI interrupt is not used. But to maximize speed for calculating in both
devices, one would disable all interrupts (and blank the C-64's screen).

A long long time ago, when I wrote "COPROC.BIN", the idea was to
demonstrate true multitasking (as opposed to what Windows calls multitasking).
I intentionally used a slow algorithm (for calculating the square root of an
integer to 119 decimal places). When the drive finished his calculation, he
toggled an IEC line to signal the C-64, and a memory-read fetched the digits
from the 1541's RAM and displayed them. And when the computer finished his
calculation, he displayed the result. Both answers appeared at roughly the
same time, as I recall.

On a side note, in a CompuServe forum recently, a fellow was talking about
his new 533 MHz Pentium Windows-equipped computer. He calculated factorial
20,000, and got the result in 15 seconds. That's 399750 clock cycles for a
multiply. There must be a huge NOP factory in Redmond, WA.

Linards Ticmanis

unread,
Apr 15, 2000, 3:00:00 AM4/15/00
to
John Iannetta wrote:
> A long long time ago, when I wrote "COPROC.BIN", the idea was to
> demonstrate true multitasking (as opposed to what Windows calls multitasking).
> I intentionally used a slow algorithm (for calculating the square root of an
> integer to 119 decimal places). When the drive finished his calculation, he
> toggled an IEC line to signal the C-64, and a memory-read fetched the digits
> from the 1541's RAM and displayed them. And when the computer finished his
> calculation, he displayed the result. Both answers appeared at roughly the
> same time, as I recall.

Actually the 1581 seems like a much more likely candidate for this kind
of stuff, having a 2Mhz CPU IIRC, and 16K of RAM.

Linards Ticmanis

Matthew Montchalin

unread,
Apr 16, 2000, 3:00:00 AM4/16/00
to
On 15 Apr 2000, John Iannetta wrote:
| "White Flame (aka David Holz)" said:
|
| "Calculating on the 1541 is only a few percentage points faster than
| calculating on the C64 (them bad lines, IRQs, etc), plus you have the
| overhead of the IEC communication. It would be a lot slower than
| calculating on the 64, and random # functions don't take up that much in
| memory.".

<snipping John's very good points...>

The desirability in using a 1541 to generate random numbers is similar
to the desirability of using a cassette-dongle; speed is not the only
criterion for deciding whether or not to export functions off the C-64
to one of the C-64's peripherals. Preventing unauthorized copying of
is one to consider though.


White Flame (aka David Holz)

unread,
Apr 17, 2000, 3:00:00 AM4/17/00
to
Matthew Montchalin wrote:
> The desirability in using a 1541 to generate random numbers is similar
> to the desirability of using a cassette-dongle; speed is not the only
> criterion for deciding whether or not to export functions off the C-64
> to one of the C-64's peripherals. Preventing unauthorized copying of
> is one to consider though.

So you actually believe that somebody can make a secure, noncrackable
piece of software for the C64 today? With sooo many veteran crackers
around? I think the 64 market can sell proper software due to the
integrity of the people in it, and that copy protection (seeing as it
will be broken anyway, if somebody wants to) is pretty much passe.

--

White Flame (aka David Holz)

http://fly.to/theflame

Matthew Montchalin

unread,
Apr 17, 2000, 3:00:00 AM4/17/00
to
On Mon, 17 Apr 2000, White Flame (aka David Holz) wrote:

|Matthew Montchalin wrote:
|> The desirability in using a 1541 to generate random numbers is similar
|> to the desirability of using a cassette-dongle; speed is not the only
|> criterion for deciding whether or not to export functions off the C-64
|> to one of the C-64's peripherals. Preventing unauthorized copying of

software

|> is one to consider though.
|
|So you actually believe that somebody can make a secure, noncrackable
|piece of software for the C64 today?

*Anything* can be "duplicated," given enough time and energy.

|With sooo many veteran crackers around?

Many of them have moved on.

|I think the 64 market can sell proper software due to the integrity of
|the people in it, and that copy protection (seeing as it will be broken
|anyway, if somebody wants to) is pretty much passe.

And what is so difficult about copying the new ROM set that I am working
on? (Nothing at all.) But it will have all the look and feel of a "new"
Commodore peripheral. Remember, it has forward and backward track &
sector links, and a nonstandard sector size. Might as well motivate
some people to modify their 1541's, rather than just sit back and do
nothing.

(And secondly, what's wrong with software that spools backwards as
well as forward, given the right TALK/LISTEN addresses?)


Cameron Kaiser

unread,
Apr 18, 2000, 3:00:00 AM4/18/00
to
"White Flame (aka David Holz)" <white...@geocities.com> writes:

>So you actually believe that somebody can make a secure, noncrackable

>piece of software for the C64 today? With sooo many veteran crackers
>around? I think the 64 market can sell proper software due to the


>integrity of the people in it, and that copy protection (seeing as it
>will be broken anyway, if somebody wants to) is pretty much passe.

I think it would be counterproductive in any case, given the huge number
of serial devices out there that aren't 1541/71s. #1 complaint with Nether
alpha was that the fastloader was 1541/71 only, and most copy protection
schemes are doubly so.

0 new messages