ARM Integrator-CP PL110 Dirty

From OSDev.wiki
Revision as of 03:41, 20 March 2012 by Pancakes (talk | contribs) (typo on 4MB instead of 8MB and removed commented out line)
Jump to navigation Jump to search

Integrator-CP QEMU PL110 16-Bit Color Example

This is quick and dirty really. I just want to get you on your feet with something that will make you enjoy doing this. Although, many people would consider going straight to working on the GUI not the correct way to do it is however very fun for many people. So here you go.

Prerequisites

Software Why
QEMU We are going to use it. We do not properly initialize the hardware. We almost do it correctly, but not quite enough for the real thing. Thats okay.
C/C++ Compiler Or Assembler You need either a compiler or an assembler. I will show you code for doing it both ways. I will handwrite the assembly so it should be easier to read and work with.
objcopy You need something like objcopy on most linux distributions. What we are going to have to do is copy out the .text section. Like I said this is just really quick and dirty example. We will make a flat binary.

C/C++ Code

typedef int(*PFN)(void);

void start(void);


void __attribute__((naked)) entry()
{
	__asm__("mov	sp, #0x60 << 8");
	__asm__("bl	start");
}

#define PL110_CR_EN		0x001
#define PL110_CR_PWR		0x800
#define PL110_IOBASE		0xc0000000
#define PL110_PALBASE		(PL110_IOBASE + 0x200)

typedef unsigned int		uint32;
typedef unsigned char		uint8;
typedef unsigned short		uint16;

typedef struct _PL110MMIO 
{
	uint32		volatile tim0;		//0
	uint32		volatile tim1;		//4
	uint32		volatile tim2;		//8
	uint32		volatile d;		//c
	uint32		volatile upbase;	//10
	uint32		volatile f;		//14
	uint32		volatile g;		//18
	uint32		volatile control;	//1c
} PL110MMIO;

void start(void)
{
	PFN		fn;
	PL110MMIO	*plio;
	int		x;
	uint16		volatile *fb;
	
	plio = (PL110MMIO*)PL110_IOBASE;
	
	/* 640x480 pixels */
	plio->tim0 = 0x3f1f3f9c;
	plio->tim1 = 0x080b61df;
	plio->upbase = 0x200000;
	/* 16-bit color */
	plio->control = 0x1829;
	fb = (uint16*)0x200000;
	for (x = 0; x < (640 * 480) - 10; ++x)
		fb[x] = 0x1f << (5 + 6) | 0xf << 5;
	
	/* uncomment this and the function pointer should crash QEMU if you set it for 8MB of ram or less */
	for(;;);
	fn = (PFN)0x800f20;
	fn();
	return;
}

A naked function implementation so we can setup the stack, which then calls the actual C/C++ function start. Then we initialize the card and draw a light blue color across the screen.

To compile it and produce the binary file to use with QEMU first copy the code above into a file named test.c. Then execute the following. Your actual binaries will likely differ. Make sure your using a cross-toolchain for ARM and not your host system unless your host system is ARM.

    ./gcc-arm test.c -nostdlib -o test.o
    ./objcopy-arm -j .text -O binary test.o test.bin
     qemu-system-arm -m 8 -kernel test.bin

If all goes well QEMU will display a light blue screen.

Assembly

main:
	eor	r0, r0, r0
	mov	r0, #0xc0 << 24
		
	mov	r1, #0x3f << 24
	add	r1, r1, #0x1f << 16
	add	r1, r1, #0x3f << 8
	add	r1, r1, #0x9c
	str	r1, [r0, #0]
	
	mov	r1, #0x08 << 24
	add	r1, r1, #0x0b << 16
	add	r1, r1, #0x61 << 8
	add	r1, r1, #0xdf
	str	r1, [r0, #4]
	
	mov	r1, #0x20 << 16
	str	r1, [r0, #0x10]
	
	mov	r1, #0x18 << 8
	add	r1, r1, #0x29
	str	r1, [r0, #0x1c]
	
	mov	r2, #0x4b << 12
	mov	r1, #0x20 << 16
	mov	r3, #0x1f
clearscreen:
	strh	r3, [r1], #2
	subs	r2, r2, #1
	bne	clearscreen
	#b	0x800000
lockme:
	b	lockme

We initialize the card then we draw red to the screen.

To assemble it and produce the binary file to use with QEMU first copy the code above into a file named test.asm. Then execute the following. Your actual binaries will likely differ. Make sure your using a cross-toolchain for ARM and not your host system unless your host system is ARM.

     ./as-arm test.asm -o test.o
     ./objcopy-arm -j .text -O binary test.o test.bin
     qemu-system-arm -m 8 -kernel test.bin

If all goes well QEMU will display a light blue screen.

Explanation

Essentially, what we did was set just enough to make it work with QEMU PC emulator version 0.12.5 (Debian 0.12.5+dfsg-3squeeze1), Copyright (c) 2003-2008 Fabrice Bellard. If it does not work and you have a different version of QEMU then that could be the problem, but then again make sure you used the correct toolchain.

Now, the real hardware. Well its going to want you to set more values then just that. Also, the pixels are 16-bit in the memory buffer at 0x200000 and they are in 565 format BGR. So blue bits occupy the most significant bits and there is 5 of them. B:5 G:6 R:5 - green has 6 bits. This is a common format and used with DXT1 compression format most graphics cards support.

After we initialize the card we just set the same value in a loop. Now, you will notice that in both the C and the assembly code that I have a loop and a commented out branch instruction or in the C code I have a function pointer which will crash the emulator. Its not going to hard crash it, but it will make the emulator stop and display the registers. This is highly useful as a debugging aid. Since you place it somewhere in your program and it will tell you what all the registers contain so you can debug difficult pieces of code. It can also tell you if the code has deadlocked. How else do you know if the code has completed properly or has deadlocked? You do not know unless you make it do something to tell you.

Since you have the ability to plot colored pixels on the screen you have another way to debug your operating system that you are going to build a interesting GUI on top on while completely forgetting about the real interesting stuff like processes, threads, timers, priority, disk I/O, devices, memory management, heaps..

But, having fun is important too it just requires forgetting about all that! So have fun because thats why I made this page.