F5 Föreläsning i Mikrodatorteknink 2006-09-05 Programräknaren visar alltid på nästa instruktion som skall utföras. Så fort en instruktion har hämtats så visar programräknaren på nästa instruktion. Programräknaren PC kallas även för Programpekare. Jämförelse mellan två av atmels microcontroller. AT90S8515 Atmega 16 512 byte SRAM 1024 byte SARAM 8 kbyte FLASH 16 kbyte FLASH För att kunna använda symboliska namn på register, så använder man sig av den information som finns i includfiler. 8515def.inc m16def.inc I dessa filer hittar man:.equ RAMEND = $25F.EQU RAMEND = $45F.EQU FLASHEND = $FFF.EQU FLASHEND = $1FFF Det hexadecimala talet 0xFFF motsvarar decimalt 4095 Det hexadecimala talet 0x1FFF motsvarar decimalt 8191 Trots detta kallas FLASH-minnet 8 kbyte respektive 16 kbyte. Flash-minnet används för program. Varje instruktion upptar 16 bitar, dvs två byte. 8191 + 1 anger maximala antalet rader kod som man kan skriva. Ramend. 0x45F motsvarar 1119 decimalt. Detta beror på att ram-minnet börjar på adress 0x60 ( 96 decimalt ) I alla programexempel har vi hittills skrivit: START: LDI R16, low(ramend) OUT SPL, R16 LDI R16, high(ramend) OUT SPH, R16 SP betyder Stack Pointer eller stackpekare. Stacken är ett lagringsutrymme för data. Stacken placerar man själv i ram-minnet. Det som skrivs in sist på stacken är det som läses ut först. Den här typen av register kallas också LIFO. Det finns vissa likheter mellan Programräknare och stackpekare. Båda finns i processorn. Programräknaren visar alltid på nästa instruktion. Stacken är ett lagringsutrymme för data, och pekaren visar alltid på närmaste lediga plats. 1
I vårt fall börjar ram-minnet på den hexadecimala adressen 0x60 och slutar på 0x45F. I regel lägger man stacken så att den startar på den högsta adressen. SRAM 0x60 Stacken växer mot lägre adresser Stackpekare 0x45F SPH SPL Data 8 bit Adress 16 bit För att lägga något på stacken använder man instruktionen. Det är bara innehållet i R0 R31 som kan sparas på stacken. Det betyder att all information som skall sparas måste gå via ett register. Push Register on Stack Description: This instruction stores the contents of register Rr on the STACK. The Stack Pointer is postdecremented by 1 after the. This instruction is not available in all devices. Refer to the device specific instruction set summary. Operation: STACK Rr Syntax: Operands: Program Counter: Stack: Rr 0 r 31 PC PC + 1 SP SP - 1 16-bit Opcode: 1001 001d dddd 1111 Push är en 16 bit instruktion. ( 16-bit Opcode ). När denna instruktion har hämtats så kommer programräknaren att visa på nästa instruktion. PC PC +1. Stackpekaren SP visar alltid på nästa lediga plats. Instruktionen Push tar denna plats i anspråk, och SP ändras så att den visar nästa lediga plats. SP SP 1 2
Exempel: Vi har ett program där stackpekaren är definierad. Någonstans i programmet finns följande rader: R0 R1 R2 R3 Då dessa fyra rader har utförsts ser stacken ut så här: Stackpekare efter R3 Stackpekare innan R0 ledigt R3 R2 R1 R0 Stacken växer Motsatsen till är POP POP Pop Register from Stack Description: This instruction loads register Rd with a byte from the STACK. The Stack Pointer is preincremented by 1 before the POP. This instruction is not available in all devices. Refer to the device specific instruction set summary. Operation: Rd STACK Syntax: Operands: Program Counter: Stack: POP Rd 0 d 31 PC PC + 1 SP SP + 1 16-bit Opcode: 1001 000d dddd 1111 Fortsätt programmet ovan med ett par rader till: R0 R1 R2 R3 POP R3 POP R2 Stackpekare efter POP R2 Stackpekare innan R0 ledigt R3 betraktas som ledigt R2 betraktas som ledigt R1 R0 Stacken krymper 3
Efter instruktionen POP R2 så pekar stackpekaren på den plats som vi har lagrat R2. Även R3 finns kvar. Dessa platser kommer att skrivas över vid nästa användning av stacken. Observera att och POP endast fungerar gentemot register. Lita aldrig på att något nedanför stackpekaren finns kvar oförändrat. I C-kursen hade vi en uppgift som gick ut på att byta värden. int main(int argc, char *argv[]) { int a= 1; int b=5; int slask; printf("a = %d och b = %d \n",a,b); slask =a; a = b; b=slask; printf("\na = %d och b = %d \n",a,b); Efter programkörningen så innehåller a värdet 5 och b innehåller värdet 1. Hur gör man i asssembler? De del av programmet som gör motsvarande är R1 R5 POP R1 POP R5 Klart. Flytta ett I/O-register till stacken. Det som är aktuellt är att flytta SREG till stacken. Det kan vara bra att ha en kopia av detta register då stacken används av en subrutin. Man kan inte kopiera registret till stacken direkt, utan det måste göras via ett register. - - IN R4, SREG R4-4
För att ta tillbaka från stacken. POP OUT - R4 SREG, R4 IN och OUT fungerar på I/O och register. Subrutiner Mnemonic CALL ICALL RCALL long call Indirekt, adress i Z-reg relativt Ett litet exempel där jag tillfogat vad programräknaren har för värde..include "m16def.inc".def temp = R16.cseg.org 0 PC 0 rjmp RESET 1 RESET: ldi temp,low(ramend) 2 out SPL, temp 3 ldi temp,high(ramend) 4 out SPH, temp 5 clr r16 5 clr r17 7 clr r18 8 ldi r16,0x12 9 ldi r17,0x13 A rcall rutin B nop ; no operation C nop D stop: rjmp stop E rutin: mov r18,r16 F add r18,r17 10 ret 5
När instruktionen out SPH, temp utförts är stackpekaren definierad. Den har den hexadecimala adresssen 0x045F. När instruktionen rcall rutin har hämtats, så pekar programräknaren PC på nästa instruktion. Den finns på adress 0x00000B Denna adress skall sparas på stacken Stacken Nästa lediga plats. Stackpekaren efter rcall rutin Innehåll Adress?????? 0x00 0x0B Ox45D 0x45E 0x45F Adressen är ett 16-bit tal. Därför går det åt två positioner i stacken. Nytt värde på programräknaren blir 0x00000E vilket är 0x0b +4. Nu startar programmet från den nya adressen. mov r18,r16 Innehållet i r16 kopieras till r18 När instruktionen RET hämtats så ställs programräknaren om. Den nya addressen hämtas från stacken. Programmet körs sedan fram till stop. Stackpekarens värde ökar med två efter det att återhoppsadressen hämtats. Nästa gång något skrivs till stacken kommer 0x0B att skrivas över. 6