Hi Henrik,
All the downloads are still valid on the fpc4gp2x page, it is just I haven't had any updates to put on the page for a while now (as you noticed).
I haven't done much in the way of ARM assembly myself yet, but if you want to learn ARM assembly programming, this site may help
http://www.heyrick.co.uk/assembler/
If you scroll down a bit you will see a An introduction to assembler link that you can follow, and right below that link is the ARM instruction set information.
Even though it is for the ARM processor in the BBC computer, quit a bit should still be relevant to GP2X programming :-)
On this topic of ARM assembly, I did manage to get a small assembler library file off the internet to compile to an .o file using the devkitGP2X arm-linux-gcc.exe program.
Code:
arm-linux-gcc.exe -c libgp2x_asm.s
libgp2x_asm.s file
Code:
@ gp2x demo article code by Dzz
@ this code is placed in the public domain. do anything you want with it.
.align 4
.globl OpenFile
.globl CloseFile
.globl WriteFile
.globl MUnmap
.globl ChangeDir
.globl ExecuteFile
.globl MMap
.globl CopyScreen
.globl ClearScreen
.globl Ioctl3
OpenFile:
swi #0x900005
mov pc, lr
CloseFile:
swi #0x900006
mov pc, lr
WriteFile:
swi #0x900004
mov pc, lr
MUnmap:
swi #0x90005B
mov pc, lr
ChangeDir:
swi #0x90000C
mov pc, lr
ExecuteFile:
swi #0x90000B
mov pc, lr
Ioctl3:
swi #0x900036
mov pc, lr
MMap:
stmdb sp!, {r0, r1, r2, r3}
mov r0, sp
swi #0x90005A
add sp, sp, #16
mov pc, lr
CopyScreen:
stmfd sp!, {r4-r10} @ remember registers 4-10
mov r2, #4800 @ we will run the loop 4800 times to copy the screen
.CopyScreenLoop:
ldmia r1!, {r3-r10} @ pull in 32 bytes from the source
stmia r0!, {r3-r10} @ write the 32 bytes to the destination
subs r2, r2, #1 @ decrement the loop counter
bne .CopyScreenLoop @ if we're not done, do it again
ldmfd sp!, {r4-r10} @ restore the registers
mov pc, lr @ return
ClearScreen:
stmfd sp!, {r4-r10} @ remember registers 4-10
mov r2, #4800 @ we will run the loop 4800 times to copy the screen
mov r3, #0 @ load up the registers with zeros
mov r4, #0
mov r5, #0
mov r6, #0
mov r7, #0
mov r8, #0
mov r9, #0
mov r10, #0
.ClearScreenLoop:
stmia r0!, {r3-r10} @ write the 32 bytes of zeros to the destination
subs r2, r2, #1 @ decrement the loop counter
bne .ClearScreenLoop @ if we're not done, do it again
ldmfd sp!, {r4-r10} @ restore the registers
mov pc, lr @ return
I was then able to link it into my FPC gp2x code, and with a header file I whipped up, use the routines in it. Some of the stuff below is duplicated in various FPC files already, but at the time I didn't know that <G> It is still usefull though :-)
Code:
Unit libgp2x;
{$link libgp2x_asm.o}
{$linklib c}
Interface
Uses
CTypes;
Const
MREMAP_MAYMOVE = 1;
MREMAP_FIXED = 2;
PROT_READ = $1; // page can be read
PROT_WRITE = $2; // page can be written
PROT_EXEC = $4; // page can be executed
PROT_NONE = $0; // page can not be accessed
MAP_SHARED = $01; // Share changes
MAP_PRIVATE = $02; // Changes are private
{$IFDEF gp2x}
MAP_FIXED = $10; // Interpret addr exactly
MAP_ANONYMOUS = $20; // don't use a file
MAP_GROWSDOWN = $0100; // stack-like segment
MAP_DENYWRITE = $0800; // ETXTBSY
MAP_EXECUTABLE = $1000; // mark it as an executable
MAP_LOCKED = $2000; // pages are locked
MAP_NORESERVE = $4000; // don't check for reservations
MS_ASYNC = 1; // sync memory asynchronously
MS_INVALIDATE = 2; // invalidate the caches
MS_SYNC = 4; // synchronous memory sync
MCL_CURRENT = 1; // lock all current mappings
MCL_FUTURE = 2; // lock all future mappings
MADV_NORMAL = $0; // default page-in behavior
MADV_RANDOM = $1; // page-in minimum required
MADV_SEQUENTIAL = $2; // read-ahead aggressively
MADV_WILLNEED = $3; // pre-fault pages
MADV_DONTNEED = $4; // discard these pages
O_RDWR = 2;
{$ENDIF}
// compatibility flags
MAP_ANON = MAP_ANONYMOUS;
MAP_FILE = 0;
MAP_FAILED = Pointer(-1);
Type
Tgp2xDevice = Class
Protected
FDev : ctypes.cint32;
FMemData : Pointer;
FMemSize : ctypes.cint32;
Public
Constructor Create(pszFile: PChar; nMode: ctypes.cint32);
Destructor Destroy; Override;
Procedure MapMem(pAddr: Pointer; nLen,nProtection,nFlags,nOff: ctypes.cint32);
Procedure UnmapMem;
Property MemData: Pointer Read FMemData;
End;
Function OpenFile(pszFile: PChar; nMode: ctypes.cint32): ctypes.cint32; Cdecl; External;
Procedure CloseFile(nFile: ctypes.cint32); Cdecl; External;
Function MMap(pAddr: Pointer; nLen,nProtection,nFlags,nFD,nOff: ctypes.cint32): Pointer; Cdecl; External;
Procedure MUnmap(p: Pointer; nSize: ctypes.cint32); Cdecl; External;
Procedure ChangeDir(pszDir: PChar); Cdecl; External;
Procedure ExecuteFile(pszFile: PChar; nZero1,nZero2: ctypes.cint32); Cdecl; External;
Function Ioctl3(fd: ctypes.cint32; ulFunction,ulParameter: ctypes.cushort): ctypes.cint32; Cdecl; External;
Procedure CopyScreen(pDest,pSrc: ctypes.pcushort); Cdecl; External;
//Procedure ClearScreen(pus: ctypes.pcushort; color: ctypes.cuint16); Cdecl; External;
Procedure ClearScreen(pus: ctypes.pcushort); Cdecl; External;
Procedure RestartMenu;
{
extern int OpenFile(char *pszFile, int nMode);
extern void CloseFile(int nFile);
extern void *MMap(void *pAddr, int nLen, int nProtection, int nFlags, int nFD, int nOff);
extern void MUnmap(void *p, int nSize);
extern void ChangeDir(char *pszDir);
extern void ExecuteFile(char *pszFile, int nZero1, int nZero2);
extern int Ioctl3(int fd, unsigned int ulFunction, unsigned int ulParameter);
extern void CopyScreen(unsigned short *pDest, unsigned short *pSrc);
extern void ClearScreen(unsigned short *pus);
extern void RestartMenu();
}
Implementation
Constructor Tgp2xDevice.Create(pszFile: PChar; nMode: ctypes.cint32);
Begin
Inherited Create;
FDev := 0;
FMemData := Nil;
FMemSize := 0;
FDev := OpenFile(pszFile,nMode);
End;
{..............................................................................}
{..............................................................................}
Destructor Tgp2xDevice.Destroy;
Begin
UnmapMem;
If FDev <> 0 Then
CloseFile(FDev);
Inherited Destroy;
End;
{..............................................................................}
{..............................................................................}
Procedure Tgp2xDevice.UnmapMem;
Begin
If FMemData <> Nil Then
Begin
MUnmap(FMemData,FMemSize);
FMemData := Nil;
FMemSize := 0;
End;
End;
{..............................................................................}
{..............................................................................}
Procedure Tgp2xDevice.MapMem(pAddr: Pointer; nLen,nProtection,nFlags,nOff: ctypes.cint32);
Begin
UnmapMem;
FMemSize := nLen;
FMemData := MMap(pAddr, FMemSize, nProtection, nFlags, FDev, nOff);
End;
{..............................................................................}
{..............................................................................}
Procedure RestartMenu;
Begin
ChangeDir (PChar('/usr/gp2x'));
ExecuteFile(PChar('/usr/gp2x/gp2xmenu'), 0, 0);
End;
{..............................................................................}
{..............................................................................}
End.
Using the above code I was able to write directly to the GP2X framebuffer pixels like so:
Code:
Function RGBTo16Bit(r,g,b: Uint8): Uint16;
Begin
Result := (((r Shl cRscale) Shr 8) Shl cRshift) +
(((g Shl cGscale) Shr 8) Shl cGshift) +
(((b Shl cBscale) Shr 8) Shl cBshift);
End;
{..............................................................................}
{..............................................................................}
Procedure TRendererModule_gp2x.ClearScreen(r,g,b: Byte);
Var
c : Uint32;
i : LongInt;
addr : PUint8;
Begin
c := RGBTo16Bit(r,g,b);
addr := Ffb1.MemData;
i := FWidth * FHeight;
While i <= 0 Do
Begin
PUint16(addr)^ := Uint16(c);
Inc(addr,2);
Dec(i);
End;
End;
{..............................................................................}
{..............................................................................}
Procedure TRendererModule_gp2x.FlipScreen;
Begin
CopyScreen(Ffb0.MemData,Ffb1.MemData);
End;
{..............................................................................}
{..............................................................................}
// open the frame buffers
Ffb0 := Tgp2xDevice.Create('/dev/fb0', O_RDWR);
Ffb1 := Tgp2xDevice.Create('/dev/fb1', O_RDWR);
// map the memory so you can read/write to the frame buffers
FWidth := 320;
FHeight := 240;
FBitsPerPixel := 16;
Ffb0.MapMem(Nil, FWidth * FHeight * (FBitsPerPixel Div 8), PROT_WRITE, MAP_SHARED, 0);
Ffb1.MapMem(Nil, FWidth * FHeight * (FBitsPerPixel Div 8), PROT_WRITE, MAP_SHARED, 0);
Var
dstAddr : Puint8;
c : Uint16;
// how to write to the frame buffer
c := RGBTo16Bit(r,g,b)
dstAddr := Ffb1.MemData;
Inc(dstAddr,x + y * FWidth * 2);
PUint16(dstAddr)^ := c;
It might not be inline assembler, but I hope this helps :-)
cheers,
Paul
Bookmarks