// EDIT This Stack Definitely Works: #define __STACK_SIZE__ 2048*1024 /*256*1024*/
#define __STACK_SIZE__ 512*1024 /*256*1024*/

#include "palmdep.h"
#include "endianutils.h"
#include "pnobridge.h"

#include "oscompat.h"
#include "endianutils.h"

// features
#define F_FPS 1           /* nolonger needed */
#define F_DO_EVENTS 1

#ifdef F_FPS
UInt32 fps_lasttime = 0;
UInt32 fps_counter = 0;
char fps_buffer [ 10 ] = "FPS: ";
#endif
// for TwMidi.h
typedef struct TwSmfType* TwSmfHandle;

// conveniance pointers
UInt16 *vram;
UInt32 display_pitch,display_height,display_width;
UInt16 ticks_per_sec;
UInt32 device_type;

// Linker still looks for ARMlet_Main as entry point, but the
// "ARMlet" name is now officially discouraged.  Compare an
// contrast to "PilotMain" for 68K applications.
#define PNO_Main ARMlet_Main

// Define DEBUG as 0 to turn off debugging support in the PNO.
// With this on, the PNO will register itself with the Palm OS
// Debugger on startup, allowing stepping through the code.
#define DEBUG 0

// ------------------------

asm unsigned int swap_stack(unsigned int new ,unsigned int dummy)
{	
	mov  r1,r13
	mov  r13,r0
	mov	 r0,r1
	bx	 lr
}

unsigned long PNO_Main(
	const void *emulStateP, 
	void *userData68KP, 
	Call68KFuncType *call68KFuncP);
	
SysAppInfoPtr SysGetAppInfo(SysAppInfoPtr *uiAppPP, SysAppInfoPtr *actionCodeAppPP);
unsigned long Run ( void );

Call68KFuncType * g_my_call68KFuncP;
const void *g_my_emulStateP;
pnobridge_t *g_pnobridge;

UInt32 currentOwnerID;
UInt32 g_host = 0;
UInt8 g_running = 0;

unsigned long PNO_Main(
	const void *emulStateP, 
	void *userData68KP, 
	Call68KFuncType *call68KFuncP)
{
#ifdef __STACK_SIZE__  
	unsigned int my_stack,my_stackal,old_stack;
#endif
	unsigned long pno_result;
	SysAppInfoPtr appInfoP,unusedAppInfoP;
#ifdef PNODEBUG
  AdnDebugNativeRegister ( 'appl', 'STRT', 'ARMC', 1 );
#endif
	
  // needed before making any OS calls using the 
  // PACEInterface library
  //InitPACEInterface ( emulStateP, call68KFuncP );

  // my cheap hack to get StrAToI happy without modifying paceinterfacelib
  g_my_call68KFuncP = call68KFuncP;
  g_my_emulStateP = emulStateP;
  g_pnobridge = userData68KP;

  currentOwnerID = SysGetAppInfo(&unusedAppInfoP, & appInfoP)->memOwnerID; 

  g_pnobridge -> vfs_max = ByteSwap32 ( g_pnobridge -> vfs_max );
  g_pnobridge -> vfs_vol = ByteSwap32 ( g_pnobridge -> vfs_vol );
  g_pnobridge -> vram = ByteSwap32 ( g_pnobridge -> vram );
  g_pnobridge -> eventf = ByteSwap32 ( g_pnobridge -> eventf );
  g_pnobridge -> display_width = ByteSwap32 ( g_pnobridge -> display_width );
  g_pnobridge -> display_height = ByteSwap32 ( g_pnobridge -> display_height );
  g_pnobridge -> display_pitch = ByteSwap32 ( g_pnobridge -> display_pitch );
  g_pnobridge -> game_rom = ByteSwap32 ( g_pnobridge -> game_rom );
  g_pnobridge -> audio_buf = ByteSwap32 ( g_pnobridge -> audio_buf );
  g_pnobridge -> flags = ByteSwap32 ( g_pnobridge -> flags );

  vram = (UInt16*) (g_pnobridge -> vram);
  display_pitch = g_pnobridge -> display_pitch;
  display_width = g_pnobridge -> display_width;
  display_height = g_pnobridge -> display_height;

  device_type = g_pnobridge -> flags & BF_ZODIAC;
  
  /*new stack*/
#ifdef __STACK_SIZE__  

  my_stack=(unsigned int)malloc(__STACK_SIZE__+32);
  if(my_stack == NULL)	ErrDisplay("Can't allocate stack space");
  my_stackal=my_stack+__STACK_SIZE__;
  my_stackal-=my_stackal&31;

  old_stack=swap_stack(my_stackal,0);
#endif 

  pno_result=Run();
  
  //FtrSet(CREATORID, FTR_OPT_SHOWBATTMODE_ID,ljp_showBattMode);  
#ifdef __STACK_SIZE__
  swap_stack(old_stack,0);
//  MemPtrFree((void*)my_stack);
  if( my_stack != NULL )	free((void*)my_stack);
#endif  
    
  // do something useful
  return pno_result;
}

/*
long int StrAToI ( const char *ptr ) {
  UInt8 params [ 4 ];

  params [ 0 ] = (UInt8)((UInt32)(ptr) >> 24);
  params [ 1 ] = (UInt8)((UInt32)(ptr) >> 16);
  params [ 2 ] = (UInt8)((UInt32)(ptr) >> 8);
  params [ 3 ] = (UInt8)(ptr);

  return ((long int)((g_my_call68KFuncP)( (void *)(g_my_emulStateP),
				       (0xA0CE & (0x00000FFF)),
				       &params, sizeof(params))));
}

UInt32 TimGetTicks ( void ) {
  return (( UInt32)((g_my_call68KFuncP)( (void*)(g_my_emulStateP),
					 (0xA0F7 & (0x00000FFF)), 0, 0)));
}

UInt16 SysTicksPerSecond ( void ) {
  return (( UInt16)((g_my_call68KFuncP)( (void*)(g_my_emulStateP),
					 (0xA2E9 & (0x00000FFF)), 0, 0)));
}

UInt32 KeyCurrentState ( void ) {
  return (( UInt32)((g_my_call68KFuncP)( (void*)(g_my_emulStateP),
					 (0xA2A2 & (0x00000FFF)), 0, 0)));
}
*/

void *GetObjectPtr ( UInt16 objectID ) {
  FormPtr frmP;
  frmP = FrmGetActiveForm();
  return FrmGetObjectPtr(frmP, FrmGetObjectIndex(frmP, objectID));
}


UInt16 palm_vfs_iter ( void ) {
  return ( g_pnobridge -> vfs_vol );
}

UInt16 palm_vfs_count ( void ) {
  return ( g_pnobridge -> vfs_max );
}


  
void stub_menu_button(int X,int Y,char *message,int state,RectanglePtr area,int size)
{
  bighostcall_t data;
  UInt32 arg[2];

  data.a=ByteSwap32((UInt32)X);
  data.b=ByteSwap32((UInt32)Y);
  data.c=ByteSwap32((UInt32)message);
  data.d=ByteSwap32((UInt32)state);
  data.e=ByteSwap32((UInt32)area);
  data.f=ByteSwap32((UInt32)size);

  arg[0] = BRIDGE_menu_button;
  arg[0] = ByteSwap32 ( arg[0] );
  arg[1] = (UInt32)(&data);
  arg[1] = ByteSwap32 ( arg[1] );

  g_my_call68KFuncP ( g_my_emulStateP,
			  (UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
			  

  if (area)
  {
	  area->topLeft.x=ByteSwap16(area->topLeft.x);
	  area->topLeft.y=ByteSwap16(area->topLeft.y);
	  area->extent.x=ByteSwap16(area->extent.x);
	  area->extent.y=ByteSwap16(area->extent.y);
  }

}

void stub_menu_message(char *message)
{
	UInt32 arg[2];

	arg[0] = BRIDGE_menu_message;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(message);
	arg[1] = ByteSwap32 ( arg[1] );

	g_my_call68KFuncP ( g_my_emulStateP,
			  (UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );

}

void stub_menu_bar(int X,int Y,int val,int max,RectangleType *area)
{
	bighostcall_t data;
	UInt32 arg[2];

	data.a=ByteSwap32((UInt32)X);
	data.b=ByteSwap32((UInt32)Y);
	data.c=ByteSwap32((UInt32)val);
	data.d=ByteSwap32((UInt32)max);
	data.e=ByteSwap32((UInt32)area);	
	
	arg[0] = BRIDGE_menu_bar;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(&data);
	arg[1] = ByteSwap32 ( arg[1] );

	g_my_call68KFuncP ( g_my_emulStateP,
			  (UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
	
	area->topLeft.x=ByteSwap16(area->topLeft.x);
  	area->topLeft.y=ByteSwap16(area->topLeft.y);
 	area->extent.x=ByteSwap16(area->extent.x);
 	area->extent.y=ByteSwap16(area->extent.y);

}

void stub_menu_messageRaw(int X,int Y,char *message)
{
	hostcall_t data;
	UInt32 arg[2];

	data.a=ByteSwap32((UInt32)X);
	data.b=ByteSwap32((UInt32)Y);
	data.c=ByteSwap32((UInt32)message);

	arg[0] = BRIDGE_menu_messageRaw;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(&data);
	arg[1] = ByteSwap32 ( arg[1] );

	g_my_call68KFuncP ( g_my_emulStateP,
			  (UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );				

}



UInt32 stub_filter_event(EventType *event)
{
	UInt32 arg[2];

	arg[0] = BRIDGE_filter_event;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(event);
	arg[1] = ByteSwap32 ( arg[1] );

	return g_my_call68KFuncP ( g_my_emulStateP,
			  (UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );	
}

void stub_handle_sysevent(EventType *event)
{
	UInt32 arg[2];

	arg[0] = BRIDGE_handle_sysevent;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(event);
	arg[1] = ByteSwap32 ( arg[1] );

	g_my_call68KFuncP ( g_my_emulStateP,
			  (UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );				
	
}

Err stub_SndStreamCreate(SndStreamRef *channel,	/* channel-ID is stored here */
						SndStreamMode mode,			/* input/output, enum */
						UInt32 samplerate,				/* in frames/second, e.g. 44100, 48000 */
						SndSampleType type,				/* enum, e.g. sndInt16 */
						SndStreamWidth width,			/* enum, e.g. sndMono */
						SndStreamBufferCallback func,	/* function that gets called to fill a buffer */
						void *userdata,					/* gets passed in to the above function */
						UInt32 buffsize,				/* preferred buffersize in frames, not guaranteed, use 0 for default */
						Boolean armNative){				/* true if callback is arm native */
	bighostcall_t data;
	Err r;
	UInt32 arg[2];

	data.a=ByteSwap32((UInt32)channel);
	data.b=ByteSwap32((UInt32)mode);
	data.c=ByteSwap32((UInt32)samplerate);
	data.d=ByteSwap32((UInt32)type);
	data.e=ByteSwap32((UInt32)width);	
	data.f=ByteSwap32((UInt32)func);	
	data.g=ByteSwap32((UInt32)userdata);	
	data.h=ByteSwap32((UInt32)buffsize);	
	data.i=ByteSwap32((UInt32)armNative);	
	
	
	arg[0] = BRIDGE_SndStreamCreate;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(&data);
	arg[1] = ByteSwap32 ( arg[1] );

	r=g_my_call68KFuncP ( g_my_emulStateP,(UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
	*channel = (SndStreamRef)ByteSwap32((UInt32)(*channel));
	return r;
}							

Err stub_SndStreamCreateExtended(	SndStreamRef *channel,	/* channel-ID is stored here */
	SndStreamMode mode,		/* input/output, enum */
	SndFormatType format,	/* enum, e.g., sndFormatMP3 */
	UInt32 samplerate,		/* in frames/second, e.g. 44100, 48000 */
	SndSampleType type,		/* enum, e.g. sndInt16, if applicable, or 0 otherwise */
	SndStreamWidth width,	/* enum, e.g. sndMono */
	SndStreamVariableBufferCallback func,	/* function that gets called to fill a buffer */
	void *userdata,			/* gets passed in to the above function */
	UInt32 buffsize,			/* preferred buffersize, use 0 for default */
	Boolean armNative)			/* true if callback is arm native */
{
	bighostcall_t data;
	Err r;
	UInt32 arg[2];

	data.a=ByteSwap32((UInt32)channel);
	data.b=ByteSwap32((UInt32)mode);
	data.c=ByteSwap32((UInt32)format);
	data.d=ByteSwap32((UInt32)samplerate);
	data.e=ByteSwap32((UInt32)type);	
	data.f=ByteSwap32((UInt32)width);	
	data.g=ByteSwap32((UInt32)func);	
	data.h=ByteSwap32((UInt32)userdata);	
	data.i=ByteSwap32((UInt32)buffsize);
	
	arg[0] = BRIDGE_SndStreamCreateExtended;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(&data);
	arg[1] = ByteSwap32 ( arg[1] );

	r=g_my_call68KFuncP ( g_my_emulStateP,(UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
	*channel = (SndStreamRef)ByteSwap32((UInt32)(*channel));
	return r;
}

Err stub_SndStreamDelete(SndStreamRef channel){
	UInt32 arg[2];
	arg[0] = BRIDGE_SndStreamDelete;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(channel);
	arg[1] = ByteSwap32 ( arg[1] );
	return g_my_call68KFuncP ( g_my_emulStateP,(UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
}

Err stub_SndStreamStart(SndStreamRef channel){
	UInt32 arg[2];
	arg[0] = BRIDGE_SndStreamStart;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(channel);
	arg[1] = ByteSwap32 ( arg[1] );
	return g_my_call68KFuncP ( g_my_emulStateP,(UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
}

Err stub_SndStreamStop(SndStreamRef channel){
	UInt32 arg[2];
	arg[0] = BRIDGE_SndStreamStop;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(channel);
	arg[1] = ByteSwap32 ( arg[1] );
	return g_my_call68KFuncP ( g_my_emulStateP,(UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
}

Err stub_SndStreamSetVolume(SndStreamRef channel, Int32 volume){
	UInt32 arg[2];
	hostcall_t data;	
	data.a=ByteSwap32((UInt32)channel);
	data.b=ByteSwap32((UInt32)volume);	
	arg[0] = BRIDGE_SndStreamSetVolume;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(&data);
	arg[1] = ByteSwap32 ( arg[1] );
	return g_my_call68KFuncP ( g_my_emulStateP,(UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
}

Err stub_SndStreamGetVolume(SndStreamRef channel, Int32 *volume){
	Int32 arg[2];
	Err r;
	hostcall_t data;	
	data.a=ByteSwap32((UInt32)channel);
	data.b=ByteSwap32((UInt32)volume);
	arg[0] = BRIDGE_SndStreamGetVolume;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(&data);
	arg[1] = ByteSwap32 ( arg[1] );
	r=g_my_call68KFuncP ( g_my_emulStateP,(UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
	*volume=ByteSwap32((UInt32)(*volume));
	return r;
}

// 0 == lock; 1 == unlock; 2 == blit; 3 == scaled blit with optionnal smooth (s)
UInt32 stubzodiacblit ( UInt32 mode, UInt32 w,UInt32 h,UInt32 s ) {
  Int32 arg[2];
  hostcall_t data;	
  UInt32 ret;
  
  data.a=ByteSwap32(mode);
  data.b=ByteSwap32(w);
  data.c=ByteSwap32(h);
  data.d=ByteSwap32(s);
  arg[0] = BRIDGE_zodiacblit;
  arg[0] = ByteSwap32 ( arg[0] );
  arg[1] = (UInt32)(&data);
  arg[1] = ByteSwap32 ( arg[1] );
	
  ret = g_my_call68KFuncP ( g_my_emulStateP,
			  (UInt32) g_pnobridge -> eventf, &arg, 8 );

  return ( ret );
}


Err stub_TwSmfStop(TwSmfHandle aSmfHandle)
{
	UInt32 arg[2];
	arg[0] = BRIDGE_midi_stop;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(aSmfHandle);
	arg[1] = ByteSwap32 ( arg[1] );
	return g_my_call68KFuncP ( g_my_emulStateP,(UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
}

Err stub_TwSmfClose(TwSmfHandle aSmfHandle)
{
	UInt32 arg[2];
	arg[0] = BRIDGE_midi_close;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(aSmfHandle);
	arg[1] = ByteSwap32 ( arg[1] );
	return g_my_call68KFuncP ( g_my_emulStateP,(UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
}

Err stub_TwSmfIsPlaying(TwSmfHandle aSmfHandle,
                   Boolean* aIsPlayingResult)
{
	Int32 arg[2];
	Err r;
	hostcall_t data;	
	data.a=ByteSwap32((UInt32)aSmfHandle);
	data.b=ByteSwap32((UInt32)aIsPlayingResult);
	arg[0] = BRIDGE_midi_playing;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(&data);
	arg[1] = ByteSwap32 ( arg[1] );
	r=g_my_call68KFuncP ( g_my_emulStateP,(UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
	aIsPlayingResult=(Boolean*)ByteSwap32((UInt32)(aIsPlayingResult));
	return r;
}

Err stub_TwMidiSetMasterVolume(Int32 aVolume)
{
	UInt32 arg[2];
	arg[0] = BRIDGE_midi_volume;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(aVolume);
	arg[1] = ByteSwap32 ( arg[1] );
	return g_my_call68KFuncP ( g_my_emulStateP,(UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
}

Err stub_TwSmfOpen(TwSmfHandle* aHandleResult,
              UInt8* aSMFData,
              UInt32* aDurationMillisecResult)
{
	Int32 arg[2];
	Err r;
	hostcall_t data;	
	data.a=ByteSwap32((UInt32)aHandleResult);
	data.b=ByteSwap32((UInt32)aSMFData);
	data.c=ByteSwap32((UInt32)aDurationMillisecResult);
	arg[0] = BRIDGE_midi_open;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(&data);
	arg[1] = ByteSwap32 ( arg[1] );
	r=g_my_call68KFuncP ( g_my_emulStateP,(UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
	*aHandleResult=(TwSmfHandle*)ByteSwap32((UInt32)(*aHandleResult));
	*aDurationMillisecResult=(UInt32)ByteSwap32((UInt32)(*aDurationMillisecResult));
	return r;
}

Err stub_TwSmfPlay(TwSmfHandle aSmfHandle,
              SndSmfOptionsType* aOptions,
              SndSmfChanRangeType* aRangeInfo,
              SndCallbackInfoType* aCallback)
{
	Int32 arg[2];
	Err r;
	hostcall_t data;	
	data.a=ByteSwap32((UInt32)aSmfHandle);
	data.b=ByteSwap32((UInt32)aOptions);
	data.c=ByteSwap32((UInt32)aRangeInfo);
	data.d=ByteSwap32((UInt32)aCallback);
	arg[0] = BRIDGE_midi_play;
	arg[0] = ByteSwap32 ( arg[0] );
	arg[1] = (UInt32)(&data);
	arg[1] = ByteSwap32 ( arg[1] );
	r=g_my_call68KFuncP ( g_my_emulStateP,(UInt32) g_pnobridge -> eventf, &(arg[0]), 8 );
	return r;
}

/*
 * The following functions provide simple time support to MSL.
 */ 
#include <ctime>
time_t __get_time(void)
{
    // Convert epoch 1904-01-01 to 1970-01-01
    return TimGetSeconds() - 2082844800;
}