AVR Studio - AVR Simulator 2 Carry Flag Issue - avr

I've just started an assembly line programming class and I'm having an issue with a problem, I'm adding 240 to 49 and I know it will overflow, my goal is to make Register 1 equal to 1 when these numbers overflow. I know that the carry flag is set when I add them but I'm unsure of how to use this flag to make r1 equal to 1.
This program should calculate:
; R0 = R16 + R17 + R18
;
;--*1 Do not change anything between here and the line starting with *--
.cseg
ldi r16, 0x30
ldi r17, 0x31
ldi r18, 0x32
;*--1 Do not change anything above this line to the --*
;***
; Your code goes here:
;
add r0, r16
add r0, r17
add r0, r18
;****
;--*2 Do not change anything between here and the line starting with *--
done: jmp done
;*--2 Do not change anything above this line to the --*

I'm sure there are smarter ways, but you can use brcs, branch if carry set:
add r0, r16
add r0, r17
add r0, r18
brcs carry ; Branch if carry set
carry: ldi r1, 0x1 ; Branch destination

Related

Multiplication table in VMLab / AVR

I am trying to figure out this question. I posted my code below. It doesn't work properly. it seems like its not multiplying the 2 least significant nibbles. I don't know AVR very well.
Write AVR that generates a multiplication table for SRAM addresses 0x0100 to 0x01FF. The value at each address is the product of the two least significant nibbles of the address. For example, at address 0x0123, the multiplicand is 3 and the multiplier is 2. calculate the product (6 in this case) and store it at address 0x0123. The answer should be about 10-12 lines of code with a loop
.include "C:\VMLAB\include\m168def.inc"
ldi r27, 0x01
ldi r26, 0x00
ldi r30, 0xff
main:
mov r16, r26
andi r16,0x0f
mov r17,r27
andi r17,0xf0
swap r17
mul r17, r16
st x+, r16
dec r30
brne main
According to the manual, the MUL instruction stores its result in R0 and R1. So you need to read R0 after MUL, not R16.
You have three errors in your code:
mul stores results to R1:R0 pair registers
if use X as the index register and content mark symbolically as ABCD then CD is located in XL register, not in XH (r27). AB is constant (01)
you loops only 255 times not 256
ldi xh, 0x01
ldi xl, 0x00
ldi r30, 0x00
main:
mov r16, xl ;put D to R16
andi r16,0x0f
mov r17, xl ;put C to R17
andi r17,0xf0
swap r17
mul r17, r16 ;multiply C x D
st x+, r0 ;store result low byte to memory
dec r30 ;repeat 256 times
brne main

How to Modify a String in Arm Assembly [duplicate]

So I'm having trouble with my program. It's supposed to read in a text file
that has a number on each line. It then stores that in an array, sorts it using selection sort, and then outputs it to a new file. The reading of and writing to the file work perfectly fine but my code for the sort isn't working properly. When I run the program, it only seems to store some of the numbers
in the array and then a bunch of zeroes.
So if my input is 112323, 32, 12, 19, 2, 1, 23. The output is 0,0,0,0, 2,1,23. I'm pretty sure the problem's with how I'm storing and loading from the array
onto the registers because assuming that part works, I can't find any reason why the selection sort algorithm shouldn't work.
Ok thanks to your help, I figured out that I needed to change the load and store instruction so that it matches the specifier used (ldr -> ldrb and str -> strb). But I need to make a sorting algorithm that works for 32 bit numbers so which combination of specifiers and load/store instructions would allow me to do that? Or would I have to load/store 8 bits a time? And if so, how would I do that?
.data
.balign 4
readfile: .asciz "myfile.txt"
.balign 4
readmode: .asciz "r"
.balign 4
writefile: .asciz "output.txt"
.balign 4
writemode: .asciz "w"
.balign 4
return: .word 0
.balign 4
scanformat: .asciz "%d"
.balign 4
printformat: .asciz "%d\n"
.balign 4
a: .space 32
.text
.global main
.global fopen
.global fprintf
.global fclose
.global fscanf
.global printf
main:
ldr r1, =return
str lr, [r1]
ldr r0, =readfile
ldr r1, =readmode
bl fopen
mov r4, r0
mov r5, #0
ldr r6, =a
loop:
cmp r5, #7
beq sort
mov r0, r4
ldr r1, =scanformat
mov r2, r6
bl fscanf
add r5, r5, #1
add r6, r6, #1
b loop
sort:
mov r5,#0 /*array parser for first loop*/
mov r6, #0 /* #stores index of minimum*/
mov r7, #0 /* #temp*/
mov r8, #0 /*# array parser for second loop*/
mov r9, #7 /*# stores length of array*/
ldr r10, =a /*# the array*/
mov r11, #0 /*#used to obtain offset for min*/
mov r12, #0 /*# used to obtain offset for second parser access*/
loop3:
cmp r5, r9 /*# check if first parser reached end of array*/
beq write /* #if it did array is sorted write it to file*/
mov r6, r5 /*#set the min index to the current position*/
mov r8, r6 /*#set the second parser to where first parser is at*/
b loop4 /*#start looking for min in this subarray*/
loop4:
cmp r8, r9 /* #if reached end of list min is found*/
beq increment /* #get out of this loop and increment 1st parser**/
lsl r7, r6, #3 /*multiplies min index by 8 */
ADD r7, r10, r7 /* adds offset to r10 address storing it in r7 */
ldr r11, [r7] /* loads value of min in r11 */
lsl r7, r8, #3 /* multiplies second parse index by 8 */
ADD r7, r10, r7 /* adds offset to r10 address storing in r7 */
ldr r12, [r7] /* loads value of second parse into r12 */
cmp r11, r12 /* #compare current min to the current position of 2nd parser !!!!!*/
movgt r6, r8 /*# set new min to current position of second parser */
add r8, r8, #1 /*increment second parser*/
b loop4 /*repeat */
increment:
lsl r11, r5, #3 /* multiplies first parse index by 8 */
ADD r11, r10, r11 /* adds offset to r10 address stored in r11*/
ldr r8, [r11] /* loads value in memory address in r11 to r8*/
lsl r12, r6, #3 /*multiplies min index by 8 */
ADD r12, r10, r12 /*ads offset to r10 address stored in r12 */
ldr r7, [r12] /* loads value in memory address in r12 to r7 */
str r8, [r12] /* # stores value of first parser where min was !!!!!*/
str r7, [r11] /*# store value of min where first parser was !!!!!*/
add r5, r5, #1 /*#increment the first parser*/
ldr r0,=printformat
mov r1, r7
bl printf
b loop3 /*#go to loop1*/
write:
mov r0, r4
bl fclose
ldr r0, =writefile
ldr r1, =writemode
bl fopen
mov r4, r0
mov r5, #0
ldr r6, =a
loop2:
cmp r5, #7
beq end
mov r0, r4
ldr r1, =printformat
ldrb r2, [r6]
bl fprintf
add r5, r5, #1
add r6, r6, #1
b loop2
end:
mov r0, r4
bl fclose
ldr r0, =a
ldr r0, [r0]
ldr lr, =return
ldr lr, [lr]
bx lr
I figured out that I needed to change the load and store instruction
so that it matches the specifier used (ldr -> ldrb and str -> strb).
But I need to make a sorting algorithm that works for 32 bit numbers
so which combination of specifiers and load/store instructions would
allow me to do that?
If you want to read 32b (4 bytes) values from memory, you have to have 4 bytes values in memory to begin with. Well that should not be surprising :)
Eg if your input is numbers 1, 2, 3, 4, each number is 32b value than in memory that would be
0x00000000: 01 00 00 00 | 02 00 00 00 <- 32b values of 1 & 2
0x00000008: 03 00 00 00 | 04 00 00 00 <- 32b values of 3 & 4
In such case ldr would read 32b each time and you would get 1, 2, 3, 4 with each read in register.
Now, you have in memory byte values (based on your statement that `ldrb` gives right result), eg
0x00000000: 01
0x00000001: 02
0x00000002: 03
0x00000003: 04
or same in one line
0x00000000: 01 02 03 04
So reading 8bit by ldrb gives you numbers 1, 2, 3, 4
But ldr would do read 32b value from memory (all 4 bytes at once) and you would get 32b value 0x04030201 in register.
Note: examples for little-endian systems

How to setup two different led delays for a specific sequence?

I am working on a program that is suppose to create a certain blink sequence on my arduino board (atmega328p). The pattern that I am trying to create is,
ON for 1/2 second
OFF for 1/2 second
ON for 1/2 second
Off for one full second
Repeat this sequence.
I approached the problem by creating two different delays one for the 1/2 sec and other for the 1 sec, and then I call them.
If I only have one delay the light will work with that pattern but once I put both delays in the loop together the light does not even follow the pattern. I apologize if this is a easy question, I don't know if I am approaching this right.
Here is my code:
#include "config.h"
.section .data
dummy: .byte 0 ; dummy global variable
.section .text
.global main
.extern delay
.org 0x0000
main:
; clear the SREG register
eor r1, r1 ; cheap zero
out _(SREG), r1 ; clear flag register
; set up the stack
ldi r28, (RAMEND & 0x00ff)
ldi r29, (RAMEND >> 8)
out _(SPH), r29
out _(SPL), r28
; initialize the CPU clock to run at full speed
ldi r24, 0x80
sts CLKPR, r24 ; allow access to clock setup
sts CLKPR, r1 ; run at full speed
; set up the LED port
sbi LED_DIR, LED_PIN ; set LED pin to output
cbi LED_PORT, LED_PIN ; start with the LED off
; enter the blink loop
1: rcall toggle
rcall delay
rcall delay2
rjmp 1b
toggle:
in r24, LED_PORT ; get current bits
ldi r25, (1 << LED_PIN) ; LED is pin 5
eor r24, r25 ; flip the bit
out LED_PORT, r24 ; write the bits back
ret
delay: ; 1/2 sec delay loop
ldi r21, 41
ldi r22, 150
ldi r23, 127
1: dec r23
brne 1b
dec r22
brne 1b
dec r21
brne 1b
ret
delay2: ; 1 sec delay loop
ldi r18, 82
ldi r19, 43
ldi r20, 0
2: dec r20
brne 2b
dec r19
brne 2b
dec r18
brne 2b
ret

LPC4088 checksum value for Thumb?

In the LPC4088 user manual (p. 876) we can read that LPC4088 microcontroler has a really extraordinary startup procedure:
This looks like a total nonsense and I need someone to help me clear things out... In the world of ARM I've heard countless times to put vector table looking like this:
reset: b _start
undefined: b undefined
software_interrupt: b software_interrupt
prefetch_abort: b prefetch_abort
data_abort: b data_abort
nop
interrupt_request: b interrupt_request
fast_interrupt_request: b fast_interrupt_request
exactly at location 0x00000000 in my binary file, but why would we do that if this location is shadowed at boot with a boot ROM vector table which can't even be changed as it is read-only?! So where can we put our own vector table? I thought about putting it at 0x1FFF0000 so it would be transferred to location 0x00000000 at reset but can't do that because of read-only area...
Now to the second part. ARM expects to find exactly 8 vectors at 0x00000000 and at reset boot ROM checks if sum of 8 vectors is zero and only if this is true user code executes. To pass this check we need to sum up first 7 vectors and save it's 2's complement to the last vector which is a vector for fast interrupt requests residing at 0x0000001C. Well this is only true if your code is 4-bytes aligned (ARM encoding) but is it still true if your code is 2-bytes aligned (Thumb encoding) which is the case with all Cortex-M4 cores that can only execute Thumb encoded opcodes... So why did they explicitly mention that 2's complement of the sum has to be at 0x0000001C when this will never come in to play with Cortex-M4. Is 0x0000000E the proper address to save the 2's complement to?
And third part. Why would boot ROM even check if sum of first 8 vectors is zero when they are already in boot ROM?! And are read-only!
Can you see something is weird here? I need someone to explain to me the unclarities in the above three paragraphs...
you need to read the arm documentation as well as the nxp documentation. The non-cortex-m cores boot differently than the cortex-m cores you keep getting stuck there.
The cortex m is documented in the armv7m ARM ARM (architectural reference manual). It is based on VECTORS not INSTRUCTIONS. An address to the handler not an instruction like in full sized arm cores. Exception 7 is documented as reserved (for the ARM7TDMI based mcus from them it was the reserved vector they used for this checksum as well). Depending on the arm core you are using they expect as many as 144 or 272 (exceptions plus up to 128 or 256 interrupts depending on what the core supports).
(note the aarch64 processor, armv8 in 64 bit mode also boots differently than the traditional full sized 32 bit arm processor, even bigger table).
This checksum thing is classic NXP and makes sense, no reason to launch into an erased or not properly prepared flash and brick or hang.
.cpu cortex-m0
.thumb
.thumb_func
.globl _start
_start:
.word 0x20001000 # 0 SP load
.word reset # 1 Reset
.word hang # 2 NMI
.word hang # 3 HardFault
.word hang # 4 MemManage
.word hang # 5 BusFault
.word hang # 6 UsageFault
.word 0x00000000 # 7 Reserved
.thumb_func
hang: b hang
.thumb_func
reset:
b hang
which gives:
Disassembly of section .text:
00000000 <_start>:
0: 20001000 andcs r1, r0, r0
4: 00000023 andeq r0, r0, r3, lsr #32
8: 00000021 andeq r0, r0, r1, lsr #32
c: 00000021 andeq r0, r0, r1, lsr #32
10: 00000021 andeq r0, r0, r1, lsr #32
14: 00000021 andeq r0, r0, r1, lsr #32
18: 00000021 andeq r0, r0, r1, lsr #32
1c: 00000000 andeq r0, r0, r0
00000020 <hang>:
20: e7fe b.n 20 <hang>
00000022 <reset>:
22: e7fd b.n 20 <hang>
now make an ad-hoc tool that does the checksum and adds it to the binary
Looking at the above program as words this is the program:
0x20001000
0x00000023
0x00000021
0x00000021
0x00000021
0x00000021
0x00000021
0xDFFFEF38
0xE7FDE7FE
and if you flash it the bootloader should be happy with it and let it run.
Now that is assuming the checksum is word based if it is byte based then you would want a different number.
99% of baremetal programming is reading and research. If you had a binary from them already built or used a sandbox that supports this processor or family you could examine the binary built and see how all of this works. Or look at someones github examples or blog to see how this works. They did document this, and they have used this scheme for many years now before they were NXP, so nothing really new...Now is it a word based or byte based checksum, the documentation implies word based and that makes more sense. but a simple experiment and/or looking at sandbox produced binaries would have resolved that.
How I did it for this answer.
#include <stdio.h>
unsigned int data[8]=
{
0x20001000,
0x00000023,
0x00000021,
0x00000021,
0x00000021,
0x00000021,
0x00000021,
0x00000000,
};
int main ( void )
{
unsigned int ra;
unsigned int rb;
rb=0;
for(ra=0;ra<7;ra++)
{
rb+=data[ra];
}
data[7]=(-rb);
rb=0;
for(ra=0;ra<8;ra++)
{
rb+=data[ra];
printf("0x%08X 0x%08X\n",data[ra],rb);
}
return(0);
}
output:
0x20001000 0x20001000
0x00000023 0x20001023
0x00000021 0x20001044
0x00000021 0x20001065
0x00000021 0x20001086
0x00000021 0x200010A7
0x00000021 0x200010C8
0xDFFFEF38 0x00000000
then cut and pasted stuff into the answer.
How I have done it in the past is make an adhoc util that I call from my makefile that operates on the objcopied .bin file and either modifies that one or creates a new .bin file that has the checksum applied. You should be able to write that in 20-50 lines of code, choose your favorite language.
another comment question:
.cpu cortex-m0
.thumb
.word one
.word two
.word three
.thumb_func
one:
nop
two:
.thumb_func
three:
nop
Disassembly of section .text:
00000000 <one-0xc>:
0: 0000000d andeq r0, r0, sp
4: 0000000e andeq r0, r0, lr
8: 0000000f andeq r0, r0, pc
0000000c <one>:
c: 46c0 nop ; (mov r8, r8)
0000000e <three>:
e: 46c0 nop ; (mov r8, r8)
the .thumb_func affects the label AFTER...

How to do analogRead() in AVR assembly language?

If I need to be specific: I'm asking about ATmega328P chip. The analog pins are under PortC on this chip.
I have learnt that digitalWrite can be done using out, and digitalRead using in.
But how can I do analogRead ?? Please explain. I'm new to this.
EXTRA: It would be helpful if you show analogWrite too (In the sense of PWM).
You can read the source code of analogRead from the Arduino environment:
https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/wiring_analog.c
The important thing is to find all the places where it reads or writes from a special function register (SFR) like ADMUX, and then make sure you do the same thing in your assembly code.
You should also look at the ATmega328P datasheet, which defines all of those SFRs, as a way to double check that you are doing the correct thing.
If you have further trouble, I recommend asking a new question where you show some code and get specific about exactly what part of analogRead is confusing to you.
This is for the future visitors who stumble upon here...
As mentioned by Rev1.0, Arduino C does make things too easy for you. A lot of complicated things are going on under the hood when you write a simple statement analogRead(). But it's not that complicated once you understand it. You should definitely read up on ADCs.
As mentioned by David Grayson, you should definitely take a look at the source code of analogRead(). Here is the datasheet of ATmega328P and the instruction set manual for ATmega328P to help you understand what is going on.
You can read this and this to get some idea on how to exactly write the code.
Now, here is what I came up with for my use-case in my project.
The bold-face words are there to tell you that this code was NOT written for a general use-case. Copy-Pasting this will most probably not work.
You see the amuont of links in this post? Read all of them. Below is only for using as a reference in case you get stuck and it might help.
adcInit:
ldi r16, 0b01100000 ; Voltage Reference: AVcc with external capacitor at AREF pin
sts ADMUX, r16 ; Enable ADC Left Adjust Result
; Analog Channel: ADC0
ldi r16, 0b10000101 ; Enable ADC
sts ADCSRA, r16 ; ADC Prescaling Factor: 32
ret
adcRead:
ldi r16, 0b01000000 ; Set ADSC flag to Trigger ADC Conversion process
lds r17, ADCSRA ;
or r17, r16 ;
sts ADCSRA, r17 ;
ret
adcWait:
lds r17, ADCSRA ; Observe the ADIF flag, it gets set by hardware when ADC conversion completes
sbrs r17, 4 ;
jmp adcWait ; Keep checking until the flag is set by hardware
ldi r16, 0b00010000 ; Set the flag again to signal 'ready-to-be-cleared' by hardware
lds r17, ADCSRA ;
or r17, r16 ;
sts ADCSRA, r17 ;
ret
It is used like this:
call adcInit
mainLoop:
call adcRead
call adcWait
lds r18, ADCL ; Must read ADCL first, and ADCH after that
lds r19, ADCH
After a long time struggle with me, I survey the datasheet of ATmega 328P and many google surfing articles, the simple and workable code is completed as below.
; UNO_asmADCapp.asm
; revised by bsliao: 2020/5/12 下午 03:39:20, TEST OK 2020/05/13, 11:33
; Reference:
; https://stackoverflow.com/questions/38972805/
; [1] how-to-code-an-adc-for-an-atmega328p-in-assembly
; Author : Dario, Created: 8/14/2016 7:34:43 AM
; [2] https://robotics.ee.uwa.edu.au/courses/des/labprep/
; LabPrep%205%20-%20Timers%20and%20ADC%20in%20ATMEL.pdf
; [3] https://www.avrfreaks.net/forum/adc-converter-assembly-using-atmega328p-mcu
; AD0 --- uno A0
; value ADCH (b9 b8) ADCL (b7- b0) <Internal> --- PB1(uno d9) PB0 (d8), PD7-PD0 (uno D7 -D0)
#define F_CPU 16000000UL
.def temp =r16
; Replace with your application code
.include "./m328Pdef.inc"
.org 0x000
rjmp start
; .org 0x002A
; rjmp ADC_conversion_complete_Handler
start:
eor r1, r1
out SREG, r1
ldi temp, HIGH(RAMEND)
out SPH, r16
ldi temp, LOW(RAMEND)
out SPL, r16
setup:
ldi temp, 0xFF ; set r16 = 1111 1111
out ddrb, temp ; set all d pins as output
out ddrd, temp ; set all b pins as output
configADC0:
;------initialize ADC0 ------- Set ADMUX and ADCSRA:
;REF1 REFS0 ALLAR - (MUX3 MUX2 MUX1 MUX0 )=(0000)
;Aref=5.0 V is used, default right-adjust result, analog in at AIN0 (ADC0)
LDI temp, 0x00
STS ADMUX, temp
;ADcENable, (ADPS2 ADPS1 ADPS0 )=(000) : division factor=128 16Mhz/128: ADC0 is applied.
LDI temp, (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
STS ADCSRA, temp
andi temp, 0b11011111
STS ADCSRA, temp
; the first conversion
LDS temp,ADCSRA
ori temp, (1<<ADSC);
STS ADCSRA, temp
LOOP:
; start the next single conversion on ADCn, here n=0
LDS temp,ADCSRA
ori temp, (1<<ADSC);
STS ADCSRA, temp
adc_read_loop:
// while (bit_is_set(ADCSRA, ADSC));
lds temp,ADCSRA
sbrc temp,ADSC ;after ADC0 conversion over, the bit ADSC in the ADCSRA is set to zero and the bit ADIF is set to one.
rjmp adc_read_loop
read_ADC_value:
lds r24,ADCL
lds r25,ADCH
display_ADC_value:
andi r25, 0x03
out PORTB, r25 ; LEDs active high, PORTB most significant byte
com r24 ; LEDs active low
out PORTD, r24 ; PORTD less significant byte
call one_sec_delay
rjmp LOOP
one_sec_delay:
ldi r20, 20
ldi r21, 255
ldi r22, 255
delay: dec r22
brne delay
dec r21
brne delay
dec r20
brne delay
ret

Resources