1 /* 2 * Common display-related functions. 3 * 4 * Copyright (C) 2018 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "display.h" 21 22 23 24 /* Initialise a display configuration. */ 25 26 void init_display(display_config_t *cfg, uint8_t *framebuffer, 27 uint32_t line_length, uint32_t line_count, int frames) 28 { 29 /* Framebuffer address. */ 30 31 cfg->framebuffer = framebuffer; 32 33 /* Frame allocation limits. */ 34 35 cfg->total_lines = (line_count + 1) * frames; 36 cfg->max_frames = frames; 37 38 /* Screen size and dimensions. */ 39 40 cfg->screen_size = line_length * line_count; 41 cfg->line_length = line_length; 42 cfg->line_count = line_count; 43 44 /* Set the number of frames and the current frame. */ 45 46 cfg->frames = frames; 47 cfg->frame = 0; 48 49 init_display_properties(cfg, 0); 50 } 51 52 /* Initialise the display constraints. */ 53 54 void init_display_properties(display_config_t *cfg, uint32_t offset) 55 { 56 /* Fixed address of the frame. */ 57 58 cfg->frame_start = cfg->framebuffer + 59 (cfg->screen_size + cfg->line_length) * cfg->frame; 60 61 /* Floating address of the screen contents. */ 62 63 cfg->screen_start = cfg->frame_start + offset; 64 65 /* Fixed limit of the frame. */ 66 67 cfg->screen_limit = cfg->frame_start + cfg->screen_size; 68 69 /* Recalculate the line multiplier. */ 70 71 cfg->line_multiplier = cfg->scanlines / cfg->line_count; 72 } 73 74 /* Set the number of frames in the framebuffer memory. */ 75 76 void set_frames(display_config_t *cfg, int frames) 77 { 78 if ((frames <= 0) || (frames > cfg->max_frames)) 79 return; 80 81 /* Recalculate the number of lines. */ 82 83 cfg->line_count = (cfg->total_lines - cfg->max_frames) / frames; 84 85 /* Recalculate the screen size. */ 86 87 cfg->screen_size = cfg->line_count * cfg->line_length; 88 89 /* Set the number of frames and the current frame. */ 90 91 cfg->frames = frames; 92 cfg->frame = 0; 93 94 init_display_properties(cfg, 0); 95 } 96 97 /* Select a frame in the framebuffer. */ 98 99 void select_frame(display_config_t *cfg, int frame, uint32_t offset) 100 { 101 if ((frame < 0) || (frame >= cfg->frames)) 102 return; 103 104 /* Update the frame details. */ 105 106 cfg->frame = frame; 107 108 /* Set the screen start offset when switching frames. */ 109 110 init_display_properties(cfg, offset); 111 } 112 113 /* Return the screen start offset. */ 114 115 uint32_t get_start_offset(display_config_t *cfg) 116 { 117 return cfg->screen_start - cfg->frame_start; 118 } 119 120 /* Return the line data position for the given pixel. */ 121 122 int get_position(display_config_t *cfg, int x) 123 { 124 int cell, offset, pos; 125 126 if (cfg->line_channels < 2) 127 return x; 128 129 /* Determine which cell is providing the position and the offset of the 130 pixel within the cell. */ 131 132 cell = x / cfg->cell_size; 133 offset = x % cfg->cell_size; 134 135 /* Determine the resulting position within the divided-up data. */ 136 137 pos = (cell / 2) * cfg->cell_size + offset; 138 139 /* Return the final position within the entire data. All cells in 140 odd-numbered positions occur in the first half, all even-numbered cells 141 in the second half. */ 142 143 return cell % 2 ? pos + cfg->line_length / 2 : pos; 144 } 145 146 /* Provide a pattern to test the line data. */ 147 148 void test_linedata(display_config_t *cfg) 149 { 150 int x, y; 151 uint8_t *linedata = cfg->screen_start; 152 153 for (y = 0; y < cfg->line_count; y++) 154 { 155 for (x = 0; x < cfg->line_length; x++) 156 { 157 /* Pixel: I0RRGGBB = Y0YYYYXX */ 158 159 linedata[get_position(cfg, x)] = (x % 2) ? 160 (((y / (cfg->line_count / 32)) & 0b1) << 7) | 161 (((y / (cfg->line_count / 16)) & 0b1111) << 2) | 162 ((x / (cfg->line_length / 4)) & 0b11) : 163 0x00; 164 } 165 166 linedata = wrap_screen_pointer(cfg, linedata + cfg->line_length); 167 } 168 } 169 170 /* Copying from/to the display to/from a backing store. */ 171 172 void copy_display(display_config_t *cfg, uint8_t *store, 173 int width, int height, int ystep, 174 int x, int y, int key, int to_display) 175 { 176 copy_display_section(cfg, store, width, height, 177 0, 0, width, height, ystep, 178 x, y, key, to_display); 179 } 180 181 /* Copying from/to the display to/from a backing store region. */ 182 183 void copy_display_section(display_config_t *cfg, uint8_t *store, 184 int width, int height, 185 int xstart, int ystart, int xsize, int ysize, int ystep, 186 int x, int y, int key, int to_display) 187 { 188 int sx, sy, dx, dy; 189 uint8_t *storeline = store + ystart * width, 190 *displayline = wrap_screen_pointer(cfg, cfg->screen_start + y * cfg->line_length), 191 pixel; 192 193 /* Define the limits of the copying in the store. */ 194 195 int xlimit = xstart + xsize, ylimit = ystart + ysize; 196 197 if (xlimit > width) 198 xlimit = width; 199 200 if (ylimit > height) 201 ylimit = height; 202 203 /* Perform the copying between the store and display. */ 204 205 for (sy = ystart, dy = y; (sy < ylimit) && (dy < cfg->line_count); sy += ystep, dy++) 206 { 207 for (sx = xstart, dx = x; (sx < xlimit) && (dx < cfg->line_length); sx++, dx++) 208 { 209 if (to_display) 210 { 211 pixel = storeline[sx]; 212 if ((key < 0) || (pixel != key)) 213 displayline[get_position(cfg, dx)] = pixel; 214 } 215 else 216 storeline[sx] = displayline[get_position(cfg, dx)]; 217 } 218 219 storeline += width * ystep; 220 displayline = wrap_screen_pointer(cfg, displayline + cfg->line_length); 221 } 222 } 223 224 /* Scroll the display. */ 225 226 void scroll_display(display_config_t *cfg, int x, int y) 227 { 228 /* Move the screen start by the given number of bytes and lines, wrapping 229 around the start and end of the framebuffer. */ 230 231 cfg->screen_start = wrap_screen_pointer(cfg, cfg->screen_start + x + 232 y * cfg->line_length); 233 } 234 235 /* Wrap a pointer within the given limits. */ 236 237 uint8_t *wrap_pointer(uint8_t *ptr, uint8_t *lower, uint8_t *upper) 238 { 239 uint32_t size = upper - lower; 240 241 if (ptr < lower) 242 return upper - (lower - ptr) % size; 243 else if (ptr >= upper) 244 return lower + (ptr - upper) % size; 245 else 246 return ptr; 247 } 248 249 /* Wrap the screen pointer to the current frame. */ 250 251 uint8_t *wrap_screen_pointer(display_config_t *cfg, uint8_t *ptr) 252 { 253 return wrap_pointer(ptr, cfg->frame_start, cfg->screen_limit); 254 }