DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dev_con.h
00001 /*
00002  *  Copyright (C) 2002-2015  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 #include "dos_inc.h"
00021 #include "../ints/int10.h"
00022 #include <string.h>
00023 #include "inout.h"
00024 #include "shiftjis.h"
00025 #include "callback.h"
00026 
00027 #define NUMBER_ANSI_DATA 10
00028 
00029 extern bool DOS_BreakFlag;
00030 
00031 Bitu INT10_Handler(void);
00032 Bitu INT16_Handler_Wrap(void);
00033 
00034 ShiftJISDecoder con_sjis;
00035 
00036 Bit16u last_int16_code = 0;
00037 
00038 static size_t dev_con_pos=0,dev_con_max=0;
00039 static char dev_con_readbuf[64];
00040 
00041 Bit8u DefaultANSIAttr() {
00042         return IS_PC98_ARCH ? 0xE1 : 0x07;
00043 }
00044 
00045 class device_CON : public DOS_Device {
00046 public:
00047         device_CON();
00048         bool Read(Bit8u * data,Bit16u * size);
00049         bool Write(const Bit8u * data,Bit16u * size);
00050         bool Seek(Bit32u * pos,Bit32u type);
00051         bool Close();
00052         void ClearAnsi(void);
00053         Bit16u GetInformation(void);
00054         bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { (void)bufptr; (void)size; (void)retcode; return false; }
00055         bool WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { (void)bufptr; (void)size; (void)retcode; return false; }
00056 private:
00057         Bit8u readcache;
00058         Bit8u lastwrite;
00059         struct ansi { /* should create a constructor, which would fill them with the appropriate values */
00060                 bool esc;
00061                 bool sci;
00062         bool pc98rab;       // PC-98 ESC [ > ...    (right angle bracket) I will rename this variable if MS-DOS ANSI.SYS also supports this sequence
00063                 bool enabled;
00064                 Bit8u attr;         // machine-specific
00065                 Bit8u data[NUMBER_ANSI_DATA];
00066                 Bit8u numberofarg;
00067                 Bit16u nrows;
00068                 Bit16u ncols;
00069                 Bit8u savecol;
00070                 Bit8u saverow;
00071                 bool warned;
00072 
00073                 void Disable() {
00074             if (!IS_PC98_ARCH)
00075                 enabled = false;
00076 
00077                         attr = DefaultANSIAttr();
00078                 }
00079         } ansi;
00080 
00081         static void Real_INT10_SetCursorPos(Bit8u row,Bit8u col,Bit8u page) {
00082                 Bit16u          oldax,oldbx,olddx;
00083 
00084                 oldax=reg_ax;
00085                 oldbx=reg_bx;
00086                 olddx=reg_dx;
00087 
00088                 reg_ah=0x2;
00089                 reg_dh=row;
00090                 reg_dl=col;
00091                 reg_bh=page;
00092 
00093         /* FIXME: PC-98 emulation should eventually use CONIO emulation that
00094          *        better emulates the actual platform. The purpose of this
00095          *        hack is to allow our code to call into INT 10h without
00096          *        setting up an INT 10h vector */
00097         if (IS_PC98_ARCH)
00098             INT10_Handler();
00099         else
00100             CALLBACK_RunRealInt(0x10);
00101 
00102                 reg_ax=oldax;
00103                 reg_bx=oldbx;
00104                 reg_dx=olddx;
00105         }
00106 
00107     /* Common function to turn specific scan codes into ANSI codes.
00108      * This is a separate function so that both Read() and GetInformation() can use it.
00109      * GetInformation needs to handle the scan code on entry in order to correctly
00110      * assert whether Read() will return data or not. Some scan codes are ignored by
00111      * the CON driver, therefore even though the BIOS says there is key data, Read()
00112      * will not return anything and will block. */
00113     bool CommonPC98ExtScanConversionToReadBuf(unsigned char code) {
00114         switch (code) {
00115             case 0x38: // INS
00116                 dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x50; dev_con_pos=0; dev_con_max=2;
00117                 break;
00118             case 0x39: // DEL
00119                 dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x44; dev_con_pos=0; dev_con_max=2;
00120                 return true;
00121             case 0x3A: // up arrow
00122                 dev_con_readbuf[0] = 0x0B; dev_con_pos=0; dev_con_max=1;
00123                 return true;
00124             case 0x3B: // left arrow
00125                 dev_con_readbuf[0] = 0x08; dev_con_pos=0; dev_con_max=1;
00126                 return true;
00127             case 0x3C: // right arrow
00128                 dev_con_readbuf[0] = 0x0C; dev_con_pos=0; dev_con_max=1;
00129                 return true;
00130             case 0x3D: // down arrow
00131                 dev_con_readbuf[0] = 0x0A; dev_con_pos=0; dev_con_max=1;
00132                 return true;
00133             case 0x62: // F1
00134                 dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x53; dev_con_pos=0; dev_con_max=2;
00135                 return true;
00136             case 0x63: // F2
00137                 dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x54; dev_con_pos=0; dev_con_max=2;
00138                 return true;
00139             case 0x64: // F3
00140                 dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x55; dev_con_pos=0; dev_con_max=2;
00141                 return true;
00142             case 0x65: // F4
00143                 dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x56; dev_con_pos=0; dev_con_max=2;
00144                 return true;
00145             case 0x66: // F5
00146                 dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x57; dev_con_pos=0; dev_con_max=2;
00147                 return true;
00148             case 0x67: // F6
00149                 dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x45; dev_con_pos=0; dev_con_max=2;
00150                 return true;
00151             case 0x68: // F7
00152                 dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x4A; dev_con_pos=0; dev_con_max=2;
00153                 return true;
00154             case 0x69: // F8
00155                 dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x50; dev_con_pos=0; dev_con_max=2;
00156                 return true;
00157             case 0x6A: // F9
00158                 dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x51; dev_con_pos=0; dev_con_max=2;
00159                 return true;
00160             case 0x6B: // F10
00161                 dev_con_readbuf[0] = 0x1B; dev_con_readbuf[1] = 0x5A; dev_con_pos=0; dev_con_max=2;
00162                 return true;
00163 #if 0
00164                 // ROLL UP  --          --          --
00165                 // POLL DOWN--          --          --
00166                 // COPY     --          --          --
00167                 // HOME/CLR 0x1A        0x1E        --
00168                 // HELP     --          --          --
00169 #endif
00170         }
00171 
00172         return false;
00173     }
00174 
00175         static void Real_INT10_TeletypeOutput(Bit8u xChar,Bit8u xAttr) {
00176                 Bit16u          oldax,oldbx;
00177 
00178         if (IS_PC98_ARCH) {
00179             if (con_sjis.take(xChar)) {
00180                 BIOS_NCOLS;
00181                 Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00182                 Bit8u cur_row=CURSOR_POS_ROW(page);
00183                 Bit8u cur_col=CURSOR_POS_COL(page);
00184                 unsigned char cw = con_sjis.doublewide ? 2 : 1;
00185 
00186                 /* FIXME: I'm not sure what NEC's ANSI driver does if a doublewide character is printed at column 79 */
00187                 if ((cur_col+cw) > ncols) {
00188                     cur_col = (Bit8u)ncols;
00189                     AdjustCursorPosition(cur_col,cur_row);
00190                 }
00191 
00192                 /* JIS conversion to WORD value appropriate for text RAM */
00193                 if (con_sjis.b2 != 0) con_sjis.b1 -= 0x20;
00194 
00195                 INT10_WriteChar((con_sjis.b2 << 8) + con_sjis.b1,xAttr,0,1,true);
00196 
00197                 cur_col += cw;
00198                 AdjustCursorPosition(cur_col,cur_row);
00199                 Real_INT10_SetCursorPos(cur_row,cur_col,page);  
00200             }
00201         }
00202         else {
00203             oldax=reg_ax;
00204             oldbx=reg_bx;
00205 
00206             reg_ah=0xE;
00207             reg_al=xChar;
00208             reg_bl=xAttr;
00209 
00210             CALLBACK_RunRealInt(0x10);
00211 
00212             reg_ax=oldax;
00213             reg_bx=oldbx;
00214         }
00215         }
00216 
00217 
00218         static void Real_WriteChar(Bit8u cur_col,Bit8u cur_row,
00219                                         Bit8u page,Bit8u chr,Bit8u attr,Bit8u useattr) {
00220                 //Cursor position
00221                 Real_INT10_SetCursorPos(cur_row,cur_col,page);
00222 
00223                 //Write the character
00224                 Bit16u          oldax,oldbx,oldcx;
00225                 oldax=reg_ax;
00226                 oldbx=reg_bx;
00227                 oldcx=reg_cx;
00228 
00229                 reg_al=chr;
00230                 reg_bl=attr;
00231                 reg_bh=page;
00232                 reg_cx=1;
00233                 if(useattr)
00234                                 reg_ah=0x9;
00235                 else    reg_ah=0x0A;
00236 
00237         /* FIXME: PC-98 emulation should eventually use CONIO emulation that
00238          *        better emulates the actual platform. The purpose of this
00239          *        hack is to allow our code to call into INT 10h without
00240          *        setting up an INT 10h vector */
00241         if (IS_PC98_ARCH)
00242             INT10_Handler();
00243         else
00244             CALLBACK_RunRealInt(0x10);
00245 
00246                 reg_ax=oldax;
00247                 reg_bx=oldbx;
00248                 reg_cx=oldcx;
00249         }//static void Real_WriteChar(cur_col,cur_row,page,chr,attr,useattr)
00250 
00251         
00252         static void AdjustCursorPosition(Bit8u& cur_col,Bit8u& cur_row) {
00253                 BIOS_NCOLS;BIOS_NROWS;
00254                 auto defattr = DefaultANSIAttr();
00255                 //Need a new line?
00256                 if(cur_col==ncols) 
00257                 {
00258                         cur_col=0;
00259                         cur_row++;
00260 
00261             if (!IS_PC98_ARCH)
00262                 Real_INT10_TeletypeOutput('\r',defattr);
00263         }
00264                 
00265                 //Reached the bottom?
00266                 if(cur_row==nrows) 
00267                 {
00268             if (IS_PC98_ARCH)
00269                         INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,defattr,0);
00270             else
00271                 Real_INT10_TeletypeOutput('\n',defattr);        //Scroll up
00272 
00273             cur_row--;
00274                 }
00275         }
00276 
00277 
00278         void Real_INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr) {
00279                 //TODO Check if this page thing is correct
00280                 Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00281 //              BIOS_NCOLS;BIOS_NROWS;
00282                 Bit8u cur_row=CURSOR_POS_ROW(page);
00283                 Bit8u cur_col=CURSOR_POS_COL(page);
00284                 switch (chr) 
00285                 {
00286                 case 7: {
00287                         // set timer (this should not be needed as the timer already is programmed 
00288                         // with those values, but the speaker stays silent without it)
00289                         IO_Write(0x43,0xb6);
00290                         IO_Write(0x42,1320&0xff);
00291                         IO_Write(0x42,1320>>8);
00292                         // enable speaker
00293                         IO_Write(0x61,IO_Read(0x61)|0x3);
00294                         for(Bitu i=0; i < 333; i++) CALLBACK_Idle();
00295                         IO_Write(0x61,IO_Read(0x61)&~0x3);
00296                         break;
00297                 }
00298                 case 8:
00299                         if(cur_col>0)
00300                                 cur_col--;
00301                         break;
00302                 case '\r':
00303                         cur_col=0;
00304                         break;
00305                 case '\n':
00306                         cur_col=0;
00307                         cur_row++;
00308                         break;
00309                 case '\t':
00310                         do {
00311                                 Real_INT10_TeletypeOutputAttr(' ',attr,useattr);
00312                                 cur_row=CURSOR_POS_ROW(page);
00313                                 cur_col=CURSOR_POS_COL(page);
00314                         } while(cur_col%8);
00315                         break;
00316                 default:
00317                         //* Draw the actual Character
00318             if (IS_PC98_ARCH) {
00319                 if (con_sjis.take(chr)) {
00320                     BIOS_NCOLS;
00321                     unsigned char cw = con_sjis.doublewide ? 2 : 1;
00322 
00323                     /* FIXME: I'm not sure what NEC's ANSI driver does if a doublewide character is printed at column 79 */
00324                     if ((cur_col+cw) > ncols) {
00325                         cur_col = (Bit8u)ncols;
00326                         AdjustCursorPosition(cur_col,cur_row);
00327                     }
00328 
00329                     /* JIS conversion to WORD value appropriate for text RAM */
00330                     if (con_sjis.b2 != 0) con_sjis.b1 -= 0x20;
00331 
00332                     INT10_WriteChar((con_sjis.b2 << 8) + con_sjis.b1,attr,0,1,true);
00333 
00334                     cur_col += cw;
00335                 }
00336             }
00337             else {
00338                 Real_WriteChar(cur_col,cur_row,page,chr,attr,useattr);
00339                 cur_col++;
00340             }
00341                 }
00342                 
00343                 AdjustCursorPosition(cur_col,cur_row);
00344                 Real_INT10_SetCursorPos(cur_row,cur_col,page);  
00345         }//void Real_INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr) 
00346 };
00347 
00348 // NEC-PC98 keyboard input notes
00349 //
00350 // on a system with KKCFUNC.SYS, NECAIK1.SYS, NECAIK2.SYS, NECAI.SYS loaded
00351 //
00352 // Key      Normal      Shift       CTRL
00353 // -------------------------------------
00354 // ESC      0x1B        0x1B        0x1B
00355 // TAB      0x09        0x09        0x09
00356 // F1       0x1B 0x53   <shortcut>  --
00357 // F2       0x1B 0x54   <shortcut>  --
00358 // F3       0x1B 0x55   <shortcut>  --
00359 // F4       0x1B 0x56   <shortcut>  Toggles 'g'
00360 // F5       0x1B 0x57   <shortcut>  --
00361 // F6       0x1B 0x45   <shortcut>  Toggle 20/25-line text mode
00362 // F7       0x1B 0x4A   <shortcut>  Toggle function row (C1/CU/etc, shortcuts, or off)
00363 // F8       0x1B 0x50   <shortcut>  Clear screen, home cursor
00364 // F9       0x1B 0x51   <shortcut>  --
00365 // F10      0x1B 0x5A   <shortcut>  --
00366 // INS      0x1B 0x50   0x1B 0x50   0x1B 0x50
00367 // DEL      0x1B 0x44   0x1B 0x44   0x1B 0x44
00368 // ROLL UP  --          --          --
00369 // POLL DOWN--          --          --
00370 // COPY     --          --          --
00371 // HOME/CLR 0x1A        0x1E        --
00372 // HELP     --          --          --
00373 // UP ARROW 0x0B        0x0B        0x0B
00374 // LF ARROW 0x08        0x08        0x08
00375 // RT ARROW 0x0C        0x0C        0x0C
00376 // DN ARROW 0x0A        0x0A        0x0A
00377 // VF1      --          --          --
00378 // VF2      --          --          --
00379 // VF3      --          --          --
00380 // VF4      --          --          --
00381 // VF5      --          --          --
00382 
00383 bool device_CON::Read(Bit8u * data,Bit16u * size) {
00384         Bit16u oldax=reg_ax;
00385         Bit16u count=0;
00386         auto defattr=DefaultANSIAttr();
00387         INT10_SetCurMode();
00388         if ((readcache) && (*size)) {
00389                 data[count++]=readcache;
00390                 if(dos.echo) Real_INT10_TeletypeOutput(readcache,defattr);
00391                 readcache=0;
00392         }
00393         while (*size>count) {
00394         if (dev_con_pos < dev_con_max) {
00395             data[count++] = (Bit8u)dev_con_readbuf[dev_con_pos++];
00396             continue;
00397         }
00398 
00399                 reg_ah=(IS_EGAVGA_ARCH)?0x10:0x0;
00400 
00401         /* FIXME: PC-98 emulation should eventually use CONIO emulation that
00402          *        better emulates the actual platform. The purpose of this
00403          *        hack is to allow our code to call into INT 16h without
00404          *        setting up an INT 16h vector */
00405         if (IS_PC98_ARCH)
00406             INT16_Handler_Wrap();
00407         else
00408             CALLBACK_RunRealInt(0x16);
00409 
00410         /* hack for DOSKEY emulation */
00411         last_int16_code = reg_ax;
00412 
00413                 switch(reg_al) {
00414                 case 13:
00415                         data[count++]=0x0D;
00416                         if (*size>count) data[count++]=0x0A;    // it's only expanded if there is room for it. (NO cache)
00417                         *size=count;
00418                         reg_ax=oldax;
00419                         if(dos.echo) { 
00420                                 Real_INT10_TeletypeOutput(13,defattr); //maybe don't do this ( no need for it actually ) (but it's compatible)
00421                                 Real_INT10_TeletypeOutput(10,defattr);
00422                         }
00423                         return true;
00424                         break;
00425                 case 8:
00426                         if(*size==1) data[count++]=reg_al;  //one char at the time so give back that BS
00427                         else if(count) {                    //Remove data if it exists (extended keys don't go right)
00428                                 data[count--]=0;
00429                                 Real_INT10_TeletypeOutput(8,defattr);
00430                                 Real_INT10_TeletypeOutput(' ',defattr);
00431                         } else {
00432                                 continue;                       //no data read yet so restart whileloop.
00433                         }
00434                         break;
00435                 case 0xe0: /* Extended keys in the  int 16 0x10 case */
00436                         if(!reg_ah) { /*extended key if reg_ah isn't 0 */
00437                                 data[count++] = reg_al;
00438                         } else {
00439                                 data[count++] = 0;
00440                                 if (*size>count) data[count++] = reg_ah;
00441                                 else readcache = reg_ah;
00442                         }
00443                         break;
00444                 case 0: /* Extended keys in the int 16 0x0 case */
00445             if (IS_PC98_ARCH) {
00446                 /* PC-98 does NOT return scan code, but instead returns nothing or
00447                  * control/escape code */
00448                 CommonPC98ExtScanConversionToReadBuf(reg_ah);
00449             }
00450             else {
00451                 /* IBM PC/XT/AT signals extended code by entering AL, AH.
00452                  * Arrow keys for example become 0x00 0x48, 0x00 0x50, etc. */
00453                         data[count++]=reg_al;
00454                         if (*size>count) data[count++]=reg_ah;
00455                         else readcache=reg_ah;
00456             }
00457                         break;
00458                 default:
00459                         data[count++]=reg_al;
00460                         break;
00461                 }
00462                 if(dos.echo) { //what to do if *size==1 and character is BS ?????
00463                         // TODO: If CTRL+C checking is applicable do not echo (reg_al == 3)
00464                         Real_INT10_TeletypeOutput(reg_al,defattr);
00465                 }
00466         }
00467         *size=count;
00468         reg_ax=oldax;
00469         return true;
00470 }
00471 
00472 bool log_dev_con = false;
00473 std::string log_dev_con_str;
00474 
00475 bool device_CON::Write(const Bit8u * data,Bit16u * size) {
00476     Bit16u count=0;
00477     Bitu i;
00478     Bit8u col,row;
00479     Bit8u tempdata;
00480     INT10_SetCurMode();
00481 
00482     if (IS_PC98_ARCH) {
00483         ansi.enabled = true; // ANSI is enabled at all times
00484         ansi.attr = mem_readb(0x71D); // 60:11D
00485     }
00486 
00487     while (*size>count) {
00488         if (log_dev_con) {
00489             if (log_dev_con_str.size() >= 255 || data[count] == '\n' || data[count] == 27) {
00490                 LOG_MSG("DOS CON: %s",log_dev_con_str.c_str());
00491                 log_dev_con_str.clear();
00492             }
00493 
00494             if (data[count] != '\n' && data[count] != '\r')
00495                 log_dev_con_str += (char)data[count];
00496         }
00497 
00498         if (!ansi.esc){
00499             if(data[count]=='\033') {
00500                 /*clear the datastructure */
00501                 ClearAnsi();
00502                 /* start the sequence */
00503                 ansi.esc=true;
00504                 count++;
00505                 continue;
00506             } else { 
00507                 /* Some sort of "hack" now that '\n' doesn't set col to 0 (int10_char.cpp old chessgame) */
00508                 if((data[count] == '\n') && (lastwrite != '\r')) Real_INT10_TeletypeOutputAttr('\r',ansi.attr,ansi.enabled);
00509                 /* ansi attribute will be set to the default if ansi is disabled */
00510                 Real_INT10_TeletypeOutputAttr(data[count],ansi.attr,true);
00511                 lastwrite = data[count++];
00512                 continue;
00513             }
00514         }
00515 
00516         if(!ansi.sci){
00517 
00518             switch(data[count]){
00519                 case '[': 
00520                     ansi.sci=true;
00521                     break;
00522                 case '7': /* save cursor pos + attr */
00523                 case '8': /* restore this  (Wonder if this is actually used) */
00524                 case 'D':/* scrolling DOWN*/
00525                 case 'M':/* scrolling UP*/ 
00526                 case '*':/* PC-98: clear screen */
00527                     if (IS_PC98_ARCH) {
00528                         Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00529 
00530                         INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page);
00531                         Real_INT10_SetCursorPos(0,0,page);
00532                         ClearAnsi();
00533                         break;
00534                     }
00535                     else {
00536                         /* fall through */
00537                     }
00538                 default:
00539                     LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unknown char %c after a esc",data[count]); /*prob () */
00540                     ClearAnsi();
00541                     break;
00542             }
00543             count++;
00544             continue;
00545         }
00546         /*ansi.esc and ansi.sci are true */
00547         Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00548         if (isdigit(data[count])) {
00549             assert(ansi.numberofarg < NUMBER_ANSI_DATA);
00550             ansi.data[ansi.numberofarg]=10*ansi.data[ansi.numberofarg]+(data[count]-'0');
00551         }
00552         else if (data[count] == ';') {
00553             if ((ansi.numberofarg+1) < NUMBER_ANSI_DATA)
00554                 ansi.numberofarg++;
00555         }
00556         else if (ansi.pc98rab) {
00557             assert(IS_PC98_ARCH);
00558 
00559             switch(data[count]){
00560                 case 'h': /* SET   MODE (if code =7 enable linewrap) */
00561                 case 'l': /* RESET MODE */
00562                     switch (ansi.data[0]) {
00563                         case 1: // show/hide function key row
00564                             void update_pc98_function_row(bool enable);
00565                             update_pc98_function_row(data[count] == 'l');
00566                             ansi.nrows = real_readb(0x60,0x112)+1;
00567                             break;
00568                         case 3: // clear screen (doesn't matter if l or h)
00569                             INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page);
00570                             Real_INT10_SetCursorPos(0,0,page);
00571                             break;
00572                         case 5: // show/hide cursor
00573                             void PC98_show_cursor(bool show);
00574                             PC98_show_cursor(data[count] == 'l');
00575                             mem_writeb(0x71B,data[count] == 'l' ? 0x01 : 0x00); /* 60:11B cursor display state */
00576                             break;
00577                         default:
00578                             LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled esc [ > %d %c",ansi.data[0],data[count]);
00579                             break;
00580                     };
00581 
00582                     ClearAnsi();
00583                     break;
00584                 default:
00585                     LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled char %c in esc [ >",data[count]);
00586                     ClearAnsi();
00587                     break;
00588             }
00589         }
00590         else {
00591             switch(data[count]){
00592                 case 'm':               /* SGR */
00593                     // NEC's ANSI driver always resets at the beginning
00594                     if(IS_PC98_ARCH) {
00595                         ansi.attr = DefaultANSIAttr();
00596                     }
00597                     for(i=0;i<=ansi.numberofarg;i++){ 
00598                         const Bit8u COLORFLAGS[][8] = {
00599                         //  Black   Red Green Yellow Blue  Pink  Cyan  White
00600                             { 0x0,  0x4,  0x2,  0x6,  0x1,  0x5,  0x3,  0x7 }, /*   IBM */
00601                             { 0x0, 0x40, 0x80, 0xC0, 0x20, 0x60, 0xA0, 0xE0 }, /* PC-98 */
00602                         };
00603                         const auto &flagset = COLORFLAGS[IS_PC98_ARCH];
00604 
00605                         if(IS_PC98_ARCH) {
00606                             // Convert alternate color codes to regular ones
00607                             if(ansi.data[i] >= 17 && ansi.data[i] <= 23) {
00608                                 const Bit8u convtbl[] = {
00609                                     31, 34, 35, 32, 33, 36, 37
00610                                 };
00611                                 ansi.data[i] = convtbl[ansi.data[i] - 17];
00612                             }
00613                         }
00614 
00615                         ansi.enabled=true;
00616                         switch(ansi.data[i]){
00617                             case 0: /* normal */
00618                                 //Real ansi does this as well. (should do current defaults)
00619                                 ansi.Disable();
00620                                 break;
00621                             case 1: /* bold mode on*/
00622                                 // FIXME: According to http://www.ninton.co.jp/?p=11, this
00623                                 // should set some sort of "highlight" flag in monochrome
00624                                 // mode, but I have no idea how to even enter that mode.
00625                                 ansi.attr |= IS_PC98_ARCH ? 0 : 0x08;
00626                                 break;
00627                             case 2: /* PC-98 "Bit 4" */
00628                                 ansi.attr |= IS_PC98_ARCH ? 0x10 : 0;
00629                                 break;
00630                             case 4: /* underline */
00631                                 if(IS_PC98_ARCH) {
00632                                     ansi.attr |= 0x08;
00633                                 } else {
00634                                     LOG(LOG_IOCTL, LOG_NORMAL)("ANSI:no support for underline yet");
00635                                 }
00636                                 break;
00637                             case 5: /* blinking */
00638                                 ansi.attr |= IS_PC98_ARCH ? 0x02 : 0x80;
00639                                 break;
00640                             case 7: /* reverse */
00641                                 //Just like real ansi. (should do use current colors reversed)
00642                                 if(IS_PC98_ARCH) {
00643                                     ansi.attr |= 0x04;
00644                                 } else {
00645                                     ansi.attr = 0x70;
00646                                 }
00647                                 break;
00648                             case 8: /* PC-98 secret */
00649                             case 16:
00650                                 ansi.attr &= IS_PC98_ARCH ? 0xFE : 0xFF;
00651                                 break;
00652                             case 30: /* fg color black */
00653                             case 31: /* fg color red */
00654                             case 32: /* fg color green */
00655                             case 33: /* fg color yellow */
00656                             case 34: /* fg color blue */
00657                             case 35: /* fg color magenta */
00658                             case 36: /* fg color cyan */
00659                             case 37: /* fg color white */
00660                                 ansi.attr &= ~(flagset[7]);
00661                                 ansi.attr |= (flagset[ansi.data[i] - 30]);
00662                                 break;
00663                             case 40:
00664                             case 41:
00665                             case 42:
00666                             case 43:
00667                             case 44:
00668                             case 45:
00669                             case 46:
00670                             case 47: {
00671                                 Bit8u shift = IS_PC98_ARCH ? 0 : 4;
00672                                 ansi.attr &= ~(flagset[7] << shift);
00673                                 ansi.attr |= (flagset[ansi.data[i] - 40] << shift);
00674                                 ansi.attr |= IS_PC98_ARCH ? 0x04 : 0;
00675                                 break;
00676                             }
00677                             default:
00678                                 break;
00679                         }
00680                     }
00681                     if (IS_PC98_ARCH) mem_writeb(0x71D,ansi.attr); // 60:11D
00682                     ClearAnsi();
00683                     break;
00684                 case 'f':
00685                 case 'H':/* Cursor Pos*/
00686                     if(!ansi.warned) { //Inform the debugger that ansi is used.
00687                         ansi.warned = true;
00688                         LOG(LOG_IOCTL,LOG_WARN)("ANSI SEQUENCES USED");
00689                     }
00690                     /* Turn them into positions that are on the screen */
00691                     if(ansi.data[0] == 0) ansi.data[0] = 1;
00692                     if(ansi.data[1] == 0) ansi.data[1] = 1;
00693                     if(ansi.data[0] > ansi.nrows) ansi.data[0] = (Bit8u)ansi.nrows;
00694                     if(ansi.data[1] > ansi.ncols) ansi.data[1] = (Bit8u)ansi.ncols;
00695                     Real_INT10_SetCursorPos(--(ansi.data[0]),--(ansi.data[1]),page); /*ansi=1 based, int10 is 0 based */
00696                     ClearAnsi();
00697                     break;
00698                     /* cursor up down and forward and backward only change the row or the col not both */
00699                 case 'A': /* cursor up*/
00700                     col=CURSOR_POS_COL(page) ;
00701                     row=CURSOR_POS_ROW(page) ;
00702                     tempdata = (ansi.data[0]? ansi.data[0] : 1);
00703                     if(tempdata > row) { row=0; } 
00704                     else { row-=tempdata;}
00705                     Real_INT10_SetCursorPos(row,col,page);
00706                     ClearAnsi();
00707                     break;
00708                 case 'B': /*cursor Down */
00709                     col=CURSOR_POS_COL(page) ;
00710                     row=CURSOR_POS_ROW(page) ;
00711                     tempdata = (ansi.data[0]? ansi.data[0] : 1);
00712                     if(tempdata + static_cast<Bitu>(row) >= ansi.nrows)
00713                     { row = ansi.nrows - 1;}
00714                     else        { row += tempdata; }
00715                     Real_INT10_SetCursorPos(row,col,page);
00716                     ClearAnsi();
00717                     break;
00718                 case 'C': /*cursor forward */
00719                     col=CURSOR_POS_COL(page);
00720                     row=CURSOR_POS_ROW(page);
00721                     tempdata=(ansi.data[0]? ansi.data[0] : 1);
00722                     if(tempdata + static_cast<Bitu>(col) >= ansi.ncols) 
00723                     { col = ansi.ncols - 1;} 
00724                     else        { col += tempdata;}
00725                     Real_INT10_SetCursorPos(row,col,page);
00726                     ClearAnsi();
00727                     break;
00728                 case 'D': /*Cursor Backward  */
00729                     col=CURSOR_POS_COL(page);
00730                     row=CURSOR_POS_ROW(page);
00731                     tempdata=(ansi.data[0]? ansi.data[0] : 1);
00732                     if(tempdata > col) {col = 0;}
00733                     else { col -= tempdata;}
00734                     Real_INT10_SetCursorPos(row,col,page);
00735                     ClearAnsi();
00736                     break;
00737                 case 'J': /*erase screen and move cursor home*/
00738                     if(ansi.data[0]==0) ansi.data[0]=2;
00739                     if(ansi.data[0]!=2) {/* every version behaves like type 2 */
00740                         LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: esc[%dJ called : not supported handling as 2",ansi.data[0]);
00741                     }
00742                     INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page);
00743                     ClearAnsi();
00744                     Real_INT10_SetCursorPos(0,0,page);
00745                     break;
00746                 case 'h': /* SET   MODE (if code =7 enable linewrap) */
00747                 case 'I': /* RESET MODE */
00748                     LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: set/reset mode called(not supported)");
00749                     ClearAnsi();
00750                     break;
00751                 case 'u': /* Restore Cursor Pos */
00752                     Real_INT10_SetCursorPos(ansi.saverow,ansi.savecol,page);
00753                     ClearAnsi();
00754                     break;
00755                 case 's': /* SAVE CURSOR POS */
00756                     ansi.savecol=CURSOR_POS_COL(page);
00757                     ansi.saverow=CURSOR_POS_ROW(page);
00758                     ClearAnsi();
00759                     break;
00760                 case 'K': /* erase till end of line (don't touch cursor) */
00761                     col = CURSOR_POS_COL(page);
00762                     row = CURSOR_POS_ROW(page);
00763                     INT10_WriteChar(' ',ansi.attr,page,ansi.ncols-col,true); //Real_WriteChar(ansi.ncols-col,row,page,' ',ansi.attr,true);
00764 
00765                     //for(i = col;i<(Bitu) ansi.ncols; i++) INT10_TeletypeOutputAttr(' ',ansi.attr,true);
00766                     Real_INT10_SetCursorPos(row,col,page);
00767                     ClearAnsi();
00768                     break;
00769                 case 'M': /* delete line (NANSI) */
00770                     col = CURSOR_POS_COL(page);
00771                     row = CURSOR_POS_ROW(page);
00772                     INT10_ScrollWindow(row,0,ansi.nrows-1,ansi.ncols-1,ansi.data[0]? -ansi.data[0] : -1,ansi.attr,0xFF);
00773                     ClearAnsi();
00774                     break;
00775                 case '>':/* proprietary NEC PC-98 MS-DOS codes (??) */
00776                     if (IS_PC98_ARCH) {
00777                         ansi.pc98rab = true;
00778                     }
00779                     else {
00780                         LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: ESC [ > not supported outside PC-98 mode");
00781                         ClearAnsi();
00782                     }
00783                     break;
00784                 case 'l':/* (if code =7) disable linewrap */
00785                 case 'p':/* reassign keys (needs strings) */
00786                 case 'i':/* printer stuff */
00787                 default:
00788                     LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled char %c in esc[",data[count]);
00789                     ClearAnsi();
00790                     break;
00791             }
00792         }
00793         count++;
00794     }
00795     *size=count;
00796     return true;
00797 }
00798 
00799 bool device_CON::Seek(Bit32u * pos,Bit32u type) {
00800     (void)pos; // UNUSED
00801     (void)type; // UNUSED
00802         // seek is valid
00803         *pos = 0;
00804         return true;
00805 }
00806 
00807 bool device_CON::Close() {
00808         return true;
00809 }
00810 
00811 extern bool dos_con_use_int16_to_detect_input;
00812 
00813 Bit16u device_CON::GetInformation(void) {
00814         if (dos_con_use_int16_to_detect_input || IS_PC98_ARCH) {
00815                 Bit16u ret = 0x80D3; /* No Key Available */
00816 
00817                 /* DOSBox-X behavior: Use INT 16h AH=0x11 Query keyboard status/preview key.
00818                  * The reason we do this is some DOS programs actually rely on hooking INT 16h
00819                  * to manipulate, hide, or transform what the DOS CON driver sees as well as
00820                  * itself. Perhaps the most disgusting example of this behavior would be the
00821                  * SCANDISK.EXE utility in Microsoft MS-DOS 6.22, which apparently relies on
00822                  * hooking INT 16h in this way to catch the Escape, CTRL+C, and some other
00823                  * scan codes in order to "eat" the scan codes before they get back to DOS.
00824                  * The reason they can get away with it apparently and still respond properly
00825                  * to those keys, is because the MS-DOS 6.22 CON driver always calls INT 16h
00826                  * AH=0x11 first before calling INT 16h AH=0x10 to fetch the scan code.
00827                  *
00828                  * Without this fix, SCANDISK.EXE does not respond properly to Escape and
00829                  * a few other keys. Pressing Escape will do nothing until you hit any other
00830                  * key, at which point it suddenly acts upon the Escape key.
00831                  *
00832                  * Since Scandisk is using INT 21h AH=0x0B to query STDIN during this time,
00833                  * this implementation is a good "halfway" compromise in that this call
00834                  * will trigger the INT 16h AH=0x11 hook it relies on. */
00835                 if (readcache || dev_con_pos < dev_con_max) return 0x8093; /* key available */
00836 
00837                 Bitu saved_ax = reg_ax;
00838 
00839                 reg_ah = (IS_EGAVGA_ARCH)?0x11:0x1; // check for keystroke
00840 
00841         /* FIXME: PC-98 emulation should eventually use CONIO emulation that
00842          *        better emulates the actual platform. The purpose of this
00843          *        hack is to allow our code to call into INT 16h without
00844          *        setting up an INT 16h vector */
00845         if (IS_PC98_ARCH)
00846             INT16_Handler_Wrap();
00847         else
00848             CALLBACK_RunRealInt(0x16);
00849 
00850         if (!GETFLAG(ZF)) { /* key is present, waiting to be returned on AH=0x10 or AH=0x00 */
00851             if (IS_PC98_ARCH && reg_al == 0) {
00852                 /* some scan codes are ignored by CON, and wouldn't read anything.
00853                  * while we're at it, take the scan code and convert it into ANSI here
00854                  * so that Read() returns it immediately instead of doing this conversion itself.
00855                  * This way we never block when we SAID a key was available that gets ignored. */
00856                 if (CommonPC98ExtScanConversionToReadBuf(reg_ah))
00857                     ret = 0x8093; /* Key Available */
00858                 else
00859                     ret = 0x80D3; /* No Key Available */
00860 
00861                 /* need to consume the key. if it generated anything it will be returned to Read()
00862                  * through dev_con_readbuf[] */
00863                 reg_ah=0x0;
00864 
00865                 /* FIXME: PC-98 emulation should eventually use CONIO emulation that
00866                  *        better emulates the actual platform. The purpose of this
00867                  *        hack is to allow our code to call into INT 16h without
00868                  *        setting up an INT 16h vector */
00869                 INT16_Handler_Wrap();
00870             }
00871             else {
00872                 ret = 0x8093; /* Key Available */
00873             }
00874         }
00875 
00876                 reg_ax = saved_ax;
00877                 return ret;
00878         }
00879         else {
00880                 /* DOSBox mainline behavior: alternate "fast" way through direct manipulation of keyboard scan buffer */
00881                 Bit16u head=mem_readw(BIOS_KEYBOARD_BUFFER_HEAD);
00882                 Bit16u tail=mem_readw(BIOS_KEYBOARD_BUFFER_TAIL);
00883 
00884                 if ((head==tail) && !readcache) return 0x80D3;  /* No Key Available */
00885                 if (readcache || real_readw(0x40,head)) return 0x8093;          /* Key Available */
00886 
00887                 /* remove the zero from keyboard buffer */
00888                 Bit16u start=mem_readw(BIOS_KEYBOARD_BUFFER_START);
00889                 Bit16u end      =mem_readw(BIOS_KEYBOARD_BUFFER_END);
00890                 head+=2;
00891                 if (head>=end) head=start;
00892                 mem_writew(BIOS_KEYBOARD_BUFFER_HEAD,head);
00893         }
00894 
00895         return 0x80D3; /* No Key Available */
00896 }
00897 
00898 device_CON::device_CON() {
00899         SetName("CON");
00900         readcache=0;
00901         lastwrite=0;
00902         ansi.Disable();
00903     if (IS_PC98_ARCH) {
00904         // NTS: On real hardware, the BIOS does NOT manage the console at all.
00905         //      TTY handling is entirely handled by MS-DOS.
00906         ansi.ncols=80;
00907         ansi.nrows=25 - 1;
00908         // the DOS kernel will call on this function to disable, and SDLmain
00909         // will call on to enable
00910     }
00911     else {
00912         ansi.ncols=real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS); //should be updated once set/reset mode is implemented
00913         ansi.nrows=real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1;
00914     }
00915         ansi.saverow=0;
00916         ansi.savecol=0;
00917         ansi.warned=false;
00918         ClearAnsi();
00919 }
00920 
00921 void device_CON::ClearAnsi(void){
00922         for(Bit8u i=0; i<NUMBER_ANSI_DATA;i++) ansi.data[i]=0;
00923     ansi.pc98rab=false;
00924         ansi.esc=false;
00925         ansi.sci=false;
00926         ansi.numberofarg=0;
00927 }
00928