Copy Link
Add to Bookmark
Report
GP32 demo code (VBlank)
This simple source (in C and for GCC) shows how to emulate VBlank interrupt on GP32. Because of my own reasons the code is not 100% SDK "pure".
vblank.c
//
// VBlank interrupt emulation example using timer4 interrupt
// and line synchronization.
// Version 0.1
//
// (c) 2002 Jouni 'Mr.Spiv' korhonen
//
// Tested on Firmware 1.5.7e and compiled with gcc 3.0.4
//
// Use at own risk!!! The code uses timer4 interrupt although
// everything could be done using SDK's timer interrupts (i.e.
// using GpTimerOptSet() )
//
// The code can calculate correct VBlank interrupt period
// based on current FrameRate, HCLK, and PCLK values. I have
// tested that VBlank works on both 66MHz & 132MHz.
//
#define __GCC3x__ // select your GCC version..
//#define __GCC2x__
//
//
//
#include "gpdef.h"
#include "gpstdlib.h"
#include "gpgraphic.h"
#include "gpmain.h"
#include "gpfont.h"
#include <stdlib.h>
#include <string.h>
//
//
//
GPDRAWSURFACE gpDraw;
GPDRAWTAG gpDrawTag;
//
volatile long* tpal = (long*)0x14a00050;
volatile long* lcdcon1 = (long*)0x14a00000;
volatile long* lcdcon2 = (long*)0x14a00004;
volatile long* lcdcon3 = (long*)0x14a00008;
volatile long* lcdcon4 = (long*)0x14a0000c;
volatile long* lcdcon5 = (long*)0x14a00010;
volatile long* lcdaddr1 = (long*)0x14a00014;
volatile long* lcdaddr2 = (long*)0x14a00018;
volatile long* lcdaddr3 = (long*)0x14a0001c;
volatile long* hwpal = (long*)0x14a00400;
// timers
volatile long* tcfg0 = (long*)0x15100000;
volatile long* tcfg1 = (long*)0x15100004;
volatile long* tcon = (long*)0x15100008;
volatile long* tcntb0 = (long*)0x1510000c;
volatile long* tcmpb0 = (long*)0x15100010;
volatile long* tcnto0 = (long*)0x15100014;
volatile long* tcntb1 = (long*)0x15100018;
volatile long* tcmpb1 = (long*)0x1510001c;
volatile long* tcnto1 = (long*)0x15100020;
volatile long* tcntb2 = (long*)0x15100024;
volatile long* tcmpb2 = (long*)0x15100028;
volatile long* tcnto2 = (long*)0x1510002c;
volatile long* tcntb3 = (long*)0x15100030;
volatile long* tcmpb3 = (long*)0x15100034;
volatile long* tcnto3 = (long*)0x15100038;
volatile long* tcntb4 = (long*)0x1510003c;
volatile long* tcnto4 = (long*)0x15100040;
// Interrupts
volatile unsigned long* srcpnd = (long*)0x14400000;
volatile unsigned long* intmod = (long*)0x14400004;
volatile unsigned long* intmsk = (long*)0x14400008;
volatile unsigned long* priority = (long*)0x1440000c;
volatile unsigned long* intpnd = (long*)0x14400010;
volatile unsigned long* intoffset = (long*)0x14400014;
//
//
//
#define MAGIC_VCLK 0x98aeb8
//
// Calculate correct CLOCKVAL to use with LCDCON1 register
//
int clockVal( void ) {
unsigned long hclk = GpHClkGet();
unsigned long vclk = MAGIC_VCLK;
int clkval = 0;
while (hclk >= vclk) {
clkval++;
hclk -= vclk;
}
vclk >>= 2;
if (hclk < vclk) { clkval--; }
return clkval;
}
//
// Calculate current FrameRate. This code expects that the
// screen has actually been set up correctly.
//
double frameRate( void ) {
long reg;
int vspw, vbpd, vfpd;
int hspw, hbpd, hfpd;
int lineval, hozval, clkval;
double frate;
long hclk = (long)GpHClkGet();
// Read values directly from LCDCONx registers
clkval = (*lcdcon1 >> 8) & 0x3ff;
reg = *lcdcon2;
vbpd = reg >> 24;
vfpd = (reg >> 6) & 0xff;
vspw = reg & 0x3f;
lineval = (reg >> 14) & 0x3ff;
reg = *lcdcon3;
hbpd = (reg >> 19) & 0x7f;
hozval = (reg >> 8) & 0x7ff;
hfpd = reg & 0xff;
hspw = *lcdcon4 & 0xff;
// and calculate..
frate = (double)hclk / (double)(((vspw+1)+(vbpd+1)+(lineval+1)+(vfpd+1))*
((hspw+1) +(hbpd+1)+(hozval+1) +(hfpd+1))*
(2*(clkval+1)));
return frate;
}
//
// Wait for specified LCD line. Note that the LCD must be enabled
// when calling this function.
//
void waitline( int line ) {
while (line != ((*lcdcon1 >> 18) & 0x1ff) );
}
//
// These two variables are used by the emulated vblank interrupt handler
//
static int _line;
static long _pclk;
//
// The timer4 interrupt handler code.. GCC version checking shamelessly
// taken from _ttl_'s sample player code -> and thus never tested on GCC 2.x something
//
#ifdef __GCC3x__
void timer4Int(void) __attribute__ ((interrupt ("IRQ")));
#elif defined(__GCC2x__)
void timer4Int(void) __attribute__ ((naked));
#else
#error "GCC version you are using is not supported";
#endif
void timer4Int(void) {
#ifdef __GCC2x__
asm volatile (
"stmdb r13!,{r0-r12,lr}"
);
#endif
hwpal[0] = 0xffff; // just show that interrupt handler got called
// Line synchronization!
waitline(_line);
// Reload & restart the timer4
//*tcntb4 = _pclk; // timer4 counter
*tcon = (*tcon & 0x0fffff) | 0x200000; // manual update timer4 counter
*tcon = (*tcon & 0x0fffff) | 0x100000; // start timer4
// Place you code here... DO NOT overflow vblank time!
// Your code ends..
hwpal[0] = 0xfc00; // Just show that interrupt handler is done..
#ifdef __GCC2x__
asm volatile (
"ldmia r13!,{r0-r12,lr}\n"
"subs pc,lr,#4"
);
#endif
}
//
// Installs timer4 interrupt and calculates correct timer values for it.
// This code SHOULD use SDK provided timers.. but I had to reinvent the wheel!
//
// Parameters:
// int line - at what rasterline the emulated vblank interrupt should occur.
// Do not place it into line 0. Lines 1 to 3 are ok.
//
void installVBlank( int line ) {
double pclk;
// Calculate timer frequencies etc..
pclk = (double)GpPClkGet();
pclk /= 16; // clock divider 1/16
pclk /= 256; // prescaler 256
pclk /= frameRate();
_pclk = (long)(pclk);
_line = line;
// Install timer4 interrupt..
ARMDisableInterrupt();
swi_install_irq(14,timer4Int);
waitline(_line);
*tcntb4 = _pclk; // timer4 counter
*tcon = (*tcon & 0x0fffff) | 0x200000; // manually update timer4 counter
*tcfg0 |= 0xff00; // prescaler for timers 2,3,4
*tcfg1 = (*tcfg1 & 0x0ffff) | 0x030000; // configure timer4, clock divider 1/16
*tcon = (*tcon & 0x0fffff) | 0x100000; // start timer4
ARMEnableInterrupt();
}
//
// Remove timer4 interrupt -> remove emulated vblank.
//
void uninstallVBlank() {
ARMDisableInterrupt();
swi_uninstall_irq(14);
ARMEnableInterrupt();
}
//
//
//
//
//
void GpMain(void *arg) {
//GpClockSpeedChange (132000000, 0x3a011, 3);
GpGraphicModeSet(GPC_DFLAG_8BPP,NULL);
GpLcdSurfaceGet(&gpDraw, 0);
GpRectFill(NULL, &gpDraw, 0, 0, gpDraw.buf_w, gpDraw.buf_h, 0x00);
GpSurfaceSet(&gpDraw); //gpDraw primary surface setting
//
installVBlank( 200 ); // Install VBlank on rasterline 200
//
while ((GpKeyGet() & (GPC_VK_FL | GPC_VK_FR)) != (GPC_VK_FL | GPC_VK_FR)) {
long reg, reg2;
waitline(319);
hwpal[0] = 0;
waitline(318);
}
uninstallVBlank();
// reboot..
while ((GpKeyGet() & (GPC_VK_SELECT | GPC_VK_START)) != (GPC_VK_SELECT | GPC_VK_START));
GpAppExit();
}