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