The project is complete, all code was tidied and presented in the project report (available on the Final Year 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 Game Boy Advance page, which includes pre-compiled versions of CommsProbe
and RS232 Terminal
programs, along with their relevant instructions.
// BitTest - initialize two variables and output the // results of applying the ~, &, | and ^ // operations #include <iostream.h> 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; }
#define false 0 #define true 1 #include <mygba.h> #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 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 <stdio.h> /* 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
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 }
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 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 <mygba.h> #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
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