// http://en.wikipedia.org/wiki/VT52 // http://vt100.net/docs/vt520-rm/ // man 4 console_codes #include #include #include #include "vesa.h" #define NPAR_START 1000 #define TERM_WIDTH 80 #define TERM_HEIGHT 24 // COLORS #define SCR_BLACK 0x0 #define SCR_BLUE 0x1 #define SCR_GREEN 0x2 #define SCR_CYAN 0x3 #define SCR_RED 0x4 #define SCR_MAGENTA 0x5 #define SCR_BROWN 0x6 #define SCR_GRAY_LIGHT 0x7 #define SCR_GRAY_DARK 0x8 #define SCR_BLUE_LIGHT 0x9 #define SCR_GREEN_LIGHT 0xa #define SCR_CYAN_LIGHT 0xb #define SCR_RED_LIGHT 0xc #define SCR_MAGENTA_LIGHT 0xd #define SCR_YELLOW 0xe #define SCR_WHITE 0xf // typedef enum{ ecma48_reset, ecma48_bold, ecma48_halfbright, ecma48_underscore, ecma48_blink, ecma48_reverse_video, ecma48_reset_mapping, ecma48_null_mapping_1, ecma48_null_mapping_2, ecma48_normalbright_1, ecma48_normalbright_2, ecma48_nounderline, ecma48_blinkoff, ecma48_reverse_video_off, ecma48_fg_black =30, ecma48_fg_red, ecma48_fg_green, ecma48_fg_brown, ecma48_fg_blue, ecma48_fg_magenta, ecma48_fg_cyan, ecma48_fg_white, ecma48_underscore_on, // set def color ecma48_underscore_off, // set def color ecma48_bg_black, //40 ecma48_bg_red, ecma48_bg_green, ecma48_bg_brown, ecma48_bg_blue, ecma48_bg_magenta, ecma48_bg_cyan, ecma48_bg_white, ecma48_bg_default =49, }terminal_settings; typedef struct term_out_struct { void (*put_char)(uint8_t c,uint8_t color_fg, uint8_t color_bg, uint32_t x, uint32_t y); void (*update_cursor)(uint32_t col,uint32_t row); }term_out; typedef struct term_in_struct { void (*put_char)(uint8_t c); }term_in; typedef struct terminal_tty_struct { uint8_t fg; uint8_t bg; bool set_buff; bool set_lfnl; bool set_echo; uint32_t width; uint32_t height; uint32_t x; uint32_t y; uint32_t *data; // screen data uint8_t *command; // command line / also holds npar for escape sequences somewhere int32_t command_l; // command line length uint8_t escaping; // escaping mode? uint8_t npar; // npar pos term_out *screen; term_in *input; bool reverse_video; }terminal_tty; static uint32_t index(terminal_tty *tty, uint32_t x, uint32_t y) { return tty->width*y+x; } static void set_char(terminal_tty *tty, uint32_t x, uint32_t y, uint8_t c, uint8_t fg, uint8_t bg) { tty->data[index(tty,x,y)]=c; tty->data[index(tty,x,y)]|=fg<<8; tty->data[index(tty,x,y)]|=bg<<16; tty->screen->put_char(c,fg,bg,x,y); } static void process_graphic_npar(terminal_tty *tty, terminal_settings s) { uint8_t buf; switch(s) { case ecma48_null_mapping_2: break; case ecma48_null_mapping_1: break; case ecma48_reset_mapping: break; case ecma48_blinkoff: break; case ecma48_blink: break; case ecma48_nounderline: break; case ecma48_underscore_on: break; case ecma48_underscore_off: break; case ecma48_underscore: break; case ecma48_halfbright: break; case ecma48_normalbright_1: break; case ecma48_normalbright_2: break; case ecma48_bold: break; case ecma48_reset: tty->fg=SCR_WHITE; tty->bg=SCR_BLACK; break; case ecma48_reverse_video: if(tty->reverse_video)break; buf=tty->fg; tty->fg=tty->bg; tty->bg=buf; break; case ecma48_reverse_video_off: if(!tty->reverse_video)break; buf=tty->fg; tty->fg=tty->bg; tty->bg=buf; break; //fg case ecma48_fg_black: tty->fg=SCR_BLACK; break; case ecma48_fg_red: tty->fg=SCR_RED; break; case ecma48_fg_green: tty->fg=SCR_GREEN; break; case ecma48_fg_brown: tty->fg=SCR_BROWN; break; case ecma48_fg_blue: tty->fg=SCR_BLUE; break; case ecma48_fg_magenta: tty->fg=SCR_MAGENTA; break; case ecma48_fg_cyan: tty->fg=SCR_CYAN; break; case ecma48_fg_white: tty->fg=SCR_WHITE; break; // bg case ecma48_bg_black: tty->bg=SCR_BLACK; break; case ecma48_bg_red: tty->bg=SCR_RED; break; case ecma48_bg_green: tty->bg=SCR_GREEN; break; case ecma48_bg_brown: tty->bg=SCR_BROWN; break; case ecma48_bg_blue: tty->bg=SCR_BLUE; break; case ecma48_bg_magenta: tty->bg=SCR_MAGENTA; break; case ecma48_bg_cyan: tty->bg=SCR_CYAN; break; case ecma48_bg_white: tty->bg=SCR_WHITE; break; case ecma48_bg_default: tty->bg=SCR_BLACK; break; } } static void process_graphic_npars(terminal_tty *tty) { char buf[4]; int b=0; tty->escaping=0; for(int i=0;inpar+1;i++) { char c=tty->command[NPAR_START+i]; buf[b++]=c; if(i==tty->npar||c==';') { if(b==2)process_graphic_npar(tty,buf[0]-'0'); if(b==3)process_graphic_npar(tty,(buf[0]-'0')*10+(buf[1]-'0')); b=0; } //terminal_put(tty,tty->command[NPAR_START+i]); } tty->npar=0; } static void reset(terminal_tty *tty) { tty->bg=SCR_BLACK; tty->fg=SCR_WHITE; for(int x=0;xwidth;x++) for(int y=0;yheight;y++) { set_char(tty,x,y,' ',tty->fg,tty->bg); } tty->x=0; tty->y=0; tty->command_l=0; } terminal_tty terminal_init(term_out *screen,term_in *input) { terminal_tty tty; tty.data=malloc(2*4096); tty.set_buff=true; // tty.set_buff=false; tty.set_lfnl=true; // tty.set_lfnl=false; tty.set_echo=true; tty.set_echo=false; tty.command=malloc(1*4096); tty.command_l=0; tty.escaping=0; tty.screen=screen; tty.input=input; tty.x=0; tty.y=0; tty.width=TERM_WIDTH; tty.height=TERM_HEIGHT; tty.reverse_video=false; reset(&tty); return tty; } term_out tout; terminal_tty tty; terminal_tty* terminal_init_vesa() { tout.put_char=vesa_console_put_char; tout.update_cursor=vesa_update_cursor; tty=(terminal_init(&tout,NULL)); return &tty; } // send one ASCII character to the terminal void terminal_put(terminal_tty *tty, uint8_t c) { // CONTROL CHARACTERS if(c==0x07){return;} // BEL (ignore) if(c==0x7F){return;} // nothing if(c==0x09) // TAB { if(tty->x==tty->width-1)return; set_char(tty,tty->x,tty->y,' ',tty->fg,tty->bg); tty->x++; while(tty->xwidth-1&&tty->x%8!=0) { set_char(tty,tty->x,tty->y,' ',tty->fg,tty->bg); tty->x++; } tty->x--; c=' '; } if(c==0x0E){return;} // G1 set (ignore) if(c==0x0F){return;} // G0 set (ignore) if(c==0x18||c==0x1A){tty->escaping=0;return;} // cancel escaping if(c==0x1B){tty->escaping=1;return;} // ESC if(c==0x9B){tty->escaping=2;tty->npar=0;return;} // CSI = ESC [ if(c==0x08) //BACKSPACE { if((tty->x>0&&tty->command_l>0)||!tty->set_echo) { set_char(tty,tty->x-1,tty->y,' ',tty->fg,tty->bg); tty->x--; } } else if(c==0x0D) // CR { tty->x=0; return; } else if(c==0x0A||c==0x0B||c==0x0C) // \n NEXTLINE { for(uint32_t x=tty->x;xwidth;x++) { set_char(tty,x,tty->y,' ',tty->fg,tty->bg); } tty->y++; if(tty->set_lfnl)tty->x=0; } else // { // ESC- but not CSI-sequences if(tty->escaping==1) { // FOOL-TERM: delchar if(c=='x') { set_char(tty, tty->x, tty->y, ' ', tty->fg, tty->bg); } // FOOL-TERM: clear to end of line if(c=='K') { for(uint32_t x=tty->x;xwidth;x++) { set_char(tty, x, tty->y, ' ', tty->fg, tty->bg); } } // FOOL-TERM: clear to end of screen if(c=='J') { for(uint32_t x=tty->x;xwidth;x++) { set_char(tty, x, tty->y, ' ', tty->fg, tty->bg); } for(uint32_t y=tty->y+1;yheight;y++) { for(uint32_t x=0;xwidth;x++) { set_char(tty,x, y, ' ', tty->fg, tty->bg); } } } // FOOL-TERM: home if(c=='H') { tty->x=0; tty->y=0; } // FOOL-TERM: movement if(c=='u') { if(tty->y>0) tty->y--; } if(c=='d') { if(tty->y+1height) tty->y++; } if(c=='f') { if(tty->x+1width) tty->x++; } if(c=='b') { if(tty->x>0) tty->x--; } /// if(c=='c'){reset(tty);} // RESET if(c=='D'){tty->y++;} // LINEFEED if(c=='E'){tty->y++;tty->x=0;} //NEWLINE //if(c=='H'){} // SET TABSTOP: TODO if(c=='M'){ // REVERSE LINEFEED if(tty->y==0) { for(int32_t y=tty->height-1;y>=0;y--) { for(uint32_t x=0;xwidth;x++) { uint32_t c=' '|tty->fg<<8|tty->bg<<16; if(y!=0) c=tty->data[index(tty,x,y-1)]; tty->data[index(tty,x,y)] = c; tty->screen->put_char(c&0xff,(c>>8)&0xff,c>>16,x,y); } } } else tty->y--; } if(c=='Z'){// IDENTIFY: claim we are vt102 if(tty->input!=NULL) { tty->input->put_char(0x1B); //ESC tty->input->put_char('['); tty->input->put_char('?'); tty->input->put_char('6'); tty->input->put_char('c'); } return;} if(c=='7'){}// SAVE STATE (coord,attributes,G0/G1?) if(c=='8'){}// RESTORE STATE if(c=='['){tty->escaping=2;tty->npar=0;return;} // CONTROL SEQ INTRODUCER // TODO // %, %@, %G, %8, #8, (, (B, (O, (U, (K // ), >, =, ] (???) // // tty->escaping=0; // DONE } // CSI SEQEUENCES (AFTER ESC [..) else if(tty->escaping==2) { //ECMA-48: TODO //ECMA-48 GRAPHIC RENDITION: OK if(c!='m') { tty->command[NPAR_START+(tty->npar++)]=c; } else { process_graphic_npars(tty); } //ECMA-48 MODE SWITCHES: TODO //ECMA-48 STATUS REPORT: TODO //DEC PRIVATE MODE: TODO //LINUX CONSOLE PRIVATE CSI: TODO // // ESC [ 1 ; n ] Set color n as the underline color // ESC [ 2 ; n ] Set color n as the dim color // ESC [ 8 ] Make the current color pair the default attributes. // ESC [ 9 ; n ] Set screen blank timeout to n minutes. // ESC [ 10 ; n ] Set bell frequency in Hz. // ESC [ 11 ; n ] Set bell duration in msec. // ESC [ 12 ; n ] Bring specified console to the front. // ESC [ 13 ] Unblank the screen. // ESC [ 14 ; n ] Set the VESA powerdown interval in minutes. } else //PROBABLY (AND HOPEFULLY) JUST A NORMAL CHAR { set_char(tty,tty->x,tty->y,c,tty->fg,tty->bg); tty->x++; } } //autobreak if(tty->x>=tty->width) { tty->x=0; tty->y++; } //autoscroll if(tty->y>=tty->height) { tty->y--; for(uint32_t y=0;yheight;y++) { for(uint32_t x=0;xwidth;x++) { uint32_t c=tty->data[index(tty,x,y+1)]; if(y==tty->height-1)c=' '|tty->fg<<8|tty->bg<<16; tty->data[index(tty,x,y)] = c; tty->screen->put_char(c&0xff,(c>>8)&0xff,c>>16,x,y); } } } //cusor pos tty->screen->update_cursor(tty->x,tty->y); return; }