Copy Link
Add to Bookmark
Report

GP32 demo code (VBlank)

sang's profile picture
Published in 
GP32
 · 8 months ago

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();
}

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT