diff options
Diffstat (limited to 'src/ppu.c')
-rw-r--r-- | src/ppu.c | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/src/ppu.c b/src/ppu.c new file mode 100644 index 0000000..6c38ab6 --- /dev/null +++ b/src/ppu.c | |||
@@ -0,0 +1,144 @@ | |||
1 | #include <string.h> | ||
2 | |||
3 | #include "ppu.h" | ||
4 | |||
5 | /* | ||
6 | Copyright (c) 2021 Devine Lu Linvega | ||
7 | Copyright (c) 2021 Andrew Alderwick | ||
8 | Copyright (c) 2021 Bad Diode | ||
9 | |||
10 | Permission to use, copy, modify, and distribute this software for any | ||
11 | purpose with or without fee is hereby granted, provided that the above | ||
12 | copyright notice and this permission notice appear in all copies. | ||
13 | |||
14 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
15 | WITH REGARD TO THIS SOFTWARE. | ||
16 | */ | ||
17 | |||
18 | static size_t screen_width = 0; | ||
19 | static size_t screen_height = 0; | ||
20 | |||
21 | static u32 *framebuffer = 0; | ||
22 | |||
23 | static u32 palette[16]; | ||
24 | static u8 *pixels_buf; | ||
25 | static u8 *dirty_lines; | ||
26 | static u8 reqdraw = 0; | ||
27 | // TODO: Probably should consider this | ||
28 | // static u32 rgb_order; | ||
29 | |||
30 | static u8 blending[5][16] = { | ||
31 | {0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0}, | ||
32 | {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}, | ||
33 | {1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1}, | ||
34 | {2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2}, | ||
35 | {1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}}; | ||
36 | |||
37 | void | ||
38 | ppu_pixel(u8 layer, u16 x, u16 y, u8 color) { | ||
39 | size_t idx = y * screen_width + x; | ||
40 | u8 *pixel = &pixels_buf[idx], shift = layer * 2; | ||
41 | if(x < screen_width && y < screen_height) { | ||
42 | *pixel = (*pixel & ~(0x3 << shift)) | (color << shift); | ||
43 | } | ||
44 | dirty_lines[y] |= 1; | ||
45 | } | ||
46 | |||
47 | void | ||
48 | ppu_1bpp(u8 layer, u16 x, u16 y, u8 *sprite, u8 color, u8 flipx, u8 flipy) { | ||
49 | u16 v, h; | ||
50 | for(v = 0; v < 8; v++) | ||
51 | for(h = 0; h < 8; h++) { | ||
52 | u8 ch1 = (sprite[v] >> (7 - h)) & 0x1; | ||
53 | if(ch1 || blending[4][color]) | ||
54 | ppu_pixel(layer, | ||
55 | x + (flipx ? 7 - h : h), | ||
56 | y + (flipy ? 7 - v : v), | ||
57 | blending[ch1][color]); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | void | ||
62 | ppu_2bpp(u8 layer, u16 x, u16 y, u8 *sprite, u8 color, u8 flipx, u8 flipy) { | ||
63 | u16 v, h; | ||
64 | for(v = 0; v < 8; v++) | ||
65 | for(h = 0; h < 8; h++) { | ||
66 | u8 ch1 = ((sprite[v] >> (7 - h)) & 0x1); | ||
67 | u8 ch2 = ((sprite[v + 8] >> (7 - h)) & 0x1); | ||
68 | u8 ch = ch1 + ch2 * 2; | ||
69 | if(ch || blending[4][color]) | ||
70 | ppu_pixel(layer, | ||
71 | x + (flipx ? 7 - h : h), | ||
72 | y + (flipy ? 7 - v : v), | ||
73 | blending[ch][color]); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | void | ||
78 | redraw_screen(void) { | ||
79 | for (size_t j = 0; j < screen_height; j++) { | ||
80 | dirty_lines[j] = 1; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | int | ||
85 | ppu_init(void) { | ||
86 | // Open frambuffer and get the size. | ||
87 | int fb = open("/dev/fb0", O_RDWR); | ||
88 | if (fb <= 0) { | ||
89 | fprintf(stderr, "couldn't open the framebuffer\n"); | ||
90 | exit(EXIT_FAILURE); | ||
91 | } | ||
92 | struct fb_var_screeninfo info; | ||
93 | if (ioctl(fb, FBIOGET_VSCREENINFO, &info) != 0) { | ||
94 | fprintf(stderr, "couldn't get the framebuffer size\n"); | ||
95 | exit(EXIT_FAILURE); | ||
96 | } | ||
97 | |||
98 | // Mmap the framebuffer to a buffer object. | ||
99 | screen_width = info.xres; | ||
100 | screen_height = info.yres; | ||
101 | size_t len = 4 * screen_width * screen_height; | ||
102 | framebuffer = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0); | ||
103 | if (framebuffer == MAP_FAILED) { | ||
104 | fprintf(stderr, "couldn't mmap the framebuffer\n"); | ||
105 | exit(EXIT_FAILURE); | ||
106 | } | ||
107 | |||
108 | // Allocate intermediate buffers. | ||
109 | pixels_buf = malloc(screen_width * screen_height); | ||
110 | dirty_lines = malloc(screen_height); | ||
111 | |||
112 | // Initialize default palette. | ||
113 | palette[0] = 0x444444; | ||
114 | palette[1] = 0xffffff; | ||
115 | palette[2] = 0x7777ff; | ||
116 | palette[3] = 0xff7777; | ||
117 | |||
118 | // Clear pixel buffer memory. | ||
119 | memset(pixels_buf, 0, screen_width * screen_height); | ||
120 | memset(dirty_lines, 1, screen_height); | ||
121 | |||
122 | // Make sure we perform an initial screen drawing. | ||
123 | reqdraw = 1; | ||
124 | redraw_screen(); | ||
125 | |||
126 | return 1; | ||
127 | } | ||
128 | |||
129 | void | ||
130 | blit_framebuffer(void) { | ||
131 | if (reqdraw == 0) { | ||
132 | return; | ||
133 | } | ||
134 | for (size_t j = 0; j < screen_height; j++) { | ||
135 | if (dirty_lines[j] != 0) { | ||
136 | for (size_t i = 0; i < screen_width; i++) { | ||
137 | size_t idx = i + j * screen_width; | ||
138 | framebuffer[idx] = palette[pixels_buf[idx] % 16]; | ||
139 | } | ||
140 | } | ||
141 | dirty_lines[j] = 0; | ||
142 | } | ||
143 | reqdraw = 0; | ||
144 | } | ||