diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index e20925a..f343197 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,4 +1,4 @@ -set(COMPONENT_SRCS "main.c" "file_server.c" "signal_to_zx.c" "lcd_display.c" "vga_display.c" "led_matrix.c" "iis_videosig.c" "signal_from_zx.c" "zx_server.c" "zx_serv_dialog.c" "wifi_sta.c" "zx_file_img.c") +set(COMPONENT_SRCS "main.c" "file_server.c" "video_attr.c" "signal_to_zx.c" "lcd_display.c" "vga_display.c" "led_matrix.c" "iis_videosig.c" "signal_from_zx.c" "zx_server.c" "zx_serv_dialog.c" "wifi_sta.c" "zx_file_img.c") set(COMPONENT_ADD_INCLUDEDIRS ".") set(COMPONENT_EMBED_FILES "favicon.ico" "upload_script.html") diff --git a/main/iis_videosig.c b/main/iis_videosig.c index ace9acd..7b619a2 100644 --- a/main/iis_videosig.c +++ b/main/iis_videosig.c @@ -202,7 +202,9 @@ static bool video_synced_state=false; static int timeout_verbose=10; - +bool vid_is_synced(){ + return video_synced_state; +} static IRAM_ATTR uint32_t vid_get_next_data() { diff --git a/main/iis_videosig.h b/main/iis_videosig.h index 7c2f8c8..9b53b1c 100644 --- a/main/iis_videosig.h +++ b/main/iis_videosig.h @@ -12,6 +12,7 @@ // call once at startup void vid_init(); void vid_cal_pixel_start(); +bool vid_is_synced(); extern uint32_t vid_pixel_mem[]; /* number of top-header lines before actual standard-character screen starts*/ diff --git a/main/lcd_display.c b/main/lcd_display.c index 54bc595..617e921 100644 --- a/main/lcd_display.c +++ b/main/lcd_display.c @@ -12,6 +12,7 @@ #include "esp_log.h" #include "driver/spi_master.h" #include "driver/gpio.h" +#include "video_attr.h" #include "iis_videosig.h" #include "lcd_display.h" @@ -62,9 +63,7 @@ typedef enum { -//画笔颜色 -#define WHITE 0xFFFF -#define BLACK 0x0000 +/* #define BLUE 0x001F #define BRED 0XF81F #define GRED 0XFFE0 @@ -74,21 +73,15 @@ typedef enum { #define GREEN 0x07E0 #define CYAN 0x7FFF #define YELLOW 0xFFE0 -#define BROWN 0XBC40 //棕色 -#define BRRED 0XFC07 //棕红色 -#define GRAY 0X8430 //灰色 -//GUI颜色 - -#define DARKBLUE 0X01CF //深蓝色 -#define LIGHTBLUE 0X7D7C //浅蓝色 -#define GRAYBLUE 0X5458 //灰蓝色 -//以上三色为PANEL的颜色 - -#define LIGHTGREEN 0X841F //浅绿色 -#define LGRAY 0XC618 //浅灰色(PANNEL),窗体背景色 +*/ +// actual_colour=0x081F; // green + //actual_colour=0x03FF; // yellow -#define LGRAYBLUE 0XA651 //浅灰蓝色(中间层颜色) -#define LBBLUE 0X2B12 //浅棕蓝色(选择条目的反色) +#define WHITE 0xFFFF +#define BLACK 0x0000 +#define BLUE 0x1F00 +#define GREEN 0xE007 +#define RED 0x00F8 static uint16_t fg_colour=BLACK; @@ -479,16 +472,37 @@ void lcd_set_colour_cmd(char cmd, uint16_t data) } } + +static uint8_t *attr_mem_fg=NULL; +static uint8_t *attr_mem_bg=NULL; + +static uint16_t trans_colour(uint8_t rgb_colour ){ + uint16_t v=0; + if(rgb_colour&VIDATTR_RED) v|= RED; + if(rgb_colour&VIDATTR_GREEN) v|= GREEN; + if(rgb_colour&VIDATTR_BLUE) v|= BLUE; + return v; +} + + +static int32_t zx_attr_hash[30]; // attribute hash per line + static bool zx_calc_lines(uint16_t *dest, int line, int frame, int linect) { bool update=false; - uint16_t val=0; + uint16_t fg,bg,val=0; uint32_t m; int ix; /* first check if we need an update */ if(init_cnt) {update=true; init_cnt--; } for (int y=line; y white + if(pattern==0xff) dark_cnt++; + } else if ( flags&VBITMAP_IS_INV_CHAR_L2 ){ + fg|=0xc; // inverse text -> yellow + dark_cnt++; + } else if ( flags&VBITMAP_IS_GREY_CHAR_L6 ){ + fg|=0x6; // chequered-> cyan + }else{ + fg|=0x4; /* some normal text */ + } + }else{ + /* upper part just empty */ + pattern=pix_mem8[ ( (24+6+y*8)*40 +4+x)^3 ]; + if(pattern==0){ + empty_cnt++; + fg|=0x4; /* empty or some normal text */ + } else { + flags=vbitm_flags[pattern]; + if ( flags&VBITMAP_IS_BLOCK_CHAR_L6 ){ + fg|=0xe; // blocks-> white + if(pattern==0xff) dark_cnt++; + } else if ( flags&VBITMAP_IS_GREY_CHAR_L6 ){ + fg|=0x6; // chequered-> cyan + }else{ + fg|=0x4; /* empty or some normal text */ + } + } + } + if(!vmode_nochars) curr_attr[(3+y)*40+4+x] = fg; // green on black + } + } + + avg_empty_cnt= (7*avg_empty_cnt + empty_cnt) /8; + avg_invalid_cnt= (7*avg_invalid_cnt + invalid_cnt) /8; + avg_dark_cnt= (7*avg_dark_cnt + dark_cnt) /8; + + if(switchmode_holdoff) { + switchmode_holdoff--; + } else { + if(vmode_nochars){ + // check if screen looks like regular characters again + if(avg_invalid_cnt<2 || avg_invalid_cnt*16 < (768-avg_empty_cnt) ){ + vmode_nochars=false; + switchmode_holdoff=50; + } + } else { + // check if screen looks like HRG or invalid + if(avg_invalid_cnt > 4 && avg_invalid_cnt*8 > (768-avg_empty_cnt) ){ + vmode_nochars=true; + switchmode_holdoff=50; + // remove colour info + for(int i=0;i<30*40;i++){ + attr_mem_fg[i] = HSYNC_MASK|VSYNC_MASK | (WHITE_MASK) ; // white on black + attr_mem_bg[i] = HSYNC_MASK|VSYNC_MASK ; // all-black + } + } + } + + if(vmode_dark){ + // check if screen looks normal/bright + if(avg_dark_cnt<284){ + vmode_dark=false; + switchmode_holdoff=50; + // remove colour info + for(int i=0;i<30*40;i++){ + attr_mem_fg[i] = HSYNC_MASK|VSYNC_MASK | (WHITE_MASK) ; // white on black + attr_mem_bg[i] = HSYNC_MASK|VSYNC_MASK ; // all-black + } + } + } else { + // check if screen looks dark by inverse + if(avg_dark_cnt > 484){ + vmode_dark=true; + switchmode_holdoff=50; + for(int i=0;i<30*40;i++){ + attr_mem_fg[i] = HSYNC_MASK|VSYNC_MASK ; // white on black + attr_mem_bg[i] = HSYNC_MASK|VSYNC_MASK | (WHITE_MASK) ; + } + } + } + } + +} + + + -static void create_fancy_colours() +static void create_fancy_colours_conventional() { uint8_t * pix_mem8=(uint8_t *)vid_pixel_mem; for(int y=0;y<24;y++){ @@ -524,6 +651,7 @@ static void vga_task(void*arg) esp_err_t ret; ESP_LOGI(TAG, "VGA-Alloc ..."); + vidattr_get_mem(&attr_mem_fg, &attr_mem_bg); alloc_scrbuf(); init_stream(); play_stream(12600000,dma_ll); @@ -533,9 +661,11 @@ static void vga_task(void*arg) //ESP_LOGI(TAG, "hostspi_task ..."); vTaskDelay(1); // allow some startup and settling time (might help, not proven) frames++; - create_fancy_colours(); + //create_fancy_colours(); if(frames%1000==10){ ESP_LOGI(TAG, "VGA intcnt %d %d, duration %d us (%d%%)",int_c_cnt,int_fr_cnt,isr_time_us, isr_time_us/SCR_CHUNK_LINES*100/32 ); + ESP_LOGI(TAG, "Avg - empty %d, dark %d, invalid %d",avg_empty_cnt,avg_dark_cnt,avg_invalid_cnt ); + ESP_LOGI(TAG, "Vmode dark %d, nochar %d",vmode_dark?1:0,vmode_nochars?1:0); } } } diff --git a/main/vga_display.h b/main/vga_display.h index 514b9b1..046d047 100644 --- a/main/vga_display.h +++ b/main/vga_display.h @@ -6,7 +6,6 @@ #include "esp_err.h" #include #include "esp_attr.h" -#include "freertos/queue.h" // call once at startup diff --git a/main/video_attr.c b/main/video_attr.c new file mode 100644 index 0000000..7b94e37 --- /dev/null +++ b/main/video_attr.c @@ -0,0 +1,276 @@ + +#include +#include +#include "esp_log.h" +#include "esp_system.h" +#include "esp_timer.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "iis_videosig.h" +#include "video_attr.h" + +static const char *TAG="vid_attr"; + +uint8_t* attr_mem_fg=NULL; // swapped words (ix^2) +uint8_t* attr_mem_bg=NULL; // swapped words (ix^2) + + +#define VBITMAP_IS_VALID_CHAR_L2 0x01 +#define VBITMAP_IS_BLOCK_CHAR_L2 0x04 +#define VBITMAP_IS_GREY_CHAR_L2 0x08 +#define VBITMAP_IS_INV_CHAR_L2 0x02 +#define VBITMAP_IS_BLOCK_CHAR_L6 0x40 +#define VBITMAP_IS_GREY_CHAR_L6 0x80 + +/* For the standard ZX81 charset, every possible bit map pattern (when looking at line 2 or 6 of the character) maps to flags that show if this is a valid char, and possible also if it is graphics etc */ + + +const static uint8_t vbitm_flags[256]={0x01,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x45,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x89,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x89,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x45,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x45}; + +static int avg_empty_cnt=0; +static int avg_invalid_cnt=0; +static int avg_dark_cnt=0; + +static bool vmode_nofancy=false; +static bool vmode_dark=false; + + + + +uint8_t preferred_fg=VIDATTR_HSYNC_MASK|VIDATTR_VSYNC_MASK|VIDATTR_GREEN; +uint8_t preferred_bg=VIDATTR_HSYNC_MASK|VIDATTR_VSYNC_MASK|VIDATTR_BLACK; + + +static bool inv_setting=false; +static bool fancymode_setting=true; +static char colour_setting='G'; +static bool applied_invert=false; + + + +static void fill_attr_mem(){ + uint8_t fg=preferred_fg; + uint8_t bg=preferred_bg; + if(vmode_nofancy){ + if(!applied_invert){ + fg=preferred_bg; + bg=preferred_fg; + } + } else { /* fancy mode */ + if(applied_invert){ + fg=VIDATTR_HSYNC_MASK|VIDATTR_VSYNC_MASK|VIDATTR_BLACK; + bg=VIDATTR_HSYNC_MASK|VIDATTR_VSYNC_MASK|VIDATTR_WHITE; // written + } else { + fg=VIDATTR_HSYNC_MASK|VIDATTR_VSYNC_MASK|VIDATTR_WHITE; // written + bg=VIDATTR_HSYNC_MASK|VIDATTR_VSYNC_MASK|VIDATTR_BLACK; + } + } + for(int i=0;i<30*40;i++){ + attr_mem_fg[i] = fg ; + attr_mem_bg[i] = bg ; + } + +} + + + +/* return pointers to the 40x30 fields attribute memory */ +void vidattr_get_mem(uint8_t** fg_mem, uint8_t** bg_mem){ + if(!attr_mem_fg) attr_mem_fg=malloc(VIDATTR_ATTR_SCR_SIZE); + if(!attr_mem_bg) attr_mem_bg=malloc(VIDATTR_ATTR_SCR_SIZE); + if(fg_mem) *fg_mem=attr_mem_fg; + if(bg_mem) *bg_mem=attr_mem_bg; +} + +/* */ +void vidattr_set_c_mode(char newcolour){ + if(colour_setting!=newcolour){ + colour_setting=newcolour; + fancymode_setting=false; // default, may be corrected later + preferred_bg=VIDATTR_HSYNC_MASK|VIDATTR_VSYNC_MASK|VIDATTR_BLACK; + applied_invert=inv_setting; + switch(newcolour){ + case 'G': + preferred_fg=VIDATTR_HSYNC_MASK|VIDATTR_VSYNC_MASK|VIDATTR_GREEN; + vmode_nofancy=true; + break; + case 'B': + preferred_fg=VIDATTR_HSYNC_MASK|VIDATTR_VSYNC_MASK|VIDATTR_WHITE; + preferred_bg=VIDATTR_HSYNC_MASK|VIDATTR_VSYNC_MASK|VIDATTR_BLUE; + vmode_nofancy=true; + break; + case 'Y': + case 'A': + preferred_fg=VIDATTR_HSYNC_MASK|VIDATTR_VSYNC_MASK|VIDATTR_YELLOW; + vmode_nofancy=true; + break; + case 'W': + preferred_fg=VIDATTR_HSYNC_MASK|VIDATTR_VSYNC_MASK|VIDATTR_WHITE; + vmode_nofancy=true; + break; + case 'F': + fancymode_setting=true; + vmode_nofancy=false; + vmode_dark=inv_setting; + break; + default: // no change + break; + } + fill_attr_mem(); + } +} + + +void vidattr_set_inv_mode(bool invert){ + if(invert!=inv_setting){ + inv_setting=invert; + if(!vmode_nofancy) + applied_invert = (inv_setting!=vmode_dark); + else + applied_invert=inv_setting; + fill_attr_mem(); + } +} + + + + + + +static void create_fancy_colours() +{ + uint8_t * pix_mem8=(uint8_t *)vid_pixel_mem; + int empty_cnt=0; + int invalid_cnt=0; + int dark_cnt=0; + static uint8_t switchmode_holdoff=2; + //uint8_t *curr_attr= attr_mem_fg; + bool v= applied_invert; + uint8_t *curr_attr= applied_invert ? attr_mem_bg:attr_mem_fg; + uint8_t TEXT_COL = v ? VIDATTR_WHITE : VIDATTR_GREEN; + uint8_t ITEXT_COL = v ? VIDATTR_GREEN : VIDATTR_YELLOW; + uint8_t BLOCK_COL = v ? VIDATTR_WHITE : VIDATTR_WHITE; + uint8_t GREY_COL = v ? VIDATTR_RED : VIDATTR_CYAN; + for(int y=0;y<24;y++){ + /* check for overscan */ + if(pix_mem8[ ( (24+2+y*8)*40 + 3)^3 ]) invalid_cnt+=4; + /* now regular area */ + for(int x=0;x<32;x++){ + uint8_t pattern=pix_mem8[ ( (24+2+y*8)*40 +4+x)^3 ]; + uint8_t fg=VIDATTR_HSYNC_MASK|VIDATTR_VSYNC_MASK; + uint8_t flags; + if(pattern!=0){ + /* upper part not empty */ + flags=vbitm_flags[pattern]; + if(0 == (flags&VBITMAP_IS_VALID_CHAR_L2) ){ + fg|=TEXT_COL; /* empty or normal text */ + invalid_cnt++; /* maybe high-res or so, nothing that fancy colours work with.. */ + } else if ( flags&VBITMAP_IS_BLOCK_CHAR_L2 ){ + fg|=BLOCK_COL; // blocks-> white + if(pattern==0xff) dark_cnt++; + } else if ( flags&VBITMAP_IS_INV_CHAR_L2 ){ + fg|=ITEXT_COL; // inverse text -> yellow + dark_cnt++; + } else if ( flags&VBITMAP_IS_GREY_CHAR_L6 ){ + fg|=GREY_COL; // chequered-> cyan + }else{ + fg|=TEXT_COL; /* some normal text */ + } + }else{ + /* upper part just empty */ + pattern=pix_mem8[ ( (24+6+y*8)*40 +4+x)^3 ]; + if(pattern==0){ + empty_cnt++; + fg|=TEXT_COL; /* empty or some normal text */ + } else { + flags=vbitm_flags[pattern]; + if ( flags&VBITMAP_IS_BLOCK_CHAR_L6 ){ + fg|=BLOCK_COL; // blocks-> white + if(pattern==0xff) dark_cnt++; + } else if ( flags&VBITMAP_IS_GREY_CHAR_L6 ){ + fg|=GREY_COL; // chequered-> cyan + }else{ + fg|=TEXT_COL; /* empty or some normal text */ + } + } + } + if(!vmode_nofancy) curr_attr[(3+y)*40+4+x] = fg; // green on black + } + } + + avg_empty_cnt= (7*avg_empty_cnt + empty_cnt) /8; + avg_invalid_cnt= (7*avg_invalid_cnt + invalid_cnt) /8; + avg_dark_cnt= (7*avg_dark_cnt + dark_cnt) /8; + + if(switchmode_holdoff) { + switchmode_holdoff--; + } else { + if(vmode_nofancy){ + // check if screen looks like regular characters again + if( ( avg_invalid_cnt<2 || avg_invalid_cnt*16 < (768-avg_empty_cnt) ) && vid_is_synced() ){ + vmode_nofancy=false; + switchmode_holdoff=50; + fill_attr_mem(); + } + } else { + // check if screen looks like HRG or invalid + if(avg_invalid_cnt > 4 && ( avg_invalid_cnt*8 > (768-avg_empty_cnt) || !vid_is_synced() ) ){ + vmode_nofancy=true; + switchmode_holdoff=50; + // remove colour info + fill_attr_mem(); + } + } + + if(vmode_dark){ + // check if screen looks normal/bright + if(avg_dark_cnt<284){ + vmode_dark=false; + switchmode_holdoff=50; + // remove colour info + applied_invert = (inv_setting!=vmode_dark); + fill_attr_mem(); + } + } else { + // check if screen looks dark by inverse + if(avg_dark_cnt > 484){ + vmode_dark=true; + switchmode_holdoff=50; + applied_invert = (inv_setting!=vmode_dark); + fill_attr_mem(); + } + } + } + +} + + + +static void vid_attr_task(void*arg) +{ + int frames=0; + while(1){ + vTaskDelay(2); + frames++; + if(fancymode_setting){ + create_fancy_colours(); + if(frames%1000==10){ + ESP_LOGI(TAG, "Avg - empty %d, dark %d, invalid %d",avg_empty_cnt,avg_dark_cnt,avg_invalid_cnt ); + ESP_LOGI(TAG, "Vmode Dark %d, nochar %d",vmode_dark?1:0,vmode_nofancy?1:0); + } + } + } +} + + +/* Function to initialize Wi-Fi at station */ +void video_attr_init() +{ + vidattr_get_mem(0,0); // allocate attribute mem + xTaskCreate(vid_attr_task, "vid_attr_task", 1024 * 2, NULL, 2, NULL); +} + + + diff --git a/main/video_attr.h b/main/video_attr.h new file mode 100644 index 0000000..f207280 --- /dev/null +++ b/main/video_attr.h @@ -0,0 +1,39 @@ + + +#ifndef _VIDEO_ATTR_H_ +#define _VIDEO_ATTR_H_ + +#include "esp_err.h" +#include + + +#define VIDATTR_HSYNC_MASK 0x40 +#define VIDATTR_VSYNC_MASK 0x80 +#define VIDATTR_RED 0x08 +#define VIDATTR_GREEN 0x04 +#define VIDATTR_BLUE 0x02 +#define VIDATTR_WHITE 0x0e +#define VIDATTR_CYAN 0x06 +#define VIDATTR_MAGENTA 0x0a +#define VIDATTR_YELLOW 0x0c +#define VIDATTR_BLACK 0x00 + + +#define VIDATTR_ATTR_SCR_SIZE (40*30) + + +/* return pointers to the 40x30 fields attribute memory */ +void vidattr_get_mem(uint8_t** fg_mem, uint8_t** bg_mem); + +/* */ +void vidattr_set_c_mode(char colour); // GYWF +void vidattr_set_inv_mode(bool invert); + + +// call once at startup +void video_attr_init(); + + + + +#endif /* _VIDEO_ATTR_H_ */ diff --git a/main/zx_serv_dialog.c b/main/zx_serv_dialog.c index c85ad6d..a22c1e3 100644 --- a/main/zx_serv_dialog.c +++ b/main/zx_serv_dialog.c @@ -27,7 +27,8 @@ Works asynchroously, thus communication is done via queues #include "zx_file_img.h" #include "signal_to_zx.h" #include "iis_videosig.h" -#include "lcd_display.h" +#include "lcd_display.h"//TODO +#include "video_attr.h" #include "zx_server.h" #include "wifi_sta.h" #include "zx_serv_dialog.h" @@ -370,12 +371,19 @@ static bool zxsrv_videooptions(const char *name, int command){ case 'C': vid_cal_pixel_start(); break; - case 'I': /* all fall threough */ - case 'N': case 'W': case 'G': case 'A': - lcd_set_colour_cmd(command,0); + case 'F': + case 'B': + vidattr_set_c_mode(command); + //lcd_set_colour_cmd(command,0); + break; + case 'I': + vidattr_set_inv_mode(true); + break; + case 'N': + vidattr_set_inv_mode(false); break; default: break; @@ -392,13 +400,13 @@ static bool zxsrv_videooptions(const char *name, int command){ sprintf(txt_buf,"###[ ZX-WESPI VIDEO SETTINGS ]###"); zxfimg_print_video(4,txt_buf); - sprintf(txt_buf," [C] CALIBRATE PIXEL PHASE"); + sprintf(txt_buf," [C]ALIBRATE PIXEL PHASE"); zxfimg_print_video(7,txt_buf); sprintf(txt_buf," [I]NVERSE OR [N]ORMAL"); zxfimg_print_video(10,txt_buf); - sprintf(txt_buf," [W]HITE, [G]REEN, OR [A]MBER"); + sprintf(txt_buf," [W]HITE, [G]REEN, [A]MBER, or [F]ANCY"); zxfimg_print_video(12,txt_buf); /* the last four lines need to be excactly this way as the patterns are used by the calibration */ @@ -424,6 +432,8 @@ static bool zxsrv_videooptions(const char *name, int command){ create_mrespond_entry(60, zxsrv_videooptions, "VID-WH", 'W' ); // "W" create_mrespond_entry(44, zxsrv_videooptions, "VID-GR", 'G' ); // "G" create_mrespond_entry(38, zxsrv_videooptions, "VID-AM", 'A' ); // "A" + create_mrespond_entry(39, zxsrv_videooptions, "VID-BU", 'B' ); // "B" + create_mrespond_entry(43, zxsrv_videooptions, "VID-FA", 'F' ); // "F" /* append default entry */ create_mrespond_entry(0, zxsrv_respond_filemenu, "/spiffs/", 0 ); diff --git a/tools/analyze_zx_charset.py b/tools/analyze_zx_charset.py new file mode 100644 index 0000000..d5ce817 --- /dev/null +++ b/tools/analyze_zx_charset.py @@ -0,0 +1,58 @@ + +import os +""" +#define VBITMAP_IS_VALID_CHAR_L2 0x01 +#define VBITMAP_IS_BLOCK_CHAR_L2 0x04 +#define VBITMAP_IS_GREY_CHAR_L2 0x08 +#define VBITMAP_IS_INV_CHAR_L2 0x02 +#define VBITMAP_IS_BLOCK_CHAR_L6 0x40 +#define VBITMAP_IS_GREY_CHAR_L6 0x80 + +""" +os.system("dir") +if __name__ == "__main__": + rom=open("tools/zx81rom.bin",'rb').read() + charset=rom[-512:] + #print(charset) + out_flags=[0]*256 # map bitmap to character flags + hshmap={} + for inv in (0,0xff): + for ch in range(64): + m2=charset[8*ch+2]^inv + m6=charset[8*ch+6]^inv + if inv: ch+=128 + f=0 + out_flags[m2] |= 0x01 # valid char + if m2 in (0x0f,0xf0,0xff): out_flags[m2] |= 0x04 # block grapics + elif m2 in (0xaa,0x55): out_flags[m2] |= 0x08 # chequered graphs + elif (m2&0x81) == 0x81: out_flags[m2] |= 0x02 # inverse letter + + if m6 in (0x0f,0xf0,0xff): out_flags[m6] |= 0x40 # block grapics in lower part + elif m6 in (0xaa,0x55): out_flags[m6] |= 0x80 # chequered graphs in lower part + + + #hsh=(charset[8*ch+1]^inv) * 16 + (charset[8*ch+4]^inv)*4 + (charset[8*ch+5]^inv) * 2 + (charset[8*ch+6]^inv) + #if inv: ch+=128 + #if hsh in hshmap: + # hshmap[hsh].append(ch) + #else: + # hshmap[hsh]=[ch] + print(len(hshmap)) + #print(hshmap) + print(out_flags) + + print(\ +""" +#define VBITMAP_IS_VALID_CHAR_L2 0x01 +#define VBITMAP_IS_BLOCK_CHAR_L2 0x04 +#define VBITMAP_IS_GREY_CHAR_L2 0x08 +#define VBITMAP_IS_INV_CHAR_L2 0x02 +#define VBITMAP_IS_BLOCK_CHAR_L6 0x40 +#define VBITMAP_IS_GREY_CHAR_L6 0x80 + +/* For the standard ZX81 charset, every possible bit map pattern (when looking at line 2 or 6 of the character) maps to flags that show if this is a valid char, and possible also if it is graphics etc */ +""" + ) + print(f"const uint8_t vbitm_flags[256]={','.join('0x%02X'%v for v in out_flags)}; \n\n") + print(f"Valid patterns: {len(out_flags)-out_flags.count(0)} of 256 possible patterns ") + diff --git a/tools/zx81rom.bin b/tools/zx81rom.bin new file mode 100644 index 0000000..557ddcb Binary files /dev/null and b/tools/zx81rom.bin differ