add preliminary blitter; fb, input handling
[ginge.git] / loader / emu.c
... / ...
CommitLineData
1// vim:shiftwidth=2:expandtab
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <sys/types.h>
6#include <sys/stat.h>
7#include <fcntl.h>
8#include <sys/mman.h>
9#include <sys/types.h>
10#include <unistd.h>
11#include <signal.h>
12#include <asm/ucontext.h>
13
14#include "header.h"
15#include "sys_cacheflush.h"
16
17//#define iolog printf
18#define iolog(...)
19
20typedef unsigned int u32;
21typedef unsigned short u16;
22typedef unsigned char u8;
23
24struct uppermem_block {
25 u32 addr; // physical
26 u32 size;
27 void *mem;
28 struct uppermem_block *next;
29};
30
31static struct uppermem_block *upper_mem;
32
33static struct {
34 u32 dstctrl;
35 u32 dstaddr;
36 u32 dststride;
37 u32 srcctrl;
38 u32 srcaddr; //
39 u32 srcstride;
40 u32 srcforcolor;
41 u32 srcbackcolor;
42 u32 patctrl; //
43 u32 patforcolor;
44 u32 patbackcolor;
45 u32 size;
46 u32 ctrl; //
47 u32 run;
48 u32 intc;
49 u32 srcfifo;
50} blitter;
51
52static struct {
53 union {
54 u32 mlc_stl_eadr;
55 struct {
56 u16 mlc_stl_eadrl;
57 u16 mlc_stl_eadrh;
58 };
59 };
60} mmsp2;
61
62static u16 *host_screen;
63static int host_stride;
64
65
66static void *upper_lookup(u32 addr, u8 **mem_end, int *stride_override)
67{
68 struct uppermem_block *ub;
69
70 // maybe the screen?
71 if (mmsp2.mlc_stl_eadr <= addr && addr < mmsp2.mlc_stl_eadr + 320*240*2) {
72 host_screen = host_video_flip(); // HACK
73 *mem_end = (u8 *)host_screen + host_stride * 240;
74 *stride_override = host_stride;
75 return (u8 *)host_screen + addr - mmsp2.mlc_stl_eadr;
76 }
77
78 for (ub = upper_mem; ub != NULL; ub = ub->next) {
79 if (ub->addr <= addr && addr < ub->addr + ub->size) {
80 *mem_end = (u8 *)ub->mem + ub->size;
81 return (u8 *)ub->mem + addr - ub->addr;
82 }
83 }
84
85 return NULL;
86}
87
88static void blitter_do(void)
89{
90 u8 *dst, *dste, *src, *srce;
91 int w, h, sstrd, dstrd;
92
93 if (blitter.srcaddr == blitter.dstaddr)
94 return; // dummy blit?
95
96 w = blitter.size & 0x7ff;
97 h = (blitter.size >> 16) & 0x7ff;
98 sstrd = blitter.srcstride;
99 dstrd = blitter.dststride;
100
101 dst = upper_lookup(blitter.dstaddr, &dste, &dstrd);
102 src = upper_lookup(blitter.srcaddr, &srce, &sstrd);
103
104 if (dst == NULL || src == NULL) {
105 printf("blit %08x->%08x %dx%d translated to %p->%p\n",
106 blitter.srcaddr, blitter.dstaddr, w, h, src, dst);
107 return;
108 }
109
110 if (dst + dstrd * h > dste) {
111 printf("blit %08x->%08x %dx%d did not fit dst\n",
112 blitter.srcaddr, blitter.dstaddr, w, h);
113 h = (dste - dst) / dstrd;
114 }
115
116 if (src + sstrd * h > srce) {
117 printf("blit %08x->%08x %dx%d did not fit src\n",
118 blitter.srcaddr, blitter.dstaddr, w, h);
119 h = (srce - src) / sstrd;
120 }
121
122 for (; h > 0; h--, dst += dstrd, src += sstrd)
123 memcpy(dst, src, w * 2);
124}
125
126static u32 xread8(u32 a)
127{
128 iolog("r8 %08x\n", a);
129 return 0;
130}
131
132static u32 xread16(u32 a)
133{
134// if ((a & 0xfff00000) == 0x7f100000) { static int a; a ^= ~1; return a & 0xffff; }
135 iolog("r16 %08x\n", a);
136 return 0;
137}
138
139static u32 xread32(u32 a)
140{
141 u32 d = 0;
142 if ((a & 0xfff00000) == 0x7f100000) {
143 u32 *bl = &blitter.dstctrl;
144 a &= 0xfff;
145 if (a < 0x40)
146 d = bl[a / 4];
147 if (a == 0x34)
148 d = 0; // not busy
149 }
150 iolog("r32 %08x\n", a);
151 return d;
152}
153
154static void xwrite8(u32 a, u32 d)
155{
156 iolog("w8 %08x %08x\n", a, d);
157}
158
159static void xwrite16(u32 a, u32 d)
160{
161 iolog("w16 %08x %08x\n", a, d);
162 if ((a & 0xfff00000) == 0x7f000000) {
163 a &= 0xffff;
164 switch (a) {
165 case 0x2912: mmsp2.mlc_stl_eadrl = d; break;
166 case 0x2914: mmsp2.mlc_stl_eadrh = d; break;
167 }
168 printf("w16 %08x %08x\n", a, d);
169 }
170}
171
172static void xwrite32(u32 a, u32 d)
173{
174 iolog("w32 %08x %08x\n", a, d);
175 if ((a & 0xfff00000) == 0x7f000000) {
176 printf("w32 %08x %08x\n", a, d);
177 return;
178 }
179 if ((a & 0xfff00000) == 0x7f100000) {
180 u32 *bl = &blitter.dstctrl;
181 a &= 0xfff;
182 if (a < 0x40)
183 bl[a / 4] = d;
184 if (a == 0x34 && (d & 1))
185 blitter_do();
186 return;
187 }
188}
189
190#define BIT_SET(v, b) (v & (1 << (b)))
191
192static void handle_op(u32 pc, u32 op, u32 *regs, u32 addr_check)
193{
194 u32 t, shift, ret, addr;
195 int rn, rd;
196
197 rd = (op & 0x0000f000) >> 12;
198 rn = (op & 0x000f0000) >> 16;
199
200 if ((op & 0x0f200090) == 0x01000090) { // AM3: LDRH, STRH
201 if (!BIT_SET(op, 5)) // !H
202 goto unhandled;
203 if (BIT_SET(op, 6) && !BIT_SET(op, 20)) // S && !L
204 goto unhandled;
205
206 if (BIT_SET(op, 22)) // imm offset
207 t = ((op & 0xf00) >> 4) | (op & 0x0f);
208 else // reg offset
209 t = regs[op & 0x000f];
210
211 if (!BIT_SET(op, 23))
212 t = -t;
213 addr = regs[rn] + t;
214
215 if (BIT_SET(op, 20)) { // Load
216 ret = xread16(addr);
217 if (BIT_SET(op, 6)) { // S
218 ret <<= 16;
219 ret = (signed int)ret >> 16;
220 }
221 regs[rd] = ret;
222 }
223 else
224 xwrite16(addr, regs[rd]);
225 }
226 else if ((op & 0x0d200000) == 0x05000000) { // AM2: LDR[B], STR[B]
227 if (BIT_SET(op, 25)) { // reg offs
228 if (BIT_SET(op, 4))
229 goto unhandled;
230
231 t = regs[op & 0x000f];
232 shift = (op & 0x0f80) >> 7;
233 switch ((op & 0x0060) >> 5) {
234 case 0: t = t << shift; break;
235 case 1: t = t >> (shift + 1); break;
236 case 2: t = (signed int)t >> (shift + 1); break;
237 case 3: goto unhandled; // I'm just lazy
238 }
239 }
240 else // imm offs
241 t = op & 0x0fff;
242
243 if (!BIT_SET(op, 23))
244 t = -t;
245 addr = regs[rn] + t;
246
247 if (BIT_SET(op, 20)) { // Load
248 if (BIT_SET(op, 22)) // Byte
249 ret = xread8(addr);
250 else
251 ret = xread32(addr);
252 regs[rd] = ret;
253 }
254 else {
255 if (BIT_SET(op, 22)) // Byte
256 xwrite8(addr, regs[rd]);
257 else
258 xwrite32(addr, regs[rd]);
259 }
260 }
261 else
262 goto unhandled;
263
264#if 0
265 if (addr != addr_check) {
266 fprintf(stderr, "bad calculated addr: %08x vs %08x\n", addr, addr_check);
267 abort();
268 }
269#endif
270 return;
271
272unhandled:
273 fprintf(stderr, "unhandled IO op %08x @ %08x\n", op, pc);
274}
275
276#define LINKPAGE_SIZE 0x1000
277#define LINKPAGE_COUNT 4
278#define LINKPAGE_ALLOC (LINKPAGE_SIZE * LINKPAGE_COUNT)
279
280struct linkpage {
281 u32 saved_regs[15];
282 u32 *lp_r1;
283 void (*handler)(u32 addr_pc, u32 op, u32 *regs, u32 addr_check);
284 u32 code[0];
285};
286
287static struct linkpage *g_linkpage;
288static u32 *g_code_ptr;
289static int g_linkpage_count;
290
291static void init_linkpage(void)
292{
293 g_linkpage->lp_r1 = &g_linkpage->saved_regs[1];
294 g_linkpage->handler = handle_op;
295 g_code_ptr = g_linkpage->code;
296}
297
298static u32 make_offset12(u32 *pc, u32 *target)
299{
300 int lp_offs, u = 1;
301
302 lp_offs = (char *)target - (char *)pc - 2*4;
303 if (lp_offs < 0) {
304 lp_offs = -lp_offs;
305 u = 0;
306 }
307 if (lp_offs >= LINKPAGE_SIZE) {
308 fprintf(stderr, "linkpage too far: %d\n", lp_offs);
309 abort();
310 }
311
312 return (u << 23) | lp_offs;
313}
314
315static u32 make_jmp(u32 *pc, u32 *target)
316{
317 int jmp_val;
318
319 jmp_val = target - pc - 2;
320 if (jmp_val < (int)0xff000000 || jmp_val > 0x00ffffff) {
321 fprintf(stderr, "jump out of range (%p -> %p)\n", pc, target);
322 abort();
323 }
324
325 return 0xea000000 | (jmp_val & 0x00ffffff);
326}
327
328static void emit_op(u32 op)
329{
330 *g_code_ptr++ = op;
331}
332
333static void emit_op_io(u32 op, u32 *target)
334{
335 op |= make_offset12(g_code_ptr, target);
336 emit_op(op);
337}
338
339static void segv_sigaction(int num, siginfo_t *info, void *ctx)
340{
341 struct ucontext *context = ctx;
342 u32 *regs = (u32 *)&context->uc_mcontext.arm_r0;
343 u32 *pc = (u32 *)regs[15];
344 u32 old_op = *pc;
345 u32 *pc_ptr, *old_op_ptr;
346 int lp_size;
347
348 if (((regs[15] ^ (u32)&segv_sigaction) & 0xff000000) == 0 || // PC is in our segment or
349 (((regs[15] ^ (u32)g_linkpage) & ~(LINKPAGE_ALLOC - 1)) == 0)) // .. in linkpage
350 {
351 // real crash - time to die
352 printf("segv %d %p @ %08x\n", info->si_code, info->si_addr, regs[15]);
353 signal(num, SIG_DFL);
354 raise(num);
355 }
356 printf("segv %d %p @ %08x\n", info->si_code, info->si_addr, regs[15]);
357
358 // spit PC and op
359 pc_ptr = g_code_ptr++;
360 old_op_ptr = g_code_ptr++;
361 *pc_ptr = (u32)pc;
362 *old_op_ptr = old_op;
363
364 // emit jump to code ptr
365 *pc = make_jmp(pc, g_code_ptr);
366
367 // generate code:
368 // TODO: our own stack
369 emit_op_io(0xe50f0000, &g_linkpage->saved_regs[0]); // str r0, [saved_regs[0]] @ save r0
370 emit_op_io(0xe51f0000, (u32 *)&g_linkpage->lp_r1); // ldr r0, =lp_r1
371 emit_op (0xe8807ffe); // stmia r0, {r1-r14}
372 emit_op (0xe2402004); // sub r2, r0, #4
373 emit_op_io(0xe51f0000, pc_ptr); // ldr r0, =pc
374 emit_op_io(0xe51f1000, old_op_ptr); // ldr r1, =old_op
375 emit_op (0xe1a04002); // mov r4, r2
376 emit_op (0xe1a0e00f); // mov lr, pc
377 emit_op_io(0xe51ff000, (u32 *)&g_linkpage->handler); // ldr pc, =handle_op
378 emit_op (0xe8947fff); // ldmia r4, {r0-r14}
379 emit_op (make_jmp(g_code_ptr, pc + 1)); // jmp <back>
380
381 // sync caches
382 sys_cacheflush(pc, pc + 1);
383 sys_cacheflush(g_linkpage, g_code_ptr);
384
385 lp_size = (char *)g_code_ptr - (char *)g_linkpage;
386 printf("code #%d %d/%d\n", g_linkpage_count, lp_size, LINKPAGE_SIZE);
387
388 if (lp_size + 13*4 > LINKPAGE_SIZE) {
389 g_linkpage_count++;
390 if (g_linkpage_count >= LINKPAGE_COUNT) {
391 fprintf(stderr, "too many linkpages needed\n");
392 abort();
393 }
394 g_linkpage = (void *)((char *)g_linkpage + LINKPAGE_SIZE);
395 init_linkpage();
396 }
397 //handle_op(regs[15], op, regs, (u32)info->si_addr);
398 //regs[15] += 4;
399}
400
401void emu_init(void *map_bottom)
402{
403 struct sigaction segv_action = {
404 .sa_sigaction = segv_sigaction,
405 .sa_flags = SA_SIGINFO,
406 };
407 void *ret;
408
409 sigemptyset(&segv_action.sa_mask);
410 sigaction(SIGSEGV, &segv_action, NULL);
411
412 g_linkpage = (void *)(((u32)map_bottom - LINKPAGE_ALLOC) & ~0xfff);
413 ret = mmap(g_linkpage, LINKPAGE_ALLOC, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
414 if (ret != g_linkpage) {
415 perror("mmap linkpage");
416 exit(1);
417 }
418 printf("linkpages @ %p\n", g_linkpage);
419 init_linkpage();
420
421 // host stuff
422 host_screen = host_video_init(&host_stride);
423 if (host_screen == NULL) {
424 printf("can't alloc screen\n");
425 exit(1);
426 }
427}
428
429int emu_read_gpiodev(void *buf, int count)
430{
431 unsigned int btns;
432
433 if (count < 4) {
434 printf("gpiodev read %d?\n", count);
435 return -1;
436 }
437
438 btns = host_read_btns();
439 memcpy(buf, &btns, 4);
440 return 4;
441}
442
443void *emu_mmap_dev(unsigned int length, int prot, int flags, unsigned int offset)
444{
445 struct uppermem_block *umem;
446 char name[32];
447 int fd;
448
449 // SoC regs
450 if ((offset & ~0xffff) == 0xc0000000) {
451 return mmap((void *)0x7f000000, length, PROT_NONE,
452 MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_NORESERVE, -1, 0);
453 }
454 // blitter
455 if ((offset & ~0xffff) == 0xe0020000) {
456 return mmap((void *)0x7f100000, length, PROT_NONE,
457 MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_NORESERVE, -1, 0);
458 }
459 // upper mem
460 if ((offset & 0xfe000000) != 0x02000000)
461 printf("unexpected devmem mmap @ %08x\n", offset);
462
463 // return mmap(NULL, length, prot, flags, memdev, offset);
464
465 umem = calloc(1, sizeof(*umem));
466 if (umem == NULL) {
467 printf("OOM\n");
468 return MAP_FAILED;
469 }
470
471 umem->addr = offset;
472 umem->size = length;
473 umem->mem = mmap(NULL, length, prot, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
474 if (umem->mem != MAP_FAILED)
475 goto done;
476
477 printf("upper mem @ %08x %d mmap fail, trying backing file\n", offset, length);
478 sprintf(name, "m%08x", offset);
479 fd = open(name, O_CREAT|O_RDWR, 0644);
480 lseek(fd, length - 1, SEEK_SET);
481 name[0] = 0;
482 write(fd, name, 1);
483
484 umem->mem = mmap(NULL, length, prot, MAP_SHARED, fd, 0);
485 if (umem->mem == MAP_FAILED) {
486 printf("failed, giving up\n");
487 close(fd);
488 free(umem);
489 return MAP_FAILED;
490 }
491
492done:
493 printf("upper mem @ %08x %d\n", offset, length);
494 umem->next = upper_mem;
495 upper_mem = umem;
496 return umem->mem;
497}
498