The following program:
PORTB = $6000
PORTA = $6001
DDRB = $6002
DDRA = $6003
E = %10000000
RW = %01000000
RS = %00100000
VARX = $4000
VARY = $4001
VARZ = $4002
NUM = $4005
DIV = $4006
RES = $4007
MOD = $4008
BACK_TO_FRONT = $4020
.org $8000
reset:
lda #%11111111 ; Set all pins on port A to output
sta DDRA
lda #%11100000 ; Set top 3 pins on port B to output
sta DDRB
lda #%00011100 ; Set 8-bit mode; 2-line display; 5x8 font
sta PORTA
lda #0 ; Clear RS/RW/E bits
sta PORTB
lda #E ; Set E bit to send instruction
sta PORTB
lda #0 ; Clear RS/RW/E bits
sta PORTB
lda #%01110000 ; Display on; cursor on; blink off
sta PORTA
lda #0 ; Clear RS/RW/E bits
sta PORTB
lda #E ; Set E bit to send instruction
sta PORTB
lda #0 ; Clear RS/RW/E bits
sta PORTB
lda #%01100000 ; Increment and shift cursor; don't shift display
sta PORTA
lda #0 ; Clear RS/RW/E bits
sta PORTB
lda #E ; Set E bit to send instruction
sta PORTB
lda #0 ; Clear RS/RW/E bits
sta PORTB
lda #$0
sta VARX
lda #$1
sta VARY
lda #$a
sta DIV
loop:
lda VARX
clv
clc
adc VARY
clc
clv
sta VARZ
lda VARY
sta VARX
lda VARZ
sta VARY
lda VARY
sta NUM
ldx #$0
store_digits:
jmp divide
return_from_div:
lda MOD
sta BACK_TO_FRONT, x
inx
lda RES
sta NUM
lda #$0
cmp NUM
bcc store_digits
dex
print_digits:
ldy BACK_TO_FRONT, x
lda digit_table, y
sta PORTA
lda #RS ; Set RS; Clear RW/E bits
sta PORTB
lda #(RS | E) ; Set E bit to send instruction
sta PORTB
lda #RS ; Clear E bits
sta PORTB
dex
sec
cpx #$1
bcc print_digits
ldx #$0
delay:
lda #%00000100
sta PORTA
lda #RS ; Set RS; Clear RW/E bits
sta PORTB
lda #(RS | E) ; Set E bit to send instruction
sta PORTB
lda #RS ; Clear E bits
sta PORTB
cpx #$f
beq next
inx
jmp delay
next:
lda #%10000000
sta PORTA
lda #0 ; Clear RS/RW/E bits
sta PORTB
lda #E ; Set E bit to send instruction
sta PORTB
lda #0 ; Clear RS/RW/E bits
sta PORTB
jmp loop
divide:
lda #$0
sta RES
lda #$0
sta MOD
div_loop:
lda NUM
cmp DIV
bcc answer
lda NUM
sec
sbc DIV
clc
clv
sta NUM
inc RES
jmp div_loop
answer:
lda NUM
sta MOD
jmp return_from_div
digit_table:
.word $8c0c
.word $cc4c
.word $ac2c
.word $ec6c
.word $9c1c
.org $fffc
.word reset
.word $0000
Runs on the 65C02 (WDC version). Connected to a ROM (starting with address 0x8000) and to a RAM chip (address 0x4000) and to the VIA (65C22), which is connected to an HD44780 (1602 standard LCD).
And the program works, but partially. It outputs 1 then 2 then 3 ... then 89 and instead of 144 prints 1, instead of 233 prints 2. Then 1 again then 98 ??? And then 61.
The way you decide to loop back to print_digits
is incorrect:
; I've omitted the SEC since it's pointless
cpx #$1
bcc print_digits
This will jump to print_digits
only if X
is unsigned lower than 1. The only possible value for which that could be true is 0, so the above can also be described as jumping to print_digits
if X
equals 0.
If we look at the entire print_digits
loop, it can therefore be described with the following pseudo-code:
x = numDigits - 1;
do
{
print(digits[x]);
x--;
} while (x == 0);
The only scenarios in which this would give you the correct output is when numDigits
is either 1 or 2. For anything larger than that you'll print one digit and then exit the loop.
What you really want is something like this:
x = numDigits - 1;
do
{
print(digits[x]);
x--;
} while (x >= 0);
That is, replace:
dex
sec
cpx #$1
bcc print_digits
with:
dex ; Updates the N flag after decrementing X
bpl print_digits ; Jump if N is clear, i.e. if X >= 0