GP2X Writing To The Screen From The 940


Orkie

Super Duper Mega GP Mania
Joined
Mar 22, 2006
Messages
2,373
Location
UK
Website
www.gp2x.dev
I've started writing an application which will run purely on the 940 in the end (I was bored ;)) and I used the main.c/code.c layout and Makefile from Dzz's demo tutorials, with my own loader using rlyeh's minimal library. I have found my way around the registers so far, but writing to the screen seems to be more difficult.

CODE
#include <stdint.h>
#define mem2addr(a) (a-0x2000000)
unsigned short *memregs16;
uint16_t *screen, *fb0, *fb1;

void fb_flip(uint32_t fb_addr)
{
memregs16[0x290E>>1] = (uint16_t)(fb_addr & 0xffff);
memregs16[0x2910>>1] = (uint16_t)(fb_addr >> 16);
fb_addr += 640;
memregs16[0x2912>>1] = (uint16_t)(fb_addr & 0xffff);
memregs16[0x2914>>1] = (uint16_t)(fb_addr >> 16);
}

void flip() {
if(screen == fb0) fb_flip((uint32_t) fb1);
else fb_flip((uint32_t) fb0);
}

void vsync(void) {
while(memregs16[0x1182>>1]&(1<<4)); // waiting for V-sync
}

void Do940Stuff()
{
// Set up registers
memregs16 = (unsigned short*)mem2addr(0xC0000000);
fb0 = (uint16_t*)mem2addr(0x03101000);
fb1 = (uint16_t*)mem2addr(0x03381000);
screen = fb0;
memregs16[0x28DA>>1] = 0x004AB; // 16bpp, only region 1 activated

memregs16[0x290C>>1] = 640; // in 16bpp mode, this means width = 320

unsigned int loop;

while(1) {
vsync();
for(loop = 76800; loop--; ) {
screen[loop] = 0xF800;
}
flip();
}
}



This in code.c finally displays something (took me ages to get anything on-screen) but it looks like pink static. It *should* fill the screen in a solid red colour. Any ideas what I can do to fix this?

Here is a binary so you can see the problem for yourself: http://x11.gp2x.de/tempfiles/940ldr.zip

Thanks
 
You don't need to do mem2addr() for addresses you pass to fb_flip(), display controller (or whatever fetches framebuffer from memory) sees the address space the same way as 920, not as 940.
 
I've now got the following, but I still get a static background with horizontal bars of a different kind of static. But now, I get a flickering screen too (possibly I'm blitting at the wrong time).

CODE

#include <stdint.h>
#define mem2addr(a) (a-0x2000000)
volatile uint16_t *memregs16;
#define PHYSICAL_SCREEN0 0x3100000

#define PHYSICAL_SCREEN1 0x3180000
volatile uint16_t *screen;
volatile uint16_t *fb0;
volatile uint16_t *fb1;
int current_screen = 0;

void fb_flip(uint32_t fb_addr)
{
memregs16[0x2912>>1] = (uint16_t)(fb_addr & 0xffff);
memregs16[0x2914>>1] = (uint16_t)(fb_addr >> 16);
}

void flip(void) {
//if(screen == fb0) fb_flip((uint32_t) fb1);
//else fb_flip((uint32_t) fb0);
if(current_screen == 0) {
memregs16[0x290E>>1] = memregs16[0x2912>>1] =

(unsigned short) (PHYSICAL_SCREEN0 & 0xFFFF);

memregs16[0x2910>>1] = memregs16[0x2914>>1] =

(unsigned short) (PHYSICAL_SCREEN0 >> 16);

current_screen = 1;
} else {
memregs16[0x290E>>1] = memregs16[0x2912>>1] =

(unsigned short) (PHYSICAL_SCREEN1 & 0xFFFF);

memregs16[0x2910>>1] = memregs16[0x2914>>1] =

(unsigned short) (PHYSICAL_SCREEN1 >> 16);

current_screen = 0;
}
}

volatile uint16_t *get_screen(void) {
if(current_screen == 0) return fb0;
else return fb1;
}

void vsync(void) {
while(memregs16[0x1182>>1]&(1<<4)); // waiting for V-sync
while(!(memregs16[0x1182>>1]&(1<<4))); // wait for end of vsync
}

void Do940Stuff()
{
// Set up registers
memregs16 = (volatile uint16_t*)mem2addr(0xC0000000);
fb0 = (volatile uint16_t*)mem2addr(PHYSICAL_SCREEN0);
fb1 = (volatile uint16_t*)mem2addr(PHYSICAL_SCREEN1);
screen = fb0;
memregs16[0x28DA>>1] = 0x04AB; // 16bpp, only region 1 activated

memregs16[0x290C>>1] = 640; // in 16bpp mode, this means width = 320

while(1) {
screen = get_screen();
int iy, ix;
for (iy=0; iy<240; iy++) {

for(ix=0; ix<320; ix++) {
screen[iy * 320 + ix] = 0xF800;
}
}
vsync();
flip();
}
}
 
I'm assuming your code is being uploaded to 0x2000000, and your bootstrap is setting the 940 start address properly?
 
Yes, the code is definitely working, I can toggle the backlight etc. I'm using rlyeh's minimal lib to load it:
CODE
#include <stdio.h>
#include "minimal.h"
#include "binsize.h"


int main() {
gp2x_init(1000, 16, 11025,16,1,60, 1);
printf("running code940.bin...\n");
gp2x_dualcore_launch_program_from_disk("code940.bin", 0, SIZE_BIN);
printf("code940.bin has been launched...\n");

while(1) {
}
gp2x_deinit();
return 0;
}

void gp2x_sound_frame(void *blah, void *buff, int samples) {}


Could this be the cause (i.e. is it setting up the hardware in a strange way)?

main.c is just copied straight from the tutorial:
CODE
extern void Do940Stuff();

void code940(void) __attribute__((naked));
void code940(void)
{
asm volatile (
"b .CodeEntryPoint @ have all the entry points jump to start of the code\n"
"b .CodeEntryPoint\n"
"b .CodeEntryPoint\n"
"b .CodeEntryPoint\n"
"b .CodeEntryPoint\n"
"b .CodeEntryPoint\n"
"b .CodeEntryPoint\n"
"b .CodeEntryPoint\n"
".CodeEntryPoint:\n"
"mov sp, #0x100000 @ set the stack top (1M)\n"
"sub sp, sp, #4 @ minus 4, just to be tidy\n"
"@ set up memory region 0 -- the whole 4GB address space, uncached\n"
"@ bit 0 = enable\n"
"@ bits 1-5: 5-bit region size (N). Size = 2^(N+1). For this\n"
"@ partition, set all bits for maximum size\n"
"@ bits 12:31: base address. Both regions in this example have a\n"
"@ base address of zero\n"
"mov r0, #0x3F @ region data\n"
"mcr p15, 0, r0, c6, c0, 0 @ data region 0\n"
"mcr p15, 0, r0, c6, c0, 1 @ code region 0\n"
"@ set up region 1 which is the first 16 megabytes.\n"
"@ bit 0 = enable\n"
"@ bits 1-5: as above. 16 MB is 2^24 so N is 23. In hex, 23==0x17.\n"
"@ (0x17<<1) | 1 = 0x2F\n"
"mov r0, #0x2F @ region data\n"
"mcr p15, 0, r0, c6, c1, 0 @ data region 1\n"
"mcr p15, 0, r0, c6, c1, 1 @ code region 1\n"
"@ set region 1 to be cacheable (so the first 16M will be cacheable)\n"
"mov r0, #2 @ 2 means region 1 (1 would have meant region 0, 4 would mean region 2, etc)\n"
"mcr p15, 0, r0, c2, c0, 0 @ turn on data cache\n"
"mcr p15, 0, r0, c2, c0, 1 @ turn on instruction cache\n"
"@ set region 1 to be bufferable too (only data)\n"
"mcr p15, 0, r0, c3, c0, 0 @ turn on write buffer\n"
"@ set protection on for all regions\n"
"mov r0, #15 @ more than we actually need\n"
"mcr p15, 0, r0, c5, c0, 0 @ set full data permission\n"
"mcr p15, 0, r0, c5, c0, 1 @ set full code permission\n"
"mrc p15, 0, r0, c1, c0, 0 @ fetch current control reg\n"
"orr r0, r0, #1 @ 0x00000001: enable protection unit\n"
"orr r0, r0, #4 @ 0x00000004: enable D cache\n"
"orr r0, r0, #0x1000 @ 0x00001000: enable I cache\n"
"orr r0, r0, #0xC0000000 @ 0xC0000000: async+fastbus\n"
"mcr p15, 0, r0, c1, c0, 0 @ set control reg\n"
// ok, now we're ready to rock
);
Do940Stuff();
}
 
check your minimal lib - rlyeh has used both 0x03000000 and 0x02000000 in his library for the 940 launch address. If it's 0x03000000, then you'll get the exact result you described.
 
If his code is working (toggling backlight etc), then isn't the address irrelevent at that point?

I fiddled and never noticed much diff between 0x02 and 0x03, but I'm not doing much on the 940 beyond pure calculations. *shrug*

jeff
 
I'm just going from what he's posted here, and that code can work at any address by default as gcc generates pretty good PIC unless you have stuff like some 'switch/case' statements. The only problem you'll get is that you'll give the wrong address to the frame buffer pointer registers and then write to a completely different address, causing garbage to be displayed.
 
Well what I've posted is the complete code. I looked through minimal.c and found that it did seem to be using 0x03000000 (CODE
gp2x_dualcore_ram=(unsigned long *)mmap(0, 0x1000000-0x4B000*8-0x4000, PROT_READ|PROT_WRITE, MAP_SHARED, gp2x_dev[2], 0x03000000);
), so I changed it to 0x02000000 and now I get a black screen.

EDIT: I'm using minimal library 0.B preview.
 
isn't 0x02000000 already used in that library? In which case I'd change it back and then change your code to suit, rather than hacking up the library.
 
What do I need to change in my code? It can't be mem2addr, because that works for getting the addresses of other registers :S.
 
Back
Top