====== Code ====== The project is complete, all code was tidied and presented in the project report (available on the [[:project]] page), and instructions on its use was added along with pre-compiled versions and //all// libraries necessary to do compilation. Note that if you are only interested in using the GBA serial port, you might do better to look at my [[:project:gba]] page, which includes pre-compiled versions of ''CommsProbe'' and ''RS232 Terminal'' programs, along with their relevant instructions. ===== Random snippets of code ===== ==== BitTest ==== // BitTest - initialize two variables and output the // results of applying the ~, &, | and ^ // operations #include char* toBinary(int n){ static char sResult[(sizeof(n)*8)]; // static so the return doesn't point to a local var // Use bit-wise AND to check if the LSB of n is set, and place into // our string accordingly (filling right->left due to numerical significance) for(int i=sizeof(sResult); i>=0; i--){ sResult[i] = (n & 1) ? '1' : '0' ; n >>= 1; } return sResult; // Return a pointer } int main(int nArg, char* pszArgs[]){ //initialize two arguments int nArg1 = 0x0f0f; int nArg2 = 0xf0f0; //now perform each operation in turn //first the unary NOT operator cout << "nArg1 = b" << toBinary(nArg1) << endl; cout << "nArg2 = b" << toBinary(nArg2) << endl; cout << "~nArg1 = b" << toBinary(~nArg1) << endl; cout << "~nArg2 = b" << toBinary(~nArg2) << endl; //now the binary operators cout << "nArg1 & nArg2 = b" << toBinary(nArg1 & nArg2) << endl; cout << "nArg1 | nArg2 = b" << toBinary(nArg1 | nArg2) << endl; cout << "nArg1 ^ nArg2 = b" << toBinary(nArg1 ^ nArg2) << endl; } ==== CommsProbe ==== #define false 0 #define true 1 #include #include "general-purpose-comms.h" short SC, SD, SI, SO, IO; int data; MULTIBOOT int main(void) { ham_Init(); ham_InitText(0); SC = SD = SI = SO = IO = 0; while(1) { if(F_CTRLINPUT_LEFT_PRESSED) SC=(SC+1)%2; if(F_CTRLINPUT_RIGHT_PRESSED) SD=(SD+1)%2; if(F_CTRLINPUT_B_PRESSED) SI=(SI+1)%2; if(F_CTRLINPUT_A_PRESSED) SO=(SO+1)%2; if(F_CTRLINPUT_L_PRESSED) IO=1; if(F_CTRLINPUT_R_PRESSED) IO=0; set_comms(SC, SD, SI, SO, IO); int i=0; while(i<250000)i++; } } Where ''mygba.h'' is the [[http://www.ngine.de|Ham]] library function and ''general-purpose-comm.h'' is as follows: #ifndef SIO_MULTIPLAYER_H #define SIO_MULTIPLAYER_H 1 //RCNT is used for mode selection #define R_RCNT *(volatile u16*) 0x4000134 #include /* GBAtek 1.4: 134h - RCNT (R) - SIO Mode, usage in GENERAL-PURPOSE Mode (R/W) Interrupts can be requested when SI changes from HIGH to LOW, as General Purpose mode does not require a serial shift clock, this interrupt may be produced even when the GBA is in Stop (low power standby) state. Bit Expl. 0 SC Data Bit (0=Low, 1=High) 1 SD Data Bit (0=Low, 1=High) 2 SI Data Bit (0=Low, 1=High) 3 SO Data Bit (0=Low, 1=High) 4 SC Direction (0=Input, 1=Output) 5 SD Direction (0=Input, 1=Output) 6 SI Direction (0=Input, 1=Output, but see below) 7 SO Direction (0=Input, 1=Output) 8 Interrupt Request (0=Disable, 1=Enable) 9-13 Not used 14 Must be "0" for General-Purpose Mode 15 Must be "1" for General-Purpose or JOYBUS Mode SI should be always used as Input to avoid problems with other hardware which does not expect data to be output there. */ // Accept a string input of 0 and 1 chars void set_comms(short SC, short SD, short SI, short SO, short IO){ u16 data; data = (1*SC) + (2*SD) + (4*SI) + (8*SO); data = data + (IO * 0xf0); // Set all pins to IO (1=Out, 0=In) data = data + 0x8000; // Set general purpose mode ('10' MSB-LSB) R_RCNT = data; char st[100]; sprintf(st,"[%04x]",R_RCNT); ham_DrawText(1,1, st); if(IO) ham_DrawText(1,2, "Output"); else ham_DrawText(1,2, "Input "); if(0x1 & R_RCNT) ham_DrawText(1,11,"SC"); else ham_DrawText(1,11,"--"); if(0x2 & R_RCNT) ham_DrawText(5,11,"SD"); else ham_DrawText(5,11,"--"); if(0x4 & R_RCNT) ham_DrawText(10,11,"SI"); else ham_DrawText(10,11,"--"); if(0x8 & R_RCNT) ham_DrawText(15,11,"SO"); else ham_DrawText(15,11,"--"); } #endif ==== SuspendTest ==== NB, has occasional problems with interrupt-driven progress indicator at present, so it's been disabled. #include "mygba.h" MULTIBOOT u32 count=0; void interrupt(); char* toBinary(u8 n); int main(void) { ham_Init(); ham_InitText(0); ham_InitPad(); // Setup the keypad interrupt register R_CTRLINT= 256+ // Button R 512+ // Button L 4+ // Select 16384+ // Enable interrupt flag 32768 // Enable logical AND mdoe (all button required) ; // Enable keypad interrupt handling ham_StartIntHandler(INT_TYPE_KEY,&interrupt); ham_DrawText(1,1,"Press A to sleep"); ham_DrawText(1,2,"Press L & R & Select to wake"); while(1) { ham_UpdatePad(); if(Pad.Pressed.A) { // BIOS 'Stop' command (turn everything off until interrupted) asm volatile("swi 0x30000"); } } return 0; } void interrupt() { count++; ham_DrawText(1,3,"%d",count); return; } char* toBinary(u8 n){ static char sResult[(sizeof(n)*8)]; // static so the return doesn't point to a local var // Use bit-wise AND to check if the LSB of n is set, and place into // our string accordingly (filling right->left due to numerical significance) int i=sizeof(sResult); while(i>=0){ sResult[i] = (n & 1) ? '1' : '0' ; n >>= 1; i--; } return sResult; // Return a pointer } ==== SRAM Backup ==== Written to solve problems with my brother's GBA cart (which was losing his save game to Fire Emblam because he plays too quickly). This is designed to use a [[http://www.devrs.com/gba/files/mbv2faqs.php|Multi-Boot v2 cable]] (often shortened to MBv2) and copies to/from the PC as well as displaying a menu on the PC which requires user input to proceed. #include #include "mbv2lib.c" MULTIBOOT int i = 0; const int SRAM_size = 65536; void displayProgress(); int main (void) { ham_Init(); ham_InitText(0); ham_DrawText(1,1,"Program started\n"); char c; int l = 1; FILE fp = 0; dprintf("Program Started.\n"); dprintf("Choose:\n"); dprintf("c : Complete (GBA->before.bin, sram.bin->GBA, verify, GBA->after.bin)\n"); dprintf("b : Backup (to sram.bin) and verify\n"); dprintf("B : Backup (to sram.bin)\n"); dprintf("v : Verify (against sram.bin)\n"); dprintf("r : Restore (from sram.bin) and verify\n"); dprintf("R : Restore (from sram.bin)\n"); dprintf("\n"); // Get character from PC keyboard c = dgetch (); dprintf("Setting SRAM waitstate\n"); R_WAITCNT = 3; // Set SRAM wait state for reliable reading/writing // Setup a timer and interrupt for displaying progress indicators M_TIM0CNT_SPEED_SELECT_SET(1); // increment timer count every 3,841ms M_TIM0COUNT_SET(3); // Count up to this number M_TIM0CNT_TIMER_START; M_TIM0CNT_IRQ_ENABLE; // This timer can trigger interrupts // Disabled as it's randomly causing the program to hang //ham_StartIntHandler(INT_TYPE_TIM0,&displayProgress); // Backup if(c == 'c' || c == 'b') { // Copy SRAM or Flash backup to PC switch(c){ case 'c': ham_DrawText(1,++l,"Backing up SRAM (before.bin)"); dprintf("\nCopying SRAM to before.bin\n"); fp = dfopen("before.bin","wb"); break; case 'B': case 'b': ham_DrawText(1,++l,"Backing up SRAM (sram.bin)"); dprintf("\nCopying SRAM to sram.bin\n"); fp = dfopen("sram.bin","wb"); break; } for (i = 0; i != SRAM_size; i++) { dfputc(*(unsigned char *)(i + 0xE000000), fp); } ham_DrawText(1,++l, "Done"); dfclose(fp); dprintf("Done.\n"); } // Restore if(c == 'c' || c == 'r' || c == 'R') { // Copy PC to SRAM ham_DrawText(1,++l,"Restoring SRAM (sram.bin)"); dprintf("\nWriting sram.bin to GBA...\n"); dprintf("Opening file..."); fp = dfopen("sram.bin","rb"); dprintf(" Done.\n"); dprintf("Entering loop.\n"); for (i = 0; i != SRAM_size; i++) { *(unsigned char *)(i + 0xE000000) = dfgetc (fp); } dprintf("End of loop, closing file..."); dfclose(fp); dprintf("Done.\n"); dprintf("Writing phase complete.\n"); ham_DrawText(1,++l,"Done"); } // Verify if(c == 'c' || c == 'b' || c == 'r' || c == 'v') { ham_DrawText(1,++l,"Verifying SRAM (sram.bin)"); dprintf("\nVerifying SRAM against sram.bin\n"); dprintf("Opening file..."); fp = dfopen("sram.bin","rb"); dprintf(" Done.\n"); dprintf("Entering loop.\n"); int errors = 0; for (i = 0; i != SRAM_size; i++) { // File char != SRAM char if(dfgetc (fp) != *(unsigned char *)(i+0xE000000)) { errors++; } } dprintf("End of loop, closing file..."); dfclose(fp); dprintf("Done.\n"); if(errors) { ham_DrawText(1,++l,"%d bytes failed verification",errors); dprintf("%d bytes failed verification.\n", errors); } else { ham_DrawText(1,++l,"Verify OK!"); dprintf("Verify OK!\n"); } } // Backup to after.bin if(c == 'c') { ham_DrawText(1,++l,"Backing up SRAM (after.bin)"); dprintf("\nCopying current SRAM to after.bin\n"); fp = dfopen("after.bin","wb"); for (i = 0; i != SRAM_size; i++) { dfputc(*(unsigned char *)(i + 0xE000000), fp); } ham_DrawText(1,++l, "Done backing up SRAM"); dfclose(fp); dprintf("Done.\n"); } // Remove interrupt to ensure clean restart ham_StopIntHandler(INT_TYPE_TIM0); M_TIM0CNT_TIMER_STOP; M_TIM0CNT_IRQ_DISABLE; displayProgress(); // Avoid the progress showing '98 percent done' etc ham_DrawText(1,++l,"Program completed"); dprintf("\nProgram complete. Press any key to restart\n\n\n"); dgetch (); return 0; } void displayProgress() { M_INTMST_DISABLE; // Disable interrupts ham_DrawText(1,19,"%d percent done. ", i / (SRAM_size/100)); M_INTMST_ENABLE; // Re-Enable interrupts } where mbv2lib.c is #define __ANSI_NAMES 0 // // v1.40 - Initial release. // v1.41 - Put received keyboard & file data in separate buffers // to prevent mixing of data during simultaneous use. // Added dfprintf library function. // v1.42 - Fixed bug in dfputc() where escape characters caused // character 0x01 to be sent to console. Thanks to // Tim Schuerewegen for finding this bug. // // MB v1.41 or later pc software is required to use this library // software. // // NOTE: THIS LIBRARY USES GLOBAL INITIALIZED DATA SO YOU MUST USE // A CRT0.S AND A LINKER SCRIPT THAT SUPPORTS THIS AS WELL. GET // CRTLS V1.1 OR LATER FROM HTTP://www.devrs.com/gba FOR PROPER SUPPORT. // // The following library functions are supported: // // Library name Standard Name Function // dprintf printf Print a string on PC console. // dputchar putchar Print a char on PC console. // dgetch getch Get a char from PC keyboard. // dkbhit kbhit Return 1 if PC keyboard char is ready. // // dfopen fopen Open PC file. // dfclose fclose Close PC file. // dfprintf fprintf Print a string to PC file. // dfgetc fgetc Get char from PC file. // dfputc fputc Write a char to PC file. // drewind rewind Set file pointer to start of file. // // If you wish to use the standard naming conventions // rather than the library names then change "__ANSI_NAMES 0" // to "__ANSI_NAMES 1" instead. // // Notes: // // Currently only ONE file may be open at a time. // // If you are sending raw binary data to a PC file, use // dfputc instead of dfprintf. Dfprintf will insert // carriage return characters before linefeed characters // on the PC side if the PC console software is running on // dos/windows for proper text formatting. // // If you are missing some .h files during compile than get // 'arminc.zip' from http://www.devrs.com/gba in the // Apps / C Compilers section. // // Example command line: // mb -s file.mb -c -w 50 -x 255 -m // // In this example, after transferring "file.mb" to the GBA, // the PC goes into console/file server mode (-c) and also // shows all of the file open/file close/fgetc/fputc commands // (-m) on screen. The -w value should be a large enough value // where the -s is reliable and the -x value should be a large // enough value where the -c is reliable with the GBA. // // [Sending a file & console mode each have // their own delay settings because they // each use a different method for transferring // data. Each method is about ideal for it's // application.] // // Example GBA Code: // // #include "mbv2lib.c" // // int main (void) // { // // int i,j,k; // FILE fp; // // dprintf ("Hello world!"); // // // Get character from PC keyboard // i = dgetch (); // // // Copy SRAM or Flash backup to PC // fp = dfopen("sram.bin","wb"); // for (i = 0; i != 0x8000; i++) // dfputc(*(unsigned char *)(i + 0xE000000), fp); // dfclose(fp); // // // Copy PC to SRAM // fp = dfopen("sram.bin","rb"); // for (i = 0; i != 0x8000; i++) // *(unsigned char *)(i + 0xE000000) = dfgetc (fp); // dfclose(fp); // // Read data from file // fp = dfopen ("foo.bin", "rb"); // i = dfgetc (fp); // j = dfgetc (fp); // k = dfgetc (fp); // dfclose (fp); // // } // Data transfer format // -------------------- // // PC -> GBA Comms: // Raw data is PC File read data. // ESCCHR 0x00 = nada (used for input polling) // ESCCHR 0x01 = Escape character from PC file read // ESCCHR 0x08 0x?? = Keyboard read data // // // GBA -> PC comms // Raw data is console print data. // ESCCHR = escape sequence // ESCCHR 0x00 = nada (used for input polling) // ESCCHR 0x01 = Escape character for console print // ESCCHR 0x02 = file open (gba -> PC) // ESCCHR 0x03 = file close (gba -> PC) // ESCCHR 0x04 = fgetc (gba -> PC) // ESCCHR 0x05 0x?? = fputc (gba -> PC) // ESCCHR 0x06 = rewind (gba -> PC) // ESCCHR 0x07 = fputc processed (gba -> pC) (Add CR before LF char if win/DOS machine) #define FILE int // Uncomment the following line to define the following types if needed //#define INC_SHORT_NAME_TYPES 1 #ifdef INC_SHORT_NAME_TYPES typedef volatile unsigned char vu8; typedef volatile unsigned short int vu16; typedef volatile unsigned int vu32; typedef volatile unsigned long long int vu64; typedef unsigned char u8; typedef unsigned short int u16; typedef unsigned int u32; typedef unsigned long long int u64; typedef signed char s8; typedef signed short int s16; typedef signed int s32; typedef signed long long int s64; #endif #ifdef INC_REG_DEFS #define REG_BASE 0x4000000 #define REG_SIOCNT (REG_BASE + 0x128) // Serial Communication Control #define REG_SIODATA8 (REG_BASE + 0x12a) // 8bit Serial Communication Data #define REG_RCNT (REG_BASE + 0x134) // General Input/Output Control #endif #include "vsprintf.c" #define __DOUTBUFSIZE 256 #define __FINBUFSIZE 256 //Must be a multiple of 2! (ex: 32,64,128,256,512..) #define __KINBUFSIZE 64 //Must be a multiple of 2! (ex: 32,64,128,256,512..) #define __ESCCHR 27 #define __ESC_NADA 0 #define __ESC_ESCCHR 1 #define __ESC_FOPEN 2 #define __ESC_FCLOSE 3 #define __ESC_FGETC 4 #define __ESC_FPUTC 5 #define __ESC_REWIND 6 #define __ESC_FPUTC_PROCESSED 7 // PC side add CR before LF if DOS machine #define __ESC_KBDCHR 8 unsigned char __outstr[__DOUTBUFSIZE]; unsigned char __finstr[__FINBUFSIZE]; unsigned char __kinstr[__KINBUFSIZE]; int finptr = 0; int foutptr = 0; int kinptr = 0; int koutptr = 0; int __dputchar (int c) { int rcv; static int LastChar = 0; static int KbdCharNext = 0; // Set non-general purpose comms mode *(u16 *)REG_RCNT = 0; // Init normal comms, 8 bit transfer, receive clocking *(u16 *)REG_SIODATA8 = c; *(u16 *)REG_SIOCNT = 0x80; // Wait until transfer is complete while (*(vu16 *)REG_SIOCNT & 0x80) {} // Wait until SC is low while (*(vu16 *)REG_RCNT & 1) {} // Force SD high *(u16 *)REG_RCNT = 0x8022; // Wait until SC is high while ((*(vu16 *)REG_RCNT & 1)==0) {} rcv = *(vu16 *)REG_SIODATA8; if (KbdCharNext) { // Put into keyboard buffer __kinstr[kinptr++] = rcv; kinptr &= (__KINBUFSIZE-1); KbdCharNext = 0; // Make received char look like a NADA character // so that it won't be buffered elsewhere. LastChar = __ESCCHR; rcv = __ESC_NADA; } if (LastChar == __ESCCHR) { // Process escape character switch (rcv) { case __ESC_ESCCHR: __finstr[finptr++] = __ESCCHR; finptr &= (__FINBUFSIZE-1); break; case __ESC_KBDCHR: KbdCharNext = 1; break; } LastChar = 0; } else { if (rcv == __ESCCHR) LastChar = __ESCCHR; else { // If char received from PC then save in receive FIFO __finstr[finptr++] = rcv; finptr &= (__FINBUFSIZE-1); } } return(1); } int dputchar (int c) { (void) __dputchar(c); if (c == __ESCCHR) (void) __dputchar(__ESC_ESCCHR); return (1); } void __PrintStr (char *str) { while (*str) (void) dputchar(*str++); } int dgetch (void) { int c; // If no character is in FIFO then wait for one. while (kinptr == koutptr) { __dputchar(__ESCCHR); __dputchar(__ESC_NADA); } c = __kinstr[koutptr++]; koutptr &= (__KINBUFSIZE-1); return (c); } int dfgetch (void) { int c; // If no character is in FIFO then wait for one. while (finptr == foutptr) { __dputchar(__ESCCHR); __dputchar(__ESC_NADA); } c = __finstr[foutptr++]; foutptr &= (__FINBUFSIZE-1); return (c); } int dkbhit (void) { return(kinptr != koutptr); } FILE dfopen (const char *file, const char *type) { __dputchar(__ESCCHR); __dputchar(__ESC_FOPEN); while (*file) (void) dputchar(*file++); dputchar(0); while (*type) (void) dputchar(*type++); dputchar(0); return(1); } int dfclose (FILE fp) { __dputchar(__ESCCHR); __dputchar(__ESC_FCLOSE); return(1); } int dfgetc (FILE fp) { __dputchar(__ESCCHR); __dputchar(__ESC_FGETC); return(dfgetch()); } int dfputc (int ch, FILE fp) { __dputchar(__ESCCHR); __dputchar(__ESC_FPUTC); __dputchar(ch); /* Bug fix. Was: dputchar(ch); */ return(1); } void drewind (FILE fp) { __dputchar(__ESCCHR); __dputchar(__ESC_REWIND); } void __PrintStrToFile (FILE fp, char *str) { while (*str) { __dputchar(__ESCCHR); __dputchar(__ESC_FPUTC_PROCESSED); dputchar(*str++); } } #ifndef dprintf #define dprintf(x...) ({ dsprintf(__outstr, x); __PrintStr(__outstr); }) #endif #define dfprintf(y,x...) ({ dsprintf(__outstr, x); __PrintStrToFile(y,__outstr); }) #ifdef __ANSI_NAMES #define printf dprintf #define fprintf dfprintf #define putchar dputchar #define getch dgetch #define kbhit dkbhit #define fopen dfopen #define fclose dfclose #define fgetc dfgetc #define fputc dfputc #define rewind drewind #endif ==== RS232 Terminal ==== Adapted from [[http://www.fivemouse.com.com/gba]] to compile with HAM and echo sent characters locally, the code is spread accross multiple files, so I've just compressed the files into a .rar file. [[http://robmeerman.co.uk/downloads/GBA%20RS232%20Terminal.rar]]