From 0afd05848a181fa7dd1570c70d871591ef9aedbe Mon Sep 17 00:00:00 2001 From: Lephe Date: Tue, 9 Apr 2024 08:57:46 +0200 Subject: [PATCH] gdb, video, r61524: gdb visual feedback + start video intf on CG - Define a draft of the video interface - Implement that dragt for CG for a single mode * Includes stub of brightness setting from disassembling 3.60 - Use the video interface to show visual feedback on GDB on CG Using the video interface avoids directly linking into a driver, which will serve modularity in gint 3. --- CMakeLists.txt | 6 +- TODO | 11 ++ include/gint/image.h | 4 +- include/gint/video.h | 91 +++++++++++++++ src/gdb/fxconv-metadata.txt | 9 ++ src/gdb/gdb.c | 38 +++++- src/gdb/icons-i1msb.png | Bin 0 -> 626 bytes src/gdb/icons-rgb565.png | Bin 0 -> 614 bytes src/r61524/r61524.c | 223 ++++++++++++++++++++++-------------- src/video/video.c | 39 +++++++ 10 files changed, 328 insertions(+), 93 deletions(-) create mode 100644 include/gint/video.h create mode 100644 src/gdb/fxconv-metadata.txt create mode 100644 src/gdb/icons-i1msb.png create mode 100644 src/gdb/icons-rgb565.png create mode 100644 src/video/video.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 505a2b3..cc5e8f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -243,10 +243,12 @@ set(SOURCES src/usb/string.c src/usb/usb.c src/usb/write4.S + # Video driver interface + src/video/video.c ) -set(ASSETS_FX src/font5x7.png) -set(ASSETS_CG src/font8x9.png) +set(ASSETS_FX src/font5x7.png src/gdb/icons-i1msb.png) +set(ASSETS_CG src/font8x9.png src/gdb/icons-rgb565.png) fxconv_declare_assets(${ASSETS_FX} ${ASSETS_CG}) include_directories( diff --git a/TODO b/TODO index 269ba4b..5e9ac98 100644 --- a/TODO +++ b/TODO @@ -53,3 +53,14 @@ R61524: mode 1 : i3??? 396x224 @? Hz mode 2 : i1msb 128x64 @? Hz # <- for g1a emulation :) mode 3 : 2i1msb 128x64 @? Hz # <- for g1a emulation :) + +enum { + /* Indexed 1-bit, 0 is white, 1 is black. Row-major, left-to-right, each + row a set of 4-byte values with leftmost pixel on the MSB. */ + IMAGE_FORMAT_I1MSB, + /* Indexed 2-bit: white, light gray, dark gray, black. Represented as a + pair of I1MSB buffers, the first being bit #0, the second bit #1. */ + IMAGE_FORMAT_2I1MSB, + /* 16-bit RGB565. Row-major, left-to-right. */ + IMAGE_FORMAT_RGB565, +}; diff --git a/include/gint/image.h b/include/gint/image.h index 7d0c711..2a640b5 100644 --- a/include/gint/image.h +++ b/include/gint/image.h @@ -35,8 +35,6 @@ extern "C" { #endif #include -#if GINT_RENDER_RGB - #include #include @@ -168,6 +166,8 @@ enum { // Image creation and destruction //--- +#if GINT_RENDER_RGB + /* image_alloc(): Create a new (uninitialized) image This function allocates a new image of the specified dimensions and format. diff --git a/include/gint/video.h b/include/gint/video.h new file mode 100644 index 0000000..6edf0b6 --- /dev/null +++ b/include/gint/video.h @@ -0,0 +1,91 @@ +//--- +// gint:video - Generic video interface +// +// This header defines the interface for video (display) drivers. It allows +// high-level code to manipulate the display independently of the underlying +// hardware. +//--- + +#ifndef GINT_VIDEO +#define GINT_VIDEO + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* Video mode offered by a driver for rendering. */ +typedef struct { + /* Mode size */ + uint16_t width; + uint16_t height; + /* Pixel format */ + int16_t format; + /* Refresh frequency, -1 if unknown */ + int16_t freq; +} video_mode_t; + +/* Flags for the update function of the interface. */ +#define VIDEO_UPDATE_ENABLE_DMA 0x01 +#define VIDEO_UPDATE_ATOMIC 0x02 + +/* Video driver interface. */ +typedef struct { + /* Associated driver (NULL if there's none). */ + gint_driver_t const *driver; + + /* List of modes (terminated by an all-0 element). The first mode is the + default mode and it should always be available. */ + video_mode_t const *modes; + /* Get current video mode. */ + uint (*mode_get)(void); + /* Set a video mode. */ + bool (*mode_set)(uint id); + + /* Minimum and maximum brightness settings. */ + int brightness_min; + int brightness_max; + /* Set a brightness setting. */ + bool (*brightness_set)(int setting); + + /* Implements video_update(); bounds are checked befoer calling. */ + bool (*update)(int x, int y, image_t const *framebuffer, int flags); + +} video_interface_t; + +/* Get the video interface currently in use. This can be NULL if the program is + being linked without a display driver. */ +video_interface_t const *video_get_current_interface(void); + +/* Get the index of the current video mode. This can be -1 if there is no + interface; however, if there is an interface, this is always >= 0. */ +int video_get_current_mode_index(void); + +/* Get the a pointer to the current video mode's definition. */ +video_mode_t const *video_get_current_mode(void); + +/* Update the contents of the display from a framebuffer image. A combination + of `VIDEO_UPDATE_*` flags can be specified to select the update method: + - `ENABLE_DMA` allows the driver to copy using DMA, when applicable; + - `ATOMIC` requires the driver to use an interrupt-less method. + Returns true on success. + + Update flags will be ignored if not applicable (e.g. `ENABLE_DMA` for a + video interface that doesn't support DMA) but will result in an error if + applicable and the specified method fails (e.g. DMA transfer error). + + This function is usually called with (x,y) = (0,0) and a contiguous + framebuffer whose size is the video mode size. Specifying images with other + sizes, positions and strides is allowed only when they result in data + transfers that are byte-aligned. DMA support is only guaranteed for + contiguous input and output. The implied rectangle must be in-bounds. */ +bool video_update(int x, int y, image_t const *fb, int flags); + +#ifdef __cplusplus +} +#endif + +#endif /* GINT_VIDEO */ diff --git a/src/gdb/fxconv-metadata.txt b/src/gdb/fxconv-metadata.txt new file mode 100644 index 0000000..d9521d8 --- /dev/null +++ b/src/gdb/fxconv-metadata.txt @@ -0,0 +1,9 @@ +icons-i1msb.png: + type: bopti-image + profile: mono + name: gint_gdb_icons_i1msb + +icons-rgb565.png: + type: bopti-image + profile: rgb565 + name: gint_gdb_icons_rgb565 diff --git a/src/gdb/gdb.c b/src/gdb/gdb.c index 42b0bca..9df9517 100644 --- a/src/gdb/gdb.c +++ b/src/gdb/gdb.c @@ -4,12 +4,40 @@ #include #include #include +#include #include #include #include #include +#define GDB_VISUAL_FEEDBACK 1 + +#if GDB_VISUAL_FEEDBACK + +enum { ICON_WORKING, ICON_ERROR, ICON_COMM, ICON_IDLE }; + +static void gdb_show_stub_status(int icon) +{ + video_mode_t const *M = video_get_current_mode(); + if(!M) + return; + + // extern image_t gint_gdb_icons_i1msb; + extern image_t gint_gdb_icons_rgb565; + + if(M->format == IMAGE_RGB565) { + image_t sub; + image_sub(&gint_gdb_icons_rgb565, 6*icon, 0, 7, 7, &sub); + if(!video_update(M->width-7, 0, &sub, 0)) + abort(); + } +} + +#else +# define gdb_show_stub_status(...) ((void)0) +#endif + static void gdb_hexlify(char* output_string, const uint8_t* input_buffer, size_t input_size) { const char* hex = "0123456789ABCDEF"; @@ -531,11 +559,11 @@ static void gdb_handle_single_step(uint32_t pc, ubc_break_mode_t break_mode) void gdb_main(gdb_cpu_state_t* cpu_state) { if (!gdb_started && gdb_start()) { - // TODO: Visual signal "GDB stub error" + gdb_show_stub_status(ICON_ERROR); return; } - // TODO: Visual signal "GDB stub idle" + gdb_show_stub_status(ICON_IDLE); if (gdb_single_step_backup.single_stepped) { if (gdb_single_step_backup.channel0_used) { @@ -557,7 +585,7 @@ void gdb_main(gdb_cpu_state_t* cpu_state) } while (1) { - // TODO: Visual signal "GDB stub communicating" + gdb_show_stub_status(ICON_COMM); char packet_buffer[256]; ssize_t packet_size = gdb_recv_packet(packet_buffer, sizeof(packet_buffer)); @@ -566,7 +594,7 @@ void gdb_main(gdb_cpu_state_t* cpu_state) continue; } - // TODO: Visual signal "GDB stub working" + gdb_show_stub_status(ICON_WORKING); switch (packet_buffer[0]) { case '?': // Halt reason @@ -620,7 +648,7 @@ void gdb_main(gdb_cpu_state_t* cpu_state) break; } - // TODO: Visual signal "GDB stub idle" + gdb_show_stub_status(ICON_IDLE); } ret: diff --git a/src/gdb/icons-i1msb.png b/src/gdb/icons-i1msb.png new file mode 100644 index 0000000000000000000000000000000000000000..f2c9be974b4911d6ca11ed711f18d38084615122 GIT binary patch literal 626 zcmV-&0*(ENP)EX>4Tx04R}tkv&MmKpe$izo&<>&w8LE?ARK&4Xp$HX1tI2)hr{E zN(#CBst|rf5Mc};gb|6E`kW}H;W@tU;p6LFoM(BT`*ZXu1(N|jfjGf*!y?`wp4qf? z&ilksR+bdvbK(huE=c^yb=l=N&LxNaJTq!!GxNkzVxiQ6*p%PCM#}rkge1Fbm zh4U6?wOVKGd-4}X3fjst*J+L-g+(kuga8?JR8WP5IIS8fCNi|2^zaWmevw=Di&K+sQX~n4UTphg3<&Q6t%hxXAKP~81PD9>S6atkYXY;Mq>R z>G?f^qix@1U>6rkxZ7J>IO`ldWEcnkEdxxKabaryvcsjKu2aBv8W zmneJPoz5RQp-QN#35OR(#QQrsv000JJOGiWi000000Qp0^e*gdg32;bRa{vG? zBLDy{BLR4&KXw2B00(qQO+^Rj1P2lh8N1$JW&i*H8FWQhbVF}#ZDnqB07G(RVRU6= zAa`kWXdp*PO;A^X4i^9b082?kK~yNu#gZ`&03Zkhum1nfbubvCqHcDfp`;K%fcc0L z#5T4&pYfva_zi0HM$%%q%K>SAF(2x}Z;px+j6&ArMbCymZs6w+RDzpA>+a$tqW}N^ M07*qoM6N<$f=_q*o&W#< literal 0 HcmV?d00001 diff --git a/src/gdb/icons-rgb565.png b/src/gdb/icons-rgb565.png new file mode 100644 index 0000000000000000000000000000000000000000..c04b975339ba6a3df7d2ce9b58cd915226993ac6 GIT binary patch literal 614 zcmV-s0-61ZP)P000LF0ssI2a#wNR0004lX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$izo&<>&w8LE?ARK&4Xp$HX1tI2)hr{E zN(#CBst|rf5Mc};gb|6E`kW}H;W@tU;p6LFoM(BT`*ZXu1(N|jfjGf*!y?`wp4qf? z&ilksR+bdvbK(huE=c^yb=l=N&LxNaJTq!!GxNkzVxiQ6*p%PCM#}rkge1Fbm zh4U6?wOVKGd-4}X3fjst*J+L-g+(kuga8?JR8WP5IIS8fCNi|2^zaWmevw=Di&K+sQX~n4UTphg3<&Q6t%hxXAKP~81PD9>S6atkYXY;Mq>R z>G?f^qix@1U>6rkxZ7J>IO`ldWEcnkEdxxKabaryvcsjKu2aBv8W zmneJPoz5RQp-QN#35OR(#QQrsv000SaNLh0L04^f{04^f|c%?sf00007bV*G` z2j~O`6BsTYIVFhz000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0000{ zNklYHpdMg^P$A1sLl|qKOd*Un#n}*sNbgh#V_xJ<2qS5^I)t%m)hY-hEG!Ia z7Th4Xad3m+#=#AO8wWQCZX6R8{m3*(!;6{#0Q+qu2-u%24FCWD07*qoM6N<$f #include #include +#include +#include #include #if GINT_HW_CG -#define DMA SH7305_DMA -#define POWER SH7305_POWER - -//--- -// Device specification sheet -//--- - -/* Registers and operations */ -enum { - device_code_read = 0x000, - driver_output_control = 0x001, - entry_mode = 0x003, - display_control_2 = 0x008, - low_power_control = 0x00b, - - ram_address_horizontal = 0x200, - ram_address_vertical = 0x201, - write_data = 0x202, - - horizontal_ram_start = 0x210, - horizontal_ram_end = 0x211, - vertical_ram_start = 0x212, - vertical_ram_end = 0x213, -}; - -typedef word_union(entry_mode_t, - uint TRI :1; - uint DFM :1; - uint :1; - uint BGR :1; - uint :2; - uint HWM :1; - uint :1; - uint ORG :1; - uint :1; - uint ID :2; - uint AM :1; - uint :1; - uint EPF :2; -); - -//--- -// Device communication primitives -//--- +/* Registers */ +#define REG_DEVICE_CODE_READ 0x000 +#define REG_DRIVER_OUTPUT_CTL 0x001 +#define REG_ENTRY_MODE 0x003 +#define REG_DISPLAY_CTL2 0x008 +#define REG_LOW_POWER_CTL 0x00b +#define REG_HADDR 0x200 +#define REG_VADDR 0x201 +#define REG_DATA 0x202 +#define REG_HSTART 0x210 +#define REG_HEND 0x211 +#define REG_VSTART 0x212 +#define REG_VEND 0x213 /* Interface with the controller */ -static volatile uint16_t *intf = (void *)0xb4000000; +static volatile uint16_t *DISPLAY = (void *)0xb4000000; /* Bit 4 of Port R controls the RS bit of the display driver */ static volatile uint8_t *PRDR = (void *)0xa405013c; +/* Select a register */ GINLINE static void select(uint16_t reg) { /* Clear RS and write the register number */ *PRDR &= ~0x10; synco(); - *intf = reg; + *DISPLAY = reg; synco(); - - /* Set RS back. We don't do this in read()/write() because the display - driver is optimized for consecutive GRAM access. LCD-transfers will - be faster when executing select() followed by several calls to - write(). (Although most applications should use the DMA instead.) */ + /* Set RS=1 to allow consecutive reads/writes after a select() */ *PRDR |= 0x10; synco(); } -GINLINE static uint16_t read(void) -{ - return *intf; -} - GINLINE static void write(uint16_t data) { - *intf = data; + *DISPLAY = data; } -uint16_t r61524_get(int ID) +GINLINE uint16_t r61524_get(int ID) { select(ID); - return read(); + return *DISPLAY; } -void r61524_set(int ID, uint16_t value) +GINLINE void r61524_set(int ID, uint16_t value) { select(ID); - write(value); + *DISPLAY = value; } //--- -// Window management +// Window management //--- void r61524_win_get(uint16_t *HSA, uint16_t *HEA, uint16_t *VSA, uint16_t *VEA) { - select(horizontal_ram_start); - *HSA = read(); - select(horizontal_ram_end); - *HEA = read(); - - select(vertical_ram_start); - *VSA = read(); - select(vertical_ram_end); - *VEA = read(); + *HSA = r61524_get(REG_HSTART); + *HEA = r61524_get(REG_HEND); + *VSA = r61524_get(REG_VSTART); + *VEA = r61524_get(REG_VEND); } void r61524_win_set(uint16_t HSA, uint16_t HEA, uint16_t VSA, uint16_t VEA) { - select(horizontal_ram_start); - write(HSA); - select(horizontal_ram_end); - write(HEA); + r61524_set(REG_HSTART, HSA); + r61524_set(REG_HEND, HEA); + r61524_set(REG_VSTART, VSA); + r61524_set(REG_VEND, VEA); +} - select(vertical_ram_start); - write(VSA); - select(vertical_ram_end); - write(VEA); +//--- +// Backlight management +//--- + +void r61525_brightness_set(int level) +{ + bool GLOBAL_backlight_high_bit[7] = { /* at 0x80399530 */ + false, false, false, false, + true, true, true, + }; + uint8_t GLOBAL_backlight_table[7] = { /* at 0x80399537 */ + 0x14, 0x4b, 0x78, 0xd2, + 0x6e, 0xa0, 0xc8, + }; + uint8_t GLOBAL_0[10] = { /* at 0x8039953e */ + 0x00, 0x01, 0x02, 0x03, 0x07, + 0x0f, 0x1f, 0x3f, 0x7f, 0xff, + }; + + if(level < 1) + level = 1; + if(level > 5) + level = 5; + + int8_t volatile *PNDR = (void *)0xa4050138; + if(GLOBAL_backlight_high_bit[level]) + *PNDR |= 0x10; + else + *PNDR &= 0xef; + + synco(); + + r61524_set(0x5a2, GLOBAL_0[(level < 2) ? 9 : 5]); + r61524_set(0x5a1, GLOBAL_backlight_table[level]); } //--- @@ -141,13 +130,11 @@ void r61524_start_frame(int xmin, int xmax, int ymin, int ymax) { /* Move the window to the desired region, then select address 0 */ r61524_win_set(395-xmax, 395-xmin, ymin, ymax); - select(ram_address_horizontal); - write(0); - select(ram_address_vertical); - write(0); + r61524_set(REG_HADDR, 0); + r61524_set(REG_VADDR, 0); /* Bind address 0xb4000000 to the data write command */ - select(write_data); + select(REG_DATA); } void r61524_display(uint16_t *vram, int start, int height, int method) @@ -281,6 +268,45 @@ void r61524_display_gray_128x64(uint32_t *light, uint32_t *dark) write(border); } +static bool r61524_update(int x, int y, image_t const *fb, int flags) +{ + // TODO: r61524_update: Handle the mono cases + if(fb->format != IMAGE_RGB565) + return false; + + uint w = fb->width; + uint h = fb->height; + + dma_transfer_wait(0); + r61524_start_frame(x, x+w-1, y, y+h-1); + + /* DMA if enabled */ + bool dma_possible = (!x && w == 396 && fb->stride == 396*2 && !(h%4)); + if((flags & VIDEO_UPDATE_ENABLE_DMA) && dma_possible) { + void *src = fb->data; + void *dst = (void *)DISPLAY; + int blocks = 99 * (h / 4); + + if(flags & VIDEO_UPDATE_ATOMIC) + dma_transfer_atomic(0, DMA_32B, blocks, src, DMA_INC, + dst, DMA_FIXED); + else + dma_transfer_async(0, DMA_32B, blocks, src, DMA_INC, + dst, DMA_FIXED, GINT_CALL_NULL); + return true; + } + + uint16_t *pixels = fb->data; + + for(int y = 0; y < fb->height; y++) { + for(int x = 0; x < fb->width; x++) + write(pixels[x]); + pixels = (void *)pixels + fb->stride; + } + + return true; +} + //--- // State and driver metadata //--- @@ -303,4 +329,33 @@ gint_driver_t drv_r61524 = { }; GINT_DECLARE_DRIVER(26, drv_r61524); +//--- +// Video driver interface +//--- + +static video_mode_t r61524_modes[] = { + /* Standard full-screen full-color mode */ + { 396, 224, IMAGE_RGB565, -1 }, +#if 0 + /* R61524 8-color mode with lower power consumption */ + { 396, 224, IMAGE_P8_RGB565, -1 }, // TODO: actually P3, that's closest + /* T6K11-emulation black-and-white mode */ + { 128, 64, IMAGE_I1MSB, -1 }, + /* T6K11-emulation gray mode */ + { 128, 64, IMAGE_2I1MSB, -1 }, +#endif + { 0 } +}; + +video_interface_t r61524_video = { + .driver = &drv_r61524, + .modes = r61524_modes, + .mode_get = NULL, // TODO + .mode_set = NULL, // TODO + .brightness_min = 0, // TODO + .brightness_max = 0, // TODO + .brightness_set = NULL, + .update = r61524_update, +}; + #endif /* GINT_HW_CG */ diff --git a/src/video/video.c b/src/video/video.c new file mode 100644 index 0000000..b458147 --- /dev/null +++ b/src/video/video.c @@ -0,0 +1,39 @@ +#include +#include + +// TODO: video: Have interface set by gint pre-main call instead +extern video_interface_t t6k11_video, r61524_video; +static video_interface_t const *current_intf = + GINT_HW_SWITCH(NULL, &r61524_video); +static int current_mode_index = GINT_HW_SWITCH(-1, 0); + +video_interface_t const *video_get_current_interface(void) +{ + return current_intf; +} + +int video_get_current_mode_index(void) +{ + return current_mode_index; +} + +video_mode_t const *video_get_current_mode(void) +{ + return (current_intf && current_mode_index >= 0) + ? ¤t_intf->modes[current_mode_index] + : NULL; +} + +bool video_update(int x, int y, image_t const *fb, int flags) +{ + video_mode_t const *M = video_get_current_mode(); + + if(!M || !current_intf->update || fb->format != M->format) + return false; + if(x < 0 || y < 0) + return false; + if(x + fb->width > M->width || y + fb->height > M->height) + return false; + + return current_intf->update(x, y, fb, flags); +}