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