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