6502 Decimal Mode

Basic and Machine Language

Moderator: Moderators

Post Reply
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

6502 Decimal Mode

Post by chysn »

Like everyone else, I've got quite a bit of social distancing time these days, so I decided to spend some of it studying aspects of 6502 programming that I've always ignored.

Subject number one: Decimal mode! Consider this VICMON session:

Code: Select all

.A 1800 SED
.A 1801 LDA #$15
.A 1803 CLC
.A 1804 ADC #$26
.A 1806 CLD
.A 1807 BRK
.G 1800

B*
 AC
.;41
Decimal mode causes math operations to be treated as decimal operations instead of hex operations. We'd usually expect #$15 + #$26 to be #$3B. But in Decimal mode, each nybble is treated as a base-ten digit instead of a base-16 digit. So #$15 + #$26 turns out to be 15+26, or #$41.

Note that Decimal mode doesn't apply to increment and decrement instructions. These are still handled as normal. So with LDX #$09, INX, then X = #$0A, and not #$10. You can take advantage of this fact to make a hex-to-decimal conversion subroutine like this:

Code: Select all

HEX2D:    PHP
          PHA
          SED
          LDA #$00
H2D_L:    CLC
          ADC #$01
          DEY
          BNE H2D_L
          TAY
          PLA
          PLP ; Your main routine may already be in Decimal mode, so I don't want to disrupt that
          RTS
Prepare for the routine by loading Y with your hex value, then JSR HEX2D. On return, the Y register will be converted into its decimal equivalent.

Note also that the behavior of the carry flag in decimal mode is as you'd expect, so...

Code: Select all

SED
LDA #$99
CLC
ADC #$01
BCS HAPPENS ; Carry flag set as expected, but
BEQ NOTSOMUCH ; Zero flag isn't set, which is probably a bug
...
CLD
ADC sets the carry flag, whereas it would not be set outside of Decimal mode. However, it doesn't set the Zero flag. I think (but cannot test) that the 65C02 fixes this. I don't know about other 6502-ish processors like 6510 or 8500.

You can, of course, use "out of range" operands in decimal mode. Assignments won't be tampered with, so

Code: Select all

SED
LDA #$0F
CLD
BRK
The accumulator will be #$0F, as expected. But if you do any kind of arithmetic,

Code: Select all

SED
LDA #$0F
CLC
ADC #$00
CLD
BRK
Then the accumulator will be #$15. So, that's pretty fascinating.

I feel like I have a somewhat-decent understanding of how the 6502 handles decimal mode. However, I don't have a specific use for it in mind. Sure, you'll usually want to display results to the end user in decimal instead of hex. But it seems that conversions should be done prior to output, rather than during the calculations. So I'm not really sure what I'd use Decimal mode for.

If you play around with Decimal mode, don't forget to clear it when you're done. The VIC-20's operating system doesn't CLD anywhere except the $FD22 reset routine. So if you leave Decimal mode on, your VIC will be confused about just about every numerical operation it tries, and things will be pretty rough.
Last edited by chysn on Mon Mar 16, 2020 4:03 pm, edited 3 times in total.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: 6502 Decimal Mode

Post by chysn »

The decimal to hex converter would be like this:

Code: Select all

DEC2H:   PHP
         PHA
         TYA
         LDY #$00
         SED
D2H_L:   INY
         SEC
         SBC #$01
         BNE D2H_L
         PLA
         PLP
         RTS
To prepare, load Y with a decimal number, then JSR DEC2H. On return, Y will be the hexadecimal equivalent.

A couple notes about this one.

First, the PHP/PLP is here because I figured that if you need to do a conversion like this, it's possible that your main routine is already in Decimal mode, and I don't want to disrupt that.

Second, you'll notice that I'm relying on the Zero flag eventually getting set in the D2H_L loop. The Zero flag bug I talked about above only applies to ADC. SBC seems to work just fine.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
User avatar
srowe
Vic 20 Scientist
Posts: 1356
Joined: Mon Jun 16, 2014 3:19 pm

Re: 6502 Decimal Mode

Post by srowe »

You'll probably find this article useful

http://www.6502.org/tutorials/decimal_mode.html
groepaz
Vic 20 Scientist
Posts: 1195
Joined: Wed Aug 25, 2010 5:30 pm

Re: 6502 Decimal Mode

Post by groepaz »

Ive also added some info in my doc
I'm just a Software Guy who has no Idea how the Hardware works. Don't listen to me.
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: 6502 Decimal Mode

Post by chysn »

srowe wrote: Tue Mar 17, 2020 12:42 am You'll probably find this article useful

http://www.6502.org/tutorials/decimal_mode.html
Yeah, that's good, thanks! This one also hints at a legitimate potential use of Decimal mode, which is that you might need actual percentages, and not approximations based on some hex value.
groepaz wrote: Tue Mar 17, 2020 8:51 am Ive also added some info in my doc
Thanks! A nice treatment there. Also, good clarification about what I perceived as a "bug" in the Zero flag:

"Z flag is not affected by decimal mode, it will be set if the binary operation would become
zero, regardless of the BCD result."


So it's either better or worse than I thought it was, depending on your perspective. Certainly 80 + 80 setting Z is a little on the weird side.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
User avatar
Mike
Herr VC
Posts: 4901
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: 6502 Decimal Mode

Post by Mike »

chysn wrote:The VIC-20's operating system doesn't CLD anywhere except the $FD22 reset routine. So if you leave Decimal mode on, your VIC will be confused about just about every numerical operation it tries, and things will be pretty rough.
One thing to be wary of: the NMOS/HMOS variants of the 65xx (these include the 6502 in the VIC-20, the 6510 or 8500 in the C64, 7501 or 8501 in the 264 series and 8502 in the C128) do not clear the D flag upon entering a IRQ, NMI or executing BRK. After a Reset, the D flag is undefined.

Neither IRQ nor NMI handlers in the VIC-20 KERNAL do a CLD at their entry, but they use ADC/SBC in some code paths and those will give unintended results. You'd be well advised to run smaller code snippets utilizing Decimal mode with interrupts off. Alternatively, provide a wedge in the IRQ and/or NMI which does a CLD first before proceeding.

I did a "CLD wedge" with MINIPAINT. The directory display employs a routine running in Decimal mode to convert the 16-bit block count to BCD (credit due to Andrew Jacobs, see here) :

Code: Select all

.3AEA  F8        SED
.3AEB  A9 00     LDA #$00
.3AED  8D 57 3B  STA $3B57
.3AF0  8D 58 3B  STA $3B58
.3AF3  8D 59 3B  STA $3B59
.3AF6  A2 10     LDX #$10
.3AF8  06 9B     ASL $9B
.3AFA  26 9C     ROL $9C
.3AFC  AD 57 3B  LDA $3B57
.3AFF  6D 57 3B  ADC $3B57
.3B02  8D 57 3B  STA $3B57
.3B05  AD 58 3B  LDA $3B58
.3B08  6D 58 3B  ADC $3B58
.3B0b  8D 58 3B  STA $3B58
.3B0e  AD 59 3B  LDA $3B59
.3B11  6D 59 3B  ADC $3B59
.3B14  8D 59 3B  STA $3B59
.3B17  CA        DEX
.3B18  D0 DE     BNE $3AF8
.3B1A  D8        CLD
.3B1B  60        RTS
The 16-bit value in $9B/$9C ends up as 6-digit BCD in $3B57..$3B59. Other routines in MINIPAINT print this number to the 128x64 pixel bitmap of the overlaid text window, omitting leading zeroes (except when the value is 0 itself).

An own IRQ wedge was needed anyway to control the blink phase of the graphics cursor, and I put a CLD at its entry to sort out the issue:

Code: Select all

.3F8C  D8        CLD        ; a CLD early in the ISR to correct 6502 bug
.3F8D  A2 00     LDX #$00
.3F8F  E6 A2     INC $A2
.3F91  D0 06     BNE $3F99
.3F93  E6 A1     INC $A1
.3F95  D0 02     BNE $3F99
.3F97  E6 A0     INC $A0
.3F99  38        SEC
.3F9A  A5 A1     LDA $A1
.3F9C  E9 1A     SBC #$1A   ; <- these two SBCs better executed
.3F9E  A5 A0     LDA $A0
.3FA0  E9 4F     SBC #$4F   ; <- with Decimal mode off!
.3FA2  90 06     BCC $3FAA
.3FA4  86 A0     STX $A0
.3FA6  86 A1     STX $A1
.3FA8  86 A2     STX $A2
.3FAA  AE BA 3F  LDX $3FBA  ; $3FBA counts through 0..39 each jiffy, 0..19 ^= blink phase 1, 20..39 ^= phase 2
.3FAD  E8        INX
.3FAE  E0 28     CPX #$28
.3FB0  90 02     BCC $3FB4
.3FB2  A2 00     LDX #$00
.3FB4  8E BA 3F  STX $3FBA
.3FB7  4C C2 EA  JMP $EAC2  ; enter KERNAL IRQ routine "behind" the original jiffy clock incr. and STOP key test
The IRQ wedge also contains a bug-fixed jiffy clock which doesn't advance to "240000" for one sixtieth second ... :mrgreen: ... and which also omits testing the STOP key, of course.
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: 6502 Decimal Mode

Post by chysn »

Mike wrote: Wed Mar 18, 2020 12:10 pm
chysn wrote:The VIC-20's operating system doesn't CLD anywhere except the $FD22 reset routine. So if you leave Decimal mode on, your VIC will be confused about just about every numerical operation it tries, and things will be pretty rough.
One thing to be wary of: the NMOS/HMOS variants of the 65xx (these include the 6502 in the VIC-20, the 6510 or 8500 in the C64, 7501 or 8501 in the 264 series and 8502 in the C128) do not clear the D flag upon entering a IRQ, NMI or executing BRK. After a Reset, the D flag is undefined.

Neither IRQ nor NMI handlers in the VIC-20 KERNAL do a CLD at their entry, but they use ADC/SBC in some code paths and those will give unintended results. You'd be well advised to run smaller code snippets utilizing Decimal mode with interrupts off. Alternatively, provide a wedge in the IRQ and/or NMI which does a CLD first before proceeding.
Good tips, thanks!

I wasn't able to find anything bad that would happen during the IRQ routine. I found use of SBC in only the jiffy clock code, and that's to check whether the clock should roll back to 0 after 24 hours and 1/60 second. Decimal mode won't affect that decision because the accumulator and the SBC operand are both treated as decimal values (when D=1) or binary values (when D=0), and the carry flag status winds up the same whether D=0 or D=1 (4F - 4F = 0, whatever the radix!). So I don't think I'd take any special measures for the IRQ. Unless I missed something.

But, this reinforces the point that you can't screw around with Decimal mode. You have to watch out when you call KERNAL routines, and even the subroutines that you write yourself, because having Decimal mode on at the wrong time can spin your code in weird directions.

Anyway... I spent my studying time last night learning all about the Overflow Flag. It's a subject with fewer "and but alsos" than Decimal mode, but definitely made my head hurt for a little while.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
Post Reply