# HG changeset patch # User Paul Boddie # Date 1542063951 -3600 # Node ID ac50267e4c5ffab5ad35eec53445eb967131294b # Parent b8630308b29e80d479bf12e04fc141743322e54c Added support for multiple display windows that can be scrolled separately. Moved scanlines and the line multiplier into the VGA display abstraction, computing the latter from the former and the total line count from all windows. Replaced the character table output with a non-scrolling banner window. diff -r b8630308b29e -r ac50267e4c5f examples/vga/main.c --- a/examples/vga/main.c Tue Nov 13 00:02:15 2018 +0100 +++ b/examples/vga/main.c Tue Nov 13 00:05:51 2018 +0100 @@ -84,7 +84,8 @@ /* Initialise memory for a multi-frame display. */ -static Display(display_config, LINE_LENGTH, LINE_COUNT, FRAME_COUNT); +static Display(banner_config, LINE_LENGTH, 10, 1); +static Display(display_config, LINE_LENGTH, LINE_COUNT - 10, FRAME_COUNT); @@ -153,7 +154,7 @@ /* Update the display with the frame details. */ - vga_set_frame(&display_config); + vga_set_frame(&display_config, 1); wait(delay); /* Select the next frame to plot to. */ @@ -190,36 +191,29 @@ } } -/* Fill the screen with characters. */ +/* Set up a banner. */ -static void write_chars(void) +static void setup_banner(void) { - const int line_height = 9; - int x = 0, y = 0; - char c; + int frame, x = 0, y = 0; init_font(&font_config, fontchars, fonttable, fontbase, fontlimit); - while (y + line_height < display_config.line_count) + for (frame = 0; frame < banner_config.frames; frame++) { - for (c = (char) font_config.base; c < (char) font_config.limit; c++) - { - if (x + get_char_definition(&font_config, c)->width > display_config.line_length) - { - x = 0; y += line_height; - } + /* Obtain the frame. */ + + display_select_frame(&banner_config, frame); - if (y + line_height >= display_config.line_count) - break; + write_string(&banner_config, &font_config, "VGA display example", &x, &y, 0xff); + } - x = write_char(&display_config, &font_config, c, x, y, 0xff); - } - } + display_select_frame(&banner_config, 0); } /* Set up a background. */ -static void setup(void) +static void setup_background(void) { int frame; @@ -234,15 +228,17 @@ image_tile_sprite(&scr, 0, 0, display_config.line_length, display_config.line_count * scr.yscale, 0, 0); - - /* Write a sequence of characters. */ - - write_chars(); } display_select_frame(&display_config, 0); } +static void setup(void) +{ + setup_banner(); + setup_background(); +} + /* Main program. */ @@ -277,13 +273,15 @@ /* Set the display to a well-defined state. */ + init_display(&banner_config); init_display(&display_config); /* Initialise VGA output with one or two line channels, configuring a line timer and any transfer timer, with an initiating channel being introduced if a transfer timer is specified. */ - init_vga_with_timers(&display_config, LINE_CHANNELS, LINE_TIMER, TRANSFER_TIMER); + init_vga_with_timers(&banner_config, LINE_CHANNELS, LINE_TIMER, TRANSFER_TIMER); + vga_add_window(&display_config); /* Configure VGA output transfer to the output register, also configuring output compare units for horizontal and vertical sync. */ diff -r b8630308b29e -r ac50267e4c5f include/display.h --- a/include/display.h Tue Nov 13 00:02:15 2018 +0100 +++ b/include/display.h Tue Nov 13 00:05:51 2018 +0100 @@ -63,14 +63,6 @@ uint8_t *screen_limit; /* = frame_start + screen_size */ - /* Number of scanlines on the display. */ - - uint32_t scanlines; - - /* Number of scanlines per display line. */ - - int line_multiplier; /* = scanlines / line_count */ - /* Number of line channels used for pixel data transfers. */ int line_channels; @@ -105,7 +97,6 @@ .frames=FRAMES, \ .max_frames=FRAMES, \ \ - .scanlines = SCANLINES, \ .line_channels = LINE_CHANNELS, \ .cell_size = CELL_SIZE}; diff -r b8630308b29e -r ac50267e4c5f include/vga_display.h --- a/include/vga_display.h Tue Nov 13 00:02:15 2018 +0100 +++ b/include/vga_display.h Tue Nov 13 00:05:51 2018 +0100 @@ -50,24 +50,40 @@ /* Current scanline and pixel line. */ - uint32_t line; + uint32_t scanline, line; uint8_t *linedata; + /* Separate display windows dividing the display vertically. */ + + int window, windows, max_windows; + + /* A collection of line numbers ending each window. */ + + uint32_t *window_end; + /* The start, limit and size of the screen are copied to this structure, allowing the display configuration to use different values such as those for a different display frame. */ /* Screen start address, frame limit. */ - uint8_t *screen_start, *screen_limit; + uint8_t **screen_start, **screen_limit; /* Screen size, used to wrap the current line pointer. */ - uint32_t screen_size; + uint32_t *screen_size; + + /* General display properties defined by the collection of windows. */ + + uint32_t line_length, line_count; - /* General display configuration. */ + /* Number of scanlines on the display. */ + + uint32_t scanlines; - display_config_t *display_config; + /* Number of scanlines per display line. */ + + int line_multiplier; /* = scanlines / line_count */ /* Display line positions. */ @@ -85,12 +101,25 @@ This macro depends on the VGA timing constants being defined, and they are included above. - VGA_Display() + VGA_Display(, int windows) */ -#define VGA_Display(NAME) \ +#define VGA_Display(NAME, WINDOWS) \ + uint32_t __##NAME##_window_end[WINDOWS]; \ + uint8_t *__##NAME##_screen_start[WINDOWS]; \ + uint8_t *__##NAME##_screen_limit[WINDOWS]; \ + uint32_t __##NAME##_screen_size[WINDOWS]; \ + \ vga_display_t NAME = { \ + .window_end = __##NAME##_window_end, \ + .screen_start = __##NAME##_screen_start, \ + .screen_limit = __##NAME##_screen_limit, \ + .screen_size = __##NAME##_screen_size, \ + .window = 0, \ + .windows = 0, \ + .max_windows = WINDOWS, \ .transfer_cell_size = TRANSFER_CELL_SIZE, \ + .scanlines = SCANLINES, \ .hfreq_limit = HFREQ_LIMIT, \ .hsync_start = HSYNC_START, \ .hsync_end = HSYNC_END, \ @@ -121,9 +150,12 @@ void vga_configure_zero_channel(int channel, int int_num, int initiating, uint32_t output); +void vga_update_properties(void); + /* Access-related operations. */ -void vga_set_frame(display_config_t *display_config); +void vga_add_window(display_config_t *display_config); +void vga_set_frame(display_config_t *display_config, int window); void vga_wait_visible(void); /* Interrupt handlers. */ diff -r b8630308b29e -r ac50267e4c5f lib/display.c --- a/lib/display.c Tue Nov 13 00:02:15 2018 +0100 +++ b/lib/display.c Tue Nov 13 00:05:51 2018 +0100 @@ -51,10 +51,6 @@ /* Fixed limit of the frame. */ cfg->screen_limit = cfg->frame_start + cfg->screen_size; - - /* Recalculate the line multiplier. */ - - cfg->line_multiplier = cfg->scanlines / cfg->line_count; } /* Initialise the screen start addresses for all frames. */ diff -r b8630308b29e -r ac50267e4c5f lib/vga_display.c --- a/lib/vga_display.c Tue Nov 13 00:02:15 2018 +0100 +++ b/lib/vga_display.c Tue Nov 13 00:05:51 2018 +0100 @@ -24,7 +24,7 @@ /* Display state. */ -VGA_Display(vga_display); +VGA_Display(vga_display, 2); /* Pixel data. */ @@ -40,12 +40,12 @@ { /* Display parameters. */ - vga_display.display_config = display_config; vga_display.line_channels = line_channels; /* Initial state. */ vga_display.state_handler = vbp_active; + vga_display.scanline = 0; vga_display.line = 0; /* Configure a general display timer to start line data transfer and for the @@ -58,9 +58,23 @@ vga_display.transfer_int_num = transfer_int_num; - /* Update the addresses used for the display. */ + /* Configure a single window. */ + + vga_display.window_end[0] = display_config->line_count; + vga_display.windows = 1; + + /* Define the line length and count from this window. */ - vga_set_frame(display_config); + vga_display.line_length = display_config->line_length; + vga_display.line_count = display_config->line_count; + + /* Update the addresses used for the first window of the display. */ + + vga_set_frame(display_config, 0); + + /* Compute display properties. */ + + vga_update_properties(); } /* Initialise a separate transfer timer if different from the general display @@ -99,6 +113,13 @@ timer_on(transfer_timer); } +/* Update computed properties. */ + +void vga_update_properties(void) +{ + vga_display.line_multiplier = vga_display.scanlines / vga_display.line_count; +} + /* Configure the transfer of pixel data. */ @@ -246,15 +267,42 @@ +/* Initialise display window details. */ + +void vga_add_window(display_config_t *display_config) +{ + if (vga_display.windows >= vga_display.max_windows) + return; + + /* Set the end of the window. */ + + vga_display.window_end[vga_display.windows] = + vga_display.window_end[vga_display.windows - 1] + + display_config->line_count; + + /* Add the window height to the display height. */ + + vga_display.line_count += display_config->line_count; + + /* Add the window. */ + + vga_set_frame(display_config, vga_display.windows); + vga_display.windows++; + + /* Compute display properties. */ + + vga_update_properties(); +} + /* Update the display addresses from the general display configuration. */ -void vga_set_frame(display_config_t *display_config) +void vga_set_frame(display_config_t *display_config, int window) { vga_wait_visible(); - vga_display.screen_start = display_config->screen_start; - vga_display.screen_limit = display_config->screen_limit; - vga_display.screen_size = display_config->screen_size; + vga_display.screen_start[window] = display_config->screen_start; + vga_display.screen_limit[window] = display_config->screen_limit; + vga_display.screen_size[window] = display_config->screen_size; } /* Wait for the visible region to be completed. */ @@ -302,7 +350,7 @@ void vga_hsync_interrupt_handler(void) { - vga_display.line += 1; + vga_display.scanline += 1; vga_display.state_handler(); } @@ -311,7 +359,6 @@ void vga_transfer_interrupt_handler(void) { - display_config_t *cfg = vga_display.display_config; uint8_t *current, *end, *output; if (vga_display.state_handler != visible_active) @@ -320,7 +367,7 @@ /* Generate the pixel signal. */ output = (uint8_t *) vga_display.output; - end = vga_display.linedata + cfg->line_length; + end = vga_display.linedata + vga_display.line_length; /* This is potentially not as efficient as loading words and shifting bytes but it appears difficult to implement that approach without experiencing @@ -340,16 +387,21 @@ void vbp_active(void) { - if (vga_display.line < vga_display.visible_start) + if (vga_display.scanline < vga_display.visible_start) return; /* Enter the visible region. */ vga_display.state_handler = visible_active; + /* Prepare to show the first window. */ + + vga_display.window = 0; + vga_display.line = 0; + /* Set the line address. */ - vga_display.linedata = vga_display.screen_start; + vga_display.linedata = vga_display.screen_start[0]; if (vga_display.line_channels) start_visible(); @@ -359,18 +411,35 @@ void visible_active(void) { - display_config_t *cfg = vga_display.display_config; - - if (vga_display.line < vga_display.vfp_start) + if (vga_display.scanline < vga_display.vfp_start) { /* Update the line address and handle wraparound. */ - if (!(vga_display.line % cfg->line_multiplier)) + if (!(vga_display.scanline % vga_display.line_multiplier)) { - vga_display.linedata += cfg->line_length; + vga_display.line += 1; + + /* Move to the next window if the end has been reached. */ + + if (vga_display.line == vga_display.window_end[vga_display.window]) + { + vga_display.window++; - if (vga_display.linedata >= vga_display.screen_limit) - vga_display.linedata -= vga_display.screen_size; + if (vga_display.window == vga_display.windows) + { + vga_display.window = 0; + vga_display.line = 0; + } + + vga_display.linedata = vga_display.screen_start[vga_display.window]; + } + else + { + vga_display.linedata += vga_display.line_length; + + if (vga_display.linedata >= vga_display.screen_limit[vga_display.window]) + vga_display.linedata -= vga_display.screen_size[vga_display.window]; + } } if (vga_display.line_channels) @@ -392,7 +461,7 @@ void vfp_active(void) { - if (vga_display.line < vga_display.vsync_start) + if (vga_display.scanline < vga_display.vsync_start) return; /* Enter the vertical sync region. */ @@ -408,12 +477,12 @@ void vsync_active(void) { - if (vga_display.line < vga_display.vsync_end) + if (vga_display.scanline < vga_display.vsync_end) return; /* Start again at the top of the display. */ - vga_display.line = 0; + vga_display.scanline = 0; vga_display.state_handler = vbp_active; /* Bring vsync high when the next line starts. */ @@ -436,7 +505,7 @@ void update_visible(void) { uint32_t transfer_start = (uint32_t) vga_display.linedata; - uint32_t transfer_length = vga_display.display_config->line_length / + uint32_t transfer_length = vga_display.line_length / vga_display.line_channels; /* Determine whether an initiating channel is used. */