1.1 --- a/examples/vga/main.c Tue Nov 13 00:02:15 2018 +0100
1.2 +++ b/examples/vga/main.c Tue Nov 13 00:05:51 2018 +0100
1.3 @@ -84,7 +84,8 @@
1.4
1.5 /* Initialise memory for a multi-frame display. */
1.6
1.7 -static Display(display_config, LINE_LENGTH, LINE_COUNT, FRAME_COUNT);
1.8 +static Display(banner_config, LINE_LENGTH, 10, 1);
1.9 +static Display(display_config, LINE_LENGTH, LINE_COUNT - 10, FRAME_COUNT);
1.10
1.11
1.12
1.13 @@ -153,7 +154,7 @@
1.14
1.15 /* Update the display with the frame details. */
1.16
1.17 - vga_set_frame(&display_config);
1.18 + vga_set_frame(&display_config, 1);
1.19 wait(delay);
1.20
1.21 /* Select the next frame to plot to. */
1.22 @@ -190,36 +191,29 @@
1.23 }
1.24 }
1.25
1.26 -/* Fill the screen with characters. */
1.27 +/* Set up a banner. */
1.28
1.29 -static void write_chars(void)
1.30 +static void setup_banner(void)
1.31 {
1.32 - const int line_height = 9;
1.33 - int x = 0, y = 0;
1.34 - char c;
1.35 + int frame, x = 0, y = 0;
1.36
1.37 init_font(&font_config, fontchars, fonttable, fontbase, fontlimit);
1.38
1.39 - while (y + line_height < display_config.line_count)
1.40 + for (frame = 0; frame < banner_config.frames; frame++)
1.41 {
1.42 - for (c = (char) font_config.base; c < (char) font_config.limit; c++)
1.43 - {
1.44 - if (x + get_char_definition(&font_config, c)->width > display_config.line_length)
1.45 - {
1.46 - x = 0; y += line_height;
1.47 - }
1.48 + /* Obtain the frame. */
1.49 +
1.50 + display_select_frame(&banner_config, frame);
1.51
1.52 - if (y + line_height >= display_config.line_count)
1.53 - break;
1.54 + write_string(&banner_config, &font_config, "VGA display example", &x, &y, 0xff);
1.55 + }
1.56
1.57 - x = write_char(&display_config, &font_config, c, x, y, 0xff);
1.58 - }
1.59 - }
1.60 + display_select_frame(&banner_config, 0);
1.61 }
1.62
1.63 /* Set up a background. */
1.64
1.65 -static void setup(void)
1.66 +static void setup_background(void)
1.67 {
1.68 int frame;
1.69
1.70 @@ -234,15 +228,17 @@
1.71 image_tile_sprite(&scr, 0, 0, display_config.line_length,
1.72 display_config.line_count * scr.yscale,
1.73 0, 0);
1.74 -
1.75 - /* Write a sequence of characters. */
1.76 -
1.77 - write_chars();
1.78 }
1.79
1.80 display_select_frame(&display_config, 0);
1.81 }
1.82
1.83 +static void setup(void)
1.84 +{
1.85 + setup_banner();
1.86 + setup_background();
1.87 +}
1.88 +
1.89
1.90
1.91 /* Main program. */
1.92 @@ -277,13 +273,15 @@
1.93
1.94 /* Set the display to a well-defined state. */
1.95
1.96 + init_display(&banner_config);
1.97 init_display(&display_config);
1.98
1.99 /* Initialise VGA output with one or two line channels, configuring a line
1.100 timer and any transfer timer, with an initiating channel being introduced
1.101 if a transfer timer is specified. */
1.102
1.103 - init_vga_with_timers(&display_config, LINE_CHANNELS, LINE_TIMER, TRANSFER_TIMER);
1.104 + init_vga_with_timers(&banner_config, LINE_CHANNELS, LINE_TIMER, TRANSFER_TIMER);
1.105 + vga_add_window(&display_config);
1.106
1.107 /* Configure VGA output transfer to the output register, also configuring
1.108 output compare units for horizontal and vertical sync. */
2.1 --- a/include/display.h Tue Nov 13 00:02:15 2018 +0100
2.2 +++ b/include/display.h Tue Nov 13 00:05:51 2018 +0100
2.3 @@ -63,14 +63,6 @@
2.4
2.5 uint8_t *screen_limit; /* = frame_start + screen_size */
2.6
2.7 - /* Number of scanlines on the display. */
2.8 -
2.9 - uint32_t scanlines;
2.10 -
2.11 - /* Number of scanlines per display line. */
2.12 -
2.13 - int line_multiplier; /* = scanlines / line_count */
2.14 -
2.15 /* Number of line channels used for pixel data transfers. */
2.16
2.17 int line_channels;
2.18 @@ -105,7 +97,6 @@
2.19 .frames=FRAMES, \
2.20 .max_frames=FRAMES, \
2.21 \
2.22 - .scanlines = SCANLINES, \
2.23 .line_channels = LINE_CHANNELS, \
2.24 .cell_size = CELL_SIZE};
2.25
3.1 --- a/include/vga_display.h Tue Nov 13 00:02:15 2018 +0100
3.2 +++ b/include/vga_display.h Tue Nov 13 00:05:51 2018 +0100
3.3 @@ -50,24 +50,40 @@
3.4
3.5 /* Current scanline and pixel line. */
3.6
3.7 - uint32_t line;
3.8 + uint32_t scanline, line;
3.9 uint8_t *linedata;
3.10
3.11 + /* Separate display windows dividing the display vertically. */
3.12 +
3.13 + int window, windows, max_windows;
3.14 +
3.15 + /* A collection of line numbers ending each window. */
3.16 +
3.17 + uint32_t *window_end;
3.18 +
3.19 /* The start, limit and size of the screen are copied to this structure,
3.20 allowing the display configuration to use different values such as those
3.21 for a different display frame. */
3.22
3.23 /* Screen start address, frame limit. */
3.24
3.25 - uint8_t *screen_start, *screen_limit;
3.26 + uint8_t **screen_start, **screen_limit;
3.27
3.28 /* Screen size, used to wrap the current line pointer. */
3.29
3.30 - uint32_t screen_size;
3.31 + uint32_t *screen_size;
3.32 +
3.33 + /* General display properties defined by the collection of windows. */
3.34 +
3.35 + uint32_t line_length, line_count;
3.36
3.37 - /* General display configuration. */
3.38 + /* Number of scanlines on the display. */
3.39 +
3.40 + uint32_t scanlines;
3.41
3.42 - display_config_t *display_config;
3.43 + /* Number of scanlines per display line. */
3.44 +
3.45 + int line_multiplier; /* = scanlines / line_count */
3.46
3.47 /* Display line positions. */
3.48
3.49 @@ -85,12 +101,25 @@
3.50 This macro depends on the VGA timing constants being defined, and they are
3.51 included above.
3.52
3.53 - VGA_Display(<name>)
3.54 + VGA_Display(<name>, int windows)
3.55 */
3.56
3.57 -#define VGA_Display(NAME) \
3.58 +#define VGA_Display(NAME, WINDOWS) \
3.59 + uint32_t __##NAME##_window_end[WINDOWS]; \
3.60 + uint8_t *__##NAME##_screen_start[WINDOWS]; \
3.61 + uint8_t *__##NAME##_screen_limit[WINDOWS]; \
3.62 + uint32_t __##NAME##_screen_size[WINDOWS]; \
3.63 + \
3.64 vga_display_t NAME = { \
3.65 + .window_end = __##NAME##_window_end, \
3.66 + .screen_start = __##NAME##_screen_start, \
3.67 + .screen_limit = __##NAME##_screen_limit, \
3.68 + .screen_size = __##NAME##_screen_size, \
3.69 + .window = 0, \
3.70 + .windows = 0, \
3.71 + .max_windows = WINDOWS, \
3.72 .transfer_cell_size = TRANSFER_CELL_SIZE, \
3.73 + .scanlines = SCANLINES, \
3.74 .hfreq_limit = HFREQ_LIMIT, \
3.75 .hsync_start = HSYNC_START, \
3.76 .hsync_end = HSYNC_END, \
3.77 @@ -121,9 +150,12 @@
3.78 void vga_configure_zero_channel(int channel, int int_num, int initiating,
3.79 uint32_t output);
3.80
3.81 +void vga_update_properties(void);
3.82 +
3.83 /* Access-related operations. */
3.84
3.85 -void vga_set_frame(display_config_t *display_config);
3.86 +void vga_add_window(display_config_t *display_config);
3.87 +void vga_set_frame(display_config_t *display_config, int window);
3.88 void vga_wait_visible(void);
3.89
3.90 /* Interrupt handlers. */
4.1 --- a/lib/display.c Tue Nov 13 00:02:15 2018 +0100
4.2 +++ b/lib/display.c Tue Nov 13 00:05:51 2018 +0100
4.3 @@ -51,10 +51,6 @@
4.4 /* Fixed limit of the frame. */
4.5
4.6 cfg->screen_limit = cfg->frame_start + cfg->screen_size;
4.7 -
4.8 - /* Recalculate the line multiplier. */
4.9 -
4.10 - cfg->line_multiplier = cfg->scanlines / cfg->line_count;
4.11 }
4.12
4.13 /* Initialise the screen start addresses for all frames. */
5.1 --- a/lib/vga_display.c Tue Nov 13 00:02:15 2018 +0100
5.2 +++ b/lib/vga_display.c Tue Nov 13 00:05:51 2018 +0100
5.3 @@ -24,7 +24,7 @@
5.4
5.5 /* Display state. */
5.6
5.7 -VGA_Display(vga_display);
5.8 +VGA_Display(vga_display, 2);
5.9
5.10 /* Pixel data. */
5.11
5.12 @@ -40,12 +40,12 @@
5.13 {
5.14 /* Display parameters. */
5.15
5.16 - vga_display.display_config = display_config;
5.17 vga_display.line_channels = line_channels;
5.18
5.19 /* Initial state. */
5.20
5.21 vga_display.state_handler = vbp_active;
5.22 + vga_display.scanline = 0;
5.23 vga_display.line = 0;
5.24
5.25 /* Configure a general display timer to start line data transfer and for the
5.26 @@ -58,9 +58,23 @@
5.27
5.28 vga_display.transfer_int_num = transfer_int_num;
5.29
5.30 - /* Update the addresses used for the display. */
5.31 + /* Configure a single window. */
5.32 +
5.33 + vga_display.window_end[0] = display_config->line_count;
5.34 + vga_display.windows = 1;
5.35 +
5.36 + /* Define the line length and count from this window. */
5.37
5.38 - vga_set_frame(display_config);
5.39 + vga_display.line_length = display_config->line_length;
5.40 + vga_display.line_count = display_config->line_count;
5.41 +
5.42 + /* Update the addresses used for the first window of the display. */
5.43 +
5.44 + vga_set_frame(display_config, 0);
5.45 +
5.46 + /* Compute display properties. */
5.47 +
5.48 + vga_update_properties();
5.49 }
5.50
5.51 /* Initialise a separate transfer timer if different from the general display
5.52 @@ -99,6 +113,13 @@
5.53 timer_on(transfer_timer);
5.54 }
5.55
5.56 +/* Update computed properties. */
5.57 +
5.58 +void vga_update_properties(void)
5.59 +{
5.60 + vga_display.line_multiplier = vga_display.scanlines / vga_display.line_count;
5.61 +}
5.62 +
5.63
5.64
5.65 /* Configure the transfer of pixel data. */
5.66 @@ -246,15 +267,42 @@
5.67
5.68
5.69
5.70 +/* Initialise display window details. */
5.71 +
5.72 +void vga_add_window(display_config_t *display_config)
5.73 +{
5.74 + if (vga_display.windows >= vga_display.max_windows)
5.75 + return;
5.76 +
5.77 + /* Set the end of the window. */
5.78 +
5.79 + vga_display.window_end[vga_display.windows] =
5.80 + vga_display.window_end[vga_display.windows - 1] +
5.81 + display_config->line_count;
5.82 +
5.83 + /* Add the window height to the display height. */
5.84 +
5.85 + vga_display.line_count += display_config->line_count;
5.86 +
5.87 + /* Add the window. */
5.88 +
5.89 + vga_set_frame(display_config, vga_display.windows);
5.90 + vga_display.windows++;
5.91 +
5.92 + /* Compute display properties. */
5.93 +
5.94 + vga_update_properties();
5.95 +}
5.96 +
5.97 /* Update the display addresses from the general display configuration. */
5.98
5.99 -void vga_set_frame(display_config_t *display_config)
5.100 +void vga_set_frame(display_config_t *display_config, int window)
5.101 {
5.102 vga_wait_visible();
5.103
5.104 - vga_display.screen_start = display_config->screen_start;
5.105 - vga_display.screen_limit = display_config->screen_limit;
5.106 - vga_display.screen_size = display_config->screen_size;
5.107 + vga_display.screen_start[window] = display_config->screen_start;
5.108 + vga_display.screen_limit[window] = display_config->screen_limit;
5.109 + vga_display.screen_size[window] = display_config->screen_size;
5.110 }
5.111
5.112 /* Wait for the visible region to be completed. */
5.113 @@ -302,7 +350,7 @@
5.114
5.115 void vga_hsync_interrupt_handler(void)
5.116 {
5.117 - vga_display.line += 1;
5.118 + vga_display.scanline += 1;
5.119 vga_display.state_handler();
5.120 }
5.121
5.122 @@ -311,7 +359,6 @@
5.123
5.124 void vga_transfer_interrupt_handler(void)
5.125 {
5.126 - display_config_t *cfg = vga_display.display_config;
5.127 uint8_t *current, *end, *output;
5.128
5.129 if (vga_display.state_handler != visible_active)
5.130 @@ -320,7 +367,7 @@
5.131 /* Generate the pixel signal. */
5.132
5.133 output = (uint8_t *) vga_display.output;
5.134 - end = vga_display.linedata + cfg->line_length;
5.135 + end = vga_display.linedata + vga_display.line_length;
5.136
5.137 /* This is potentially not as efficient as loading words and shifting bytes
5.138 but it appears difficult to implement that approach without experiencing
5.139 @@ -340,16 +387,21 @@
5.140
5.141 void vbp_active(void)
5.142 {
5.143 - if (vga_display.line < vga_display.visible_start)
5.144 + if (vga_display.scanline < vga_display.visible_start)
5.145 return;
5.146
5.147 /* Enter the visible region. */
5.148
5.149 vga_display.state_handler = visible_active;
5.150
5.151 + /* Prepare to show the first window. */
5.152 +
5.153 + vga_display.window = 0;
5.154 + vga_display.line = 0;
5.155 +
5.156 /* Set the line address. */
5.157
5.158 - vga_display.linedata = vga_display.screen_start;
5.159 + vga_display.linedata = vga_display.screen_start[0];
5.160
5.161 if (vga_display.line_channels)
5.162 start_visible();
5.163 @@ -359,18 +411,35 @@
5.164
5.165 void visible_active(void)
5.166 {
5.167 - display_config_t *cfg = vga_display.display_config;
5.168 -
5.169 - if (vga_display.line < vga_display.vfp_start)
5.170 + if (vga_display.scanline < vga_display.vfp_start)
5.171 {
5.172 /* Update the line address and handle wraparound. */
5.173
5.174 - if (!(vga_display.line % cfg->line_multiplier))
5.175 + if (!(vga_display.scanline % vga_display.line_multiplier))
5.176 {
5.177 - vga_display.linedata += cfg->line_length;
5.178 + vga_display.line += 1;
5.179 +
5.180 + /* Move to the next window if the end has been reached. */
5.181 +
5.182 + if (vga_display.line == vga_display.window_end[vga_display.window])
5.183 + {
5.184 + vga_display.window++;
5.185
5.186 - if (vga_display.linedata >= vga_display.screen_limit)
5.187 - vga_display.linedata -= vga_display.screen_size;
5.188 + if (vga_display.window == vga_display.windows)
5.189 + {
5.190 + vga_display.window = 0;
5.191 + vga_display.line = 0;
5.192 + }
5.193 +
5.194 + vga_display.linedata = vga_display.screen_start[vga_display.window];
5.195 + }
5.196 + else
5.197 + {
5.198 + vga_display.linedata += vga_display.line_length;
5.199 +
5.200 + if (vga_display.linedata >= vga_display.screen_limit[vga_display.window])
5.201 + vga_display.linedata -= vga_display.screen_size[vga_display.window];
5.202 + }
5.203 }
5.204
5.205 if (vga_display.line_channels)
5.206 @@ -392,7 +461,7 @@
5.207
5.208 void vfp_active(void)
5.209 {
5.210 - if (vga_display.line < vga_display.vsync_start)
5.211 + if (vga_display.scanline < vga_display.vsync_start)
5.212 return;
5.213
5.214 /* Enter the vertical sync region. */
5.215 @@ -408,12 +477,12 @@
5.216
5.217 void vsync_active(void)
5.218 {
5.219 - if (vga_display.line < vga_display.vsync_end)
5.220 + if (vga_display.scanline < vga_display.vsync_end)
5.221 return;
5.222
5.223 /* Start again at the top of the display. */
5.224
5.225 - vga_display.line = 0;
5.226 + vga_display.scanline = 0;
5.227 vga_display.state_handler = vbp_active;
5.228
5.229 /* Bring vsync high when the next line starts. */
5.230 @@ -436,7 +505,7 @@
5.231 void update_visible(void)
5.232 {
5.233 uint32_t transfer_start = (uint32_t) vga_display.linedata;
5.234 - uint32_t transfer_length = vga_display.display_config->line_length /
5.235 + uint32_t transfer_length = vga_display.line_length /
5.236 vga_display.line_channels;
5.237
5.238 /* Determine whether an initiating channel is used. */