1/* 2 * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7/* CAmkES provides a generated header that prototypes all the relevant 8 * generated symbols. 9 */ 10#include <camkes.h> 11#include <camkes/io.h> 12 13#include <assert.h> 14#include <bga/bga.h> 15#include <ringbuffer/ringbuffer.h> 16#include <stdbool.h> 17#include <stdlib.h> 18#include <string.h> 19 20static ps_io_port_ops_t io_port_ops; 21 22/* The following code drives the Bochs Graphics Array (BGA) video device to 23 * display the various components of the multilevel terminal. Note that it only 24 * uses libsel4bga's simple interface (bga_set_pixel). In a real, performant 25 * system we would enable raw access to the frame buffer and memcpy directly 26 * into it or use banked mode. For more information about the BGA: 27 * http://wiki.osdev.org/Bochs_VBE_Extensions 28 */ 29 30/* Opaque pointer to the Bochs Graphics Array (VESA device) meta data. */ 31static bga_p bga; 32 33/* Provided in the generated chars.c */ 34extern char *to_pixels(char c); 35 36/* Pixel dimensions of a character's XBM representation. See chars.sh or 37 * chars.c. 38 */ 39#define CHAR_WIDTH 14 40#define CHAR_HEIGHT 26 41 42/* Draw a single character on the screen at (x, y). */ 43static void draw(unsigned int x, unsigned int y, char c) 44{ 45 /* Get the character's XBM representation. */ 46 char *pixels = to_pixels(c); 47 assert(pixels != NULL); 48 49 char white[] = { 255, 255, 255 }; 50 char black[] = { 0, 0, 0 }; 51 52 /* Slightly messy row calculation because the XBM format dictates that the 53 * row is padded to a byte boundary. 54 */ 55 unsigned int row_width = CHAR_WIDTH; 56 if (row_width % 8 != 0) { 57 row_width += 8 - row_width % 8; 58 } 59 60 /* Write the representation out to the screen with white filled pixels and 61 * black empty pixels. 62 */ 63 for (unsigned int i = 0; i < CHAR_HEIGHT; i++) { 64 for (unsigned int j = 0; j < CHAR_WIDTH; j++) { 65 char block = pixels[(j + i * row_width) / 8]; 66 unsigned int offset = (j + i * row_width) % 8; 67 bga_set_pixel(bga, x + j, y + i, 68 (block & (1 << offset)) ? white : black); 69 } 70 } 71} 72 73/* Draw a string on the screen starting at (x, y). */ 74static void draw_string(unsigned int x, unsigned int y, const char *s) 75{ 76 while (*s != '\0') { 77 draw(x, y, *s++); 78 x += CHAR_WIDTH; 79 } 80} 81 82/* Width and height of the domain windows. */ 83#define WIDTH 312 84#define HEIGHT 300 85 86/* Draw coloured borders around each input's virtual framebuffer. */ 87static void borders(void) 88{ 89 char purple[] = { 210, 101, 141 }; 90 char blue[] = { 197, 103, 0 }; 91 92 /* The below is full of magic numbers, but they're all basically pixel 93 * tweaks to make sure lines and headings look semi-natural. 94 */ 95 96 for (unsigned int i = 100; i < 100 + WIDTH; i++) { 97 for (unsigned int j = 100; j < 100 + 4 + CHAR_HEIGHT; j++) { 98 bga_set_pixel(bga, i, j, purple); 99 } 100 bga_set_pixel(bga, i, 100 + HEIGHT, purple); 101 } 102 for (unsigned int i = 100; i < 100 + HEIGHT; i++) { 103 bga_set_pixel(bga, 100, i, purple); 104 bga_set_pixel(bga, 100 + WIDTH, i, purple); 105 } 106 draw_string(100 + 100 + 2 * CHAR_WIDTH, 102, "Low"); 107 108 for (unsigned int i = 200 + WIDTH; i < 200 + 2 * WIDTH; i++) { 109 for (unsigned int j = 100; j < 100 + 4 + CHAR_HEIGHT; j++) { 110 bga_set_pixel(bga, i, j, blue); 111 } 112 bga_set_pixel(bga, i, 100 + HEIGHT, blue); 113 } 114 for (unsigned int i = 100; i < 100 + HEIGHT; i++) { 115 bga_set_pixel(bga, 200 + WIDTH, i, blue); 116 bga_set_pixel(bga, 200 + 2 * WIDTH, i, blue); 117 } 118 draw_string(200 + WIDTH + 100 + 2 * CHAR_WIDTH, 102, "High"); 119} 120 121/* Return true if we're capable of displaying this character. */ 122static bool printable(char c) 123{ 124 return (c >= 'A' && c <= 'Z') || 125 (c >= 'a' && c <= 'z') || 126 (c == ' ') || 127 (c >= '0' && c <= '9'); 128} 129 130/* Write a character to the low domain. */ 131static void write_low(char c) 132{ 133 if (!printable(c)) { 134 return; 135 } 136 137 /* Again, magic numbers are basically pixel alignments. */ 138 static unsigned int x = 100 + 2; 139 static unsigned int y = 100 + 4 + CHAR_HEIGHT; 140 if (y >= 100 + HEIGHT - CHAR_HEIGHT) { 141 return; 142 } 143 draw(x, y, c); 144 x += CHAR_WIDTH; 145 if (x >= 100 + WIDTH - 3) { 146 x = 100 + 2; 147 y += CHAR_HEIGHT; 148 } 149} 150 151/* Write a character to the high domain. */ 152static void write_high(char c) 153{ 154 if (!printable(c)) { 155 return; 156 } 157 static unsigned int x = 200 + WIDTH + 2; 158 static unsigned int y = 100 + 4 + CHAR_HEIGHT; 159 if (y >= 100 + HEIGHT - CHAR_HEIGHT) { 160 return; 161 } 162 draw(x, y, c); 163 x += CHAR_WIDTH; 164 if (x >= 200 + 2 * WIDTH - 3) { 165 x = 200 + WIDTH + 2; 166 y += CHAR_HEIGHT; 167 } 168} 169 170/* Callbacks used below. */ 171static void out16(uint16_t port, uint16_t value) 172{ 173 ps_io_port_out(&io_port_ops, port, IOSIZE_16, value); 174} 175 176static uint16_t in16(uint16_t port) 177{ 178 uint32_t result = 0; 179 int error = ps_io_port_in(&io_port_ops, port, IOSIZE_16, &result); 180 if (error) { 181 return 0; 182 } 183 return (uint16_t) result; 184} 185 186/* This function is invoked by the main CAmkES thread in this component. */ 187int run(void) 188{ 189 int error = camkes_io_port_ops(&io_port_ops); 190 assert(!error); 191 192 /* Use the dataport address */ 193 void *bga_ptr = (void *)mock_hdmi; 194 195 bga = bga_init(bga_ptr, out16, in16); 196 bga_set_mode(bga, 1024, 768, 24); /* 1024x768 resolution at 24 BPP */ 197 198 ringbuffer_t *low = rb_new((void *)low_input, sizeof(*low_input)); 199 if (low == NULL) { 200 abort(); 201 } 202 203 ringbuffer_t *high = rb_new((void *)high_input, sizeof(*high_input)); 204 if (high == NULL) { 205 abort(); 206 } 207 208 borders(); 209 210 /* Check both inputs for data and pass it to the relevant framebuffer. */ 211 while (true) { 212 char c; 213 214 if ((c = (char)rb_poll_byte(low)) != 0) { 215 write_low(c); 216 } 217 218 if ((c = (char)rb_poll_byte(high)) != 0) { 219 write_high(c); 220 } 221 } 222 223 return 0; 224} 225