CS453 Colorado State University ================================================================ Meggy Jr Simple Library and avr-gcc, 8-bit AVR Assembly Language ================================================================ Plan for today and Thursday Meggy Jr Simple library ATmega328p chip avr assembly ----------------- Meggy Jr Simple concepts and their interface See the Meggy Jr RGB Programming Guide (show them on line) http://www.cs.colostate.edu/~cs453/yr2013/MeggyJavaInfo/ProgrammingMeggyJr.pdf LED screen 8x8 grid x coordinate goes from left to right, 0 through 7 y coordinate goes from bottom to top, 0 through 7 Colors are single bytes Auxiliary LEDs SetAUXLEDs( number ); Least significant bit for number represents by left-most LED. Buttons GetButtons() returns a byte. B is represented with least significant bit, value 1. A with 2. Up with 4. Down with 8. Left with 16. Right with 32. Two options, no opinion yet on which is better (1) if (4 & GetButtons()) (2) CheckButtonsDown(); if (Button_Up) Speaker Takes a tone frequency divisor and a duration in milliseconds. -> show the tone definitions in MeggyJrSimple.h and frequency equation Logistics, or Interfaces? - layered on top of Meggy library Color indices appear to be one of the main reasons. - what it adds Color indices Pre-defined tone divisors Nice programming guide. =) - what we add to it delay_ms GetButtons ------------------ AVR ISA Why assembly? AVR ISA Handling GetButton and SetPixel calls, (Calling Convention) Handling if statements (Condition Codes and Branches) Handling expression evaluation (Operations and Stack instructions) Variables on the stack and in the heap --------------- Why is it useful to be comfortable with assembly? -it is the main target language for compilers -programs for embedded processors are sometimes written in assembly -some of these low-level concepts are needed to implement drivers -useful to understand an example of how data is organized in memory because might have to use a hexdump to do some nasty debugging some day -translating to C avoids the joy of learning how the run-time stack works in detail --------------- ATmega328p chip Terminology http://www.engineersgarage.com/articles/avr-microcontroller, really useful Uses a Harvard Architecture Separate program and data memory Thriving programming community email and website resources http://www.avrfreaks.net/ http://www.arduino.cc/ =================== avr assembly ISA: instruction set architecture, interface machine provides RISC - ld-store architecture -> picture of abstraction: memory (data, text, heap, stack), registers, CPU - only load-store operations have access to memory - AVR has some stack architecture features such as push and pop -> draw stack architecture alternative where operations only performed on a stack -------------------- Handling GetButton and SetPixel calls - Need to follow the avr-gcc calling convention, because linking with avr-gcc generated code. - Since AVR code must go through MJSIM.jar, might need to change some of the assembly code if use a similar .cpp file as a starting point and then use the provided Makefile at http://www.cs.colostate.edu/~cs453/yr2013/MeggyJavaInfo/building-for-meggy.html to create a .s file. // assume you have written a PA2bluedot.cpp make PA2bluedot.s // will create a .s file - In MJSIM.jar GUI, select Help to see the instructions that are supported. ------------------------------ calling convention reference: http://www.nongnu.org/avr-libc/user-manual/FAQ.html - calling convention is interface between caller and callee - callers have to pass parameters to callee - callees have to pass return values to caller - callers and callees have to agree who is storing what registers - stack pointer and frame pointer are used to keep track of the runtime stack Arguments - allocated left to right, r25 to r8 r24, r25 parameter 1, only use r24 if just a byte parameter r22, r23 parameter 2 r20, r21 parameter 3 r18, r19 parameter 4 ... r8, r9 parameter 9 Return values 8-bit in r24 (not r25!), 16-bit in r25:r24, up to 32 bits in r22-r25, up to 64 bits in r18-r25. 8-bit return values are zero/sign-extended to 16 bits by the called function (unsigned char is more efficient than signed char - just clr r25). Arguments to functions with variable argument lists (printf etc.) are all passed on stack, and char is extended to int. Who saves what registers caller saved r18-r27, r30-r31 callee saved r2-r17, r28-r29 Stack pointer: pointer to first available loation on RTS, used for pop and push in e.g.expression evaluation; therefore varies Frame pointer: pointer to a fixed location of stack frame -------------------- Handling if statements (Condition Codes and Branches) - need to set condition codes with a comparison - and then check the result with a br instruction cp r24, r18 // compare two bytes and set condition codes brne L1 // branch if not equal to label cp r24, r18 // compare lo bits cpc r25, r19 // compare hi bits with carry from lo bits breq L5 // conditional branch, this one is branch if equal tst r24 // sets flags ZNVS jmp L2 // jump to label anywhere in memory L3: // label to jump to, nothing else on this line -------------------- Handling expression evaluation (Operations and Stack instructions) - values need to be in registers to perform operations on them - we will be keeping temporary results on the stack pop r24 // pop byte from RTS and put into r24 push r25 // push byte in r25 onto RTS ldi r24, 42 // load constant 42 into r24 ldi r22, lo(352) // load 8 lo bits of 352 into r22 ldi r23, hi(352) // load 8 hi bits of 352 into r23 muls r22, r18 // r1:r0 = r22 * r18 add r22, r18 // r22 = r22 + r18 adc r23, r19 // add with carry, r23 = r23 + r19 + C sub r22, r18 // r22 = r22 - r18 sbc r23, r19 // subtract with carry, r23 = r23 - r19 - C and r22, r18 // r22 = r22 & r18, bitwise AND -------------------- Handling variables on the stack and the heap slide 21 -------------------- Loads and Stores in r28,__SP_L__ // putting the stack pointer into r29:r28 in r29,__SP_H__ ldd r24, Y+3 // load byte that is 3 bytes from address in r29:r28 // r24 = M[r29:r28 + 3] std Y+1, r24 // store value in r24 to address r29:r28+1 // M[r29:r28 + 1] = r24 ------------------- add r30, r24 // perform some addressing arithmetic into r31:r30 adc r31, r25 ldd r24, Z+0 // load byte at the address in r31:r30 -------------------- Registers http://www.nongnu.org/avr-libc/user-manual/FAQ.html caller saved r18-r27, r30-r31 callee saved r2-r17, r28-r29 Y is 29:28, used for frame pointer Z is 31:30, can be used for other pointers -------------------- Pushes and Pops -> Why do we need a run-time stack? What feature in almost all programming languages requires something like the run-time stack? Dated hint: Fortran 77 does not have this feature. - Talk about what pushes and pops do in AVR. -------------------- Learning AVR with avr-gcc General approach For each language feature, write an example .cpp program, compile it to a .s file, and then figure out the .s file. Examples: PA3bluedot.cpp and PA4buttondot.cpp make PA3bluedot.s make PA4buttondot.s TRICKY BITS (1) Will want to try with optimization for space on and off -> Show how to change the compiler options in the Makefile Remove the -Os flag in CFlags (2) The AVR simulator called MJSIM DOES NOT simulate ALL of the AVR instructions. Your compiler will be required to generate a subset of instructions. See MJSIM for the list of implemented instructions --> Look at the PA4Cyclon.java.s example sent out last night and run it on device. ----------------------------------------------------- AVR instruction subset Show them MJSIM and the available instructions Show them Atmel AVR See http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf for details on these instructions. --> show them these instructions ----------------- main prologue and epilogue __SREG__ = 0x3f __SP_H__ = 0x3e __SP_L__ = 0x3d __tmp_reg__ = 0 __zero_reg__ = 1 .global __do_copy_data .global __do_clear_bss .text .global main .type main, @function main: push r29 push r28 in r28,__SP_L__ in r29,__SP_H__ /* prologue: function */ call _Z18MeggyJrSimpleSetupv /* Need to call this so that the meggy library gets set up */ ... /* epilogue start */ endLabel: jmp endLabel ret .size main, .-main -------------------------------------------- examples of other instructions the simulator will handle pop r24 // pop byte from RTS and put into r24 push r25 // push byte in r25 onto RTS ldi r24, 42 // load constant 42 into r24 ldi r22, lo(352) // load 8 lo bits of 352 into r22 ldi r23, hi(352) // load 8 hi bits of 352 into r23 muls r22, r18 // r1:r0 = r22 * r18 add r22, r18 // r22 = r22 + r18 adc r23, r19 // add with carry, r23 = r23 + r19 + C sub r22, r18 // r22 = r22 - r18 sbc r23, r19 // subtract with carry, r23 = r23 - r19 - C and r22, r18 // r22 = r22 & r18, bitwise AND cp r24, r18 // compare two bytes and set condition codes brne L1 // branch if not equal to label cp r24, r18 // compare lo bits cpc r25, r19 // compare hi bits with carry from lo bits breq L5 // conditional branch, this one is branch if equal tst r24 // sets flags ZNVS jmp L2 // jump to label anywhere in memory L3: // label to jump to, nothing else on this line call funcname // call function, funcname is just a label in r28,__SP_L__ // putting the stack pointer into r29:r28 in r29,__SP_H__ ldd r24, Y+3 // load byte that is 3 bytes from address in r29:r28 // r24 = M[r29:r28 + 3] std Y+1, r24 // store value in r24 to address r29:r28+1 // M[r29:r28 + 1] = r24 ---------------------------------------- Allocating space on the heap with malloc Library function for malloc(int n): Allocates n consecutive bytes in the heap and returns the address of the first byte allocated. Reference: http://www.nongnu.org/avr-libc/user-manual/malloc.html ------------------------ mstrout@cs.colostate.edu, 2/10/11 updated WB Spr 2012 updated MMS Spr 2013