Homebrew GBARunner2

Gericom

Well-Known Member
OP
Member
Joined
Jun 30, 2011
Messages
1,359
Trophies
0
Age
23
XP
3,873
Country
Netherlands
GBARunner2 is a hypervisor that runs GBA games on DS/DSi/3DS in DS mode, basically like Nintendont does for running Gamecube games on Wii.

Download
Source and releases can be found on github: https://github.com/Gericom/GBARunner2

Compatibility List
On the gbatemp wiki: https://wiki.gbatemp.net/wiki/GBARunner2
Many thanks to @Dodain47 for investing so much time in testing games!

Usage
  • Place a GBA bios on your sd card. Either /bios.bin or /gba/bios.bin will work.
    • Note: Do not use Normmatt's open-source GBA BIOS. It won't work, as many patches are applied at fixed addresses. The right checksums are listed below.
  • If you have a gba folder on the root of your sd, this folder will be opened by default
  • Do NOT use SRAM patches unless stated on the wiki. They cause problems with the internal patching of GBARunner2 and may actually break saving.
  • If you are using GBARunner2 with TWiLightMenu on a DSi or 3DS with the SD card, make sure you use the dldi on ARM7 build
If you want to use existing save files, they should have the same name as the gba file but instead of .gba they should be .sav (so the save for mygame.gba is mygame.sav).

BIOS Checksums
The BIOS used should have the following checksums (checking one should be enough):
  • CRC32: 81977335
  • MD5: a860e8c0b6d573d191e4ec7db1b1e4f6
  • SHA1: 300c20df6731a33952ded8c436f7f186d25d3492
  • SHA256: fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570
You can check the MD5 with WinMD5Free for example.
 
Last edited by Gericom,

windwakr

Well-Known Member
Member
Joined
Sep 13, 2009
Messages
495
Trophies
0
Website
Visit site
XP
1,605
Country
United States
Very impressive. Can't wait to see the compatibility improve.

Commit 6c1dc96:
Advance Wars - Seems to run fast with good audio, but a lot of graphical bugs. Will freeze if you don't skip the intro.
Zelda Minish Cap - Extremely slow, graphical bugs, garbage audio. Too slow to bother trying to get in game.
Castlevania Circle of the Moon - Very slow, a few graphical bugs, good audio.
Final Fantasy Tactics Advance - Extreme graphical bugs, title screen seems to have some sort of palette issue then freezes.


Tested on a launch day phat(wow, it's 12 years old now) with an Acekard 2i.
 
Last edited by windwakr,

nxwing

Well-Known Member
Member
Joined
Jul 22, 2013
Messages
2,232
Trophies
1
XP
2,363
Country
Philippines
This is pretty nice! I'll test it out with a few games and I'll try and make an entry on the wiki for this.
 

Gericom

Well-Known Member
OP
Member
Joined
Jun 30, 2011
Messages
1,359
Trophies
0
Age
23
XP
3,873
Country
Netherlands
This is pretty nice! I'll test it out with a few games and I'll try and make an entry on the wiki for this.
Thanks, that would be nice. Make sure to include a field for the commit number, as things might change from commit to commit.
 

Coto

-
Member
Joined
Jun 4, 2010
Messages
2,921
Trophies
1
XP
2,306
Country
Chile
arm7 touchscreen
I got RTC working in gbaemu4ds long ago so i'll share how it was done:

(I just literally mapped them)

first I used IPC to transfer stuff between ARM7->ARM9

//ipc header definition
Code:
ipc.h

//---------------------------------------------------------------------------------
typedef struct sMyIPC {
//---------------------------------------------------------------------------------
  int16 touchX,  touchY;  // raw x/y
   int16 touchXpx, touchYpx; // TFT x/y pixel

   int16 touchZ1,  touchZ2;  // TSC x-panel measurements
   uint16 tdiode1,  tdiode2;  // TSC temperature diodes
   uint32 temperature;  // TSC computed temperature

   uint16 buttons;  // keypad buttons
  uint16 buttons_xy_folding;  // X, Y, /PENIRQ buttons

   u8 touched;          //TSC touched?
  u8 touch_pendown;  //TSC already held before?

   uint16 battery;  // battery life status
   uint16 aux;  // SPI AUX port status


  //IPC Clock
  //[0]; //yy
  //[1]; //mth
  //[2]; //dd
  //[3]; //wat - day of week?
  //[4]; //hh
  //[5]; //mm
  //[6]; //ss
  u8 clockdata[0x20];


} tMyIPC;

//Shared Work  027FF000h 4KB  -  -  -  R/W
//IPC Struct
#define SHARED_ADDR_REGION ((tMyIPC volatile *)(0x027FF000))


//ipc definition opcodes (i dont really care about BCD conversion....)
Code:
//gbaemu4ds clock opcodes
u8 gba_get_yearbytertc(){
   return (u8)(u32)SHARED_ADDR_REGION->clockdata[0];
}

u8 gba_get_monthrtc(){
   return (u8)(u32)SHARED_ADDR_REGION->clockdata[1];
}

u8 gba_get_dayrtc(){
   return (u8)(u32)SHARED_ADDR_REGION->clockdata[2];
}

u8 gba_get_dayofweekrtc(){
   return (u8)(u32)SHARED_ADDR_REGION->clockdata[3];
}


u8 gba_get_hourrtc(){
   return (u8)(u32)SHARED_ADDR_REGION->clockdata[4];
}

u8 gba_get_minrtc(){
   return (u8)(u32)SHARED_ADDR_REGION->clockdata[5];
}

u8 gba_get_secrtc(){
   return (u8)(u32)SHARED_ADDR_REGION->clockdata[6];
}



//ARM7

Code:
touch_ipc.h

#ifdef __cplusplus
extern "C" {
#endif

extern void updateMyIPC();

#ifdef __cplusplus
}
#endif

Code:
touch_ipc.c

#include "touch_ipc.h"
#include "../../common/gba_ipc.h"

#include <nds/ndstypes.h>
#include <nds/system.h>
#include <nds/arm7/touch.h>
#include <nds/input.h>
#include <nds/interrupts.h>

#include <nds/bios.h>
#include <nds/arm7/clock.h>
#include <nds/ipc.h>
#include <stdlib.h>

//coto: use it at vcount interrupts so you save battery (or vblank but vcount works just fine)
void updateMyIPC()
{
   uint16 but=0, batt=0;
   int t1=0, t2=0;
   uint32 temp=0;
   u32 i=0;
   u8 clock_buf[sizeof(SHARED_ADDR_REGION->clockdata)];

  touchPosition tempPos;
  touchReadXY(&tempPos);

   // Read the touch screen
   but = REG_KEYXY;
   batt = touchRead(TSC_MEASURE_BATTERY);
   // Read the time
   rtcGetTimeAndDate(clock_buf);

   // Read the temperature
   temp = touchReadTemperature(&t1, &t2);

  // Update the IPC struct
   SHARED_ADDR_REGION->buttons     = but;
   SHARED_ADDR_REGION->touched  = ((tempPos.px > 0) || (tempPos.py > 0)) ? 1 : 0;

  //raw x/y
  SHARED_ADDR_REGION->touchX  = tempPos.rawx;
  SHARED_ADDR_REGION->touchY  = tempPos.rawy;

  //TFT x/y pixel
  SHARED_ADDR_REGION->touchXpx = tempPos.px;
  SHARED_ADDR_REGION->touchYpx = tempPos.py;

   SHARED_ADDR_REGION->touchZ1 = tempPos.z1;
   SHARED_ADDR_REGION->touchZ2 = tempPos.z2;
   SHARED_ADDR_REGION->battery     = batt;

  //Get time
  for(i=0; i< sizeof(clock_buf); i++){
  SHARED_ADDR_REGION->clockdata[i] = clock_buf[i];
  }

   SHARED_ADDR_REGION->temperature = temp;
   SHARED_ADDR_REGION->tdiode1 = t1;
   SHARED_ADDR_REGION->tdiode2 = t2;

}



//ARM9

arm9 touchscreen handler (only if you want touchscreen on custom handlers)
Code:
touch_ipc.h


#ifndef GBAEMU4DS_ARM9TOUCH
#define GBAEMU4DS_ARM9TOUCH

#include <nds.h>
#include <nds/touch.h>

#endif


#ifdef __cplusplus
extern "C"{
#endif

extern void touchReadXY_gbaemu4ds(touchPosition * touchpos_inst);

#ifdef __cplusplus
}
#endif

Code:
touch_ipc.c

#include "touch_ipc.h"
#include <nds.h>
#include <nds/touch.h>
#include "../../../common/gba_ipc.h"

//usage: struct touchPosition touchposition;
//touchReadXY_gbaemu4ds(&touchposition);

void touchReadXY_gbaemu4ds(touchPosition * touchpos_inst){

  touchpos_inst->rawx =  SHARED_ADDR_REGION->touchX;
  touchpos_inst->rawy =  SHARED_ADDR_REGION->touchY;

  //TFT x/y pixel
  touchpos_inst->px  =  SHARED_ADDR_REGION->touchXpx;
  touchpos_inst->py  =  SHARED_ADDR_REGION->touchYpx;

  touchpos_inst->z1  =  SHARED_ADDR_REGION->touchZ1;
  touchpos_inst->z2  =  SHARED_ADDR_REGION->touchZ2;
}



and then VBA libs:
Code:
RTC.H

// -*- C++ -*-
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2004 Forgotten and the VBA development team

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or(at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#ifndef VBA_RTC_H
#define VBA_RTC_H
extern u16 rtcRead(u32 address);
extern bool rtcWrite(u32 address, u16 value);
extern void rtcEnable(bool);
extern bool rtcIsEnabled();
extern void rtcReset();

extern void rtcReadGame(gzFile file);
extern void rtcSaveGame(gzFile file);

#endif

Code:
RTC.C

// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2005 Forgotten and the VBA development team

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or(at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#include "ipc.h"

#include "System.h"
#include "GBA.h"
#include "Globals.h"
#include "Port.h"
#include "Util.h"
#include "NLS.h"

#include <time.h>
#include <nds/memory.h>//#include <memory.h> ichfly
#include <nds/ndstypes.h>
#include <nds/memory.h>
#include <nds/bios.h>
#include <nds/system.h>
#include <nds/arm9/math.h>
#include <nds/arm9/video.h>
#include <nds/arm9/videoGL.h>
#include <nds/arm9/trig_lut.h>
#include <nds/arm9/sassert.h>

enum RTCSTATE { IDLE, COMMAND, DATA, READDATA };

typedef struct {
  u8 byte0;
  u8 byte1;
  u8 byte2;
  u8 command;
  int dataLen;
  int bits;
  RTCSTATE state;
  u8 data[12];
  // reserved variables for future
  u8 reserved[12];
  bool reserved2;
  u32 reserved3;
} RTCCLOCKDATA;

static RTCCLOCKDATA rtcClockData;
static bool rtcEnabled = false;

void rtcEnable(bool e)
{
  rtcEnabled = e;
}

bool rtcIsEnabled()
{
  return rtcEnabled;
}

u16 rtcRead(u32 address)
{
  if(rtcEnabled) {
  if(address == 0x80000c8)
  return rtcClockData.byte2;
  else if(address == 0x80000c6)
  return rtcClockData.byte1;
  else if(address == 0x80000c4) {
  return rtcClockData.byte0;
  }
  }

  return READ16LE((&rom[address & 0x1FFFFFE])); //your little endian u16 read from gba map gets replaced here
}

static u8 toBCD(u8 value)
{
  value = value % 100;
  int l = value % 10;
  int h = value / 10;
  return h * 16 + l;
}

bool rtcWrite(u32 address, u16 value)
{
  if(!rtcEnabled)
  return false;

  if(address == 0x80000c8) {
  rtcClockData.byte2 = (u8)value; // enable ?
  } else if(address == 0x80000c6) {
  rtcClockData.byte1 = (u8)value; // read/write
  } else if(address == 0x80000c4) {
  if(rtcClockData.byte2 & 1) {
  if(rtcClockData.state == IDLE && rtcClockData.byte0 == 1 && value == 5) {
  rtcClockData.state = COMMAND;
  rtcClockData.bits = 0;
  rtcClockData.command = 0;
  } else if(!(rtcClockData.byte0 & 1) && (value & 1)) { // bit transfer
  rtcClockData.byte0 = (u8)value;
  switch(rtcClockData.state) {
  case COMMAND:
  rtcClockData.command |= ((value & 2) >> 1) << (7-rtcClockData.bits);
  rtcClockData.bits++;
  if(rtcClockData.bits == 8) {
  rtcClockData.bits = 0;
  switch(rtcClockData.command) {
  case 0x60:
  // not sure what this command does but it doesn't take parameters
  // maybe it is a reset or stop
  rtcClockData.state = IDLE;
  rtcClockData.bits = 0;
  break;
  case 0x62:
  // this sets the control state but not sure what those values are
  rtcClockData.state = READDATA;
  rtcClockData.dataLen = 1;
  break;
  case 0x63:
  rtcClockData.dataLen = 1;
  rtcClockData.data[0] = 0x40;
  rtcClockData.state = DATA;
  break;
  case 0x64:
  break;
  case 0x65:
  {

  /*
  struct tm *newtime;
  time_t long_time;

  time( &long_time );  // Get time as long integer.
  newtime = localtime( &long_time );// Convert to local time.

  rtcClockData.dataLen = 7;
  rtcClockData.data[0] = toBCD(newtime->tm_year);
  rtcClockData.data[1] = toBCD(newtime->tm_mon+1);
  rtcClockData.data[2] = toBCD(newtime->tm_mday);
  rtcClockData.data[3] = toBCD(newtime->tm_wday);
  rtcClockData.data[4] = toBCD(newtime->tm_hour);
  rtcClockData.data[5] = toBCD(newtime->tm_min);
  rtcClockData.data[6] = toBCD(newtime->tm_sec);
  rtcClockData.state = DATA;
  */

  //coto: own IPC.
  rtcClockData.dataLen = 7;
  rtcClockData.data[0] = toBCD(gba_get_yearbytertc());
  rtcClockData.data[1] = toBCD(gba_get_monthrtc());
  rtcClockData.data[2] = toBCD(gba_get_dayrtc());
  rtcClockData.data[3] = toBCD(gba_get_dayofweekrtc());
  rtcClockData.data[4] = toBCD(gba_get_hourrtc());
  rtcClockData.data[5] = toBCD(gba_get_minrtc());
  rtcClockData.data[6] = toBCD(gba_get_secrtc());
  rtcClockData.state = DATA;

  }
  break;
  case 0x67:
  {
  /*
  struct tm *newtime;
  time_t long_time;

  time( &long_time );  // Get time as long integer.
  newtime = localtime( &long_time ); // Convert to local time.

  rtcClockData.dataLen = 3;
  rtcClockData.data[0] = toBCD(newtime->tm_hour);
  rtcClockData.data[1] = toBCD(newtime->tm_min);
  rtcClockData.data[2] = toBCD(newtime->tm_sec);
  rtcClockData.state = DATA;
  */

  //coto: own IPC.
  rtcClockData.dataLen = 3;
  rtcClockData.data[0] = toBCD(gba_get_hourrtc());
  rtcClockData.data[1] = toBCD(gba_get_minrtc());
  rtcClockData.data[2] = toBCD(gba_get_secrtc());
  rtcClockData.state = DATA;

  }
  break;
  default:
  systemMessage(0, N_("Unknown RTC command %02x"), rtcClockData.command);
  rtcClockData.state = IDLE;
  break;
  }
  }
  break;
  case DATA:
  if(rtcClockData.byte1 & 2) {
  } else {
  rtcClockData.byte0 = (rtcClockData.byte0 & ~2) |
  ((rtcClockData.data[rtcClockData.bits >> 3] >>
  (rtcClockData.bits & 7)) & 1)*2;
  rtcClockData.bits++;
  if(rtcClockData.bits == 8*rtcClockData.dataLen) {
  rtcClockData.bits = 0;
  rtcClockData.state = IDLE;
  }
  }
  break;
  case READDATA:
  if(!(rtcClockData.byte1 & 2)) {
  } else {
  rtcClockData.data[rtcClockData.bits >> 3] =
  (rtcClockData.data[rtcClockData.bits >> 3] >> 1) |
  ((value << 6) & 128);
  rtcClockData.bits++;
  if(rtcClockData.bits == 8*rtcClockData.dataLen) {
  rtcClockData.bits = 0;
  rtcClockData.state = IDLE;
  }
  }
  break;
     default:
  break;
  }
  } else
  rtcClockData.byte0 = (u8)value;
  }
  }
  return true;
}

void rtcReset()
{
  memset(&rtcClockData, 0, sizeof(rtcClockData));

  rtcClockData.byte0 = 0;
  rtcClockData.byte1 = 0;
  rtcClockData.byte2 = 0;
  rtcClockData.command = 0;
  rtcClockData.dataLen = 0;
  rtcClockData.bits = 0;
  rtcClockData.state = IDLE;
}

Then

Code:
//your little endian u16 write handler to gba map gets replaced here
CPUWriteHalfWord(u32 address, u16 value){
  switch(address >> 24) {
  case 8:
  case 9:
  if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) {
  if(!rtcWrite(address, value))
  goto unwritable;
  } else if(!agbPrintWrite(address, value)) goto unwritable;
  break;
  }

}


//your little endian u16 read handler from gba map gets replaced here
u16 CPUReadHalfWord(u32 address){
switch(address >> 24) {
  case 8:
  case 9:
  case 10:
  case 11:
  case 12:
  if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8)
  value = rtcRead(address);
  else
   {
     value = READ16LE(((u16 *)&rom[address & 0x1FFFFFE]));
   }
  break;

}
}


so before gba execution begins you:
Code:
rtcReset();

then

rtcEnable(true); //nds7 RTC support.

You could print the RTC values and see if they read the clock. Libnds does this but since we are in broken defaulttemplateland stuff like this must be done.

edit: 06/12/2016 fixed SHARED_ADDR_REGION declaration usage across source files.
 
Last edited by Coto,

Gericom

Well-Known Member
OP
Member
Joined
Jun 30, 2011
Messages
1,359
Trophies
0
Age
23
XP
3,873
Country
Netherlands
arm7 touchscreen
I got RTC working in gbaemu4ds long ago so i'll share how it was done:

(I just literally mapped them)

first I used IPC to transfer stuff between ARM7->ARM9

//ipc header definition
Code:
ipc.h

//---------------------------------------------------------------------------------
typedef struct sMyIPC {
//---------------------------------------------------------------------------------
  int16 touchX,  touchY;  // raw x/y
   int16 touchXpx, touchYpx; // TFT x/y pixel

   int16 touchZ1,  touchZ2;  // TSC x-panel measurements
   uint16 tdiode1,  tdiode2;  // TSC temperature diodes
   uint32 temperature;  // TSC computed temperature

   uint16 buttons;  // keypad buttons
  uint16 buttons_xy_folding;  // X, Y, /PENIRQ buttons

   u8 touched;          //TSC touched?
  u8 touch_pendown;  //TSC already held before?
  
   uint16 battery;  // battery life status
   uint16 aux;  // SPI AUX port status

  
  //IPC Clock
  //[0]; //yy
  //[1]; //mth
  //[2]; //dd
  //[3]; //wat - day of week?
  //[4]; //hh
  //[5]; //mm
  //[6]; //ss
  u8 clockdata[0x20];
 
  
} tMyIPC;

//Shared Work  027FF000h 4KB  -  -  -  R/W
//IPC Struct
#define SHARED_ADDR_REGION 0x027FF000


//ipc definition opcodes (i dont really care about BCD conversion....)
Code:
//gbaemu4ds clock opcodes
u8 gba_get_yearbytertc(){
   return (u8)(u32)GBAEMU4DS_IPC->clockdata[0];
}

u8 gba_get_monthrtc(){
   return (u8)(u32)GBAEMU4DS_IPC->clockdata[1];
}

u8 gba_get_dayrtc(){
   return (u8)(u32)GBAEMU4DS_IPC->clockdata[2];
}

u8 gba_get_dayofweekrtc(){
   return (u8)(u32)GBAEMU4DS_IPC->clockdata[3];
}


u8 gba_get_hourrtc(){
   return (u8)(u32)GBAEMU4DS_IPC->clockdata[4];
}

u8 gba_get_minrtc(){
   return (u8)(u32)GBAEMU4DS_IPC->clockdata[5];
}

u8 gba_get_secrtc(){
   return (u8)(u32)GBAEMU4DS_IPC->clockdata[6];
}



//ARM7

Code:
touch_ipc.h

#ifdef __cplusplus
extern "C" {
#endif

extern void updateMyIPC();

#ifdef __cplusplus
}
#endif

Code:
touch_ipc.c

#include "touch_ipc.h"
#include "../../common/gba_ipc.h"

#include <nds/ndstypes.h>
#include <nds/system.h>
#include <nds/arm7/touch.h>
#include <nds/input.h>
#include <nds/interrupts.h>

#include <nds/bios.h>
#include <nds/arm7/clock.h>
#include <nds/ipc.h>
#include <stdlib.h>

//coto: use it at vcount interrupts so you save battery (or vblank but vcount works just fine)
void updateMyIPC()
{
   uint16 but=0, batt=0;
   int t1=0, t2=0;
   uint32 temp=0;
   u32 i=0;
   u8 clock_buf[sizeof(GBAEMU4DS_IPC->clockdata)];
  
  touchPosition tempPos;
  touchReadXY(&tempPos);
  
   // Read the touch screen
   but = REG_KEYXY;
   batt = touchRead(TSC_MEASURE_BATTERY);
   // Read the time
   rtcGetTimeAndDate(clock_buf);

   // Read the temperature
   temp = touchReadTemperature(&t1, &t2);
   GBAEMU4DS_IPC->mailBusy = 1;
  
  // Update the IPC struct
   GBAEMU4DS_IPC->buttons     = but;
   GBAEMU4DS_IPC->touched  = ((tempPos.px > 0) || (tempPos.py > 0)) ? 1 : 0;
  
  //raw x/y
  GBAEMU4DS_IPC->touchX  = tempPos.rawx;
  GBAEMU4DS_IPC->touchY  = tempPos.rawy;
  
  //TFT x/y pixel
  GBAEMU4DS_IPC->touchXpx = tempPos.px;
  GBAEMU4DS_IPC->touchYpx = tempPos.py;  
  
   GBAEMU4DS_IPC->touchZ1 = tempPos.z1;
   GBAEMU4DS_IPC->touchZ2 = tempPos.z2;
   GBAEMU4DS_IPC->battery     = batt;
  
  //Get time
  for(i=0; i< sizeof(clock_buf); i++){
  GBAEMU4DS_IPC->clockdata[i] = clock_buf[i];
  }
  
   GBAEMU4DS_IPC->temperature = temp;
   GBAEMU4DS_IPC->tdiode1 = t1;
   GBAEMU4DS_IPC->tdiode2 = t2;  
  
  GBAEMU4DS_IPC->mailBusy = 0;
}



//ARM9

arm9 touchscreen handler (only if you want touchscreen on custom handlers)
Code:
touch_ipc.h


#ifndef GBAEMU4DS_ARM9TOUCH
#define GBAEMU4DS_ARM9TOUCH

#include <nds.h>
#include <nds/touch.h>

#endif


#ifdef __cplusplus
extern "C"{
#endif

extern void touchReadXY_gbaemu4ds(touchPosition * touchpos_inst);

#ifdef __cplusplus
}
#endif

Code:
touch_ipc.c

#include "touch_ipc.h"
#include <nds.h>
#include <nds/touch.h>
#include "../../../common/gba_ipc.h"

//usage: struct touchPosition touchposition;
//touchReadXY_gbaemu4ds(&touchposition);

void touchReadXY_gbaemu4ds(touchPosition * touchpos_inst){
  
  touchpos_inst->rawx =  GBAEMU4DS_IPC->touchX;
  touchpos_inst->rawy =  GBAEMU4DS_IPC->touchY;
  
  //TFT x/y pixel
  touchpos_inst->px  =  GBAEMU4DS_IPC->touchXpx;
  touchpos_inst->py  =  GBAEMU4DS_IPC->touchYpx;
  
  touchpos_inst->z1  =  GBAEMU4DS_IPC->touchZ1;
  touchpos_inst->z2  =  GBAEMU4DS_IPC->touchZ2;
}



and then VBA libs:
Code:
RTC.H

// -*- C++ -*-
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2004 Forgotten and the VBA development team

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or(at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#ifndef VBA_RTC_H
#define VBA_RTC_H
extern u16 rtcRead(u32 address);
extern bool rtcWrite(u32 address, u16 value);
extern void rtcEnable(bool);
extern bool rtcIsEnabled();
extern void rtcReset();

extern void rtcReadGame(gzFile file);
extern void rtcSaveGame(gzFile file);

#endif

Code:
RTC.C

// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2005 Forgotten and the VBA development team

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or(at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#include "ipc.h"

#include "System.h"
#include "GBA.h"
#include "Globals.h"
#include "Port.h"
#include "Util.h"
#include "NLS.h"

#include <time.h>
#include <nds/memory.h>//#include <memory.h> ichfly
#include <nds/ndstypes.h>
#include <nds/memory.h>
#include <nds/bios.h>
#include <nds/system.h>
#include <nds/arm9/math.h>
#include <nds/arm9/video.h>
#include <nds/arm9/videoGL.h>
#include <nds/arm9/trig_lut.h>
#include <nds/arm9/sassert.h>

enum RTCSTATE { IDLE, COMMAND, DATA, READDATA };

typedef struct {
  u8 byte0;
  u8 byte1;
  u8 byte2;
  u8 command;
  int dataLen;
  int bits;
  RTCSTATE state;
  u8 data[12];
  // reserved variables for future
  u8 reserved[12];
  bool reserved2;
  u32 reserved3;
} RTCCLOCKDATA;

static RTCCLOCKDATA rtcClockData;
static bool rtcEnabled = false;

void rtcEnable(bool e)
{
  rtcEnabled = e;
}

bool rtcIsEnabled()
{
  return rtcEnabled;
}

u16 rtcRead(u32 address)
{
  if(rtcEnabled) {
  if(address == 0x80000c8)
  return rtcClockData.byte2;
  else if(address == 0x80000c6)
  return rtcClockData.byte1;
  else if(address == 0x80000c4) {
  return rtcClockData.byte0;
  }
  }

  return READ16LE((&rom[address & 0x1FFFFFE])); //your little endian u16 read from gba map gets replaced here
}

static u8 toBCD(u8 value)
{
  value = value % 100;
  int l = value % 10;
  int h = value / 10;
  return h * 16 + l;
}

bool rtcWrite(u32 address, u16 value)
{
  if(!rtcEnabled)
  return false;

  if(address == 0x80000c8) {
  rtcClockData.byte2 = (u8)value; // enable ?
  } else if(address == 0x80000c6) {
  rtcClockData.byte1 = (u8)value; // read/write
  } else if(address == 0x80000c4) {
  if(rtcClockData.byte2 & 1) {
  if(rtcClockData.state == IDLE && rtcClockData.byte0 == 1 && value == 5) {
  rtcClockData.state = COMMAND;
  rtcClockData.bits = 0;
  rtcClockData.command = 0;
  } else if(!(rtcClockData.byte0 & 1) && (value & 1)) { // bit transfer
  rtcClockData.byte0 = (u8)value;  
  switch(rtcClockData.state) {
  case COMMAND:
  rtcClockData.command |= ((value & 2) >> 1) << (7-rtcClockData.bits);
  rtcClockData.bits++;
  if(rtcClockData.bits == 8) {
  rtcClockData.bits = 0;
  switch(rtcClockData.command) {
  case 0x60:
  // not sure what this command does but it doesn't take parameters
  // maybe it is a reset or stop
  rtcClockData.state = IDLE;
  rtcClockData.bits = 0;
  break;
  case 0x62:
  // this sets the control state but not sure what those values are
  rtcClockData.state = READDATA;
  rtcClockData.dataLen = 1;
  break;
  case 0x63:
  rtcClockData.dataLen = 1;
  rtcClockData.data[0] = 0x40;
  rtcClockData.state = DATA;
  break;
  case 0x64:
  break;
  case 0x65:
  {

  /*
  struct tm *newtime;
  time_t long_time;

  time( &long_time );  // Get time as long integer.
  newtime = localtime( &long_time );// Convert to local time.
  
  rtcClockData.dataLen = 7;
  rtcClockData.data[0] = toBCD(newtime->tm_year);
  rtcClockData.data[1] = toBCD(newtime->tm_mon+1);
  rtcClockData.data[2] = toBCD(newtime->tm_mday);
  rtcClockData.data[3] = toBCD(newtime->tm_wday);
  rtcClockData.data[4] = toBCD(newtime->tm_hour);
  rtcClockData.data[5] = toBCD(newtime->tm_min);
  rtcClockData.data[6] = toBCD(newtime->tm_sec);
  rtcClockData.state = DATA;
  */
  
  //coto: own IPC.
  rtcClockData.dataLen = 7;
  rtcClockData.data[0] = toBCD(gba_get_yearbytertc());
  rtcClockData.data[1] = toBCD(gba_get_monthrtc());
  rtcClockData.data[2] = toBCD(gba_get_dayrtc());
  rtcClockData.data[3] = toBCD(gba_get_dayofweekrtc());
  rtcClockData.data[4] = toBCD(gba_get_hourrtc());
  rtcClockData.data[5] = toBCD(gba_get_minrtc());
  rtcClockData.data[6] = toBCD(gba_get_secrtc());
  rtcClockData.state = DATA;
  
  }
  break;  
  case 0x67:
  {
  /*
  struct tm *newtime;
  time_t long_time;

  time( &long_time );  // Get time as long integer.
  newtime = localtime( &long_time ); // Convert to local time.
  
  rtcClockData.dataLen = 3;
  rtcClockData.data[0] = toBCD(newtime->tm_hour);
  rtcClockData.data[1] = toBCD(newtime->tm_min);
  rtcClockData.data[2] = toBCD(newtime->tm_sec);
  rtcClockData.state = DATA;
  */
  
  //coto: own IPC.
  rtcClockData.dataLen = 3;
  rtcClockData.data[0] = toBCD(gba_get_hourrtc());
  rtcClockData.data[1] = toBCD(gba_get_minrtc());
  rtcClockData.data[2] = toBCD(gba_get_secrtc());
  rtcClockData.state = DATA;
  
  }
  break;
  default:
  systemMessage(0, N_("Unknown RTC command %02x"), rtcClockData.command);
  rtcClockData.state = IDLE;
  break;
  }
  }
  break;
  case DATA:
  if(rtcClockData.byte1 & 2) {
  } else {
  rtcClockData.byte0 = (rtcClockData.byte0 & ~2) |
  ((rtcClockData.data[rtcClockData.bits >> 3] >>
  (rtcClockData.bits & 7)) & 1)*2;
  rtcClockData.bits++;
  if(rtcClockData.bits == 8*rtcClockData.dataLen) {
  rtcClockData.bits = 0;
  rtcClockData.state = IDLE;
  }
  }
  break;
  case READDATA:
  if(!(rtcClockData.byte1 & 2)) {
  } else {
  rtcClockData.data[rtcClockData.bits >> 3] =
  (rtcClockData.data[rtcClockData.bits >> 3] >> 1) |
  ((value << 6) & 128);
  rtcClockData.bits++;
  if(rtcClockData.bits == 8*rtcClockData.dataLen) {
  rtcClockData.bits = 0;
  rtcClockData.state = IDLE;
  }
  }
  break;
     default:
  break;
  }
  } else
  rtcClockData.byte0 = (u8)value;
  }
  }
  return true;
}

void rtcReset()
{
  memset(&rtcClockData, 0, sizeof(rtcClockData));

  rtcClockData.byte0 = 0;
  rtcClockData.byte1 = 0;
  rtcClockData.byte2 = 0;
  rtcClockData.command = 0;
  rtcClockData.dataLen = 0;
  rtcClockData.bits = 0;
  rtcClockData.state = IDLE;
}

Then

Code:
//your little endian u16 write handler to gba map gets replaced here
CPUWriteHalfWord(u32 address, u16 value){
  switch(address >> 24) {
  case 8:
  case 9:
  if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) {
  if(!rtcWrite(address, value))
  goto unwritable;
  } else if(!agbPrintWrite(address, value)) goto unwritable;
  break;
  }

}


//your little endian u16 read handler from gba map gets replaced here
u16 CPUReadHalfWord(u32 address){
switch(address >> 24) {
  case 8:
  case 9:
  case 10:
  case 11:
  case 12:
  if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8)
  value = rtcRead(address);
  else
   {
     value = READ16LE(((u16 *)&rom[address & 0x1FFFFFE]));   
   }
  break;  

}
}


so before gba execution begins you:
Code:
rtcReset();

then

rtcEnable(true); //nds7 RTC support.

You could print the RTC values and see if they read the clock. Libnds does this but since we are in broken defaulttemplateland stuff like this must be done.
This sounds like it should be quite straightforward to implement. I think I'll have a look at it.
awesome! can it play games from a folder or is the runner.gba the only game I can run??
Currently it can only start runner.gba, but later on I want to implement a gui.
 

Gericom

Well-Known Member
OP
Member
Joined
Jun 30, 2011
Messages
1,359
Trophies
0
Age
23
XP
3,873
Country
Netherlands
Great, I will test it ASAP!
With the GUI coming in the future, would it be possible to provide precompiled .nds files that loads the bios.bin directly from the SD?
Yes, I will be implementing that indeed later on. I was now mainly focusing on getting the games to run as good as possible.
 
  • Like
Reactions: TeamScriptKiddies

Coto

-
Member
Joined
Jun 4, 2010
Messages
2,921
Trophies
1
XP
2,306
Country
Chile
Also remember that NIFI requires stable arm7 interrupts (if you ever want multiplayer working). Which is why I was asking to have a separate branch for ARM7 / ARM9 - libfat, C code (interrupts, timer cycles, IPC like the RTC example shown above). It will truly save you a lot of headaches once you start implementing more things that must be synced(which would be better to maintain to remove any race attack condition events).

Also,if you run DSWIFI libs to sync at vblank nds clock cycle, its pretty stable.

If you dont at least handle vblank properly in ARM9 NIFI will work but unstable (and socket-like services devkitpro, will cause lockups).

edit: also I have flash/eeprom/sram handling like the RTC example working. If you are interested to see those examples (should be C code in my opinion) since it is not a timing dependant operation, unless you want to emulate the clockchip cycles (definitely not required)
 
Last edited by Coto,
  • Like
Reactions: zfreeman

AtlasFontaine

Well-Known Member
Member
Joined
Jul 18, 2015
Messages
1,095
Trophies
0
Age
24
Location
Venezuela-Zulia.
XP
845
Country
Venezuela
Tested Zelda minish cap (after SRAM patching it), it runs until the intro video, after ending the intro it crashes on black screen (graphics are messed up on title screen also).

Tried Advanced Wars 2 Black Hole Rising but the game crashed right after the bios intro and showed this on the bottom screen:
c9c0cb8716294fba9bc40186fcfe9e3c.png

Castlevania Aria of Sorrow ran pretty good (60fps), I played it for 15 minutes.

Completed the first level of Wario Land 4 and it worked perfectly.

played Fire Emblem to the 2nd mission and it worked perfectly.
 
Last edited by AtlasFontaine,

Gericom

Well-Known Member
OP
Member
Joined
Jun 30, 2011
Messages
1,359
Trophies
0
Age
23
XP
3,873
Country
Netherlands
Tested Zelda minish cap (after SRAM patching it), it runs until the intro video, after ending the intro it crashes on black screen (raphics are messed up on title screen also).

Tried Advanced Wars 2 Black Hole Rising but the game crashed right after the bios intro and showed this on the bottom screen:
c9c0cb8716294fba9bc40186fcfe9e3c.png

Castlevania Aria of Sorrow ran pretty good (60fps), I played it for 15 minutes.

Completed the first level of Wario Land 4 and it worked perfectly.

played Fire Emblem to the 2nd mission and it worked perfectly.
Thanks for your feedback, we really need a compatibility list like the one gbaemu4ds has. Would be interesting to compare compatibility too.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
    Dark_Phoras @ Dark_Phoras: I do live in the vortex of materialism of any place I've ever been