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 #include "utils.h" 22 23 24 25 /* Initialise a display configuration. */ 26 27 void init_display(display_config_t *cfg, uint8_t *framebuffer, 28 uint32_t line_length, uint32_t line_count, int frames) 29 { 30 /* Framebuffer address. */ 31 32 cfg->framebuffer = framebuffer; 33 34 /* Frame allocation limits. */ 35 36 cfg->total_lines = (line_count + 1) * frames; 37 cfg->max_frames = frames; 38 39 /* Screen size and dimensions. */ 40 41 cfg->screen_size = line_length * line_count; 42 cfg->line_length = line_length; 43 cfg->line_count = line_count; 44 45 /* Set the number of frames and the current frame. */ 46 47 cfg->frames = frames; 48 cfg->frame = 0; 49 50 init_display_properties(cfg, 0); 51 } 52 53 /* Initialise the display constraints. */ 54 55 void init_display_properties(display_config_t *cfg, uint32_t offset) 56 { 57 /* Fixed address of the frame. */ 58 59 cfg->frame_start = cfg->framebuffer + 60 (cfg->screen_size + cfg->line_length) * cfg->frame; 61 62 /* Floating address of the screen contents. */ 63 64 cfg->screen_start = cfg->frame_start + offset; 65 66 /* Fixed limit of the frame. */ 67 68 cfg->screen_limit = cfg->frame_start + cfg->screen_size; 69 70 /* Recalculate the line multiplier. */ 71 72 cfg->line_multiplier = cfg->scanlines / cfg->line_count; 73 } 74 75 76 77 /* Select a frame in the framebuffer. */ 78 79 void display_select_frame(display_config_t *cfg, int frame, uint32_t offset) 80 { 81 if ((frame < 0) || (frame >= cfg->frames)) 82 return; 83 84 /* Update the frame details. */ 85 86 cfg->frame = frame; 87 88 /* Set the screen start offset when switching frames. */ 89 90 init_display_properties(cfg, offset); 91 } 92 93 /* Set the number of frames in the framebuffer memory. */ 94 95 void display_set_frames(display_config_t *cfg, int frames) 96 { 97 if ((frames <= 0) || (frames > cfg->max_frames)) 98 return; 99 100 /* Recalculate the number of lines. */ 101 102 cfg->line_count = (cfg->total_lines - cfg->max_frames) / frames; 103 104 /* Recalculate the screen size. */ 105 106 cfg->screen_size = cfg->line_count * cfg->line_length; 107 108 /* Set the number of frames and the current frame. */ 109 110 cfg->frames = frames; 111 cfg->frame = 0; 112 113 init_display_properties(cfg, 0); 114 } 115 116 117 118 /* Return the line data position for the given pixel. */ 119 120 int display_get_position(display_config_t *cfg, int x) 121 { 122 int cell, offset, pos; 123 124 if (cfg->line_channels < 2) 125 return x; 126 127 /* Determine which cell is providing the position and the offset of the 128 pixel within the cell. */ 129 130 cell = x / cfg->cell_size; 131 offset = x % cfg->cell_size; 132 133 /* Determine the resulting position within the divided-up data. */ 134 135 pos = (cell / 2) * cfg->cell_size + offset; 136 137 /* Return the final position within the entire data. All cells in 138 odd-numbered positions occur in the first half, all even-numbered cells 139 in the second half. */ 140 141 return cell % 2 ? pos + cfg->line_length / 2 : pos; 142 } 143 144 /* Return the screen start offset. */ 145 146 uint32_t display_get_start_offset(display_config_t *cfg) 147 { 148 return cfg->screen_start - cfg->frame_start; 149 } 150 151 152 153 /* Copying from/to the display to/from a backing store. */ 154 155 void display_copy(display_config_t *cfg, uint8_t *store, 156 int width, int height, int ystep, 157 int x, int y, int key, int to_display) 158 { 159 display_copy_section(cfg, store, width, height, 160 0, 0, width, height, ystep, 161 x, y, key, to_display); 162 } 163 164 /* Copying from/to the display to/from a backing store region. */ 165 166 void display_copy_section(display_config_t *cfg, uint8_t *store, 167 int width, int height, 168 int xstart, int ystart, int xsize, int ysize, int ystep, 169 int x, int y, int key, int to_display) 170 { 171 int sx, sy, dx, dy; 172 uint8_t *storeline = store + ystart * width, 173 *displayline = display_wrap_pointer(cfg, cfg->screen_start + y * cfg->line_length), 174 pixel; 175 176 /* Define the limits of the copying in the store. */ 177 178 int xlimit = xstart + xsize, ylimit = ystart + ysize; 179 180 if (xlimit > width) 181 xlimit = width; 182 183 if (ylimit > height) 184 ylimit = height; 185 186 /* Perform the copying between the store and display. */ 187 188 for (sy = ystart, dy = y; (sy < ylimit) && (dy < cfg->line_count); sy += ystep, dy++) 189 { 190 for (sx = xstart, dx = x; (sx < xlimit) && (dx < cfg->line_length); sx++, dx++) 191 { 192 if (to_display) 193 { 194 pixel = storeline[sx]; 195 if ((key < 0) || (pixel != key)) 196 displayline[display_get_position(cfg, dx)] = pixel; 197 } 198 else 199 storeline[sx] = displayline[display_get_position(cfg, dx)]; 200 } 201 202 storeline += width * ystep; 203 displayline = display_wrap_pointer(cfg, displayline + cfg->line_length); 204 } 205 } 206 207 /* Scroll the display. */ 208 209 void display_scroll(display_config_t *cfg, int x, int y) 210 { 211 /* Move the screen start by the given number of bytes and lines, wrapping 212 around the start and end of the framebuffer. */ 213 214 cfg->screen_start = display_wrap_pointer(cfg, cfg->screen_start + x + 215 y * cfg->line_length); 216 } 217 218 /* Provide a pattern to test the line data. */ 219 220 void display_test_linedata(display_config_t *cfg) 221 { 222 int x, y; 223 uint8_t *linedata = cfg->screen_start; 224 225 for (y = 0; y < cfg->line_count; y++) 226 { 227 for (x = 0; x < cfg->line_length; x++) 228 { 229 /* Pixel: I0RRGGBB = Y0YYYYXX */ 230 231 linedata[display_get_position(cfg, x)] = (x % 2) ? 232 (((y / (cfg->line_count / 32)) & 0b1) << 7) | 233 (((y / (cfg->line_count / 16)) & 0b1111) << 2) | 234 ((x / (cfg->line_length / 4)) & 0b11) : 235 0x00; 236 } 237 238 linedata = display_wrap_pointer(cfg, linedata + cfg->line_length); 239 } 240 } 241 242 /* Wrap the screen pointer to the current frame. */ 243 244 uint8_t *display_wrap_pointer(display_config_t *cfg, uint8_t *ptr) 245 { 246 return wrap_pointer(ptr, cfg->frame_start, cfg->screen_limit); 247 }