DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dev_con.h
00001 /*
00002  *  Copyright (C) 2002-2020  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 along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 extern unsigned char pc98_function_row_mode;
00031 
00032 Bitu INT10_Handler(void);
00033 Bitu INT16_Handler_Wrap(void);
00034 
00035 bool inhibited_ControlFn(void);
00036 void pc98_function_row_user_toggle(void);
00037 void update_pc98_function_row(unsigned char setting,bool force_redraw=false);
00038 void PC98_GetFuncKeyEscape(size_t &len,unsigned char buf[16],const unsigned int i);
00039 void PC98_GetShiftFuncKeyEscape(size_t &len,unsigned char buf[16],const unsigned int i);
00040 void PC98_GetEditorKeyEscape(size_t &len,unsigned char buf[16],const unsigned int scan);
00041 void PC98_GetVFuncKeyEscape(size_t &len,unsigned char buf[16],const unsigned int i);
00042 void PC98_GetShiftVFuncKeyEscape(size_t &len,unsigned char buf[16],const unsigned int i);
00043 void PC98_GetCtrlFuncKeyEscape(size_t &len,unsigned char buf[16],const unsigned int i);
00044 void PC98_GetCtrlVFuncKeyEscape(size_t &len,unsigned char buf[16],const unsigned int i);
00045 
00046 ShiftJISDecoder con_sjis;
00047 
00048 Bit16u last_int16_code = 0;
00049 
00050 static size_t dev_con_pos=0,dev_con_max=0;
00051 static unsigned char dev_con_readbuf[64];
00052 
00053 Bit8u DefaultANSIAttr() {
00054         return IS_PC98_ARCH ? 0xE1 : 0x07;
00055 }
00056 
00057 class device_CON : public DOS_Device {
00058 public:
00059         device_CON();
00060         bool Read(Bit8u * data,Bit16u * size);
00061         bool Write(const Bit8u * data,Bit16u * size);
00062         bool Seek(Bit32u * pos,Bit32u type);
00063         bool Close();
00064         Bit16u GetInformation(void);
00065         bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { (void)bufptr; (void)size; (void)retcode; return false; }
00066         bool WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { (void)bufptr; (void)size; (void)retcode; return false; }
00067     bool ANSI_SYS_installed();
00068 private:
00069         void ClearAnsi(void);
00070         void Output(Bit8u chr);
00071         Bit8u readcache;
00072         struct ansi { /* should create a constructor, which would fill them with the appropriate values */
00073         bool installed;     // ANSI.SYS is installed (and therefore escapes are handled)
00074                 bool esc;
00075                 bool sci;
00076         bool equcurp;       // ????? ESC = Y X      cursor pos    (not sure if PC-98 specific or general to DOS ANSI.SYS)
00077         bool pc98rab;       // PC-98 ESC [ > ...    (right angle bracket) I will rename this variable if MS-DOS ANSI.SYS also supports this sequence
00078                 bool enabled;
00079                 Bit8u attr;         // machine-specific
00080                 Bit8u data[NUMBER_ANSI_DATA];
00081                 Bit8u numberofarg;
00082                 Bit16u nrows;
00083                 Bit16u ncols;
00084                 Bit8u savecol;
00085                 Bit8u saverow;
00086                 bool warned;
00087         } ansi;
00088 
00089     // ESC M
00090     void ESC_M(void) {
00091         LineFeedRev();
00092         ClearAnsi();
00093     }
00094 
00095     // ESC D
00096     void ESC_D(void) {
00097         LineFeed();
00098         ClearAnsi();
00099     }
00100 
00101     // ESC E
00102     void ESC_E(void) {
00103         Real_INT10_TeletypeOutputAttr('\n',ansi.attr,ansi.enabled);
00104         ClearAnsi();
00105     }
00106 
00107     // ESC [ A
00108     void ESC_BRACKET_A(void) {
00109         Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00110         Bit8u tempdata;
00111         Bit8u col,row;
00112 
00113         col=CURSOR_POS_COL(page) ;
00114         row=CURSOR_POS_ROW(page) ;
00115         tempdata = (ansi.data[0]? ansi.data[0] : 1);
00116         if(tempdata > row) { row=0; } 
00117         else { row-=tempdata;}
00118         Real_INT10_SetCursorPos(row,col,page);
00119         ClearAnsi();
00120     }
00121 
00122     // ESC [ B
00123     void ESC_BRACKET_B(void) {
00124         Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00125         Bit8u tempdata;
00126         Bit8u col,row;
00127 
00128         col=CURSOR_POS_COL(page) ;
00129         row=CURSOR_POS_ROW(page) ;
00130         if (!IS_PC98_ARCH)
00131             ansi.nrows = real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1;
00132         tempdata = (ansi.data[0]? ansi.data[0] : 1);
00133         if(tempdata + static_cast<Bitu>(row) >= ansi.nrows)
00134         { row = ansi.nrows - 1;}
00135         else    { row += tempdata; }
00136         Real_INT10_SetCursorPos(row,col,page);
00137         ClearAnsi();
00138     }
00139 
00140     // ESC [ C
00141     void ESC_BRACKET_C(void) {
00142         Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00143         Bit8u tempdata;
00144         Bit8u col,row;
00145 
00146         col=CURSOR_POS_COL(page);
00147         row=CURSOR_POS_ROW(page);
00148         if (!IS_PC98_ARCH)
00149             ansi.ncols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
00150         tempdata=(ansi.data[0]? ansi.data[0] : 1);
00151         if(tempdata + static_cast<Bitu>(col) >= ansi.ncols) 
00152         { col = ansi.ncols - 1;} 
00153         else    { col += tempdata;}
00154         Real_INT10_SetCursorPos(row,col,page);
00155         ClearAnsi();
00156     }
00157 
00158     // ESC [ D
00159     void ESC_BRACKET_D(void) {
00160         Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00161         Bit8u tempdata;
00162         Bit8u col,row;
00163 
00164         col=CURSOR_POS_COL(page);
00165         row=CURSOR_POS_ROW(page);
00166         tempdata=(ansi.data[0]? ansi.data[0] : 1);
00167         if(tempdata > col) {col = 0;}
00168         else { col -= tempdata;}
00169         Real_INT10_SetCursorPos(row,col,page);
00170         ClearAnsi();
00171     }
00172 
00173     // ESC = Y X
00174     void ESC_EQU_cursor_pos(void) {
00175         Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00176 
00177         /* This is what the PC-98 ANSI driver does */
00178         if(ansi.data[0] >= 0x20) ansi.data[0] -= 0x20;
00179         else ansi.data[0] = 0;
00180         if(ansi.data[1] >= 0x20) ansi.data[1] -= 0x20;
00181         else ansi.data[1] = 0;
00182 
00183         if (!IS_PC98_ARCH) {
00184             ansi.ncols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
00185             ansi.nrows = real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1;
00186         }
00187         /* Turn them into positions that are on the screen */
00188         if(ansi.data[0] >= ansi.nrows) ansi.data[0] = (Bit8u)ansi.nrows - 1;
00189         if(ansi.data[1] >= ansi.ncols) ansi.data[1] = (Bit8u)ansi.ncols - 1;
00190         Real_INT10_SetCursorPos(ansi.data[0],ansi.data[1],page);
00191 
00192         ClearAnsi();
00193     }
00194 
00195         static void Real_INT10_SetCursorPos(Bit8u row,Bit8u col,Bit8u page) {
00196                 Bit16u          oldax,oldbx,olddx;
00197 
00198                 oldax=reg_ax;
00199                 oldbx=reg_bx;
00200                 olddx=reg_dx;
00201 
00202                 reg_ah=0x2;
00203                 reg_dh=row;
00204                 reg_dl=col;
00205                 reg_bh=page;
00206 
00207         /* FIXME: PC-98 emulation should eventually use CONIO emulation that
00208          *        better emulates the actual platform. The purpose of this
00209          *        hack is to allow our code to call into INT 10h without
00210          *        setting up an INT 10h vector */
00211         if (IS_PC98_ARCH)
00212             INT10_Handler();
00213         else
00214             CALLBACK_RunRealInt(0x10);
00215 
00216                 reg_ax=oldax;
00217                 reg_bx=oldbx;
00218                 reg_dx=olddx;
00219         }
00220 
00221     /* Common function to turn specific scan codes into ANSI codes.
00222      * This is a separate function so that both Read() and GetInformation() can use it.
00223      * GetInformation needs to handle the scan code on entry in order to correctly
00224      * assert whether Read() will return data or not. Some scan codes are ignored by
00225      * the CON driver, therefore even though the BIOS says there is key data, Read()
00226      * will not return anything and will block. */
00227     bool CommonPC98ExtScanConversionToReadBuf(unsigned char code) {
00228         size_t esclen;
00229 
00230         switch (code) {
00231             case 0x36: // ROLL UP
00232             case 0x37: // ROLL DOWN
00233             case 0x38: // INS
00234             case 0x39: // DEL
00235             case 0x3A: // UP ARROW
00236             case 0x3B: // LEFT ARROW
00237             case 0x3C: // RIGHT ARROW
00238             case 0x3D: // DOWN ARROW
00239             case 0x3E: // HOME/CLR
00240             case 0x3F: // HELP
00241             case 0x40: // KEYPAD -
00242                 PC98_GetEditorKeyEscape(/*&*/esclen,dev_con_readbuf,code); dev_con_pos=0; dev_con_max=esclen;
00243                 return (dev_con_max != 0)?true:false;
00244             case 0x52: // VF1
00245             case 0x53: // VF2
00246             case 0x54: // VF3
00247             case 0x55: // VF4
00248             case 0x56: // VF5
00249                 PC98_GetVFuncKeyEscape(/*&*/esclen,dev_con_readbuf,code+1u-0x52u); dev_con_pos=0; dev_con_max=esclen;
00250                 return (dev_con_max != 0)?true:false;
00251             case 0x62: // F1
00252             case 0x63: // F2
00253             case 0x64: // F3
00254             case 0x65: // F4
00255             case 0x66: // F5
00256             case 0x67: // F6
00257             case 0x68: // F7
00258             case 0x69: // F8
00259             case 0x6A: // F9
00260             case 0x6B: // F10
00261                 PC98_GetFuncKeyEscape(/*&*/esclen,dev_con_readbuf,code+1u-0x62u); dev_con_pos=0; dev_con_max=esclen;
00262                 return (dev_con_max != 0)?true:false;
00263             case 0x82: // Shift+F1
00264             case 0x83: // Shift+F2
00265             case 0x84: // Shift+F3
00266             case 0x85: // Shift+F4
00267             case 0x86: // Shift+F5
00268             case 0x87: // Shift+F6
00269             case 0x88: // Shift+F7
00270             case 0x89: // Shift+F8
00271             case 0x8A: // Shift+F9
00272             case 0x8B: // Shift+F10
00273                 PC98_GetShiftFuncKeyEscape(/*&*/esclen,dev_con_readbuf,code+1u-0x82u); dev_con_pos=0; dev_con_max=esclen;
00274                 return (dev_con_max != 0)?true:false;
00275             case 0x92: // Control+F1
00276             case 0x93: // Control+F2
00277             case 0x94: // Control+F3
00278             case 0x95: // Control+F4
00279             case 0x96: // Control+F5
00280             case 0x97: // Control+F6
00281             case 0x98: // Control+F7
00282             case 0x99: // Control+F8
00283             case 0x9A: // Control+F9
00284             case 0x9B: // Control+F10
00285                 if (inhibited_ControlFn()) {
00286                     PC98_GetCtrlFuncKeyEscape(/*&*/esclen,dev_con_readbuf,code+1u-0x92u); dev_con_pos=0; dev_con_max=esclen;
00287                     return (dev_con_max != 0)?true:false;
00288                 }
00289                 else if (code == 0x97) {// CTRL+F6   Toggle 20/25-line text      HANDLED INTERNALLY, NEVER RETURNED TO CONSOLE
00290                     /* toggle the bit and change the text layer */
00291                     {
00292                         Bit8u b = real_readb(0x60,0x113);
00293                         real_writeb(0x60,0x113,b ^ 1);
00294 
00295                         reg_ah = 0x0A; /* Set CRT mode */
00296                         reg_al = b;
00297                         CALLBACK_RunRealInt(0x18);
00298 
00299                         reg_ah = 0x11; /* show cursor (Func 0x0A hides the cursor) */
00300                         CALLBACK_RunRealInt(0x18);
00301                     }
00302 
00303                     /* clear the screen */
00304                     {
00305                         Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00306 
00307                         /* it also redraws the function key row */
00308                         update_pc98_function_row(pc98_function_row_mode,true);
00309 
00310                         INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page);
00311                         Real_INT10_SetCursorPos(0,0,page);
00312                     }
00313                 }
00314                 else if (code == 0x98) {// CTRL+F7   Toggle function key row     HANDLED INTERNALLY, NEVER RETURNED TO CONSOLE
00315                     void pc98_function_row_user_toggle(void);
00316                     pc98_function_row_user_toggle();
00317                 }
00318                 else if (code == 0x99) {// CTRL+F8   Clear screen, home cursor   HANDLED INTERNALLY, NEVER RETURNED TO CONSOLE
00319                     Bit8u page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00320 
00321                     /* it also redraws the function key row */
00322                     update_pc98_function_row(pc98_function_row_mode,true);
00323 
00324                     INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page);
00325                     Real_INT10_SetCursorPos(0,0,page);
00326                     ClearAnsi();
00327                 }
00328                 break;
00329 
00330             case 0xC2: // VF1
00331             case 0xC3: // VF2
00332             case 0xC4: // VF3
00333             case 0xC5: // VF4
00334             case 0xC6: // VF5
00335                 PC98_GetShiftVFuncKeyEscape(/*&*/esclen,dev_con_readbuf,code+1u-0xC2u); dev_con_pos=0; dev_con_max=esclen;
00336                 return (dev_con_max != 0)?true:false;
00337 
00338             case 0xD2: // VF1
00339             case 0xD3: // VF2
00340             case 0xD4: // VF3
00341             case 0xD5: // VF4
00342             case 0xD6: // VF5
00343                 if (inhibited_ControlFn()) {
00344                     PC98_GetCtrlVFuncKeyEscape(/*&*/esclen,dev_con_readbuf,code+1u-0xD2u); dev_con_pos=0; dev_con_max=esclen;
00345                     return (dev_con_max != 0)?true:false;
00346                 }
00347                 break;
00348 #if 0
00349                 // ROLL UP  --          --          --
00350                 // POLL DOWN--          --          --
00351                 // COPY     --          --          --
00352                 // HOME/CLR 0x1A        0x1E        --
00353                 // HELP     --          --          --
00354 #endif
00355         }
00356 
00357         return false;
00358     }
00359 
00360         static void Real_INT10_TeletypeOutput(Bit8u xChar,Bit8u xAttr) {
00361         if (IS_PC98_ARCH) {
00362             if (con_sjis.take(xChar)) {
00363                 BIOS_NCOLS;
00364                 Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00365                 Bit8u cur_row=CURSOR_POS_ROW(page);
00366                 Bit8u cur_col=CURSOR_POS_COL(page);
00367                 unsigned char cw = con_sjis.doublewide ? 2 : 1;
00368 
00369                 /* FIXME: I'm not sure what NEC's ANSI driver does if a doublewide character is printed at column 79 */
00370                 if ((cur_col+cw) > ncols) {
00371                     cur_col = (Bit8u)ncols;
00372                     AdjustCursorPosition(cur_col,cur_row);
00373                 }
00374 
00375                 /* JIS conversion to WORD value appropriate for text RAM */
00376                 if (con_sjis.b2 != 0) con_sjis.b1 -= 0x20;
00377 
00378                 INT10_WriteChar((con_sjis.b2 << 8) + con_sjis.b1,xAttr,0,1,true);
00379 
00380                 cur_col += cw;
00381                 AdjustCursorPosition(cur_col,cur_row);
00382                 Real_INT10_SetCursorPos(cur_row,cur_col,page);  
00383             }
00384         }
00385         else {
00386             Bit16u oldax,oldbx;
00387             oldax=reg_ax;
00388             oldbx=reg_bx;
00389 
00390             reg_ah=0xE;
00391             reg_al=xChar;
00392             reg_bl=xAttr;
00393 
00394             CALLBACK_RunRealInt(0x10);
00395 
00396             reg_ax=oldax;
00397             reg_bx=oldbx;
00398         }
00399         }
00400 
00401 
00402         static void Real_WriteChar(Bit8u cur_col,Bit8u cur_row,
00403                                         Bit8u page,Bit8u chr,Bit8u attr,Bit8u useattr) {
00404                 //Cursor position
00405                 Real_INT10_SetCursorPos(cur_row,cur_col,page);
00406 
00407                 //Write the character
00408                 Bit16u          oldax,oldbx,oldcx;
00409                 oldax=reg_ax;
00410                 oldbx=reg_bx;
00411                 oldcx=reg_cx;
00412 
00413                 reg_al=chr;
00414                 reg_bl=attr;
00415                 reg_bh=page;
00416                 reg_cx=1;
00417                 if(useattr)
00418                                 reg_ah=0x9;
00419                 else    reg_ah=0x0A;
00420 
00421         /* FIXME: PC-98 emulation should eventually use CONIO emulation that
00422          *        better emulates the actual platform. The purpose of this
00423          *        hack is to allow our code to call into INT 10h without
00424          *        setting up an INT 10h vector */
00425         if (IS_PC98_ARCH)
00426             INT10_Handler();
00427         else
00428             CALLBACK_RunRealInt(0x10);
00429 
00430                 reg_ax=oldax;
00431                 reg_bx=oldbx;
00432                 reg_cx=oldcx;
00433         }//static void Real_WriteChar(cur_col,cur_row,page,chr,attr,useattr)
00434 
00435     static void LineFeedRev(void) { // ESC M
00436                 BIOS_NCOLS;BIOS_NROWS;
00437                 auto defattr = DefaultANSIAttr();
00438                 Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00439                 Bit8u cur_row=CURSOR_POS_ROW(page);
00440                 Bit8u cur_col=CURSOR_POS_COL(page);
00441 
00442                 if(cur_row==0) 
00443                 {
00444             INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),1,defattr,0);
00445         }
00446         else {
00447             cur_row--;
00448         }
00449 
00450                 Real_INT10_SetCursorPos(cur_row,cur_col,page);  
00451     }
00452 
00453     static void LineFeed(void) { // ESC D
00454                 BIOS_NCOLS;BIOS_NROWS;
00455                 auto defattr = DefaultANSIAttr();
00456                 Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00457                 Bit8u cur_row=CURSOR_POS_ROW(page);
00458                 Bit8u cur_col=CURSOR_POS_COL(page);
00459 
00460         if (cur_row < nrows) cur_row++;
00461 
00462                 if(cur_row==nrows) 
00463                 {
00464             INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,defattr,0);
00465             cur_row--;
00466                 }
00467 
00468                 Real_INT10_SetCursorPos(cur_row,cur_col,page);  
00469     }
00470         
00471         static void AdjustCursorPosition(Bit8u& cur_col,Bit8u& cur_row) {
00472                 BIOS_NCOLS;BIOS_NROWS;
00473                 auto defattr = DefaultANSIAttr();
00474                 //Need a new line?
00475                 if(cur_col==ncols) 
00476                 {
00477                         cur_col=0;
00478                         cur_row++;
00479 
00480             if (!IS_PC98_ARCH)
00481                 Real_INT10_TeletypeOutput('\r',defattr);
00482         }
00483                 
00484                 //Reached the bottom?
00485                 if(cur_row==nrows) 
00486                 {
00487             if (IS_PC98_ARCH)
00488                         INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,defattr,0);
00489             else
00490                 Real_INT10_TeletypeOutput('\n',defattr);        //Scroll up
00491 
00492             cur_row--;
00493                 }
00494         }
00495 
00496 
00497         void Real_INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr) {
00498                 //TODO Check if this page thing is correct
00499                 Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00500 //              BIOS_NCOLS;BIOS_NROWS;
00501                 Bit8u cur_row=CURSOR_POS_ROW(page);
00502                 Bit8u cur_col=CURSOR_POS_COL(page);
00503                 switch (chr) 
00504                 {
00505                 case 7: {
00506                         // set timer (this should not be needed as the timer already is programmed 
00507                         // with those values, but the speaker stays silent without it)
00508                         IO_Write(0x43,0xb6);
00509                         IO_Write(0x42,1320&0xff);
00510                         IO_Write(0x42,1320>>8);
00511                         // enable speaker
00512                         IO_Write(0x61,IO_Read(0x61)|0x3);
00513                         for(Bitu i=0; i < 333; i++) CALLBACK_Idle();
00514                         IO_Write(0x61,IO_Read(0x61)&~0x3);
00515                         break;
00516                 }
00517                 case 8:
00518                         if(cur_col>0)
00519                                 cur_col--;
00520                         break;
00521                 case '\r':
00522                         cur_col=0;
00523                         break;
00524                 case '\n':
00525                         cur_col=0;
00526                         cur_row++;
00527                         break;
00528                 case '\t':
00529                         do {
00530                                 Real_INT10_TeletypeOutputAttr(' ',attr,useattr);
00531                                 cur_row=CURSOR_POS_ROW(page);
00532                                 cur_col=CURSOR_POS_COL(page);
00533                         } while(cur_col%8);
00534                         break;
00535                 default:
00536                         //* Draw the actual Character
00537             if (IS_PC98_ARCH) {
00538                 if (con_sjis.take(chr)) {
00539                     BIOS_NCOLS;
00540                     unsigned char cw = con_sjis.doublewide ? 2 : 1;
00541 
00542                     /* FIXME: I'm not sure what NEC's ANSI driver does if a doublewide character is printed at column 79 */
00543                     if ((cur_col+cw) > ncols) {
00544                         cur_col = (Bit8u)ncols;
00545                         AdjustCursorPosition(cur_col,cur_row);
00546                     }
00547 
00548                     /* JIS conversion to WORD value appropriate for text RAM */
00549                     if (con_sjis.b2 != 0) con_sjis.b1 -= 0x20;
00550 
00551                     INT10_WriteChar((con_sjis.b2 << 8) + con_sjis.b1,attr,0,1,true);
00552 
00553                     cur_col += cw;
00554                 }
00555             }
00556             else {
00557                 Real_WriteChar(cur_col,cur_row,page,chr,attr,useattr);
00558                 cur_col++;
00559             }
00560                 }
00561                 
00562                 AdjustCursorPosition(cur_col,cur_row);
00563                 Real_INT10_SetCursorPos(cur_row,cur_col,page);  
00564         }//void Real_INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr) 
00565 public:
00566     // INT DC interface: CL=0x10 AH=0x03
00567     void INTDC_CL10h_AH03h(Bit16u raw) {
00568         /* NTS: This emulates translation behavior seen in INT DCh interface:
00569          *
00570          *      DX = raw
00571          *      DX += 0x2020
00572          *      XCHG DL, DH
00573          *      CX = DX
00574          *      CALL "ESC = HANDLING CODE"
00575          *
00576          * Technically this means there is a bug where if DL(X) is 0xE0 or larger it will carry into DH(Y) */
00577         raw += 0x2020;
00578 
00579         ansi.data[0] = (raw >> 8); // Y
00580         ansi.data[1] = raw & 0xFF; // X
00581         ESC_EQU_cursor_pos();
00582     }
00583 
00584     void INTDC_CL10h_AH04h(void) {
00585         ESC_D();
00586     }
00587 
00588     void INTDC_CL10h_AH05h(void) {
00589         ESC_M();
00590     }
00591 
00592     void INTDC_CL10h_AH06h(Bit16u count) {
00593         ansi.data[0] = (Bit8u)count; /* truncation is deliberate, just like the actual ANSI driver */
00594         ESC_BRACKET_A();
00595     }
00596 
00597     void INTDC_CL10h_AH07h(Bit16u count) {
00598         ansi.data[0] = (Bit8u)count; /* truncation is deliberate, just like the actual ANSI driver */
00599         ESC_BRACKET_B();
00600     }
00601 
00602     void INTDC_CL10h_AH08h(Bit16u count) {
00603         ansi.data[0] = (Bit8u)count; /* truncation is deliberate, just like the actual ANSI driver */
00604         ESC_BRACKET_C();
00605     }
00606 
00607     void INTDC_CL10h_AH09h(Bit16u count) {
00608         ansi.data[0] = (Bit8u)count; /* truncation is deliberate, just like the actual ANSI driver */
00609         ESC_BRACKET_D();
00610     }
00611 
00612 };
00613 
00614 // NEC-PC98 keyboard input notes
00615 //
00616 // on a system with KKCFUNC.SYS, NECAIK1.SYS, NECAIK2.SYS, NECAI.SYS loaded
00617 //
00618 // Key      Normal      Shift       CTRL
00619 // -------------------------------------
00620 // ESC      0x1B        0x1B        0x1B
00621 // TAB      0x09        0x09        0x09
00622 // F1       0x1B 0x53   <shortcut>  --
00623 // F2       0x1B 0x54   <shortcut>  --
00624 // F3       0x1B 0x55   <shortcut>  --
00625 // F4       0x1B 0x56   <shortcut>  Toggles 'g'
00626 // F5       0x1B 0x57   <shortcut>  --
00627 // F6       0x1B 0x45   <shortcut>  Toggle 20/25-line text mode
00628 // F7       0x1B 0x4A   <shortcut>  Toggle function row (C1/CU/etc, shortcuts, or off)
00629 // F8       0x1B 0x50   <shortcut>  Clear screen, home cursor
00630 // F9       0x1B 0x51   <shortcut>  --
00631 // F10      0x1B 0x5A   <shortcut>  --
00632 // INS      0x1B 0x50   0x1B 0x50   0x1B 0x50
00633 // DEL      0x1B 0x44   0x1B 0x44   0x1B 0x44
00634 // ROLL UP  --          --          --
00635 // POLL DOWN--          --          --
00636 // COPY     --          --          --
00637 // HOME/CLR 0x1A        0x1E        --
00638 // HELP     --          --          --
00639 // UP ARROW 0x0B        0x0B        0x0B
00640 // LF ARROW 0x08        0x08        0x08
00641 // RT ARROW 0x0C        0x0C        0x0C
00642 // DN ARROW 0x0A        0x0A        0x0A
00643 // VF1      --          --          --
00644 // VF2      --          --          --
00645 // VF3      --          --          --
00646 // VF4      --          --          --
00647 // VF5      --          --          --
00648 
00649 // TODO for PC-98 mode:
00650 //
00651 // According to:
00652 //
00653 // http://hackipedia.org/browse.cgi/Computer/Platform/PC%2c%20NEC%20PC%2d98/Collections/PC%2d9801%20Bible%20%e6%9d%b1%e4%ba%ac%e7%90%86%e7%a7%91%e5%a4%a7%e5%ad%a6EIC%20%281994%29%2epdf
00654 //
00655 // Section 4-8.
00656 //
00657 // The PDF documents ANSI codes defined on PC-98, which may or may not be a complete listing.
00658 
00659 bool device_CON::Read(Bit8u * data,Bit16u * size) {
00660         Bit16u oldax=reg_ax;
00661         Bit16u count=0;
00662         auto defattr=DefaultANSIAttr();
00663         INT10_SetCurMode();
00664         if ((readcache) && (*size)) {
00665                 data[count++]=readcache;
00666                 if(dos.echo) Real_INT10_TeletypeOutput(readcache,defattr);
00667                 readcache=0;
00668         }
00669         while (*size>count) {
00670         if (dev_con_pos < dev_con_max) {
00671             data[count++] = (Bit8u)dev_con_readbuf[dev_con_pos++];
00672             continue;
00673         }
00674 
00675                 reg_ah=(IS_EGAVGA_ARCH)?0x10:0x0;
00676 
00677         /* FIXME: PC-98 emulation should eventually use CONIO emulation that
00678          *        better emulates the actual platform. The purpose of this
00679          *        hack is to allow our code to call into INT 16h without
00680          *        setting up an INT 16h vector */
00681         if (IS_PC98_ARCH)
00682             INT16_Handler_Wrap();
00683         else
00684             CALLBACK_RunRealInt(0x16);
00685 
00686         /* hack for DOSKEY emulation */
00687         last_int16_code = reg_ax;
00688 
00689                 switch(reg_al) {
00690                 case 13:
00691                         data[count++]=0x0D;
00692                         if (*size>count) data[count++]=0x0A;    // it's only expanded if there is room for it. (NO cache)
00693                         *size=count;
00694                         reg_ax=oldax;
00695                         if(dos.echo) { 
00696                                 Real_INT10_TeletypeOutput(13,defattr); //maybe don't do this ( no need for it actually ) (but it's compatible)
00697                                 Real_INT10_TeletypeOutput(10,defattr);
00698                         }
00699                         return true;
00700                         break;
00701                 case 8:
00702                         if(*size==1) data[count++]=reg_al;  //one char at the time so give back that BS
00703                         else if(count) {                    //Remove data if it exists (extended keys don't go right)
00704                                 data[count--]=0;
00705                                 Real_INT10_TeletypeOutput(8,defattr);
00706                                 Real_INT10_TeletypeOutput(' ',defattr);
00707                         } else {
00708                                 continue;                       //no data read yet so restart whileloop.
00709                         }
00710                         break;
00711                 case 0xe0: /* Extended keys in the  int 16 0x10 case */
00712                         if(!reg_ah) { /*extended key if reg_ah isn't 0 */
00713                                 data[count++] = reg_al;
00714                         } else {
00715                                 data[count++] = 0;
00716                                 if (*size>count) data[count++] = reg_ah;
00717                                 else readcache = reg_ah;
00718                         }
00719                         break;
00720                 case 0: /* Extended keys in the int 16 0x0 case */
00721             if (IS_PC98_ARCH) {
00722                 /* PC-98 does NOT return scan code, but instead returns nothing or
00723                  * control/escape code */
00724                 CommonPC98ExtScanConversionToReadBuf(reg_ah);
00725             }
00726             else {
00727                 /* IBM PC/XT/AT signals extended code by entering AL, AH.
00728                  * Arrow keys for example become 0x00 0x48, 0x00 0x50, etc. */
00729                         data[count++]=reg_al;
00730                         if (*size>count) data[count++]=reg_ah;
00731                         else readcache=reg_ah;
00732             }
00733                         break;
00734                 default:
00735                         data[count++]=reg_al;
00736                         if (*size > 1 && reg_al == 3)
00737                                 {
00738                                 dos.errorcode=77;
00739                                 *size=count;
00740                                 reg_ax=oldax;
00741                                 return false;
00742                                 }
00743                         break;
00744                 }
00745                 if(dos.echo) { //what to do if *size==1 and character is BS ?????
00746                         // TODO: If CTRL+C checking is applicable do not echo (reg_al == 3)
00747                         Real_INT10_TeletypeOutput(reg_al,defattr);
00748                 }
00749         }
00750         dos.errorcode=0;
00751         *size=count;
00752         reg_ax=oldax;
00753         return true;
00754 }
00755 
00756 bool log_dev_con = false;
00757 std::string log_dev_con_str;
00758 
00759 bool device_CON::Write(const Bit8u * data,Bit16u * size) {
00760     Bit16u count=0;
00761     Bitu i;
00762     Bit8u col,row,page;
00763 
00764     INT10_SetCurMode();
00765 
00766     if (IS_PC98_ARCH) {
00767         ansi.enabled = true; // ANSI is enabled at all times
00768         ansi.attr = mem_readb(0x71D); // 60:11D
00769     }
00770 
00771     while (*size>count) {
00772         if (log_dev_con) {
00773             if (log_dev_con_str.size() >= 255 || data[count] == '\n' || data[count] == 27) {
00774                 LOG_MSG("DOS CON: %s",log_dev_con_str.c_str());
00775                 log_dev_con_str.clear();
00776             }
00777 
00778             if (data[count] != '\n' && data[count] != '\r')
00779                 log_dev_con_str += (char)data[count];
00780         }
00781 
00782         if (!ansi.esc){
00783             if(data[count]=='\033' && ansi.installed) {
00784                 /*clear the datastructure */
00785                 ClearAnsi();
00786                 /* start the sequence */
00787                 ansi.esc=true;
00788                 count++;
00789                 continue;
00790             } else if(data[count] == '\t' && !dos.direct_output) {
00791                 /* expand tab if not direct output */
00792                 page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00793                 do {
00794                     Output(' ');
00795                     col=CURSOR_POS_COL(page);
00796                 } while(col%8);
00797                 count++;
00798                 continue;
00799             } else if (data[count] == 0x1A && IS_PC98_ARCH) {
00800                 page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00801 
00802                 /* it also redraws the function key row */
00803                 update_pc98_function_row(pc98_function_row_mode,true);
00804 
00805                 INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page);
00806                 Real_INT10_SetCursorPos(0,0,page);
00807             } else if (data[count] == 0x1E && IS_PC98_ARCH) {
00808                 page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00809 
00810                 Real_INT10_SetCursorPos(0,0,page);
00811             } else { 
00812                 Output(data[count]);
00813                 count++;
00814                 continue;
00815             }
00816         }
00817 
00818         if(!ansi.sci){
00819 
00820             switch(data[count]){
00821                 case '[': 
00822                     ansi.sci=true;
00823                     break;
00824                 case '*':/* PC-98: clear screen (same code path as CTRL+Z) */
00825                     if (IS_PC98_ARCH) {
00826                         page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00827 
00828                         /* it also redraws the function key row */
00829                         update_pc98_function_row(pc98_function_row_mode,true);
00830 
00831                         INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page);
00832                         Real_INT10_SetCursorPos(0,0,page);
00833                         ClearAnsi();
00834                     }
00835                     else {
00836                         LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unknown char %c after a esc",data[count]); /*prob () */
00837                         ClearAnsi();
00838                     }
00839                     break;
00840                 case 'D':/* cursor DOWN (with scrolling) */
00841                     ESC_D();
00842                     break;
00843                 case 'E':/* cursor DOWN, carriage return (with scrolling) */
00844                     ESC_E();
00845                     break;
00846                 case 'M':/* cursor UP (with scrolling) */ 
00847                     ESC_M();
00848                     break;
00849                 case '=':/* cursor position */
00850                     ansi.equcurp=true;
00851                     ansi.sci=true;
00852                     break;
00853                 case '7': /* save cursor pos + attr TODO */
00854                 case '8': /* restore this TODO */
00855                 default:
00856                     LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unknown char %c after a esc",data[count]); /*prob () */
00857                     ClearAnsi();
00858                     break;
00859             }
00860             count++;
00861             continue;
00862         }
00863         /*ansi.esc and ansi.sci are true */
00864         if (!dos.internal_output) ansi.enabled=true;
00865         page = real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00866         if (ansi.equcurp) { /* proprietary ESC = Y X command */
00867             ansi.data[ansi.numberofarg++] = data[count];
00868             if (ansi.numberofarg >= 2) {
00869                 ESC_EQU_cursor_pos(); /* clears ANSI state */
00870             }
00871         }
00872         else if (isdigit(data[count])) {
00873             assert(ansi.numberofarg < NUMBER_ANSI_DATA);
00874             ansi.data[ansi.numberofarg]=10*ansi.data[ansi.numberofarg]+(data[count]-'0');
00875         }
00876         else if (data[count] == ';') {
00877             if ((ansi.numberofarg+1) < NUMBER_ANSI_DATA)
00878                 ansi.numberofarg++;
00879         }
00880         else if (ansi.pc98rab) {
00881             assert(IS_PC98_ARCH);
00882 
00883             switch(data[count]){
00884                 case 'h': /* SET   MODE (if code =7 enable linewrap) */
00885                 case 'l': /* RESET MODE */
00886                     switch (ansi.data[0]) {
00887                         case 1: // show/hide function key row
00888                             update_pc98_function_row(data[count] == 'l');
00889                             ansi.nrows = real_readb(0x60,0x112)+1;
00890                             break;
00891                         case 3: // clear screen (doesn't matter if l or h)
00892                             INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page);
00893                             Real_INT10_SetCursorPos(0,0,page);
00894                             break;
00895                         case 5: // show/hide cursor
00896                             void PC98_show_cursor(bool show);
00897                             PC98_show_cursor(data[count] == 'l');
00898                             mem_writeb(0x71B,data[count] == 'l' ? 0x01 : 0x00); /* 60:11B cursor display state */
00899                             break;
00900                         default:
00901                             LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled esc [ > %d %c",ansi.data[0],data[count]);
00902                             break;
00903                     };
00904 
00905                     ClearAnsi();
00906                     break;
00907                 default:
00908                     LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled char %c in esc [ >",data[count]);
00909                     ClearAnsi();
00910                     break;
00911             }
00912         }
00913         else {
00914             switch(data[count]){
00915                 case 'm':               /* SGR */
00916                     // NEC's ANSI driver always resets at the beginning
00917                     if(IS_PC98_ARCH) {
00918                         ansi.attr = DefaultANSIAttr();
00919                     }
00920                     for(i=0;i<=ansi.numberofarg;i++){ 
00921                         const Bit8u COLORFLAGS[][8] = {
00922                         //  Black   Red Green Yellow Blue  Pink  Cyan  White
00923                             { 0x0,  0x4,  0x2,  0x6,  0x1,  0x5,  0x3,  0x7 }, /*   IBM */
00924                             { 0x0, 0x40, 0x80, 0xC0, 0x20, 0x60, 0xA0, 0xE0 }, /* PC-98 */
00925                         };
00926                         const auto &flagset = COLORFLAGS[IS_PC98_ARCH];
00927 
00928                         if(IS_PC98_ARCH) {
00929                             // Convert alternate color codes to regular ones
00930                             if(ansi.data[i] >= 17 && ansi.data[i] <= 23) {
00931                                 const Bit8u convtbl[] = {
00932                                     31, 34, 35, 32, 33, 36, 37
00933                                 };
00934                                 ansi.data[i] = convtbl[ansi.data[i] - 17];
00935                             }
00936                         }
00937 
00938                         switch(ansi.data[i]){
00939                             case 0: /* normal */
00940                                 //Real ansi does this as well. (should do current defaults)
00941                                 ansi.attr = DefaultANSIAttr();
00942                                 break;
00943                             case 1: /* bold mode on*/
00944                                 // FIXME: According to http://www.ninton.co.jp/?p=11, this
00945                                 // should set some sort of "highlight" flag in monochrome
00946                                 // mode, but I have no idea how to even enter that mode.
00947                                 ansi.attr |= IS_PC98_ARCH ? 0 : 0x08;
00948                                 break;
00949                             case 2: /* PC-98 "Bit 4" */
00950                                 ansi.attr |= IS_PC98_ARCH ? 0x10 : 0;
00951                                 break;
00952                             case 4: /* underline */
00953                                 if(IS_PC98_ARCH) {
00954                                     ansi.attr |= 0x08;
00955                                 } else {
00956                                     LOG(LOG_IOCTL, LOG_NORMAL)("ANSI:no support for underline yet");
00957                                 }
00958                                 break;
00959                             case 5: /* blinking */
00960                                 ansi.attr |= IS_PC98_ARCH ? 0x02 : 0x80;
00961                                 break;
00962                             case 7: /* reverse */
00963                                 //Just like real ansi. (should do use current colors reversed)
00964                                 if(IS_PC98_ARCH) {
00965                                     ansi.attr |= 0x04;
00966                                 } else {
00967                                     ansi.attr = 0x70;
00968                                 }
00969                                 break;
00970                             case 8: /* PC-98 secret */
00971                             case 16:
00972                                 ansi.attr &= IS_PC98_ARCH ? 0xFE : 0xFF;
00973                                 break;
00974                             case 30: /* fg color black */
00975                             case 31: /* fg color red */
00976                             case 32: /* fg color green */
00977                             case 33: /* fg color yellow */
00978                             case 34: /* fg color blue */
00979                             case 35: /* fg color magenta */
00980                             case 36: /* fg color cyan */
00981                             case 37: /* fg color white */
00982                                 ansi.attr &= ~(flagset[7]);
00983                                 ansi.attr |= (flagset[ansi.data[i] - 30]);
00984                                 break;
00985                             case 40:
00986                             case 41:
00987                             case 42:
00988                             case 43:
00989                             case 44:
00990                             case 45:
00991                             case 46:
00992                             case 47: {
00993                                 Bit8u shift = IS_PC98_ARCH ? 0 : 4;
00994                                 ansi.attr &= ~(flagset[7] << shift);
00995                                 ansi.attr |= (flagset[ansi.data[i] - 40] << shift);
00996                                 ansi.attr |= IS_PC98_ARCH ? 0x04 : 0;
00997                                 break;
00998                             }
00999                             default:
01000                                 break;
01001                         }
01002                     }
01003                     if (IS_PC98_ARCH) mem_writeb(0x71D,ansi.attr); // 60:11D
01004                     ClearAnsi();
01005                     break;
01006                 case 'f':
01007                 case 'H':/* Cursor Pos*/
01008                     if(!ansi.warned) { //Inform the debugger that ansi is used.
01009                         ansi.warned = true;
01010                         LOG(LOG_IOCTL,LOG_WARN)("ANSI SEQUENCES USED");
01011                     }
01012                     if (!IS_PC98_ARCH) {
01013                         ansi.ncols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
01014                         ansi.nrows = real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1;
01015                     }
01016                     /* Turn them into positions that are on the screen */
01017                     if(ansi.data[0] == 0) ansi.data[0] = 1;
01018                     if(ansi.data[1] == 0) ansi.data[1] = 1;
01019                     if(ansi.data[0] > ansi.nrows) ansi.data[0] = (Bit8u)ansi.nrows;
01020                     if(ansi.data[1] > ansi.ncols) ansi.data[1] = (Bit8u)ansi.ncols;
01021                     Real_INT10_SetCursorPos(--(ansi.data[0]),--(ansi.data[1]),page); /*ansi=1 based, int10 is 0 based */
01022                     ClearAnsi();
01023                     break;
01024                     /* cursor up down and forward and backward only change the row or the col not both */
01025                 case 'A': /* cursor up*/
01026                     ESC_BRACKET_A();
01027                     break;
01028                 case 'B': /*cursor Down */
01029                     ESC_BRACKET_B();
01030                     break;
01031                 case 'C': /*cursor forward */
01032                     ESC_BRACKET_C();
01033                     break;
01034                 case 'D': /*Cursor Backward  */
01035                     ESC_BRACKET_D();
01036                     break;
01037                 case 'J': /*erase screen and move cursor home*/
01038                     if(ansi.data[0]==0) ansi.data[0]=2;
01039                     if(ansi.data[0]!=2) {/* every version behaves like type 2 */
01040                         LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: esc[%dJ called : not supported handling as 2",ansi.data[0]);
01041                     }
01042                     INT10_ScrollWindow(0,0,255,255,0,ansi.attr,page);
01043                     ClearAnsi();
01044                     Real_INT10_SetCursorPos(0,0,page);
01045                     break;
01046                 case 'h': /* SET   MODE (if code =7 enable linewrap) */
01047                 case 'I': /* RESET MODE */
01048                     LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: set/reset mode called(not supported)");
01049                     ClearAnsi();
01050                     break;
01051                 case 'u': /* Restore Cursor Pos */
01052                     Real_INT10_SetCursorPos(ansi.saverow,ansi.savecol,page);
01053                     ClearAnsi();
01054                     break;
01055                 case 's': /* SAVE CURSOR POS */
01056                     ansi.savecol=CURSOR_POS_COL(page);
01057                     ansi.saverow=CURSOR_POS_ROW(page);
01058                     ClearAnsi();
01059                     break;
01060                 case 'K': /* erase till end of line (don't touch cursor) */
01061                     col = CURSOR_POS_COL(page);
01062                     row = CURSOR_POS_ROW(page);
01063                     if (!IS_PC98_ARCH) {
01064                         ansi.ncols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
01065                     }
01066                                         INT10_WriteChar(' ',ansi.attr,page,ansi.ncols-col,true); //Real_WriteChar(ansi.ncols-col,row,page,' ',ansi.attr,true);
01067 
01068                     //for(i = col;i<(Bitu) ansi.ncols; i++) INT10_TeletypeOutputAttr(' ',ansi.attr,true);
01069                     Real_INT10_SetCursorPos(row,col,page);
01070                     ClearAnsi();
01071                     break;
01072                 case 'M': /* delete line (NANSI) */
01073                     row = CURSOR_POS_ROW(page);
01074                     if (!IS_PC98_ARCH) {
01075                         ansi.ncols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
01076                         ansi.nrows = real_readb(BIOSMEM_SEG,BIOSMEM_NB_ROWS) + 1;
01077                     }
01078                                         INT10_ScrollWindow(row,0,ansi.nrows-1,ansi.ncols-1,ansi.data[0]? -ansi.data[0] : -1,ansi.attr,0xFF);
01079                     ClearAnsi();
01080                     break;
01081                 case '>':/* proprietary NEC PC-98 MS-DOS codes (??) */
01082                     if (IS_PC98_ARCH) {
01083                         ansi.pc98rab = true;
01084                     }
01085                     else {
01086                         LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: ESC [ > not supported outside PC-98 mode");
01087                         ClearAnsi();
01088                     }
01089                     break;
01090                 case 'l':/* (if code =7) disable linewrap */
01091                 case 'p':/* reassign keys (needs strings) */
01092                 case 'i':/* printer stuff */
01093                 default:
01094                     LOG(LOG_IOCTL,LOG_NORMAL)("ANSI: unhandled char %c in esc[",data[count]);
01095                     ClearAnsi();
01096                     break;
01097             }
01098         }
01099         count++;
01100     }
01101     *size=count;
01102     return true;
01103 }
01104 
01105 bool device_CON::Seek(Bit32u * pos,Bit32u type) {
01106     (void)pos; // UNUSED
01107     (void)type; // UNUSED
01108         // seek is valid
01109         *pos = 0;
01110         return true;
01111 }
01112 
01113 bool device_CON::Close() {
01114         return true;
01115 }
01116 
01117 extern bool dos_con_use_int16_to_detect_input;
01118 
01119 Bit16u device_CON::GetInformation(void) {
01120         if (dos_con_use_int16_to_detect_input || IS_PC98_ARCH) {
01121                 Bit16u ret = 0x80D3; /* No Key Available */
01122 
01123                 /* DOSBox-X behavior: Use INT 16h AH=0x11 Query keyboard status/preview key.
01124                  * The reason we do this is some DOS programs actually rely on hooking INT 16h
01125                  * to manipulate, hide, or transform what the DOS CON driver sees as well as
01126                  * itself. Perhaps the most disgusting example of this behavior would be the
01127                  * SCANDISK.EXE utility in Microsoft MS-DOS 6.22, which apparently relies on
01128                  * hooking INT 16h in this way to catch the Escape, CTRL+C, and some other
01129                  * scan codes in order to "eat" the scan codes before they get back to DOS.
01130                  * The reason they can get away with it apparently and still respond properly
01131                  * to those keys, is because the MS-DOS 6.22 CON driver always calls INT 16h
01132                  * AH=0x11 first before calling INT 16h AH=0x10 to fetch the scan code.
01133                  *
01134                  * Without this fix, SCANDISK.EXE does not respond properly to Escape and
01135                  * a few other keys. Pressing Escape will do nothing until you hit any other
01136                  * key, at which point it suddenly acts upon the Escape key.
01137                  *
01138                  * Since Scandisk is using INT 21h AH=0x0B to query STDIN during this time,
01139                  * this implementation is a good "halfway" compromise in that this call
01140                  * will trigger the INT 16h AH=0x11 hook it relies on. */
01141                 if (readcache || dev_con_pos < dev_con_max) return 0x8093; /* key available */
01142 
01143                 Bit16u saved_ax = reg_ax;
01144 
01145                 reg_ah = (IS_EGAVGA_ARCH)?0x11:0x1; // check for keystroke
01146 
01147         /* FIXME: PC-98 emulation should eventually use CONIO emulation that
01148          *        better emulates the actual platform. The purpose of this
01149          *        hack is to allow our code to call into INT 16h without
01150          *        setting up an INT 16h vector */
01151         if (IS_PC98_ARCH)
01152             INT16_Handler_Wrap();
01153         else
01154             CALLBACK_RunRealInt(0x16);
01155 
01156         if (!GETFLAG(ZF)) { /* key is present, waiting to be returned on AH=0x10 or AH=0x00 */
01157             if (IS_PC98_ARCH && reg_al == 0) {
01158                 /* some scan codes are ignored by CON, and wouldn't read anything.
01159                  * while we're at it, take the scan code and convert it into ANSI here
01160                  * so that Read() returns it immediately instead of doing this conversion itself.
01161                  * This way we never block when we SAID a key was available that gets ignored. */
01162                 if (CommonPC98ExtScanConversionToReadBuf(reg_ah))
01163                     ret = 0x8093; /* Key Available */
01164                 else
01165                     ret = 0x80D3; /* No Key Available */
01166 
01167                 /* need to consume the key. if it generated anything it will be returned to Read()
01168                  * through dev_con_readbuf[] */
01169                 reg_ah=0x0;
01170 
01171                 /* FIXME: PC-98 emulation should eventually use CONIO emulation that
01172                  *        better emulates the actual platform. The purpose of this
01173                  *        hack is to allow our code to call into INT 16h without
01174                  *        setting up an INT 16h vector */
01175                 INT16_Handler_Wrap();
01176             }
01177             else {
01178                 ret = 0x8093; /* Key Available */
01179             }
01180         }
01181 
01182                 reg_ax = saved_ax;
01183                 return ret;
01184         }
01185         else {
01186                 /* DOSBox mainline behavior: alternate "fast" way through direct manipulation of keyboard scan buffer */
01187                 Bit16u head=mem_readw(BIOS_KEYBOARD_BUFFER_HEAD);
01188                 Bit16u tail=mem_readw(BIOS_KEYBOARD_BUFFER_TAIL);
01189 
01190                 if ((head==tail) && !readcache) return 0x80D3;  /* No Key Available */
01191                 if (readcache || real_readw(0x40,head)) return 0x8093;          /* Key Available */
01192 
01193                 /* remove the zero from keyboard buffer */
01194                 Bit16u start=mem_readw(BIOS_KEYBOARD_BUFFER_START);
01195                 Bit16u end      =mem_readw(BIOS_KEYBOARD_BUFFER_END);
01196                 head+=2;
01197                 if (head>=end) head=start;
01198                 mem_writew(BIOS_KEYBOARD_BUFFER_HEAD,head);
01199         }
01200 
01201         return 0x80D3; /* No Key Available */
01202 }
01203 
01204 device_CON::device_CON() {
01205         Section_prop *section=static_cast<Section_prop *>(control->GetSection("dos"));
01206 
01207         SetName("CON");
01208         readcache=0;
01209 
01210     if (IS_PC98_ARCH) {
01211         /* On real MS-DOS for PC-98, ANSI.SYS is effectively part of the DOS kernel, and cannot be turned off. */
01212         ansi.installed=true;
01213     }
01214     else {
01215         /* Otherwise (including IBM systems), ANSI.SYS is not installed by default but can be added to CONFIG.SYS.
01216          * For compatibility with DOSBox SVN and other forks ANSI.SYS is installed by default. */
01217         ansi.installed=section->Get_bool("ansi.sys");
01218     }
01219 
01220         ansi.enabled=false;
01221         ansi.attr=DefaultANSIAttr();
01222     if (IS_PC98_ARCH) {
01223         // NTS: On real hardware, the BIOS does NOT manage the console at all.
01224         //      TTY handling is entirely handled by MS-DOS.
01225         ansi.ncols=80;
01226         ansi.nrows=25 - 1;
01227         // the DOS kernel will call on this function to disable, and SDLmain
01228         // will call on to enable
01229     }
01230         ansi.saverow=0;
01231         ansi.savecol=0;
01232         ansi.warned=false;
01233         ClearAnsi();
01234 }
01235 
01236 void device_CON::ClearAnsi(void){
01237         for(Bit8u i=0; i<NUMBER_ANSI_DATA;i++) ansi.data[i]=0;
01238     ansi.pc98rab=false;
01239     ansi.equcurp=false;
01240         ansi.esc=false;
01241         ansi.sci=false;
01242         ansi.numberofarg=0;
01243 }
01244 
01245 void device_CON::Output(Bit8u chr) {
01246         if (dos.internal_output || ansi.enabled) {
01247                 if (CurMode->type==M_TEXT) {
01248                         Bit8u page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
01249                         Bit8u col=CURSOR_POS_COL(page);
01250                         Bit8u row=CURSOR_POS_ROW(page);
01251                         BIOS_NCOLS;BIOS_NROWS;
01252                         if (nrows==row+1 && (chr=='\n' || (ncols==col+1 && chr!='\r' && chr!=8 && chr!=7))) {
01253                                 INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,ansi.attr,page);
01254                                 INT10_SetCursorPos(row-1,col,page);
01255                         }
01256                 }
01257                 Real_INT10_TeletypeOutputAttr(chr,ansi.attr,true);
01258         } else Real_INT10_TeletypeOutput(chr,DefaultANSIAttr());
01259 }
01260 
01261 bool device_CON::ANSI_SYS_installed() {
01262     return ansi.installed;
01263 }
01264