Daniel Vindevåg November 2003 Göteborg
Förord Denna rapport avser att redovisa kurserna Mikrodatorteknik 1 och 2, FY3200 och FY3300. Första kursen innebär konstruktion av en miniräknare baserad på Atmel AVR 2313 mikrokontroller och den andra en mätstation basserad på den något större 8535. Detta kompendium innehåller en genomgång av den teori som krävs samt en beskrivning av konstruktionen. Daniel Vindevåg, november 2003 Email: daniel@vindevag.com i
Innehåll 1 INLEDNING...1 1.1 Bakgrund... 1 1.2 Syfte... 1 1.3 Begränsningar... 1 1.4 Metod...1 2 AVHANDLING... 3 2.1 Microprocessor... 3 2.1.1 ALU, CPU, MPU... 3 2.1.2 Arkitektur... 3 2.1.3 AVR... 3 2.2 Mikrodator... 4 2.2.1 Minnestyper... 4 2.2.2 Stack och minne... 4 2.2.3 Parallellport... 4 2.2.4 Serieport... 4 2.3 Programmeringsspråk... 5 2.3.1 Högnivåspråk... 5 2.3.2 Assembler... 5 2.3.3 Exempel... 5 2.3.4 Talsystem... 5 2.3.5 Avbrott och subrutiner... 6 2.3.6 Emulator och Simulator... 6 3 PROJEKT... 7 3.1 Miniräknare... 7 3.1.1 Ksen... 7 3.1.2 Användning... 7 3.1.3 Programmets huvudstruktur... 9 3.1.4 Övrigt... 9 4 REFERENSER... 11 4.1 Internet... 11 4.2 Litteratur... 11 APP. A HÅRDVARA MINIRÄKNAREN... 13 APP. B KÄLLKOD MINIRÄKNAREN... 17 B.1 MAIN.ASM... 17 B.2 INIT.ASM... 22 B.3 BINARY_OP.ASM... 25 ii
B.4 UNARY_OP.ASM... 26 B.5 STACK.ASM... 29 B.6 ARITHMETIC.ASM... 33 B.7 OUTPUT.ASM... 40 B.8 UART.ASM... 44 B.9 DIGIT.ASM... 45 B.10 DEBUG.ASM... 47 iii
1 Inledning 1.1 Bakgrund Denna rapport är en slutrapport i kursen Mikrodatorteknik. Rapporten avser att sammanfatta kursen samt dokumentera de två konstruktionsuppgifterna, miniräknaren och mätstationen. 1.2 Syfte Syftet med denna rapport är att dokumentera de två konstruktionsuppgifterna samt sammanfatta den teori som krävdes för detta. 1.3 Begränsningar Programmen är skrivna i assembler, fokus ligger därför på lågnivåprogrammering och hårdvarukonstruktion och inte på systemutveckling. 1.4 Metod De två uppgifterna, miniräknaren och mätstationen, kännetecknas av att de tillsammans använder det mesta av funktionaliteten hos mikrodator. Uppgiften har därför lösts stegvis genom att experimentellt studera använda de olika funktionerna på AVR, tex. utmatning på parallellporten, in och utmatning på serieporten etc. 1
2 Avhandling 2.1 Microprocessor 2.1.1 ALU, CPU, MPU Mikroprocessor består i princip av aritmetisk logisk enhet, ALU och register. Registren är i huvudsak generella dataregister som innehåller aktuella data, status/flaggregister, programräknare som pekar ut adressen till nästa instruktion och olika pekarregister. Pekarregistren används för att adressera minnet på olika sätt, segmentering, samt för att hantera stacken, stackpekaren. Flaggregist innehåller bitar som ger information om föregående operation, t.ex. overflow. Processorn kommunicerar med omvärlden via kontroll- data- och adressbuss. Databussen är dubbelriktad och har till uppgift att flytta data mellan processor och övriga enheter, adressbussen styr vilken enhet/minnesadress som är aktiverad och därmed har tillgång till databussen. Kontrollbussen Hanterar övriga kontrollfunktioner, som hårdvaruavbrott. Mikroprocessorn sitter vanligtvis i en mikrodator, denna består i huvudsak, förutom mikroprocessorn, av primär och sekundärminne samt in och utmatningsenheter. Om allt detta integreras på ett enskilt chip får man en mikrocontroller. En mikrocontroller används oftast i ett inbäddat system (embedded), till skillnad från en mikrodator har detta en strikt begränsad uppgift, som t.ex. styr och kontrollfunktioner. 2.1.2 Arkitektur Mikroprocessorer kan antingen vara av typen CISC, complex instruction set computer, eller RISC, reduced instruction set computer. En CISC processor har ett stort antal instruktioner där varje instruktion kan vara olika lång och tar olika många klockcykler att utföra. I en RISC dator har antalet instruktioner minskats och därmed instruktionslängden, oftast tar alla instruktioner lika många (en) klockpulser att utföra. En von Neuman dator består av följande fem delar, CPU (kontrollenhet och ALU), minne, databussar och input/output. Kännetecknande är att samma minne används för data och programinstruktioner. I en dator med Harward-arkitektur har man delat upp minnet i data minne och instruktions minne, varje minne har då egen adress och databuss. Signalprocessorer är oftast av denna typ för att uppnå bättre prestanda. I de fall processorn ingår i ett inbäddat system där samma program alltid körs är det önskvärt att ha programkoden i ett permanentminne och därmed slippa ladda in det vid reset. 2.1.3 AVR AVR-familjen är av typen RISC och är en microcontroller. 3
AVR 2313 består i huvudsak av en mikroprocessor, minne, parallellportar, synkron och asynkron serieport, analog komparator, timer. Dessutom finns avbrottsenhet som tar hand om interna och externa avbrott, reset-ks och programmeringslogik. Exempel på andra mikroprocessorfamiljer Motorola 6800/68000 (CISC) Microchip PIC (microcontroller, Harward-arkitektur) Intel x86 (CISC) MIPS (32bits RISC) 2.2 Mikrodator 2.2.1 Minnestyper Det finns flera typer av minnen, dynamiska som används som primärminne och statiska som EEPROM, FLASH-Minnen. Dynamiska måste ha spänning för att behålla sitt innehåll. 2.2.2 Stack och minne Datorns primärminne är i huvudsak uppdelad i två delar, stacken som börjar på högsta adressen och växer nedåt samt det fria minnet (heap) som börjar på den lägsta adressen och växer uppåt. I det fall det rör sig om en von Neuman arkitektur ligger programkoden innan det fria minnet. Det fria minnet går att disponera fritt medan stacken hanteras enligt sist in först (POP och PUSH funktioner) eller med en offset relaterat till stackpekaren. 2.2.3 Parallellport En parallell port skickar data i flera parallella elektriska ledare, oftast åtta eller 16 bitar. 2.2.4 Serieport Med en serieport skickas databitarna en i taget. Serieporten kan vara asynkron, då används start och/eller stoppbitar, eller synkron då data synkas efter en gemensam klocksignal. Överföringen kan vara antingen hel eller halv duplex beroende på om data kan skickas och tas emot samtidigt. <Standard och specialiserade Periferienheter> 4
2.3 Programmeringsspråk 2.3.1 Högnivåspråk Det finns en stor mängd högnivåspråk, det språk som är vanligast inom inbäddade system är C eftersom det är relativt maskinnära och väl etablerat. 2.3.2 Assembler Assembler är ett språk som direkt svarar mot den aktuella processorarkitekturens maskinkod, programmeraren har därmed mycket stor kontroll över hårdvaran. I gengäld blir därmed källkoden mycket lång och svår att överblicka. En rimlig kompromiss är att skriva de hårdvarunära rutinerna i assembler och huvudprogrammet i ett högnivåspråk (C). Man måste då tänka på att följa språkets/kompilatorns calling convention, hur parametrar skickas till och från rutiner samt vilka register som måste bevaras. 2.3.3 Exempel Nedan följer ett exempel på en subrutin skriven i C och sedan översatt till AVR assembler. Parameter 1 skickas i r16:r17 och parameter 2 i r18:r19, övriga parametrar på stacken och urvärdet i r16:r17. Detta är en calling convention som används av en C-kompilator för AVR, short är en 16bit integer (AVR arkitekturen är åtta bitar så C kompilatorer kommer sätta int som 16 bitar (short), short är dock alltid 16 bitar). usigned short test(short a, short b) { if (a< b) urn 1; else urn 0; } test: cp r16, r19 cpc r18, r20 brlt _less clr r16 ; R16 = 0 _less: ldi r16, 1 ; R16 = 1 _end: ; compare ; compare with carry ; branch if lass than clr r17 ; R17 = 0 ; Return from subroutine 2.3.4 Talsystem Binära talsystemet bygger likt det decimala och hexadecimala talsystemet på positionssystemet, ett tals bas skrivs som index efter talet (10 10 ). Ett tal T n skrivet i basen b skrivs enligt: Tb = an b 0 5
Med binära tal behövs bara två tecken (tillstånd). Hexadecimala tal används oftast för att skriva binära tal då varje grupp om fyra bitar kan direkt översättas till en hexadecimal siffra. Negativa binära tal representeras med två-komplement, då avsätts hälften av alla representerbara tal som positiva och resten som negativa. Om detta gäller ett fyrabitarstal kommer 0 till 7 användas som positiva och 8 till 15 som negativa, -1 kommer att representeras som talet 15. Fördelen med att använda två-komplement för negativa tal är att samma logik kan användas som för positiva tal och subtraktion hanteras som addition av negativt tal. Två-komplement kan jämföras med tio-komplement som används på mekaniska räkneverk; på ett räkneverk med tre siffror (0 till 999) kommer -2 att visas som 998. Två-komplementet beräknas genom att ta ett-komplementet, vilket är att ändra värde på samtliga bitar, och addera 1. Tvåkomplementet av ett åttabitars tal T beräknas enligt: (T XOR 0xFF) +1 2.3.5 Avbrott och subrutiner Subrutiner är programkod som kan anropas av annan kod för att sedan återgå till ursprungskoden. Subrutiner kan ha inparametrar samt urnera ett värde. Avbrottsrutiner är subrutiner som anropas genom att en händelse inträffar, t.ex. hårdvaruavbrott eller timeravbrott. Avbrottsrutiner kan inte ha in eller utparametrar och måste återställa samtliga register. Om ett avbrott inträffar kommer den pågående instruktionen att slutföras och därefter anropas avbrottsrutinen. Eftersom vissa instruktioner tar flera klockcycler kan detta medföra att systemet inte blir ett strikt realtidssystem. Det kan därför vara önskvärt att försätta processorn i sleep mode då den är inaktiv istället för att köra en busy-loop. 2.3.6 Emulator och Simulator För att underlätta programutvecklingen kan man använda sig av simulatorer och emulatorer. I detta fall är en emulator en fysisk ks som kopplas in istället för microkontroller n och som är övervakningsbar (via en dator). En simulator är ett program, t.ex. AVR Studio, som simulerar microkontroller n. 6
3 Projekt 3.1 Miniräknare 3.1.1 Ksen Jag har valt en kristall med frekvensen 3.68 Mhz av anledningen att den frekvensen fungerar bra för seriekommunikation. Ksen använder 9600 bps, åtta databitar en stoppbit och ingen paritet (9600 8N1). 3.1.2 Användning Miniräknaren är av typen RPN (reverse polish notation, postfixnotation), Hewlett-Packard-räknare. Detta innebär att räknaren använder sig av en stack med fyra nivåer/register. Till skillnad från en normal stack har RPNstacken alltid fyra nivåer och kan aldrig bli tom, bara innehålla nollor. Stacknivåerna benämns X, Y, Z och T. Räknaren förväntar sig att antingen få siffror inmatade eller kommandon/operatorer, alla operatorer använder gemener, ogiltiga tecken ignoreras. Sifferinmatningar Sifferinmatningar gör att det aktuella talet hamnar i X regist och stacken lyfts (Z -> T, Y ->Z och X ->Y). Inmatning av tal avslutas med enter eller en binär (+, -, *, /) eller unär (r, q, n) operator. Skydd mot overflow saknas. Binära operatorer Binära operatorer tar två argument, från X och Y-registren, resultatet hamnar i X-regist och stacken faller (Z kopieras till Y och T till Z). Det ursprungliga innehållet i X kopieras till LASTX regist. Binära operatorer är de fyra räknesätten. Dessutom finns operatorn (\) som gör division enligt principen upprepad subtraktion, eftersom denna rutin är väldigt långsam passar den för att pröva watchdog en. Division med noll kommer att generera noll. Skydd mot overflow saknas. Då division resulterar i en rest visas detta som KVOT ::REST, X-regist kommer att innehålla enbart kvoten. Unära operatorer Tar ett argument från X-regist och lämnar resultatet i X-regist, övriga stacken opåverkad. Ursprungliga värdet kopieras till LASTX. q: Kvadrera r: Kvadratrot n: Negera (-1) X regist 7
Försök att dra kvadratrot ur ett negativt tal kommer att resultera i kvadratroten ur motsvarande positiva tal, rest efter kvadratrot visas som rest efter division. Försök att kvadrera tal större än 46340 kommer lämna X- regist orört (46340 är det största tal vars kvadrat ryms i en 32 bit signed integer). Till skillnad från en vanlig RPN-räknare går det inte att slå N (ändra tecken) mitt under en sifferinmatning, N kommer avsluta inmatningen. Stack kommandon Kommandon som manipulerar stacken, dessa är Enter: Avslutar en sifferinmatning eller lyfter stacken (kopierar Z ->T, Y->T och X->Y) s: Swap X <-> Y c: Clear stack l: Kopiera LASTX till X regist d: Drop X, låt stacken falla och skriva över X >: Rulla stacken nedåt <: Rulla stacken uppåt Systemkommandon Systemkommandona styr funktionen av räknaren, dessa kan utföras när som helst, även under en sifferinmatning. w: Sätt på watchdog, stäng av sleep mode e: Stäng av watchdog, sätt på sleep mode z: Scroll a lysdioder på PORTB (testfunktion) b: Starta om (reset) t Scroll a lysdioderna på PORTB om tre sekunder, (testfunktion för att demonstrera timer-avbrott) Som exempel slås uträkningen (10-1)/(5-2) enligt: 10 ENTER 1 MINUS 5 ENTER 2 MINUS DELAT Uträkningen har använt register X, Y och Z, sva 3 ligger nu i X-regist. Register Y och Z kommer att innehålla kopior av T-regist. 8
3.1.3 Programmets huvudstruktur Programmet använder sig av avbrottet för UART mottagning klar. När inget händer rullar programmet i en evig loop som består av att nollställa watchdog en; eller om den är inaktiverad, försätta processorn i idle mode. Avbrottsrutinen består av en huvudslinga som kollar vilket tecken som mottagits, ogiltiga tecken ignoreras. Ett statusregister håller reda på om programmet befinner sig mitt under inmatning av ett tal. Textmeddelanden är lagrade i EEPROM; X, Y samt ett temporärregister ligger i processorns interna register (r0->r15). Övriga RPN register (Z, T, LASTX samt möjlighet att skapa kopior av X och Y) lagras i SRAM. Samtliga tal håller 32 bitars precision, totalt används 20 bytes av minnet. 3.1.4 Väl godkänt För att erhålla betyget väl godkänt har jag lagt till vissa debugfunktioner. Detta gör att det går att få en utskrift av de tre olika minnena samt av processorregistren. Detta kan göras när som helst utan att avbryta pågående operationer. För att dessa rutiner skulle få plats i 2313 s programminne var jag tvungen att ta bort rutinerna för watchdog och den gamla divisionsrutinen (med upprepad subtraktion). Koden är borttagen med assemblatordirektiv (.equ calculator_vg = 1 i början av huvudprogrammet). För att komma åt Debugfunktionerna används kommandona S, F, E, R (versaler), för att visa innehållet SRAM, flash respektive processorregistren. 3.1.5 Övrigt I tidigare versioner av AVR studio gick det inte att deklarera sträng-konstanter utan dessa måste skrivas som vektorfält, tecken för tecken med apostrof på varje tecken samt kommatecken emellan. Eftersom det blir onödigt jobbigt att skriva in strängar i källkoden skrev jag följande program som översätter en sträng till en vektor av tecken. 9
3.2 Mätstationen 10
4 Referenser 4.1 Internet http://www.atmel.com/ 4.2 Litteratur 11
App. A Hårdvara miniräknaren Ksen ritades upp i Eagle. Efter två misslyckade försök att fräsa kortet i ET-labbet beställde jag prototyptillverkning av kortet från Olimex (http://www.olimex.com/). Kortet har STK500 pin-kompatibla anslutningar för PORTB och PORTD dessutom går lysdioderna att koppla ur, detta gör att miniräknaren kan användas som en generell AVR-ks. Trots att detta var tredje gången jag ritade upp ksen missade jag följande detaljer: Likriktardioder saknas, ksen måste ha +polaritet på centerpinnen Reset-knapp saknas Ksschema över miniräknaren 13
Prototypkort av miniräknaren Baksidan av prototypkortet 14
Miniräknaren färdigmonterad 15
App. B Källkod miniräknaren Nedan finns miniräknarens källkod. Eftersom jag avser erövra världen använder jag engelska variabelnamn och kommentarer. Jag har använt mig av editorn TextPad som kan färgmarkera källkod samt även generera sådan som html, vilken är inklistrad här. Jag fick dock skriva en egen syntaxdefinition för AVR assembler, vilken jag även skickat in som ett tillägg till TextPad, om de väljer att publicera den kommer den att hamna på http://www.textpad.com/add-ons/syna2g.html. B.1 MAIN.ASM Huvudprogrammet. ; 32bit RPN Calculator over RS-232, 9600BPS,8N1 ; Low byte of XREG is displayed on PORTB ; ; (c) Daniel Vindevåg 2003 ; ;========================================.INCLUDE "F:\AVR\INCLUDE\namnkort.txt".NOLIST.INCLUDE "F:\AVR\INCLUDE\2313def2.inc".LIST ;========================================.equ calculator_vg = 1 ; 1 compile for version "VG" ; with debug routines, code for watchdog ; and old division routine excluded ; ; 0: original "G" version. ; CPU registers r01->r15, RPN registers.def xreg = r00 ; RPN Register X.DEF xreg2 = r01.def xreg3 = r02.def xreg4 = r03.def yreg = r04.def yreg2 = r05.def yreg3 = r06.def yreg4 = r07.def tempreg = r08.def tempreg2 = r09.def tempreg3 = r10.def tempreg4 = r11 ; RPN Register Y ; RPN Temp register.def statusreg = r27.def uart_in = r28.def uart_out = r29.def paramreg = r20 ; The state of the calculator ; 2: Enter was last key ; 1: Digit input mode ; 0: Waiting for command or input ; UART input ; UART output ; Use r20 for parameters 17
; Pointer register Z: r30:r31 ; Temp registers: r16-r28 ;========== DATA SEGMENT ==========.DSEG ;Pointers to variables i SRAM.EQU pzreg = $60 ; RPN Register Z (4 bytes).equ ptreg = $64 ; RPN Register T (4 bytes).equ plastx= $68 ; RPN Register LASTx (4 bytes).equ psave_y=$72 ; Temp space for saving Y (4 bytes).equ psave_x=$80 ; Temp space for saving X (4 bytes) ; Memory used: 40 bytes, 88 bytes left for stack ;========== EEPROM Contents ==========.ESEG greeting:.db '3', '2', 'b', 'i', 't', ' ', 'R', 'P', 'N', ' ', 'C', 'a', 'l', 'c', 'u', 'l', 'a', 't', 'o', 'r' greeting2:.db 10, 13 greeting3:.db '(', 'c', ')', ' ', 'D', 'a', 'n', 'i', 'e', 'l', ' ', 'V', 'i', 'n', 'd', 'e', 'v', 'å', 'g', ' ', '2', '0', '0', '3' linefeed:.db 10, 13, $0, $0 ok:.db 'O', 'k', 10, 13, $0, $0 help_str:.db 10, 13, 'O', 'p', '2', ':', 9, '+', ',', ' ', '-', ',', ' ', '*', ',', ' ', '/', ' ' help_str2:.db 10, 13, 'O', 'p', '1', ':', 9, 'n', ',', ' ', 'q', ',', ' ', 'r' help_str3:.db 10, 13, 'S', 't', 'a', 'c', 'k', ':', 9, 'E', 'n', 't', ',', ' ', 'l', ',', ' ', 's', ' ', '<', ',', ' ', '>', ',', ' ', 'd' ;help_str4:.db 10, 13, 'M', 'i', 's', 'c', ':', 9, 'h', ',', ' ', 'w', 10 help_str5:.db 10, 13, 0, 0 ;========== CODE SEGMENT ==========.CSEG.org $0 rjmp RESET.org INT0addr i.org INT1addr i.org ICP1addr i.org OC1addr i.org OVF1addr rjmp TIM_OVF1.org OVF0addr rjmp TIM_OVF1.org URXCaddr rjmp uart_rxd_irq.org UDREaddr i ; Reset vektor ; External Interrupt0 Vector ; External Interrupt1 Vector ; Input Capture1 Interrupt Vector ; Output Compare1 Interrupt Vector ; Timer1 Overflow handle ; Timer0 Overflow handle ; RX UART, RX Complete ; UART Data Register Empty 18
.org UTXCaddr i.org ACIaddr i ; UART Transmit Complete ; Analog Comparator.org $00b RESET:.INCLUDE "init.asm" ; Initalize stack, uart and display greeting main: wdr sleep rjmp main ; Main busy loop ; Watchdog reset, watchdog and sleep mode don't mix ; Enter sleep mode, S/M disabled when W/D active loop: i ; Return here ; Return from interrupt.include "binary_op.asm" ; Binary operators, called by main loop branches uart_rxd_irq: ; rcall wait4in ; Wait for uart, not needed whith irq in uart_in, UDR ; Read ASCII from uart cpi uart_in, 't' breq timer_reset cpi uart_in, '+' breq add_xy cpi uart_in, '-' breq sub_xy cpi uart_in, '*' breq mul_xy cpi uart_in, '/' breq div_xy ; Reset TIMER1, scroll LEDs in 3s ; ADD ; SUB ; MUL ; DIV.IF!calculator_vg cpi uart_in, 92 ; (\) Div by repeated subtractions, slow! breq div_xy_old.endif cpi uart_in, 'q' breq sq_x cpi uart_in, 'r' breq sq_root cpi uart_in, 'n' breq neg_x ; Square ; Square root ; Negate X cpi uart_in, 13 breq enter ; Enter 19
cpi uart_in, 'd' breq drop_x cpi uart_in, 'l' breq last_x cpi uart_in, 's' breq swap_xy cpi uart_in, '>' breq roll_down cpi uart_in, '<' breq roll_up cpi uart_in, 'c' breq clear ; Drop X ; LastX ; Swap X and Y registers ; Roll RPN stack down ; Roll RPN stack up ; Clear stack cpi uart_in, 'b' breq reboot cpi uart_in, 'z' breq scroll ; Reset / Reboot ; Scroll LED's on PORTB.IF!calculator_vg cpi uart_in, 'w' breq turn_watchdog_on cpi uart_in, 'e' breq turn_watchdog_off.endif ; Watchdog on, sleep mode off ; Watchdog off, sleep mode on cpi uart_in, 'h' breq print_help ; Help.IF calculator_vg cpi uart_in, 'S' breq _debug_sram cpi uart_in, 'F' breq _debug_flash cpi uart_in, 'R' breq _debug_reg cpi uart_in, 'E' breq _debug_eeprom.endif rjmp input_digit ; View SRAM ; View SRAM ; View SRAM ; View SRAM ; Process digits, then jump to loop.if calculator_vg _debug_sram: rjmp print_sram _debug_flash: rjmp print_flash 20
_debug_reg: rjmp print_reg _debug_eeprom: rjmp print_eeprom.endif ;.INCLUDE "binary_op.asm" ; Binary operators, called by main loop branches.include "unary_op.asm" ; Unary operators, called by main loop branches.include "stack.asm".include "arithmic.asm".include "output.asm".include "uart.asm".include "digit.asm" ; RPN stack manipulations ; Arithmetic for X and Y registers ; Output to UART ; Send and recieve ASCII from UART, ; watchdog on/off ; Process digits.if calculator_vg.include "debug.asm".org FLASHEND.dw $0201.ENDIF ; Debug routines ; Last pos in memmory 21
B.2 INIT.ASM Initieringsrutiner. init: ldi r16, RAMEND out SPL, r16 ldi r16, $FF out DDRB,r16 ldi r16,23 out UBRR, r16 ldi r16, $98 out UCR, r16 ; Init stack ; PORTB for output ; Init UART ; 25 & 4 MHz -> 9600 bps ; 23 & 3.68 MHz -> 9600 bps ;$08 = 0b00001000: no interupt, RxD off, TxD on, 8 bits ;$18 = 0b00011000: no interupt, RxD on, TxD on, 8 bits ;$98 = 0b10011000: RxD interupt, RxD on, TxD on, 8 bits ldi r16, (1<<SE) & ~(1<<SM) ; Enable sleep mode, use idle mode out MCUCR, r16 clr r16 out GIMSK, r16 ; Disable external interrupts out TIMSK, r16 ; Disable timer interrupts rcall send_linefeed ldi statusreg, 0 ldi r20, greeting rcall output_eeprom rcall scroll_leds ldi r20, ok rcall output_eeprom rcall clear_rpn_stack rcall clr_last_x sei rjmp main; ; Not in digit input mode ; Send Greeting ; Send OK ; Clear stack ; Enable interrupts ; Begin! scroll_leds: push r16 ldi r16, 1 _led_scroll1: com r16 out PORTB, r16 rcall delay20 com r16 lsl r16 brne _led_scroll1 ldi r16, $80 _led_scroll2: com r16 ; Scroll left, low -> high ; Scroll right, high -> low 22
out PORTB, r16 rcall delay20 com r16 lsr r16 brne _led_scroll2 com r16 out PORTB, r16 pop r16 delay20: push r20 push r21 push r22 ldi r20,45 _loop20: ldi r21,45 _loop21: ldi r22,45 _loop22: dec r22 brne _loop22 dec r21 brne _loop21 dec r20 brne _loop20 pop r22 pop r21 pop r20 TIM_OVF1: push xreg rcall scroll_leds com xreg out PORTB, xreg clr xreg out TIMSK, xreg pop xreg i ; Timer Overflow, Scroll LED's ; Disable timer timer_reset: push r16 push r17 ldi r16, 128 out TIMSK, r16 ; Timer interrupt setup ; ldi r16, 0xf0 ; 1s ; ldi r17, 0xf1 ldi r16, 0xd0 ; 3s ldi r17, 0xd5 ; ldi r16, 0xe4 ; 150 ms ; ldi r17, 0xfd 23
out TCNT1H, r17 out TCNT1L, r16 ; Important lo load high byte first ldi r16, (1<<CS12) & ~(1<<CS11) (1<<CS10) ; Timer1 CLK / 1024 out TCCR1B, r16 sei pop r17 pop r16 ; Enable interrupts 24
B.3 BINARY_OP.ASM Trampolinfunktioner för operatorer som kräver två parametrar. sub_xy: ldi r20, '-' rcall store_last_x rcall neg_register_x rcall add_register_xy rcall pop_rpn_stack rjmp cont_operators ; Y + (-X) add_xy: ldi r20, '+' rcall store_last_x rcall add_register_xy rcall pop_rpn_stack rjmp cont_operators mul_xy: ldi r20, '*' rcall store_last_x rcall mul_register_xy rcall pop_rpn_stack rjmp cont_operators div_xy: ldi r20, '/' rcall store_last_x rcall div_register_xy_shift rcall pop_rpn_stack rjmp cont_operators.if!calculator_vg div_xy_old: ldi r20, '\' rcall store_last_x rcall div_register_xy rcall pop_rpn_stack rjmp cont_operators.endif 25
B.4 UNARY_OP.ASM Trampolinfunktioner för operatorer som kräver en parameter samt stack och system kommandon sq_root: rjmp _sq_root sq_x: rcall store_last_x rcall square_x ldi r20, 'q' rjmp cont_operators neg_x: rcall neg_register_x ldi r20, 'n' rjmp cont_operators ; Stack operators enter: rcall rpn_enter rjmp loop drop_x: rjmp _drop_x last_x: rcall push_rpn_stack rcall get_last_x ldi r20, ' ' rjmp cont_operators swap_xy: rcall swap_xy_reg ldi r20, ' ' rjmp cont_operators roll_down: ldi r20, '>' rcall roll_down_rpn_stack rjmp cont_operators roll_up: ldi r20, '<' rcall roll_up_rpn_stack rjmp cont_operators 26
clear: rcall clear_rpn_stack rjmp loop ; System commands reboot: rjmp RESET scroll: rjmp _scroll.if!calculator_vg turn_watchdog_on: rcall watchdog_on rjmp loop turn_watchdog_off: rcall watchdog_off rjmp loop.endif print_help: push r20 ldi r20, help_str rcall output_eeprom rcall send_linefeed rcall print_x pop r20 rjmp loop _sq_root: rcall store_last_x rcall sq_root_x ldi r20, 'r' rjmp cont_operators _drop_x: rcall copy_y_to_x rcall pop_rpn_stack rcall send_linefeed rcall print_x rjmp loop _scroll: rcall scroll_leds com xreg out PORTB, xreg com xreg rjmp loop cont_operators: 27
ldi r18, ' ' ; Output space rcall wait4uart_send out UDR, r18 rcall wait4uart_send ; and operator out UDR, r20 push tempreg push tempreg2 push tempreg3 push tempreg4 rcall send_linefeed rcall print_x pop tempreg4 pop tempreg3 pop tempreg2 pop tempreg rcall print_remainder ; Save remainder ; Print X ; Restore remainder ; Print remainder clr statusreg rjmp loop 28
B.5 STACK.ASM Rutiner som hanterar RPN-stacken. rpn_enter: rcall push_rpn_stack rcall send_linefeed rcall print_x ldi statusreg, 2 rjmp loop ; Copy X->Y ; Enter was last key clr_x: clr xreg clr xreg2 clr xreg3 clr xreg4 clr_temp_reg: clr tempreg clr tempreg2 clr tempreg3 clr tempreg4 clr_last_x: push r16 clr r16 sts plastx, r16 sts plastx+1, r16 sts plastx+2, r16 sts plastx+3, r16 pop r16 store_last_x: sts plastx, xreg sts plastx+1, xreg2 sts plastx+2, xreg3 sts plastx+3, xreg4 get_last_x: lds xreg, plastx lds xreg2, plastx+1 lds xreg3, plastx+2 lds xreg4, plastx+3 swap_xy_reg: push xreg push yreg pop xreg 29
pop yreg push xreg2 push yreg2 pop xreg2 pop yreg2 push xreg3 push yreg3 pop xreg3 pop yreg3 push xreg4 push yreg4 pop xreg4 pop yreg4 copy_y_to_x: mov xreg, yreg mov xreg2, yreg2 mov xreg3, yreg3 mov xreg4, yreg4 push_rpn_stack: ; Move rpn stack down, Y->Z, Z->T, Keep X push r16 lds r16, pzreg ; Z -> T sts ptreg, r16 lds r16, pzreg+1 sts ptreg+1, r16 lds r16, pzreg+2 sts ptreg+2, r16 lds r16, pzreg+3 sts ptreg+3, r16 sts pzreg, yreg sts pzreg+1, yreg2 sts pzreg+2, yreg3 sts pzreg+3, yreg4 mov yreg, xreg mov yreg2, xreg2 mov yreg3, xreg3 mov yreg4, xreg4 pop r16 ; Y -> Z ; X -> Y pop_rpn_stack: ; Move RPN stack up, Z->Y, T->Z, Keep X and T lds yreg, pzreg lds yreg2, pzreg+1 lds yreg3, pzreg+2 lds yreg4, pzreg+3 lds r16, ptreg sts pzreg, r16 lds r16, ptreg+1 sts pzreg+1, r16 lds r16, ptreg+2 30
sts pzreg+2, r16 lds r16, ptreg+3 sts pzreg+3, r16 roll_down_rpn_stack: push r16 push xreg4 push xreg3 push xreg2 push xreg rcall swap_xy_reg rcall pop_rpn_stack pop r16 sts ptreg, r16 pop r16 sts ptreg+1, r16 pop r16 sts ptreg+2, r16 pop r16 sts ptreg+3, r16 pop r16 ; Y -> X ; Z -> Y, T ->Z ; X ->T roll_up_rpn_stack: push xreg4 push xreg3 push xreg2 push xreg lds xreg, ptreg lds xreg2, ptreg+1 lds xreg3, ptreg+2 lds xreg4, ptreg+3 rcall push_rpn_stack pop yreg pop yreg2 pop yreg3 pop yreg4 ; T -> X ; Move rpn stack down, Y->Z, Z->T, Keep X ; X -> Y clear_rpn_stack: rcall clr_x rcall push_rpn_stack rcall push_rpn_stack rcall push_rpn_stack clr statusreg rcall send_linefeed rcall print_x ; clear Y ; clear Z ; Clear T save_y_reg: sts psave_y, yreg sts psave_y+1, yreg2 sts psave_y+2, yreg3 sts psave_y+3, yreg4 31
restore_y_reg: lds yreg, psave_y lds yreg2, psave_y+1 lds yreg3, psave_y+2 lds yreg4, psave_y+3 save_x_reg: sts psave_x, xreg sts psave_x+1, xreg2 sts psave_x+2, xreg3 sts psave_x+3, xreg4 restore_x_reg: lds xreg, psave_x lds xreg2, psave_x+1 lds xreg3, psave_x+2 lds xreg4, psave_x+3 32
B.6 ARITHMETIC.ASM Aritmetiska funktioner som arbetar mot stacken, (X och Y regist). add_register_xy: add xreg, yreg adc xreg2, yreg2 adc xreg3, yreg3 adc xreg4, yreg4 rcall clr_temp_reg mul_register_xy: push r17 ; Holds 1 push r19 ; Holds 0 push r20 ; Holds Product sign rcall save_y_reg rcall absolute_xy clr r19 ; Return 0 if Y = 0 cp yreg, r19 cpc yreg2, r19 cpc yreg3, r19 cpc yreg4, r19 brne _mul_xy_cont rcall clr_x rjmp _mul_xy_end _mul_xy_cont: ; Return 0 if X = 0 cp xreg, r19 cpc xreg2, r19 cpc xreg3, r19 cpc xreg4, r19 brne _mul_xy_cont2 rcall clr_x ; Hmm, is is already 0! rjmp _mul_xy_end _mul_xy_cont2: cp xreg, yreg cpc xreg2, yreg2 cpc xreg3, yreg3 cpc xreg4, yreg4 brsh _mul_xy_cont3 rcall swap_xy_reg _mul_xy_cont3: mov tempreg, xreg mov tempreg2, xreg2 mov tempreg3, xreg3 mov tempreg4, xreg4 ldi r17, 1 _mul_xy_loop: add xreg, tempreg adc xreg2, tempreg2 adc xreg3, tempreg3 adc xreg4, tempreg4 ; Put the largest factor in X ; tempreg = X ; X = X + tempreg 33
sub yreg, r17 ; Y-- sbc yreg2, r19 sbc yreg3, r19 sbc yreg4, r19 brne _mul_xy_loop ; Y > 0? sub xreg, tempreg sbc xreg2, tempreg2 sbc xreg3, tempreg3 sbc xreg4, tempreg4 cpi r20, 1 brne _mul_xy_end rcall neg_register_x ; We added Y+1 times, ; remove 1 factor from X ; r20 = 1 -> product negative _mul_xy_end: rcall restore_y_reg rcall clr_temp_reg pop r20 pop r19 pop r17.if!calculator_vg div_register_xy: push r16 ; Holds 1 push r17 ; Holds 0 push r20 ; Holds Product sign ldi r16, 1 ldi r17, 0 rcall save_y_reg rcall absolute_xy mov tempreg, xreg ; tempreg = X mov tempreg2, xreg2 mov tempreg3, xreg3 mov tempreg4, xreg4 rcall clr_x ; X = 0 cp tempreg, r17 cpc tempreg2, r17 cpc tempreg3, r17 cpc tempreg4, r17 breq _div_xy_end ; Div by zero! urn 0 _div_xy_loop: add xreg, r16 ; X++ adc xreg2, r17 adc xreg3, r17 adc xreg4, r17 sub yreg, tempreg ; Y = Y - divisor sbc yreg2, tempreg2 sbc yreg3, tempreg3 sbc yreg4, tempreg4 brpl _div_xy_loop ; Y>= 0? sub xreg, r16 sbc xreg2, r17 ; X--, One turn to much! 34
sbc xreg3, r17 sbc xreg4, r17 add yreg, tempreg adc yreg2, tempreg2 adc yreg3, tempreg3 adc yreg4, tempreg4 mov tempreg, yreg mov tempreg2, yreg2 mov tempreg3, yreg3 mov tempreg4, yreg4 rcall restore_y_reg cpi r20, 1 brne _div_xy_end rcall neg_register_x ; Restore remainder ; r20 = 1 -> product negative.endif _div_xy_end: pop r20 pop r17 pop r16 div_register_xy_shift: push r20 push r21 rcall save_y_reg rcall absolute_xy ; Holds Product sign ; Loop counter clr r21 cp xreg, r21 cpc xreg2, r21 cpc xreg3, r21 cpc xreg4, r21 breq _div_xy_shift_end ; Div by zero! urn 0 ; Perform unsigned 32bit division, Y / X ; Result -> Y, Remainder -> Tempreg _div_xy_shift_0: clr tempreg ; Clear remainder Low byte clr tempreg2 clr tempreg3 sub tempreg4, tempreg4 ; Clear remainder High byte and carry ldi r21, 33 ; Init loop counter _div_xy_shift_1: rol yreg ; Shift left dividend rol yreg2 rol yreg3 rol yreg4 dec r21 ; decrement counter brne _div_xy_shift_2 rjmp _div_xy_shift_end1 ; end if counter = 0 _div_xy_shift_2: rol tempreg ; Shift dividend into remainder rol tempreg2 rol tempreg3 rol tempreg4 sub tempreg, xreg ; remainder = remainder - divisor 35
sbc tempreg2, xreg2 sbc tempreg3, xreg3 sbc tempreg4, xreg4 brcc _div_xy_shift_3 add tempreg, xreg adc tempreg2, xreg2 adc tempreg3, xreg3 adc tempreg4, xreg4 clc rjmp _div_xy_shift_1 _div_xy_shift_3: sec rjmp _div_xy_shift_1 ; (branch if carry clear) ; if result negative restore remainder ; clear carry to be shifted into result ; set carry to be shifted into result _div_xy_shift_end1: rcall swap_xy_reg rcall restore_y_reg cpi r20, 1 brne _div_xy_shift_end rcall neg_register_x ; put result in x ; r20 = 1 -> result negative _div_xy_shift_end: pop r21 pop r20 sq_root_x: push r16 ; n = r16:r17 push r17 push r18 ; Holds 0 push r19 ; Holds 1 push r20 ; Holds 2 rcall clr_temp_reg inc tempreg ; tempreg = 1 clr r16 ; Largest root of 32bit signed int is clr r17 ; n = 46341, fits in 16bit unsigned int ldi r18, 0 ldi r19, 1 ldi r20, 2 ; Check if X < 0 cp xreg, r18 cpc xreg2, r18 cpc xreg3, r18 cpc xreg4, r18 brpl _sq_loop rcall neg_register_x _sq_loop: sub xreg, tempreg ; X = X - (n * 2) sbc xreg2, tempreg2 sbc xreg3, tempreg3 sbc xreg4, tempreg4 brmi _root_end ; X > 0? add tempreg, r20 ; tempreg = tempreg + 2 adc tempreg2, r18 adc tempreg3, r18 36
adc tempreg4, r18 add r16, r19 adc r17, r18 rjmp _sq_loop _root_end: add xreg, tempreg adc xreg2, tempreg2 adc xreg3, tempreg3 adc xreg4, tempreg4 mov tempreg, xreg mov tempreg2, xreg2 mov tempreg3, xreg3 mov tempreg4, xreg4 rcall clr_x mov xreg, r16 mov xreg2, r17 pop r20 pop r19 pop r18 pop r17 pop r16 ; n++ (r16:r17) ; One turn too much ; X = n square_x: push r18 push r19 push r20 push r21 rcall save_y_reg rcall save_x_reg rcall absolute_xy ldi r18, byte1(46341) ldi r19, byte2(46341) ldi r20, byte3(46341) ldi r21, byte4(46341) cp xreg, r18 cpc xreg2, r19 cpc xreg3, r20 cpc xreg4, r21 brlo _square_x_cont rcall restore_x_reg rjmp _square_x_end _square_x_cont: mov tempreg, xreg mov tempreg2, xreg2 mov tempreg3, xreg3 mov tempreg4, xreg4 mov r18, xreg mov r19, xreg2 mov r20, xreg3 mov r21, xreg4 _square_loop: add xreg, tempreg adc xreg2, tempreg2 adc xreg3, tempreg3 ; r18:r19:r20:r21 holds copy of X ;..Byte4 ; n = 46340^2 is the largest number ; that will fit in a 32bit signed int ; Overflow, exit ; tempreg = X ; r18:r19:r20:r21 = X ; X = X + factor 37
adc xreg4, tempreg4 subi r18, 1 sbci r19, 0 sbci r20, 0 sbci r21, 0 brne _square_loop sub xreg, tempreg sbc xreg2, tempreg2 sbc xreg3, tempreg3 sbc xreg4, tempreg4 ; (r18:r19:r20:r21)-- ; We added Y+1 times, ; remove 1 factor from X _square_x_end: rcall restore_y_reg rcall clr_temp_reg pop r21 pop r20 pop r19 pop r18 ; ******************************* ; * Make X & Y positive * ; * Return product sign in r20 * ; * 0 -> (+), 1 -> (-) * ; ******************************* absolute_xy: push r19 clr r20 mov r19, xreg4 andi r19, 0x80 cpi r19, 0x80 brne _check_y rcall neg_register_x ldi r20, 1 _check_y: rcall swap_xy_reg mov r19, xreg4 andi r19, 0x80 cpi r19, 0x80 brne _absolute_xy_end rcall neg_register_x ldi r19, 1 eor r20, r19 ; X = X ; r20 = sign(x) ; Y = Y ; r19 = sign(y) ; r20 = sign(x,y) _absolute_xy_end: rcall swap_xy_reg pop r19 neg_register_x: push r16 ; push r17 ; push r18 ; push r19 ldi r16, byte1(-1) ; ldi r17, byte2(-1) ; -1 = FF:FF:FF:FF 38
; ldi r18, byte3(-1) ; ldi r19, byte4(-1) com xreg com xreg2 com xreg3 com xreg4 sub xreg, r16 sbc xreg2, r16 sbc xreg3, r16 sbc xreg4, r16 ; pop r19 ; pop r18 ; pop r17 pop r16 39
B.7 OUTPUT.ASM Rutiner för utskrift (till serieporten) send_linefeed: ldi uart_out, 10 rcall wait4uart_send out UDR, uart_out ldi uart_out, 13 rcall wait4uart_send out UDR, uart_out print_remainder: push r16 clr r16 cp tempreg, r16 cpc tempreg2, r16 cpc tempreg3, r16 cpc tempreg4, r16 breq _print_remainder_end push xreg push xreg2 push xreg3 push xreg4 ; no remainder! ; Save Xreg ldi uart_out, ' ' rcall wait4uart_send out UDR, uart_out ldi uart_out, '+' rcall wait4uart_send out UDR, uart_out ldi uart_out, ' ' rcall wait4uart_send out UDR, uart_out mov xreg, tempreg mov xreg2, tempreg2 mov xreg3, tempreg3 mov xreg4, tempreg4 rcall print_x cpi r20, 'r' breq _print_remainder_end_ ldi uart_out, '/' rcall wait4uart_send out UDR, uart_out rcall get_last_x rcall print_x _print_remainder_end_: pop xreg4 pop xreg3 pop xreg2 pop xreg ; Move remainder to xreg ; Print remainder ; If squareroot, no divisor ; Get and print divisor ; Restore X 40
_print_remainder_end: pop r16 print_x:.def digit = r24.def t2=r17 push r17 push r18 push r24 push r20 push r21 push r22 push r23 rcall save_x_reg clr r18 ; Check if X < 0 ldi t2, 0 ; cp xreg, t2 cpc xreg2, t2 cpc xreg3, t2 cpc xreg4, t2 brpl _print_x_cont2 rcall neg_register_x rcall wait4uart_send ldi t2, '-' out UDR, t2 ; current digit ; temp register ; temp register ; Don't print leading zeros ; holds current digit ; r20:r21:r22:r23 ; Holds 10^n for current digit ; High byte ; Send '-' _print_x_cont2: mov tempreg, xreg mov tempreg2, xreg2 mov tempreg3, xreg3 mov tempreg4, xreg4 ;Begin cycle through digits clr digit ldi r20, low(1000000000) ldi r21, high(1000000000) ldi r22, byte3(1000000000) ldi r23, byte4(1000000000) rcall output_digit clr digit ldi r20, low(100000000) ldi r21, high(100000000) ldi r22, byte3(100000000) ldi r23, byte4(100000000) rcall output_digit clr digit ldi r20, low(10000000) ldi r21, high(10000000) ldi r22, byte3(10000000) ldi r23, byte4(10000000) rcall output_digit ; 1e9 ; 1e8 ; 1e7 41
clr digit ldi r20, low(1000000) ldi r21, high(1000000) ldi r22, byte3(1000000) ldi r23, byte4(1000000) rcall output_digit clr digit ldi r20, low(100000) ldi r21, high(100000) ldi r22, byte3(100000) clr r23 rcall output_digit ; 1e6 ; 1e5 clr r22 clr digit ldi r20, low(10000) ldi r21, high(10000) rcall output_digit clr digit ldi r20, low(1000) ldi r21, high(1000) rcall output_digit clr digit ldi r20, low(100) clr r21 rcall output_digit clr digit ldi r20, low(10) rcall output_digit ldi t2, 48 add tempreg, t2 rcall wait4uart_send out UDR, tempreg mov t2, xreg com t2 out PORTB, t2 ; Whatever's left in temp is last digit ; Show low byte on PORTB leds rcall restore_x_reg rcall clr_temp_reg pop r23 pop r22 pop r21 pop r20 pop r24 pop r18 pop r17 output_digit: sub tempreg, r20 sbc tempreg2, r21 sbc tempreg3, r22 sbc tempreg4, r23 ; Subtract low byte of 10^n 42
brmi _output_digit_done inc digit ldi r18, 1 rjmp output_digit _output_digit_done: add tempreg, r20 adc tempreg2, r21 adc tempreg3, r22 adc tempreg4, r23 cpi r18, 0 breq _output_digit_end rcall wait4uart_send ldi t2, 48 add digit, t2 out UDR, digit _output_digit_end: ; Going negative. Branch if we do ; Still positive, so count it ; Digit > 0, start printing digits ; Add back last 10^n ; Should we print the digit? ; Send ASCI digit ; *************************************** ; * Output null-terminated strings * ; * from EEPROM to UARRT * ; * Ponter to EEPROM first byte in r20 * ; *************************************** output_eeprom: push r17 push r21 _eeprom_next: out EEAR,r20 _wait4ee: in r17, EECR ; EEPROM Ready? andi r17, 0b00000010 brne _wait4ee ldi r17,1 out EECR, r17 in r21, EEDR ; Read mode ; Read from EEPROM rcall wait4uart_send out UDR,r21 ; Send ASCII to UART inc r20 cpi r21, $0 brne _eeprom_next pop r21 pop r17 ; NULL terminated strings 43
B.8 UART.ASM Rutiner för UART samt watchdog. wait4uart_send: push r17 _wait4uart_send_loop:.if!calculator_vg wdr.endif in r17, USR andi r17,0b00100000 cpi r17,0b00100000 brne _wait4uart_send_loop pop r17 ; Watchdog reset wait4in: push r17 _wait4in_loop:.if!calculator_vg wdr.endif in r17, USR andi r17, 0b10000000 cpi r17, 0b10000000 brne _wait4in_loop pop r17 ; Watchdog reset.if!calculator_vg watchdog_on: push r17 ldi r17,0b00001111 ; Longest timout ~2s out WDTCR, r17 wdr ldi r17, ~(1<<SE) ; disable sleep mode out MCUCR, r17 pop r17 watchdog_off: push r17 ldi r17,0b00011000 out WDTCR, r17 ldi r17,0b00000000 out WDTCR, r17 ldi r17, (1<<SE).ENDIF out MCUCR, r16 pop r17 ; Enable sleep mode 44
B.9 DIGIT.ASM Rutiner för att ta emot siffror. input_digit: push r16 push r17 mov r16, uart_in ldi r17, 48 sub r16, r17 cpi r16, 10 brsh _input_digit_end ; Holds the digit ; Temp ; ASCII -> num ; End if not a digit cpi statusreg, 1 breq _input_digit_cont cpi statusreg, 2 enter breq _input_digit_begin_digit rcall push_rpn_stack _input_digit_begin_digit: rcall clr_x rcall send_linefeed ; Are we in digit input mode? ; Don't push stack if last key was ; begin digit input mode _input_digit_cont: ldi statusreg, 1 rcall _xregmul10 clr r17 add xreg, r16 adc xreg2, r17 adc xreg3, r17 adc xreg4, r17 mov r16, xreg com r16 out PORTB, r16 rcall wait4uart_send out UDR, uart_in ; X * 10 + new digit ; Display low byte on PORTB ; Echo the character back _input_digit_end: clr uart_in rcall clr_temp_reg pop r16 pop r17 rjmp loop _xregmul10: push r16 ldi r16, 9 mov tempreg, xreg mov tempreg2, xreg2 45
mov tempreg3, xreg3 mov tempreg4, xreg4 _xregmul10_loop: add xreg, tempreg adc xreg2, tempreg2 adc xreg3, tempreg3 adc xreg4, tempreg4 dec r16 brne _xregmul10_loop pop r16 46
B.10 DEBUG.ASM Rutiner som möjliggör utskrift av innehållet i program, data och eepromminnet samt processorregistren. Dessa rutiner är för betyget väl godkänt. after_debug: pop r31 pop r30 after_debug_3: pop r17 after_debug_2: pop r16 rcall send_linefeed rcall send_linefeed rcall print_x rjmp loop ; Output byte in register uart_out as 2 hex ; digits and space send_byte_and_space: push r16 push uart_out push uart_out swap uart_out rcall _do_half_digit pop uart_out rcall _do_half_digit rcall wait4uart_send ldi uart_out, ' ' out UDR, uart_out pop uart_out pop r16 _do_half_digit: andi uart_out, 0x0F ldi r16, 48 add uart_out, r16 cpi uart_out, 58 brlo _mem_output _hex_digit: ldi r16, 7 add uart_out, r16 _mem_output: rcall wait4uart_send out UDR, uart_out ; output low byte ; -> ascii ; A -> F ; Print contents of SRAM print_sram: push r16 47
push r17 push r30 ; ZL push r31 ; ZH clr r31 clr r30 clr r17 ldi ZL, 0x60 _print_sram_next_line: cpi r17, 0 brne _print_sram_next ldi r17, 24 rcall send_linefeed ; SRAM start ; 24 bytes ber line _print_sram_next: dec r17 ld uart_out, Z+ rcall send_byte_and_space cpi ZL, RAMEND+1 brne _print_sram_next_line rjmp after_debug print_flash: push r16 push r17 push r30 push r31 clr r31 clr r30 clr r17 push r0 ; ZL ; ZH ; used by lpm _print_flash_next_line: cpi r17, 0 brne _print_flash_next ldi r17, 24 rcall send_linefeed _print_flash_next: lpm mov uart_out, r0 rcall send_byte_and_space dec r17 adiw ZH:ZL, 1 cpi ZH, high((flashend+1)*2) brlt _print_flash_next_line pop r0 rjmp after_debug print_reg: 48
push r16 push r17 push r30 ; ZL push r31 ; ZH clr r30 clr r31 push uart_out rcall send_linefeed _print_reg_next_line: rcall send_linefeed _print_reg_next: ld uart_out, Z+ rcall send_byte_and_space cpi r30, 16 breq _print_reg_next_line cpi r30, 29 brlo _print_reg_next pop uart_out pop r31 pop r30 pop r17 clr r16 push uart_out push r31 push r30 push r29 ; ZH ; ZL ; uart_out _print_upper_regs: pop uart_out rcall send_byte_and_space inc r16 cpi r16, 3 brlt _print_upper_regs pop uart_out rjmp after_debug_2 print_eeprom: push r16 push r17 push r21 push r20 clr r20 clr r21 _print_eeprom_next_line: cpi r21, 0 brne _print_eeprom_next ldi r21, 24 rcall send_linefeed 49
_print_eeprom_next: out EEAR, r20 ; read address wait4ee: in r16, EECR ; EEPROM Ready? andi r16, 0b00000010 brne wait4ee ldi r16, 1 ; Read mode out EECR, r16 in uart_out, EEDR ; Read from EEPROM rcall send_byte_and_space inc r20 dec r21 cpi r20, E2END ; END? brne _print_eeprom_next_line pop r20 pop r21 rjmp after_debug_3 50