Saturday, July 3, 2021

0000 0000 0110 1000

16-bit arithmetic on an 8 bit device in assembly (Part One)

The PFS154 has three 11-bit (2048 increments) hardware PWM channels, which is great - but it is an 8-bit microcontroller. So it might be time to explore 16-bit arithmetic to cover the possibility I might use the full PWM capabilities on this device.

Crazily I have also decided to explore this from an AVR assembly perspective (???). The problem with this is that the PFS154 is not part of the AVR family, I have no familiarity with the instruction set for Padauk assembly, and as well no experience with the toolchain to program the PFS154 in assembly. So...

I'm going to start with subtraction. From my AVR by example booklet, the sub command simply subtracts one register from another. When zero is reached the flag is raised and a branch can be initiated. It's pretty simple code - check out the video below for the code running on Gerd's excellent AVR-SIM

.nolist
.include "tn13adef.inc" ; Define device ATtiny13A
.list

.dseg
.org SRAM_START

.cseg
.org 000000

Main:

ldi r16, low(RAMEND)
out SPL, r16           ; Init LSB stack pointer
.equ mycount = 146     ; initialise countdown (0x92)

Loop:
ldi r16, mycount       ; load counter
ldi r19, 1

rcall Countdown
subi r22, -1           ; how many loops?
rjmp Loop

Countdown:
sub r16, r19
cpi r16, 0
brbc 1, Countdown
ret

; End of source code

A quick screen grab shows the beast humming along happily subtracting (and keeping an 8-bit count of the loops in R22). Note that AVR has no "add immediate"(addi) command on the premise that you can subtract immediate (subi) using -1 instead. It's a little weird but 5 - ( -1) = 6 and that's OK with the AVR (and me).

Two byte (16-bit) subtraction uses both sub and subc for the low and high bytes respectively. The subc command is subtract with carry which will use the registers defined to effectively result in a 16-bit operation, as per the code below.

.nolist
.include "tn13adef.inc" ; Define device ATtiny13A
.list

.dseg
.org SRAM_START

.cseg
.org 000000

Main:

ldi r16, low(RAMEND)
out SPL, r16           ; Init LSB stack pointer
.equ mycount = 900     ; initialise countdown (0x0384)

Loop:
ldi r16, high(mycount) ; load high byte 0x03
ldi r17, low(mycount)  ; load low byte 0x84
ldi r19, 1
ldi r20, 0
rcall Countdown
subi r22, -1
rjmp Loop

Countdown:
sub r17, r19
sbc r16, r20
cpi r17, 0
brbc 1, Countdown
cpi r16, 0
brbc 1, Countdown
ret

; End of source code

See below for a quick screen grab of this code in action.


Note that this code only subtracts one at a time, but the same process applies to larger 16 bit integers as per the code below.

.nolist
.include "tn13adef.inc" ; Define device ATtiny13A
.list

.dseg
.org SRAM_START

.cseg
.org 000000

Main:

ldi r16, low(RAMEND)
out SPL, r16           	; Init LSB stack pointer
.equ higher = 425 	; 0x01A9
.equ lower = 342 	; 0x0156

Loop:
ldi r16, high(higher) 	; load high byte 0x01
ldi r17, low(higher) 	; load low byte 0xA9
ldi r18, high(lower) 	; load high byte 0x01
ldi r19, low(lower) 	; load low byte 0x56
sub r17, r19 		; subtract low byte
sbc r16, r18 		; subtract high byte with carry

; result in r16:r17 = 0x0053

rjmp Loop

; End of source code





No comments:

Post a Comment