How do I set up the speaker on a HCS12 microcontroller? - button

Basically, my microcontroller is supposed to play a sound based on the push buttons (PTH) I press and light up the LED (PORTB). What I am stuck on is how to set up the speaker to output sound when the push buttons are pressed. A lot of the code is based off code from here: http://www.microdigitaled.com/HCS12/Hardware/Dragon12-Plus-Support.htm
Specifically the link about the buzzer. Also when I use the code provided I am able to hear sound coming from my microcontroller; however not when I try to include the push button into the code.
I would greatly appreciate any hints, comments, tips, or suggestions. Thank you so much!
Here is my code so far:
;----------------------USE $1000-$2FFF for Scratch Pad
R1 EQU $1001
R2 EQU $1002
R3 EQU $1003
R4 EQU $1004
;code section
ORG $2000 ;Flash ROM address for Dragon12+
Entry:
LDS #$2000 ;Stack
BSET DDRT,%00100000 ;PTT5 as Output pin for buzzer
BSET DDRP,#$0F ; Set Port P pins 0-3 to output
BSET PTP, #$0F ; Disable 7-Segment Display
; LED
BSET DDRB,$FF
BSET DDRJ,$02
BCLR PTJ,$02
; PBs
BCLR DDRH,$0F
BCLR PTH,$FF ;PORTB as Output
;-------Sound the Buzzer at PTT5
BACK
;-------Get data from DIP switches connected to PORTH and send it to LEDs of PORTB
;how is the speaker supposed to be set up? because when I try the code the speaker does not output any sound.
LDAA PTH ;Get data from DIP Switches of PTH
STAA PORTB ;and send it to PORTB
BSET PTT,%00100000 ;PTT5=1
JSR DELAY
BCLR PTT,%00100000 ;PTT5=0
JSR DELAY
BRA BACK ;Keep toggling buzzer
Is my delay okay? Am I on the right track or is there a more convenient way of changing the notes?
;----------DELAY
DELAY
COMA
TSTA
CMPA #$01
BNE no_note1
PSHA ;Save Reg A on Stack
LDAA #7 ;Change this value to hear
STAA R3 ;different Buzzer sounds
;--1 msec delay. The Serial Monitor works at speed of 48MHz with XTAL=8MHz on Dragon12+ board
;Freq. for Instruction Clock Cycle is 24MHz (1/2 of 48Mhz).
;(1/24MHz) x 10 Clk x240x10=1 msec. Overheads are excluded in this calculation.
L3 LDAA #10
STAA R2
L2 LDAA #200
STAA R1
L1 NOP ;1 Intruction Clk Cycle
NOP ;1
NOP ;1
DEC R1 ;4
BNE L1 ;3
DEC R2 ;Total Instr.Clk=10
BNE L2
DEC R3
BNE L3
PULA ;Restore Reg A
no_note1:
CMPA #$02
BNE no_note2
PSHA ;Save Reg A on Stack
LDAA #3 ;Change this value to hear
STAA R3 ;different Buzzer sounds
;--1 msec delay. The Serial Monitor works at speed of 48MHz with XTAL=8MHz on Dragon12+ board
;Freq. for Instruction Clock Cycle is 24MHz (1/2 of 48Mhz).
;(1/24MHz) x 10 Clk x240x10=1 msec. Overheads are excluded in this calculation.
L9 LDAA #10
STAA R2
L8 LDAA #210
STAA R1
L7 NOP ;1 Intruction Clk Cycle
NOP ;1
NOP ;1
DEC R1 ;4
BNE L7 ;3
DEC R2 ;Total Instr.Clk=10
BNE L8
DEC R3
BNE L9
PULA ;Restore Reg A
no_note2:
CMPA #$04
BNE no_note3
PSHA ;Save Reg A on Stack
LDAA #2 ;Change this value to hear
STAA R3 ;different Buzzer sounds
;--1 msec delay. The Serial Monitor works at speed of 48MHz with XTAL=8MHz on Dragon12+ board
;Freq. for Instruction Clock Cycle is 24MHz (1/2 of 48Mhz).
;(1/24MHz) x 10 Clk x240x10=1 msec. Overheads are excluded in this calculation.
L4 LDAA #15
STAA R2
L5 LDAA #240
STAA R1
L6 NOP ;1 Intruction Clk Cycle
NOP ;1
NOP ;1
DEC R1 ;4
BNE L6 ;3
DEC R2 ;Total Instr.Clk=10
BNE L5
DEC R3
BNE L4
PULA ;Restore Reg A
no_note3:
CMPA #$08
BNE no_note4
PSHA ;Save Reg A on Stack
LDAA #9 ;Change this value to hear
STAA R3 ;different Buzzer sounds
;--1 msec delay. The Serial Monitor works at speed of 48MHz with XTAL=8MHz on Dragon12+ board
;Freq. for Instruction Clock Cycle is 24MHz (1/2 of 48Mhz).
;(1/24MHz) x 10 Clk x240x10=1 msec. Overheads are excluded in this calculation.
L30 LDAA #16
STAA R2
L20 LDAA #249
STAA R1
L10 NOP ;1 Intruction Clk Cycle
NOP ;1
NOP ;1
DEC R1 ;4
BNE L10 ;3
DEC R2 ;Total Instr.Clk=10
BNE L20
DEC R3
BNE L30
;--------------
PULA ;Restore Reg A
no_note4:
RTS
;-------------------
;----------------------------------------------------
; Interrupt Vector
;----------------------------------------------------
; Port H Vector
; $3E4C = $FFCC - $C180
ORG $3E4C
FDB DELAY
FINISH:
NOP
END

Related

How is recursion possible in AVR Assembly?

I can't seem to wrap my head around recursion in Assembly Language. I understand how it works in higher level languages, but I don't understand how it is possible in assembly when the return value cannot be passed directly to the function.
I'm trying to make a recursive factorial function in AVR, but I don't understand how the stack passes the value when factorial requires n * (n-1), requiring both n and n-1 simultaneously
I just helped another person with the small code below to calculate factorial in AVR AtMega assembly.
It produces a factorial from 1~10, resulting in decimal 3628800 (hex 0x375F00).
It uses exactly what the OP wanted, if selected 8! as number! in R2, it will move 8 to the resulting bytes, then multiply by number!-1 and so on, until it reaches 1, then it ends. The multiplication 24x8 is the trickiest I could write, saving registers and clock cycles. It doesn't use stack nor RAM, straight use of AVR registers.
; Input at R2, value 1~10, from 1! to 10!
; Result 1~3628800 (0x375F00) at: R20:R21:R22 (LSB)
; Temporary Multiplication Middle Byte: R17
ldi r16, low(RAMEND)
out SPL, r16
ldi r16, high(RAMEND)
out SPH, r16
Mov R16, R2 ; Get Value to factor
Rcall A0 ; Call Factorial
...
A0: Clr R20 ; Results = Number!
Clr R21 ;
Ldi R22, R16 ;
A1: Dec R16 ; Number! - 1
Cpi R16,1 ; If 1 then ended
Brne A2 ;
Ret
; This multiplication 24x8 is tricky, fast and save bytes
A2: Mul R22, R16 ; Mul Result LSB x Number!-1
Mov R22, R0 ; LSB Mul to Result LSB Byte
Mov R17, R1 ; MSB Mul to Temporary Middle Byte
Mul R20, R16 ; Mul Result MSB x Number!-1
Mov R20, R0 ; LSB Mul to MSB Result Byte, ignore MSB Mul, will be zero
Mul R21, R16 ; Mul Result Middle x Number!-1
Mov R21, R0 ; LSB Mul to Result Middle Byte
Add R21, R17 ; Add Temporary Middle to Result Middle Byte
Adc R20, R1 ; Add MSB Mul with Carry to Result MSB Byte
Rjmp A1
Using addition instead of multiplication
unsigned int accumulate(unsigned int n)
{
if(n) return(n+accumulate(n-1));
return(1);
}
and a different instruction set, perhaps easier to follow
00000000 <accumulate>:
0: e3500000 cmp r0, #0
4: 0a000005 beq 20 <accumulate+0x20>
8: e3a03000 mov r3, #0
c: e0833000 add r3, r3, r0
10: e2500001 subs r0, r0, #1
14: 1afffffc bne c <accumulate+0xc>
18: e2830001 add r0, r3, #1
1c: e12fff1e bx lr
20: e3a00001 mov r0, #1
24: e12fff1e bx lr
In this case the compiler didnt actually call the function, it detected what was going on and just made a loop.
Since there is nothing magic about recursion there is no difference in whether you call the same function or some other function.
unsigned int otherfun ( unsigned int );
unsigned int accumulate(unsigned int n)
{
if(n) return(n+otherfun(n-1));
return(1);
}
00000000 <accumulate>:
0: e92d4010 push {r4, lr}
4: e2504000 subs r4, r0, #0
8: 03a00001 moveq r0, #1
c: 0a000002 beq 1c <accumulate+0x1c>
10: e2440001 sub r0, r4, #1
14: ebfffffe bl 0 <otherfun>
18: e0800004 add r0, r0, r4
1c: e8bd4010 pop {r4, lr}
20: e12fff1e bx lr
so this shows how it works. Instead of using the stack to store the sum, the cheaper solution if you have the registers is to use a non-volatile register save that register to the stack then use that register during the funciton, depends on how many registers you have and how many local intermediate values you need to track. So r4 gets a copy of n coming in, then that is added (for factorial it is a multiply which depending on the instruction set and code can produce a lot more code that can confuse the understanding so I used addition instead) to the return value from the call to the next function (with recursion where the compiler didnt figure out what we were doing this would have been a call to ourselves, and we can write this asm and make it a call to ourselves to see how it works)
Then the function returns the sum.
If we assume that otherfun is really accumulate we enter this function with a 4 lets say
00000000 <accumulate>:
0: e92d4010 push {r4, lr}
4: e2504000 subs r4, r0, #0
8: 03a00001 moveq r0, #1
c: 0a000002 beq 1c <accumulate+0x1c>
10: e2440001 sub r0, r4, #1
14: ebxxxxxx bl accumulate
18: e0800004 add r0, r0, r4
1c: e8bd4010 pop {r4, lr}
20: e12fff1e bx lr
r4 and lr are saved on the stack (call this r4-4 and lr-4)
r4 = n (4)
r0 = n-1 (3)
call accumulate with n-1 (3)
r4 (4) and lr are saved on the stack (r4-3, lr-3) lr now points back into
r4 = n (3)
r0 = n-1 (2)
call accumulate with n-1 (2)
r4 (3) and lr are saved on the stack (r4-2, lr-2)
r4 = n (2)
r0 = n-1 (1)
call accumulate with n-1 (1)
r4 (2) and lr are saved on the stack (r4-1, lr-1)
r0 = n-1 (0)
call accumulate with n-1 (0)
now things change...
r0 = 1
return to lr-1 which is into accumulate after the call to accumulate
r4 gets 2 from the stack
r0 (1) = r0 (1) + r4 (2) = 3
return to lr-2 which is into accumulate r4 gets 3 from the stack
r0 (3) = r0 (3) + r4 (3) = 6
return to lr-3 which is into accumulate r4 gets 4 from the stack
r0 (6) = r0 (6) + r4 (4) = 10
return to lr-4 which is the function that called accumulate r4 is restored
to what it was before accumulate was first called, r4 is non-volatile you have to for this instruction set return r4 the way you found it (as well
as others, but we didnt modify those)
so the addition in this case multiplication in your desired case is
result = 1 + 2 + 3 + 4
How that happened is we basically pushed n on the stack then called the function with n-1. In this case we push 4, 3, 2, 1 then we start to unwind that and each return processes 1 then 2 then 3 then 4 as it returns
taking those from the stack essentially.
the bottom line is you dont have to care about recursion to support recursion simply use an abi that supports recursion, which is not hard to
do, then hand code the instructions in assembly as if you were the compiler
Maybe this makes it easier to see. n coming in is both a parameter coming in but also for the duration of the function it is a local variable, local
variables go on the stack.
unsigned int accumulate(unsigned int n)
{
unsigned int m;
m = n;
if(n) return(m+accumulate(n-1));
return(1);
}
back to this
unsigned int accumulate(unsigned int n)
{
if(n) return(n+accumulate(n-1));
return(1);
}
so independent of the instruction set
accumulate:
if(n!=0) jump over
return_reg = 1
return
over:
push n on the stack
first parameter (stack or register) = n - 1
call accumulate
pop or load n from the stack
return_reg = return_reg + n
clean stack
return
And also deal with return addresses for the instruction set if required.
The ABI may use the stack to pass parameters or registers.
If I didnt follow the arm abi I could implement
accumulate:
cmp r0,#0
bne over
mov r0,#1
bx lr
over:
push {lr}
push {r0}
sub r0,#1
bl accumulate
pop {r1}
add r0,r0,r1
pop {lr}
bx lr
for grins an instruction set that uses the stack for most things not
registers
00000000 <_accumulate>:
0: 1166 mov r5, -(sp)
2: 1185 mov sp, r5
4: 10a6 mov r2, -(sp)
6: 1d42 0004 mov 4(r5), r2
a: 0206 bne 18 <_accumulate+0x18>
c: 15c0 0001 mov $1, r0
10: 1d42 fffc mov -4(r5), r2
14: 1585 mov (sp)+, r5
16: 0087 rts pc
18: 1080 mov r2, r0
1a: 0ac0 dec r0
1c: 1026 mov r0, -(sp)
1e: 09f7 ffde jsr pc, 0 <_accumulate>
22: 6080 add r2, r0
24: 65c6 0002 add $2, sp
28: 1d42 fffc mov -4(r5), r2
2c: 1585 mov (sp)+, r5
2e: 0087 rts pc
it does a stack frame thing
gets the n parameter from the stack
saves that n parameter to the stack
compares and branches if not zero
in the if zero case we set the return value to 1
clean up the stack and return
now in the if not zero case
make the first parameter n-1
call a function (ourself)
do the addition and return

loRecursion Example in ARM Assembly

Can someone give me an example of how recursion would be done in ARM Assembly with only the instructions listed here (for visUAL)?
I am trying to do a recursive fibonacci and factorial function for class. I know recursion is a function that calls a function, but I have no idea how to simulate that in ARM.
https://salmanarif.bitbucket.io/visual/supported_instructions.html
In case the link doesn't work, I am using visUAL and these are the only instructions I can use:
MOV
MVN
ADR
LDR
ADD
ADC
SUB
SBC
RSB
RSC
AND
EOR
BIC
ORR
LSL
LSR
ASR
ROR
RRX
CMP
CMN
TST
TEQ
LDR
LDM
STM
B
BL
FILL
END
This doesn't load an older value for R4, so R4 just doubles every time the function calls itself.
;VisUAL initializess all registers to 0 except for R13/SP, which is -16777216
MOV R4, #0
MOV R5, #1
MOV r0, #4
MOV LR, #16 ;tells program to move to 4th instruction
FIB
STMDB SP!, {R4-R6, LR} ;Stores necessary values on stack (PUSH command)
LDR R4, [SP] ;Loads older value for R4 from memory
ADD R4, R4, R5 ;Adds R5 to R4
STR R4, [SP], #8 ;stores current value for R4 to memory
MOV R5, R4 ;Makes R5 = R4
CMP R4, #144 ;If R4 >= 144:
BGE POP ;Branch to POP
MOV PC, LR ;Moves to STMDB(PUSH) statement
POP
LDMIA SP!, {R4-R6, LR} ;Pops registers off stack
END ;ends program
You need to use the stack, STMDB and LDMIA instructions. On real ARM tools with "unified" notation, they also have mnemonics PUSH and POP.
Fibonnaci and factorial are not great examples as they don't "need" recursion. But let's pretend they do. I'll pick Fibonacci as you don't have a MUL instruction!? You want to do something like this:
START
MOV R0, #6
BL FIB
END ; pseudo-instruction to make your simulator terminate
FIB ; int fib(int i) {
STMDB SP!, {R4,R5,R6,LR} ; int n, tmp;
MOV R4, R0 ; n = i;
CMP R0, #2 ; if (i <= 2) {
MOV R0, #1 ; return 1;
BLE FIB_END ; }
SUB R0, R4, #2 ; i = n-2;
BL FIB ; i = fib(i);
MOV R5, R0 ; tmp = i;
SUB R0, R4, #1 ; i = n-1;
BL FIB ; i = fib(i);
ADD R0, R0, R5 ; i = i + tmp;
FIB_END ; return i;
LDMIA SP!, {R4,R5,R6,PC} ; }
It should terminate with R0 containing fib(6) == 8. Of course this code is very inefficient as it repeatedly calls FIB for the same values.
The STM is needed so you can use registers r4,r5 because another function call can change r0-r3 and LR. Pushing LR and popping PC is like B LR. If you were calling C code you should push an even number of registers to keep SP 64-bit aligned (we don't really need to do that here; ignore R6).
some other recursive function:
unsigned int so ( unsigned int x )
{
static unsigned int z=0;
z+=x;
if(x==0) return(z);
so(x-1);
return(z);
}
build/disassemble
arm-none-eabi-gcc -O2 -c Desktop/so.c -o so.o
arm-none-eabi-objdump -D so.o
00000000 <so>:
0: e92d4010 push {r4, lr}
4: e59f4034 ldr r4, [pc, #52] ; 40 <so+0x40>
8: e5943000 ldr r3, [r4]
c: e3500000 cmp r0, #0
10: e0803003 add r3, r0, r3
14: e5843000 str r3, [r4]
18: 1a000002 bne 28 <so+0x28>
1c: e1a00003 mov r0, r3
20: e8bd4010 pop {r4, lr}
24: e12fff1e bx lr
28: e2400001 sub r0, r0, #1
2c: ebfffffe bl 0 <so>
30: e5943000 ldr r3, [r4]
34: e8bd4010 pop {r4, lr}
38: e1a00003 mov r0, r3
3c: e12fff1e bx lr
40: 00000000
If you dont understand it then is it worth it. Is it cheating to let a tool do it for you?
push is a pseudo instruction for stm, pop a pseudo instruction for ldm, so you can use those.
I used a static local which I call a local global, it lands in .data not on the stack (well .bss in this case as I made it zero)
Disassembly of section .bss:
00000000 <z.4099>:
0: 00000000
the first to loads are loading this value into r3.
the calling convention says that r0 will contain the first parameter on entry into the function (there are exceptions, but it is true in this case).
so we go and get z from memory, r0 already has the parameter x so we add x to z and save it to memory
the compiler did the compare out of order for who knows performance reasons, the add and str as written dont modify flags so that is okay,
if x is not equal to zero it branches to 28 which does the so(x-1) call
reads r3 back from memory (the calling convention says that r0-r3 are volatile a function you can can modify them at will and doesnt have to preserve them so our version of z in r3 might have been destroyed but r4 is preserved by any callee, so we read z back into r3. we pop r4 and the return address off the stack, we prepare the return register r0 with z and do the return.
if x was equal to zero (bne on 18 failed we run 1c, then 20, then 24) then we copy z (r3 version) into r0 which is the register used for returning from this function per the calling convention used by this compiler (arms recommendation). and returns.
the linker is going to fill in the address of z to the offset 0x40, this is an object not a final binary...
arm-none-eabi-ld -Ttext=0x1000 -Tbss=0x2000 so.o -o so.elf
arm-none-eabi-ld: warning: cannot find entry symbol _start; defaulting to 0000000000001000
arm-none-eabi-objdump -D so.elf
so.elf: file format elf32-littlearm
Disassembly of section .text:
00001000 <so>:
1000: e92d4010 push {r4, lr}
1004: e59f4034 ldr r4, [pc, #52] ; 1040 <so+0x40>
1008: e5943000 ldr r3, [r4]
100c: e3500000 cmp r0, #0
1010: e0803003 add r3, r0, r3
1014: e5843000 str r3, [r4]
1018: 1a000002 bne 1028 <so+0x28>
101c: e1a00003 mov r0, r3
1020: e8bd4010 pop {r4, lr}
1024: e12fff1e bx lr
1028: e2400001 sub r0, r0, #1
102c: ebfffff3 bl 1000 <so>
1030: e5943000 ldr r3, [r4]
1034: e8bd4010 pop {r4, lr}
1038: e1a00003 mov r0, r3
103c: e12fff1e bx lr
1040: 00002000
Disassembly of section .bss:
00002000 <z.4099>:
2000: 00000000
the point here is not to cheat and use a compiler, the point here is there is nothing magical about a recursive function, certainly not if you follow a calling convention or whatever your favorite term is.
for example
if you have parameters r0 is first, r1 second, up to r3 (if they fit, make your code such that it does and you have four or less parameters)
the return value is in r0 if it fits
you need to push lr on the stack as you will be calling another function
r4 on up preserve if you need to modify them, if you want some local storage either use the stack by modifying the stack pointer accordingly (or doing pushes/stms). you can see that gcc instead saves what was in the register to the stack and then uses the register during the function, at least up to a few local variables worth, beyond that it would need to bang on the stack a lot, sp relative.
when you do the recursive call you do so as you would any other normal function according to the calling convention, if you need to save r0-r3 before calling then do so either in a register r4 or above or on the stack, restore after the function returns. you can see it is easier just to put the values you want to keep before and after a function call in a register r4 or above.
the compiler could have done the compare of r0 just before the branch, reads easier that way. Likewise could have done the mov to r0 of the return value before the pop
I didnt put parameters, so my build of gcc here appears to be armv4t, if I ask for something a little newer
arm-none-eabi-gcc -O2 -c -mcpu=mpcore Desktop/so.c -o so.o
arm-none-eabi-objdump -D so.o
so.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <so>:
0: e92d4010 push {r4, lr}
4: e59f402c ldr r4, [pc, #44] ; 38 <so+0x38>
8: e3500000 cmp r0, #0
c: e5943000 ldr r3, [r4]
10: e0803003 add r3, r0, r3
14: e5843000 str r3, [r4]
18: 1a000001 bne 24 <so+0x24>
1c: e1a00003 mov r0, r3
20: e8bd8010 pop {r4, pc}
24: e2400001 sub r0, r0, #1
28: ebfffffe bl 0 <so>
2c: e5943000 ldr r3, [r4]
30: e1a00003 mov r0, r3
34: e8bd8010 pop {r4, pc}
38: 00000000
You can see the returns read a little easier
although an optimization was missed it could have done an ldr r0,[r4] and saved an instruction. or leave that tail end as is and the bne could have been a beq to 30 (mov r0,r3; pop{r4,pc} and shared an exit.
a little more readable
so:
push {r4, lr}
# z += x
ldr r4, zptr
ldr r3, [r4]
add r3, r0, r3
str r3, [r4]
# if x==0 return z
cmp r0, #0
beq l30
# so(x - 1)
sub r0, r0, #1
bl so
ldr r3, [r4]
l30:
# return z
mov r0, r3
pop {r4, pc}
zptr: .word z
.section .bss
z: .word 0
arm-none-eabi-as so.s -o so.o
arm-none-eabi-objdump -D so.o
so.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <so>:
0: e92d4010 push {r4, lr} (stmdb)
4: e59f4024 ldr r4, [pc, #36] ; 30 <zptr>
8: e5943000 ldr r3, [r4]
c: e0803003 add r3, r0, r3
10: e5843000 str r3, [r4]
14: e3500000 cmp r0, #0
18: 0a000002 beq 28 <l30>
1c: e2400001 sub r0, r0, #1
20: ebfffff6 bl 0 <so>
24: e5943000 ldr r3, [r4]
00000028 <l30>:
28: e1a00003 mov r0, r3
2c: e8bd8010 pop {r4, pc} (ldmia)
00000030 <zptr>:
30: 00000000
Disassembly of section .bss:
00000000 <z>:
0: 00000000
EDIT
So lets walk through this last one.
push {r4,lr} which is a pseudo instruction for stmdb sp!,{r4,lr}
Lr is the r14 which is the return address look at the bl instruction
branch and link, so we branch to some address but lr (link register) is
set to the return address, the instruction after the bl. So when main or some other function calls so(4); lets assume so is at address 0x1000 so the program counter, r15, pc gets 0x1000, lr will get the value of the instruction after the caller so lets say that is 0x708. Lets also assume the stack pointer during this first call to so() from main is at 0x8000, and lets say that .bss is at 0x2000 so z lives at address 0x2000 (which also means the value at 0x1030, zptr is 0x2000.
We enter the function for the first time with r0 (x) = 4.
When you read the arm docs for stmdb sp!,{r4,lr} it decrements before (db) so sp on entry this time is 0x8000 so it decrements for the two items to 0x7FF8, the first item in the list is written there so
0x7FF8 = r4 from main
0x7FFC = 9x 0x708 return address to main
the ! means sp stays modified so sp-0x7ff8
then ldr r4,zptr r4 = 0x2000
ldr r3,[r4] this is an indirect load so what is at address r4 is read to
put in r3 so r3 = [0x2000] = 0x0000 at this point the z variable.
z+=x; add r3,r0,r3 r3 = r0 + r3 = 4 + 0 = 4
str r3,[r4] [r4] = r3, [0x2000] = r3 write 4 to 0x2000
cmp r0,#0 4 != 0
beq to 28 nope, not equal so no branch
sub r0,r0,#1 r0 = 4 - 1 = 3
bl so so this is so(3); pc = 0x1000 lr = 0x1024
so now we enter so for the second time with r0 = 3
stmdb sp!,{r4,lr}
0x7FF0 = r4 (saving from so(4) call but we dont care its value even though we know it)
0x7FF4 = lr from so(4) = 0x1024
sp=0x7FF0
ldr r4,zptr r4 = 0x2000
ldr r3,[r4] r3 = [0x2000] = 4
add r3,r0,r3 r3 = 3 + 4 = 7
str r3,[r4] write 7 to 0x2000
cmp r0,#0 3 != 0
beq 0x1028 not equal so dont branch
sub r0,r0,#1 r0 = 3-1 = 2
bl so pc=0x1000 lr=0x1024
so(2)
stmdb sp!,{r4,lr}
0x7FE8 = r4 from caller, just save it
0x7FEC = lr from caller, 0x1024
sp=0x7FE8
ldr r4,zprt r4=0x2000
ldr r3,[r4] r3 = read 7 from 0x2000
add r3,r0,r3 r3 = 2 + 7 = 9
str r3,[r4] write 9 to 0x2000
cmp r0,#0 2 != 0
beq 0x1028 not equal so dont branch
sub r0,r0,#1 r0 = 2 - 1 = 1
bl 0x1000 pc=0x1000 lr=0x1024
so(1)
stmdb sp!,{r4,lr}
0x7FE0 = save r4
0x7FE4 = lr = 0x1024
sp=0x7FE0
ldr r4,zptr r4=0x2000
ldr r3,[r4] r3 = read 9 from 0x2000
add r3,r0,r3 r3 = 1 + 9 = 10
str r3,[r4] write 10 to 0x2000
cmp r0,#0 1 != 0
beq 0x1028 not equal so dont branch
sub r0,r0,#1 r0 = 1 - 1 = 0
bl 0x1000 pc=0x1000 lr=0x1024
so(0)
stmdb sp!,{r4,lr}
0x7FD8 = r4
0x7FDC = lr = 0x1024
sp = 0x7FD8
ldr r4,zptr r4 = 0x2000
ldr r3,[r4] r3 = read 10 from 0x2000
add r3,r0,r3 r3 = 0 + 10 = 10
str r0,[r4] write 10 to 0x2000
cmp r0,#0 0 = 0 so it matches
beq 0x1028 it is equal so we finally take this branch
mov r0,r3 r0 = 10
ldmia sp!,{r4,pc}
increment after
r4 = [sp+0] = [0x7FD8] restore r4 from caller
pc = [sp+4] = [0x7FDC] = 0x1024
sp += 8 = 0x7FE0
(branch to 0x1024)(return from so(0) to so(1))
ldr r3,[r4] read 10 from 0x2000
mov r0,r3 r0 = 10
ldmia sp!,{r4,pc}
r4 = [sp+0] = [0x7FE0] restore r4 from caller
pc = [sp+4] = [0x7FE4] = 0x1024
sp += 8 = 0x7FE8
(branch to 0x1024)(return from so(1) to so(2))
ldr r3,[r4] read 10 from 0x2000
mov r0,r3 r0 = 10
ldmia sp!,{r4,pc}
r4 = [sp+0] = [0x7FE8] restore r4 from caller
pc = [sp+4] = [0x7FEC] = 0x1024
sp += 8 = 0x7FF0
(branch to 0x1024)(return from so(2) to so(3))
ldr r3,[r4] read 10 from 0x2000
mov r0,r3 r0 = 10
ldmia sp!,{r4,pc}
r4 = [sp+0] = [0x7FF0] restore r4 from caller
pc = [sp+4] = [0x7FF4] = 0x1024
sp += 8 = 0x7FF8
(branch to 0x1024)(return from so(3) to so(4))
ldr r3,[r4] read 10 from 0x2000
mov r0,r3 r0 = 10
ldmia sp!,{r4,pc}
r4 = [sp+0] = [0x7FF8] restore r4 from caller (main()'s r4)
pc = [sp+4] = [0x7FFC] = 0x708
sp += 8 = 0x8000
(branch to 0x708)(return from so(4) to main())
and we are done.
A stack is like a dixie cup holder which might be before your time. A cup holder where you pull a cup down and the next and rest of the cups stay in the holder, well you can shove one back up in there.
So a stack is temporary storage for the function, write one data item on the cup, then shove it up into the holder (save r4 from caller) write another item and shove it up into the holder (lr, return address from caller). we only used two items per function here, so each function I can push two cups up into the holder, each call of the function I get two NEW AND UNIQUE storage locations to store this local information. As I exit the function I pull the two cups down out of the holder and use their values (and discard them). This is to some extent the key to recursion, the stack gives you new local storage for each call, separate from prior calls to the same function, if nothing else you need a return address (although did make some even simpler recursion example that didnt when optimized was smart enough to make a loop out of it basically).
ldr rd,[rn] think of he brakets as saying the item at that address so read memory at the address in rn and save that value in rd.
str rd,[rn] the one messed up arm instruction as the rest the first parameter is the left side of the equals (add r1,r2,r3 r1 = r2 + r3, ldr r1,[r4] r1 = [r4]) this one is backward [rn] = rd store the value in rd to the memory location described by the address r4, one level of indirection.
stmdb sp!, means decrement the stack pointer before doing anything 4 bytes times the number of registers in the list, then write the first, lowest numbered register to [sp+0], then next to [sp+4] and so on the last one will be four less than the starting value of sp. The ! means the function finishes with sp being that decremented value. You can use ldm/stm for things other than stack pushes and pops. Like memcpy,but that is another story...
All of this is in the arm documentation from infocenter.arm.com which you should already have (arm architectural reference manual, armv5 is the preferred first one if you have not read one).

Creating simple TCNT0 program ATMEGA 328 P-PU

I have a trouble with my beginner program for Arduino + ATMEGA 328 P-PU with tcnt0 in CTC mode, where I am using overflow made by OCR0A to make some visible delay. It looks, that counter is working somehow, because OC0A lit, however delay made with TOV0 or OCF0A doesnot work, because of "strange lit of apropriate diode". Could you help me to find mistake(s) in my program? Thanks all for help!
Btw: Do you know some good and verified debuger under Linux?
Program with mistakes:
; TCCR0 registers addresses
.equ tccr0a, 0x44
.equ tccr0b, 0x45
.equ tcnt0, 0x46
.equ ocr0a, 0x47
.equ ocr0b, 0x48
.equ tifr0, 0x35
.equ timsk0, 0x6E
.equ io_tccr0a, 0x24
.equ io_tccr0b, 0x25
.equ io_tcnt0, 0x26
.equ io_ocr0a, 0x27
.equ io_ocr0b, 0x28
.equ io_tifr0, 0x15
; PORT registers addresses
.equ pinb, 0x23
.equ io_pinb, 0x3
.equ ddrb, 0x24
.equ io_ddrb, 0x4
.equ portb, 0x25
.equ io_portb, 0x5
.equ pinc, 0x26
.equ io_pinc, 0x6
.equ ddrc, 0x27
.equ io_ddrc, 0x7
.equ portc, 0x28
.equ io_portc, 0x8
.equ pind, 0x29
.equ io_pind, 0x9
.equ ddrd, 0x2A
.equ io_ddrd, 0xA
.equ portd, 0x2B
.equ io_portd, 0xB
; SREG and STACK registers addresses
.equ sreg, 0x5F
.equ io_sreg, 0x3F
.equ sph, 0x5E
.equ io_sph, 0x3E
.equ spl, 0x5D
.equ io_spl, 0x3D
; Constants
.equ RAMEND, 0x8FF ; End of SRAM for ATMEGA328P
.equ MILIS_VALUE, 0x7C ; EDIT: FUCK THE TIME, TCNT0 HAS NO 128 PRESCALER, PRESCALER WILL BE 1024 JUST BECAUSE I WANT PREVIOUS COMMENT HAS NO MEANING NOW ---> ; Time in milisecond counted for 16MHz with 128 prescaler
.org 0 ; RESTART interupt vector
rjmp INITIALIZATION ; Go to the start of the program
.org 0x1C ; TOV0 interupt vector
rjmp DELAY ; Create visible delay from counter interval
.org 0x34 ; Start the program right behind interupt vector table
INITIALIZATION:
ldi r16, hi8(RAMEND) ; High 8 bit value for SPH
out io_sph, r16 ; Store SPH value to SRAM
ldi r16, lo8(RAMEND) ; Low 8 bit value for SPL
out io_spl, r16 ; Store SPL value to SRAM
clr r16 ; R16 = 0
out io_sreg, r16 ; SREG = 0
ldi r16, 0xF0 ; Only high nibble is set
out io_ddrc, r16 ; High nibble in PORTB is set as output because of possibility to check possible error with ease
ldi r16, 0xF0 ; Byte for Output
out io_ddrd, r16 ; Pin OC0A and other pins (PORTD.7-4 included) is set as output
rcall TOOGLE_LED ; Inicialization LED and delay register(s) to the start condition
TCNT_INIT: ; TCNT0 Initialization - not used label
clr r16 ; Prepare value for counter stop, no FOC, third mode bit WGM2 is 0
out io_tccr0b, r16 ; Stop counter
out io_tcnt0, r16 ; Counting register = 0
ldi r16, 0x42 ; Set OC0A to toogle and CTC mode for TCNT0
out io_tccr0a, r16 ; Set control register a for timer 0
ldi r16, 0x1 ; Interupt on TOV0
sts timsk0, r16 ; Enable overflow interupt
ldi r16, MILIS_VALUE ; Count number for 125 count cycles
out io_ocr0a, r16 ; Set top value for counter 0
sei ; Enable ingerupts globaly
out io_tccr0b, 0x05 ; Set divisor to 1024, counter is running now!
MAIN_LOOP:
rjmp MAIN_LOOP ; Infinite loop because of getting interupt from TCNT0
DELAY: ; TOV0 interupt subroutine
in r20, io_sreg
clr r21
out io_sreg, R21
inc r18
cpi r18, 100
brlt END_DELAY
out io_sreg, R21
clr r18
inc r17
cpi r17, 100
brlt END_DELAY
out io_sreg, R21
clr r17
inc r19
cpi r19, 1
brlt END_DELAY
rcall TOOGLE_LED
out io_sreg, r20
END_DELAY:
reti
TOOGLE_LED:
ldi r17, 0x8 ; The highest bit of the lowest nibble is set for xor mask
in r16, io_portc ; Get actual value of port B
eor r16, r17 ; Toogle the highest pin of port B
out io_portc, r16 ; write changed value to the port B
clr r17 ; R17 = 0 <--- register is used for delay so it must be reseted
clr r18 ; R18 = 0 <--- register is used for delay so it must be reseted
clr r19 ; R19 = 0 <--- register is used for delay so it must be reseted
ret
I will be senior programmer, before someone will answer here with help. ;-)
There are several mistakes in the code.
1) I shouldnot use TOV0 interupt, but I have to set OCIE0A in TCNT_INIT section, therefore use apropriate interrupt vector (what I partialy did in wrong code, because I have tested both, as you can see from old comment).
2) At the end of TCNT_INIT sequence I tried to run counter in CTC mode. It is absolutely wrong, because I put value into place, where address of register should be. Because 0x5 is value for register, it was sucesfuly compiled.
3) The most mean mistake - The interrupt vectors. I copied them directly from datasheet, so I thought, that they are absolutely correct. However after some looking for code examples, I saw very different numbers as interrupt vectors. Trouble was, that in datasheet is information that shows interrupts vectors, BUT EACH ADDRESS IN FLASH POINTS TO TWO BYTES, SO ADDRESS FOR VECTOR FROM DATASHEET MUST BE MULTIPLIED BY TWO FOR ASSEMBLER.
Code that should be working is enclosed below. Mistakes should be fixed. In addition there few lines, that could be erased, because they were used for testing.
Have a nice day.
; TCCR0 registers addresses
.equ tccr0a, 0x44
.equ tccr0b, 0x45
.equ tcnt0, 0x46
.equ ocr0a, 0x47
.equ ocr0b, 0x48
.equ tifr0, 0x35
.equ timsk0, 0x6E
.equ io_tccr0a, 0x24
.equ io_tccr0b, 0x25
.equ io_tcnt0, 0x26
.equ io_ocr0a, 0x27
.equ io_ocr0b, 0x28
.equ io_tifr0, 0x15
; PORT registers addresses
.equ pinb, 0x23
.equ io_pinb, 0x3
.equ ddrb, 0x24
.equ io_ddrb, 0x4
.equ portb, 0x25
.equ io_portb, 0x5
.equ pinc, 0x26
.equ io_pinc, 0x6
.equ ddrc, 0x27
.equ io_ddrc, 0x7
.equ portc, 0x28
.equ io_portc, 0x8
.equ pind, 0x29
.equ io_pind, 0x9
.equ ddrd, 0x2A
.equ io_ddrd, 0xA
.equ portd, 0x2B
.equ io_portd, 0xB
; SREG and STACK registers addresses
.equ sreg, 0x5F
.equ io_sreg, 0x3F
.equ sph, 0x5E
.equ io_sph, 0x3E
.equ spl, 0x5D
.equ io_spl, 0x3D
.equ mcucr, 0x55
.equ io_mcucr, 0x35
; Constants
.equ RAMEND, 0x8FF ; End of SRAM for ATMEGA328P
.equ MILIS_VALUE, 0x7C ; EDIT: FUCK THE TIME, TCNT0 HAS NO 128 PRESCALER, PRESCALER WILL BE 1024 JUST BECAUSE I WANT PREVIOUS COMMENT HAS NO MEANING NOW ---> ; Time in milisecond counted for 16MHz with 128 prescaler
.org 0 ; RESTART interupt vector
jmp INITIALIZATION ; Go to the start of the program
.org 0x38 ; OCF0A interupt vector <--- IT LOOKS, THAT ADDRESSES FROM DATASHEET MUST BE DOUBLED BECAUSE OF CALL INSTRUCTION! SEE /usr/lib/avr/include/avr/iom328p.h ,io.h AND OTHER INCLUDE FILES! THIS INFORMATION MUST BE VERIFIED!
jmp DELAY ; Create visible delay from counter interval
.org 0x68 ; Start the program right behind interupt vector table
INITIALIZATION:
ldi r16, hi8(RAMEND) ; High 8 bit value for SPH
out io_sph, r16 ; Store SPH value to SRAM
ldi r16, lo8(RAMEND) ; Low 8 bit value for SPL
out io_spl, r16 ; Store SPL value to SRAM
clr r16 ; R16 = 0
out io_sreg, r16 ; SREG = 0
out io_mcucr, r16 ; MCUCR = 0
ldi r16, 0xFF ; Only high nibble is set
out io_ddrc, r16 ; High nibble in PORTB is set as output because of possibility to check possible error with ease
ldi r16, 0xF0 ; Byte for Output
out io_ddrd, r16 ; Pin OC0A and other pins (PORTD.7-4 included) is set as output
rcall DELAY_INIT ; Inicialization of DELAY subroutine
TCNT_INIT: ; TCNT0 Initialization - not used label
clr r16 ; Prepare value for counter stop, no FOC, third mode bit WGM2 is 0
out io_tccr0b, r16 ; Stop counter
out io_tcnt0, r16 ; Counting register = 0
ldi r16, 0x42 ; Set OC0A to toogle and CTC mode for TCNT0
out io_tccr0a, r16 ; Set control register a for timer 0
ldi r16, 0x2 ; Interupt on OCIE0A
sts timsk0, r16 ; Enable overflow created by OCR0A interupt
ldi r16, MILIS_VALUE ; Count number for 125 count cycles
out io_ocr0a, r16 ; Set top value for counter 0
sei ; Enable global interupts
ldi r16, 0x05
out io_tccr0b, r16 ; Set divisor to 1024, counter is running now! <<<-------THERE WAS ANOTHER MISTAKE!!!!
; in r16, io_sreg ; TEST ONLY !!!! SREG test, control of enabling interupts
; out io_portd, r16 ; TEST ONLY !!!! SREG test, control of enabling interupts
; sbi io_portc, 0 ; TEST ONLY !!!! Test of signal that DELAY subroutine was activated.
MAIN_LOOP:
rjmp MAIN_LOOP ; Infinite loop because of getting interupt from TCNT0
DELAY: ; TOV0 interupt subroutine
in r20, io_sreg
sbi io_portc, 0 ; Tell me, that this interupt was working one times at minimum!
clr r21
out io_sreg, R21
inc r18
cpi r18, 100
brlt END_DELAY
out io_sreg, R21
clr r18
inc r17
cpi r17, 1
brlt END_DELAY
out io_sreg, R21
clr r17
inc r19
cpi r19, 1
brlt END_DELAY
rcall TOOGLE_LED
out io_sreg, r20
END_DELAY:
reti
TOOGLE_LED:
ldi r17, 0x8 ; The highest bit of the lowest nibble is set for xor mask
in r16, io_portc ; Get actual value of port B
eor r16, r17 ; Toogle the highest pin of port B
out io_portc, r16 ; write changed value to the port B
DELAY_INIT:
clr r17 ; R17 = 0 <--- register is used for delay so it must be reseted
clr r18 ; R18 = 0 <--- register is used for delay so it must be reseted
clr r19 ; R19 = 0 <--- register is used for delay so it must be reseted
ret

How to use a button which is port c pin 0 in assembly?

I tried to write some code but i'm beginner at assembly i could not complete i want to blink 2 leds when button pressed. I can blink the leds without button but i could not do with button.
# STM32F107 - Assembly template
.thumb
.syntax unified
# Keep the STACKINIT variable.
.equ STACKINIT, 0x20008000
.equ DELAY, 80000
.equ RCC_APB2ENR, 0x40021018 #
.equ GPIOD_CRL, 0x40011400 # D portuna clock
.equ GPIOD_ODR, 0x4001140C # D portunu output olarak belirledik 0x0C offset
.equ GPIOC_CRL, 0x40011000 # C portuna clock
.equ GPIOC_IDR, 0x40011008 # C portunu input olarak belirledik 0x08 offset
.section .text
.word STACKINIT
.word _start + 1
######################################################################################
# Main code starts from here
######################################################################################
_start:
LDR R6, = RCC_APB2ENR # Load peripheral clock enable regiser
LDR R5, [R6] # Read its content
ORR R5, 0x00000020 # Buranın nasıl bulunacağını biliyorum portlara göre - Bit( A 2 B 3 C 4 D 5 ...)
STR R5, [R6] # Store back the result in Perihperal clock enable register
# Make GIOOD Pin1 as output pin (Page 170 from RM0008)
LDR R6, = GPIOD_CRL # Load GPIOD control register low (Pin1 is in CRL register)
LDR R5, = 0x22222222 # hepsi output yap
STR R5, [R6] # Store back the result in GPIOD control register low
# Enable GPIOC Peripheral Clock (
LDR R6, = RCC_APB2ENR
LDR R5, [R6]
ORR R5, 0x00000010 # c portu
STR R5, [R6]
# Make GIOOC Pins as input pin
LDR R6, = GPIOC_CRL
LDR R5, = 0x11111111 # hepsi input mu oldu butonların bilmiyorum ?
STR R5, [R6]
dongu:
LDR R6, = GPIOC_IDR
LDR R5, = 0x00000001
STR R5, [R6]
BTFSC GPIOC,1
GOTO dongu
# Set GIOOD Pin1 to 1 (Page 172 from RM0008)
LDR R6, = GPIOD_ODR # Load GPIOD output data register
LDR R5, = 0x00000001 # 1. lede elaktirik ver :)
STR R5, [R6] # Store back the result in GPIOD output data register
LDR R1, = 1460000
loop:
SUBS R1 , 1
BNE loop
LDR R6, = GPIOD_ODR # Load GPIOD output data register
LDR R5, = 0x00000002 # 2. LED
STR R5, [R6]
LDR R1, = 1460000
loop1:
SUBS R1 , 1
BNE loop1
B dongu
two problems, I think you are configuring port C as an output not an input. mode should be 00 not 01. and a typo the assembler should of caught, dont you want 0x40011008 not 0x4001108h as the address to port c IDR? the port should reset as an input so long as you dont mess with that or so long as port c pin 0 is not an exception to that rule (I dont see an exception in the register description it defaults to 0x44444444 which is all ports as digital inputs) you can just poll IDR after enabling port C in the RCC.

8051 Serial Port Receive Data Loss

I am making simple messaging program such that I have two 8051 machine one of them is transmitter and the other is receiver. Transmitter sends 8 charachters which is coded as:
...
mov r7, #8
mov r0, #30h
TRS: clr TI
mov sbuf, #r0
inc r0
jnb TI, $
djnz r7, TRS
I get those 8 charachters from keyboard and they are surely on 30h to 37h. After getting those 8 chars I send the data. However the thing that i see in the receivers LCD is the first charachter of 8 byte. ie if i typed 1 2 3 4 5 6 7 8, receiver can get only 1 and displays it. This is the code for receiver:
...
mov r7, #8
REC: jnb RI, $
mov a, sbuf
acall SEND_DATA
clr RI
djnz r7, REC
Receiver waits for another 7 chars. When i type 64 chars from transmitter receiver takes only 8 of them which are mod8=1. I am clearing the RI flag and waiting for the next rising edge for RI which is supposed to be next char coming from trasmitter. What could be the reason?
Regards

Resources