Table of Contents

Code

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.

Random snippets of code

BitTest

// 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;
}

CommsProbe

#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

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 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

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