GBA_Programming

GBA_Programming - Fundamentals of GBA Programming Concepts...

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: Fundamentals of GBA Programming Concepts Each of the following discussions is divided into two sections: the specifics of GBA hardware requirements and in general the C programming concepts required. Basic operation of the GBA GBA Specifics Write a program that sets up the GBA and then waits indefinitely. The program actually stops when you turn power off the GBA. The simulator stops when you close the simulator window. C Coding Write a C program containing the function: int main() where the program starts, and at the end of the function put: while(1) { } Paint a pixel a certain color in true color mode GBA Specifics 1. set Mode 3 and BG2 data bits in the display control register (address 0X4000000) 2. calculate the memory offset for the appropriate pixel 3. calculate a color word 4. set the color value offset from the video buffer at 0X6000000 C Coding typedef #define to create your own language tools pointers hexadecimal arithmetic logical operators | (or) and << (shift) Draw a rectangle on the screen GBA Specifics starting at the top left of the rectangle: for each row of the rectangle for each column of the rectangle set the pixel at that row and column to the appropriate color C Coding for loops: // define an index variable int i; // the for loop has three parts separated by semicolons for( i = 0; // initialization i < n; // the test to keep doing it i++) { // the increment part // the code body } // the parts are repeatedly executed in the following order // until the test fails: 1. initialization 2. test ‐ finish if false 3. code body 4. increment part To wait for the right time to draw GBA Specifics We want to change the display when the display processor is not drawing scanlineCounter is a volatile register at 0X4000006 that tells us where the display processor is currently looking To draw at the right time: 1. wait until the display is drawing (scanlineCounter < 160) 2. wait until it is not drawing (scanlineCounter > 159 C Coding while loops stop the processor until a condition is met. Create a "bouncing ball" effect GBA Specifics 1. Wait for the right time to draw 2. Draw a black rectangle where the ball was last time to erase it 3. change the ball coordinates 4. paint a white rectangle where the ball now is 5. check the screen edges and reverse direction where necessary C Coding Abstraction: Things are getting messy enough to start using function calls rather than in‐line code for everything. Get a button pressed input GBA Specifics Buttons are in one memory location (0x4000130) in different bit positions. (volatile memory) #define name BUTTON_A BUTTON_B value 1<<0 1 1<<1 2 Emulator Key z x BUTTON_SELECT BUTTON_START BUTTON_RIGHT BUTTON_LEFT BUTTON_UP BUTTON_DOWN BUTTON_R BUTTON_L C Coding To test a bit in a register: 1<<2 4 1<<3 8 1<<4 16 1<<5 32 1<<6 64 1<<7 128 1<<8 256 1<<9 512 backspace enter right arrow left arrow up arrow down arrow s a 1. shift 1 left to that bit position 2. one's complement that with the ~ operator 3. AND that with the button register (&) and test the result Speed up memory transfers by making use of DMA in mode 3 GBA Specifics Set the following memory locations: 1. REG_DMxCNT to 0 to stop previous activity 2. REG_DMxSAD to the source address 3. REG_DMxDAD to the destination address 4. REG_DMxCNT has the following fields: a. bits 0‐15: number of u16's to transfer b. bits 21‐22: Destination address control 0 ‐ normal 1 ‐ decrement 2 ‐ fixed (used for sound) c. bits 23‐24: Source address control (same values) 2 ‐ fixed (used for fill) Processor stops until the transfer is complete C Coding This has some ugly implications in the code: 1. to define these 4 operation in one macro requires some really slippery stuff: a. multi‐line macros have a back slash (\) at the end of each continuation line b. wrap multiple statements in a do { …} while(0) construct 2. Our one file is getting out of hand ‐ we need to divide the code into separate files in a disciplined way: a. put all the declarations (typedefs, #defines, function prototypes) into header files like whatnot.h b. bring them into the main program by using #include "whatnot.h" c. put the implementations into whatnot.c and include that in the project. You don't need to include the header file(s) in the project. d. however, VisualHam will not automatically save .h files before compiling. If in doubt, use F10 to clean out the project then F7 to recompile. Prepare an image to be imported into the GBA. [the GBA has no file system to read ]! GBA Specifics Use usenti to process your true color image file as follows: 1. with your favorite image processor, create an image of the right size (240 * 160 is full screen on the GBA) 2. save it as a Windows Bitmap (.bmp file) 3. open it in usenti (not the world's greatest image editor, but you can fine‐tune the image there, including setting the image size) 4. when you're ready to save the image for the GBA, select Image / Export 5. choose the directory where you want to save the files and make sure the type is GBA source (...) 6. select Save ‐ this takes you to a slightly intimidating set of options. We need to select the following: a. under Gfx top left, change tile to bitmap(GBA) b. change bpp (bits per pixel) to 16 c. at top right, uncheck Pal ‐ we don't need to export the palette for this GBA mode d. in the file box, make sure that .h is checked and the data type selected is u16. 7. when all that is done, select OK, and two files will be created ‐ (image).c and (image).h C Coding This produces two files (image).c and (image).h that you then import into your project file. A. If the image is full‐screen, 240 * 160, one DMA call will copy the image to the video buffer. B. Otherwise, you can use a DMA rectangle copy (with or without DMA) to copy the image wherever you want it on the screen. Trajectories Save a series of data points for use as a trajectory, and draw an object that traverses that trajectory GBA Specifics 1. create an array of structures each containing the x, y, z and time values at each point 2. keep up with the time value of each frame 3. linearly interpolate between the current and next waypoint using the following logic: If x0 and x1 are the values of some variable at t0 and t1, and the time t is between t0and t1, the current value of the parameter x would be given by: x = x0 + (x1 ‐ x0)/(t1 ‐ t0) This works for all t values as long as they are monotonically increasing. C Coding To use a structure, you must predefine it as follows. Suppose we want a structure called NAME containing x, y, z and t, all as integer quantities. The code looks like: typedef int int int int } NAME; struct x; // y; // z; // t; // name_t { comment comment comment comment Shrink an object as a function of its distance from the observer. Distance from the observer is z GBA Specifics Compute the size of the object according to the formula: size = A/z + B If, for example, z went from 1 (not 0) where the object size is N to 300 where the object size is 1, the formula boils down to: size = (N‐1)/z + 1 C Coding Nothing special CapaMaximize the use of the computation resources of the GBAbility GBA Specifics Although we have to wait for the end of a frame to do manything that changes the display, we can do any complex computation that sets up the display changes (like the interpolations above or some image manipulation) before we wait for the end of the frame update. C Coding In the while loop, delay the call that waits for the frame buffer until you are ready to actually update the display. "Green screen" an image before putting it on the display. Some images (like the golf ball image) are not square, and you only want to paint the round ball on the screen. GBA Specifics This needs a pixel‐by ‐pixel look at the image to replace the background color (black for the golf ball) with the pixels from the background. You could fall back on the pixel‐by‐pixel copying we started with, but this might take too long. As an alternative, you could use the extra time in the display cycle to create a copy of the image pixel‐by‐lixel that is then ready for DMA writing to the display. C Coding Make a second image array to store the dynamically created image and populate it before using DMA to transfer it to the right place in the video buffer. Shrinking an image GBA Specifics We would need to fall back to the Matlab approach for shrinking or stretching images that uses linspace(…) to create a vector of index values, and use that vector to shrink the image as necessary. C Coding To implement a vector in C, we need two technologies: 1. a structure containing the size of the vector and a pointer to an array of that size 2. memory that can be dynamically allocated and de‐allocated to give us arrays of sizes known only at compile time. This is accomplished using the following C functions: #include <malloc.h> // note the different brackets . . int *array = (int *) malloc( n * sizeof(int) ); // or: int *array = (int *) calloc(n, sizeof(int) );//zeros out the array . . free(array); Controlling the state of your program GBA Specifics Frequently, your program needs to operate in different states. This is often accompanied by changing the functionality of buttons, or screen appearance. In our golf game, we might want to initialize the game, but wait for the user to press start before the ball moves. When the ball reaches its target, it should stop there until a different button is pressed; then, the ball should return to the tee and wait for the start button. State machines provide this capability. The golf simulation would have three states: INIT, FLYING and GREEN. States and their transitions are usually depicted graphically. C Coding To code a state machine, you should use an enumerated data type for the states (to improve readability) and a switch statement to process the different states: typedef {INIT, FLYING, GREEN} GAME_STATE; . . GAME_STATE state; switch(state) { case INIT: // wait for the start button break; case FLYING: // dynamics; wait to end of waypoint array break; case GREEN: // wait for select switch break; } Extensive debugging in Visual C++ Visual Ham allows some debugging capability, but when things go seriously sideways, you need the tools available in a serious development environment. Visual Studio is an industrial strength code development environment. You can obtain it at no cost either: a. from Microsoft.com as Visual Studio Express ‐ a limited version of the full thing that is plenty for what we need, or b. get the full MSDNAA version as an ECE student (I have no idea how to do that). However, Visual Studio doesn't execute the hardware instructions that the Visual Ham emulator does. GBA Specifics It is tempting to copy your code into the Visual Studio world and then comment out the stuff that shouldn't be executed. However, when you have the code working right, you then have to repair that damage in Visual Ham. There is a better way. You can use conditional compilation to tailor your code for different environments. When Visual Studio is used in its default Debug mode, it automatically executes this: #define _DEBUG We can use this to make our source code totally transportable between platforms. C Coding VS defaults its project folder location to My Documents\VS something or other\Projects. It's a good idea to leave this alone so you can find stuff. A. To use VS to run code saved in the class archive (like Tests.zip), do the following: 1. unzip the files to the VS Projects directory. This should put everything in a subdirectory (like Tests in this case.) 2. Use Windows Explorer to open that directory and double‐click the .sln file (Tests.sln in my case) 3. this should launch VS with an appearance very similar to VH. You can print stuff to the console or to a file, set break points, view variable values and step through code as we used to in Matlab. B. To use VS to create a new project, do the following: 1. open VS and on the File menu, select New ... Project. 2. the wizard asks you to name the project and its location. Leave the location as the Projects directory, and check that you want to create a directory for the project. 3. Select Next because the default project setup isn't what you want. 4. Check the box next to Empty Project, uncheck everything else and click Finish. 5. VS should give you a new, empty project. Right click the project and select Add .. new file. 6. If you are adding a .h file, select it and hit Finish. 7. There isn't a choice for .c files. So if you want a .c file, select .cpp (C++), but when you name it, call it fred.c (or whatever) As soon as VS sees a file with a .c extension, it switches automagically to strict C compilation (actually, more strict than VH, which is good.) C. To use VS to diagnose VH code whose myLib.c is set up (like my latest code files) with #ifdef _DEBUG, do the following: 1. Do steps B 1‐4 above to make an empty project. 2. Using Windows Explorer, copy all the .c and .h files from your VH project to the project folder you just created. 3. In VS, right click the project and select Add .. Existing sources. 4. a file selection window pops up ‐ select all the .c and .h files and hit Open (Accept, or whatever). The files will be imported and are ready to compile and run. D. Compiling and running VS projects: 1. Pull down the Build menu and select Clean and Build, (a safe thing to do first, then EITHER: 2. Press F5 (run in Debug mode) which will save, compile and run like Matlab, OR 3. Press Ctl‐F5 to save, compile and run ignoring any breakpoints you might have set. 4. To set a break point, click in the grey strip to the left of the line numbers. When you stop at a bre3akpoint, a window shows you local variable values that are updated as you step through the code. You can step into or over function calls, etc. with the debugging toolbar. Suppose you have a function in myLib.c like this: void setMode3BG2() { REG_DISPCNT = BG2_ENABLE | MODE_3; } Being a bit‐mapped I/O operation, who knows what this would do in Visual Studio! You want to disable it, so use a #ifdef ... #endif (actually, #ifndef) pair to prevent this line of code from being seen by the Visual Studio compiler: void setMode3BG2() { #ifndef _DEBUG REG_DISPCNT = BG2_ENABLE | MODE_3; #endif } Then, you realize that you actually want to trace the fact that you did this, so you include a line of code only for Visual Studio like this: void setMode3BG2() { #ifdef _DEBUG fprintf(debug, "set mode 3 and BG 2\n"); // this assumes that you have FILE *debug defined and initialized only in _DEBUG mode #else REG_DISPCNT = BG2_ENABLE | MODE_3; #endif } Speeding up the processor by double buffering the graphics activities. Note for down the road: if you are using images, since there can be only one color palette, all images must share the same bit map. GBA Specifics Switch from Mode 3 to Mode 4 ‐ allows you to write for the whole display cycle then swap to the second video buffer. Unfortunately, this halves the size of each buffer, so you have only 8 bits for each pixel instead of 16. The video memory contents in Mode 4 are therefore not color values, but rather, like Windows BMP images, just an index into a color map (PALETTE in GBA terminology): 1. PALETTE memory is stored at 0X5000000 2. to flip between pages, you use bit 4 (0X10) in the REG_DISPCTL to specify whether to display from the front buffer (0X6000000) or the back buffer (0X600A000). In addition to flipping the bits, you have to change the buffer into which your current changes are made. 3. Setting an individual pixel gets awkward because the memory is addressed 16 bits at a time, and we only want to change 8. So we have to mask and insert correctly. 4. DMA for memory filling gets weird because you are only filling half the buffer you used to, and you have to be really careful with the contents of the word you want to use to fill memory. C Coding 1. is no problem ‐ just define PALETTE memory as a u16 pointer, and populate that buffer with as many colors as you need. In our example, we only need 2. 2. requires some thought to get bit checking right. If theBit contains the bit you are testing in register REG, you test it with: if( REG & theBit ) { ... } else { ... }; To reset the bit, use: REG = REG & ~theBit; To set the bit, use: REG = REG | theBit; 3. setting video buffer bits 8 bits at a time is also awkward. You compute the memory offset for the row and column you are dealing with as usual, but then divide it by 2. You then address the video memory at that offset. If the column you are writing to is odd, you have to mask the data into the left half of the buffer memory. If even, to the right side. If you are changing memory at buffer[ndx] inserting the unsigned byte v, the following shows you how to mask in the correct byte. If col is odd, put v in the left byte with: buffer[index] = (unsigned int) v << 8 | buffer[index] & 0X00FF; If col is even, you put v in the lower byte with: buffer[index] = buffer[index] & 0XFF00 | v; // v can't overflow 8 bits 4. To set up DMA in Mode 4 putting byte v into a block of memory, you first create a local u16 variable with v in each half, and then DMA half the block you expected. Since your local variable doesn't ever seem to the compiler to be used, it must be declared volatile so that the compiler doesn't just delete it. Avoiding code replication GBA Specifics We have two rectangle functions in the library with identical content except thy call different functions to actually set the pixel value. In mode 3, setPixel3 is easy ‐ just replacing the indexed value. In mode 4, it's trickier because the video buffer is only 8 bits per pixel ‐ need the pixel masking stuff. C Coding Function pointers allow you to customize a function by passing the address of a function as a parameter. In C, when a function is called, its name is resolved by the compiler as the memory location of the function code. We normally don't know or care about that. But that address can be passed as a parameter, as we see in the code for Mode4Square. Manipulating Strings GBA Specifics Since strings are a peripheral part of the GBA, we will use Visual Studio to experiment with string manipulation. C Coding Check the text book chapter(s) that deal with strings. The functions are contained in the string.h header. High­performance GBA background graphics ­ Programming Mode 0 GBA Specifics Most high‐performance GBA games use tiles to define backgrounds. See the slides for Lecture 10 for details. There are two GBA challenges that drive the code to create background maps: 1. where the information is stored, and 2. how the screen index information is organized Storing screen information: The same video buffer we used in Mode 3 or 4 is used, with entirely different meanings. It is used to store two things: images of the tiles and indices of the tiles used to define the map on the screen. Tile images are stored in Character Blocks beginning on 16kb boundaries ‐ there are 4 possible starting places at 0X6000000, 0X6004000, 0X6008000, and 0X600C000. Each tile is stored as 64 palette indices, either 4 bits (16 colors) or 8 bits (256 colors). the smaller tiles occupy only 32 bytes of memory; the larger ones 64 bytes. The tile indices are stored in one of 32 Screen Blocks on 2kb boundaries (there are 8 Screen Blocks to a Character Block). They share the video buffer with the tile images, but obviously shouldn't overlap them. usenti can be persuaded to save your image with 3 sets of data: the palette, the tile images and the indices you could use to display all the tiles at the top left of your screen. You choose the CBB location for the tile images (usually, 0X6000000) and the SBB location of the tile indices (I use the 24th block, the first Screen Block in the 4th Character Block). If you use a map 32*32, the linear offset in memory for the tile indices is obvious: col + row * 32. However, the 32*64, 64*32 and 64*64 maps are not organized linearly. See detals in Tonc. C Coding The tidiest way to code the tile locations is to define structs containing 8k u16's for the tile blocks, and then define an array pointer to these things. typedef struct { u16 tileimg[8192]; } charblock; extern charblock *charbase; For the screen blocks, define a struct containing 1024 screen entries, and an array pointer to these. typedef struct { u16 tilemap[1024]; } screenblock; extern screenblock *screenbase; To compute the offset in general, use this function: u32 se_index(u32 row, u32 col, u32 pitch){ u32 sbb= ((col>>5)+(row>>5)*(pitch>>5)); return sbb*1024 + ((col&31)+(row&31)*32); } where pitch is the number of tiles across the map (32 or 64) Dynamic Objects on the screen – Sprites GBA Specifics Sprites are designed and stored in the same way backgrounds are, but they are manipulated differently: ‐ their palette is stored in a different place ‐ their images are stored in the 5th or 6th character blocks ‐ they can be displayed in a number of sizes and configurations ‐ the tiles for each configuration must be contiguous in memory ‐ they are controlled from Object Attribute Memory ‐ unlike backgrounds whose x and y offsets slide the screen around over the image, the sprite [x,y] location is always specified in absolute screen coordinates See the lecture slides for details of the sprite mechanizations. C Coding Nothing new Using Interrupts GBA Specifics Rather than being polled for data like waitForVBlank() or keyPressed(), devices can actively interrupt the processor. The interrupt levels are: 0 VB – vertical blank interrupt 1 HB – horizontal blank interrupt 2 VC – vertical scanline count interrupt 3 T0 – timer 0 interrupt 4 T1 – timer 1 interrupt 5 T2 – timer 2 interrupt 6 T3 – timer 3 interrupt 7 COM – serial communication interrupt 8 DMA0 – DMA0 finished interrupt 9 DMA1 – DMA1 finished interrupt 10 DMA2 – DMA2 finished interrupt 11 DMA3 – DMA3 finished interrupt 12 BUTTON – button interrupt 13 CART – game cartridge interrupt 14‐15 unused C Coding Nothing exciting Implementing Interrupts GBA Specifics The steps to implementing an interrupt are: 1. Enable interrupts globally by putting 1 in the interrupt master enable register REG_IME at 0X4000208 2. Enable interrupts on the device you need to hear from (device dependent, see Timers below) 3. Enable the specific interrupt required by setting the appropriate bit in REG_IE at 0X4000200 4. Establish where the interrupts are processed by putting the address of an interrupt handler at REG_INTERRUPT, 0X3007FFC (all interrupts go to this address; you have to use the bits set in the interrupt flags, REG_IF at 0X4000202, to determine which interrupt happened) 5. coding the interrupt processor to do the following: ‐ disable all interrupts by setting REG_IME to 0 ‐ check the bits in REG_IF to determine which interrupt was enabled ‐ reset the interrupt flag in REG_IF, usually by setting REG_IF to either REG_IF or just the interrupt bit processed ‐ re‐enable REG_IME with a 1 The interrupt bits are: INT_VB INT_HB INT_VC INT_T0 INT_T1 INT_T2 INT_T3 INT_COM INT_DMA0 INT_DMA1 INT_DMA2 INT_DMA3 INT_BUTTON INT_CART 1 << 0 1 << 1 1 << 2 1 << 3 1 << 4 1 << 5 1 << 6 1 << 7 1 << 8 1 << 9 1 << 10 1 << 11 1 << 12 1 << 13 // VB – vertical blank interrupt // HB – horizontal blank interrupt // VC – vertical scanline count interrupt // T0 – timer 0 interrupt // T1 – timer 1 interrupt // T2 – timer 2 interrupt // T3 – timer 3 interrupt // COM – serial communication interrupt // DMA0 – DMA0 finished interrupt // DMA1 – DMA1 finished interrupt // DMA2 – DMA2 finished interrupt // DMA3 – DMA3 finished interrupt // BUTTON – button interrupt // CART – game cartridge interrupt Implementing Timers You don’t have to use interrupts to make use of timers on the GBA. The values of timers can be polled. However, this discussion assumes that you want to use an interrupt to change specific values on a regular basis. GBA Specifics As indicated above, 4 timers are available on the GBA; each can be independently set to one of 4 frequencies, and they can be chained together to create timers with really long intervals. Each timer has two registers: REG_TMxCNT to control it and REG_TMxD to hold its current and reset values. Each control register has 8 bits as follows: Bits 0‐1 specify the frequency of this timer Bit 2 specifies that it is linked to the previous timer Bit 6 specifies that it should interrupt the CPU when the timer reaches 0 Bit 7 turns the timer on. This table shows the frequency settings: Value 00 01 10 11 Cycles 1 1 clock cycle 64 256 1024 Frequency 24 Period 59.604ns 3.811μs 15.259μs 59.382μs 2 Hz or 16,777,216 Hz 18 2 Hz or 262,144 Hz 16 2 Hz or 65,536 Hz 14 2 or 16,384 Hz The REG_TMxD registers have two functions. When the timer is disabled, you write to it to set the value of the counter when it resets. When the timer is running, you read it to find the current timer count (useful if you are polling). The counter register addresses are: #define REG_TM0CNT #define REG_TM1CNT #define REG_TM2CNT #define REG_TM3CNT #define REG_TM0D #define REG_TM1D #define REG_TM2D #define REG_TM3D *(volatile *(volatile *(volatile *(volatile *(volatile *(volatile *(volatile *(volatile u16*)0x4000102 u16*)0x4000106 u16*)0x400010A u16*)0x400010E u16*)0x4000100 u16*)0x4000104 u16*)0x4000108 u16*)0x400010C Example For example, suppose you want an interrupt on your GBA once a minute. The clock frequency is 224 Hz so that there will be 1024*0X4000 ticks in one second. So if we set the frequency of a timer, say #2, to 1024 and set the counter reset to 0X4000, timer 0 will time out in one second. Note that you can’t do this with one timer because 60 * 0X4000 is greater than the size of the reset register. Then, we link that to timer 3, give it a reset count of ‐60 and set its interrupt request. Timer 3 will then interrupt once every 60 sec, or every minute. C Coding Steps to implementing the once‐a‐minute interrupt: 1. Establish the interrupt and timer definitions: //primary interrupt locations #define REG_IME *(u16*)0x4000208 #define REG_IE *(u16*)0x4000200 #define REG_IF *(volatile u16*)0x4000202 #define REG_INTERRUPT *(u32*)0x3007FFC #define REG_DISPSTAT *(u16*)0x4000004 //interrupt constants for turning them on #define INT_VBLANK_ENABLE 1 << 3 //interrupt constants for checking which type of interrupt happened #define INT_VB 1 << 0 // VB – vertical blank interrupt #define INT_HB 1 << 1 // HB – horizontal blank interrupt #define INT_VC 1 << 2 // VC – vertical scanline count interrupt #define INT_T0 1 << 3 // T0 – timer 0 interrupt #define INT_T1 1 << 4 // T1 – timer 1 interrupt #define INT_T2 1 << 5 // T2 – timer 2 interrupt #define INT_T3 1 << 6 // T3 – timer 3 interrupt #define INT_COM 1 << 7 // COM – serial communication interrupt #define INT_DMA0 1 << 8 // DMA0 – DMA0 finished interrupt #define INT_DMA1 1 << 9 // DMA1 – DMA1 finished interrupt #define INT_DMA2 1 << 10 // DMA2 – DMA2 finished interrupt #define INT_DMA3 1 << 11 // DMA3 – DMA3 finished interrupt #define INT_BUTTON 1 << 12 // BUTTON – button interrupt #define INT_CART 1 << 13 // CART – game cartridge interrupt #define #define #define #define #define #define #define #define REG_TM0CNT REG_TM1CNT REG_TM2CNT REG_TM3CNT REG_TM0D REG_TM1D REG_TM2D REG_TM3D *(volatile *(volatile *(volatile *(volatile u16*)0x4000102 u16*)0x4000106 u16*)0x400010A u16*)0x400010E u16*)0x4000100 u16*)0x4000104 u16*)0x4000108 u16*)0x400010C *(volatile *(volatile *(volatile *(volatile // Bits 0-1: Frequency #define TM_FREQ_1 0 #define TM_FREQ_64 1 #define TM_FREQ_256 2 #define TM_FREQ_1024 3 // Toggle overflow from previous timer #define TM_CASCADE (1<<2) // Generate interrupt when timer register is full #define TM_IRQ (1<<6) // Enable timer #define TM_ON (1<<7) 2. Create the interrupt handler and setup functions: int timer = 0; void interruptHandler(void) { REG_IME = 0; //disable interrupts // Check which event happened, and do something if // you care about it if (REG_IF == INT_T3) { // A timer event happened, increment counter timer++; // Static declared elsewhere } REG_IF = INT_T3; // Tell GBA that interrupt has // been handled REG_IME = 1; //enable interrupts } void enableTimerInterrupt() { REG_TM2CNT = 0; // Turn off timer 2 REG_TM3CNT = 0; // Turn off timer 3 REG_TM2D = -0x4000; // 1 sec* REG_TM3D = -60; // seconds in a minute REG_TM2CNT = TM_FREQ_1024 | TM_ON; REG_TM3CNT = TM_IRQ | TM_CASCADE | TM_ON; REG_IE = REG_IE | INT_T3; // Enable timer 3 interrupt detection } void setupInterrupts(void) { REG_IME = 0x0; //disable interrupts REG_INTERRUPT = (u32)interruptHandler; //set int handler enableTimerInterrupt(); REG_IME = 0x1; //enable interrupts } 3. Call the setup function at the beginning of main() 4. Use the value of timer whenever you want the minutes counter. Using Sound GBA Specifics The technical underpinnings of making sound work are pretty brutal. They consist of three elements: ‐ interrupts ‐ timers and ‐ sound files. There are three eloquent lectures on these topics. However, at this point in the semester, it might be best to just grab a convenient set of tools and make it happen. One of our intrepid former TAs has written such a tool set. It is one Matlab function called wav2c.m. Here's how to use it: 1. find a working copy of Matlab and change the directory to a place containing wav2c.m and a .wav file you want the GBA to play. 2. in the command window, enter wav2c( <sound file>, <name> ); where <sound file> is the file name for the .wav file, and <name> is the name you want to associate it with. Suppose you did this with the name 'fred'. If all went well (it might not ‐ there are some .wav file formats that Matlab can't read) wav2c would write three files: fred.c, fred.h and fred.txt. 3. Assuming you have a GBA project with a main.c file that has some graphics to which you want to add sound, you need these three files and a whole bunch more stuff for mylib.h. Grab the latest mylib.h and mylib.c from the Timer project. 4. Include this latest mylib and both fred code files in your project. 5. now, open the file fred.txt and carefully follow the directions for inserting the code that the right places in your main.c 6. Compile and run the thing ‐ your sound should play continuously. 7. To control when the sounds get played, leave the sound setup logic outside the while loop, cut the sound start logic that iswrapped in the if(!playing) { ... } body and paste it wherever in your logic you want the sound to play. C Coding Nothing spectacular ...
View Full Document

Ask a homework question - tutors are online