DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/ints/int10_char.cpp
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 /* Character displaying moving functions */
00021 
00022 #include "dosbox.h"
00023 #include "bios.h"
00024 #include "mem.h"
00025 #include "inout.h"
00026 #include "int10.h"
00027 #include "shiftjis.h"
00028 #include "callback.h"
00029 
00030 Bit8u DefaultANSIAttr();
00031 
00032 #if defined(_MSC_VER)
00033 # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */
00034 #endif
00035 
00036 static void MCGA2_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
00037     Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
00038     PhysPt dest=base+((CurMode->twidth*rnew)*cheight+cleft);
00039     PhysPt src=base+((CurMode->twidth*rold)*cheight+cleft);
00040     Bitu copy=(Bitu)(cright-cleft);
00041     Bitu nextline=CurMode->twidth;
00042     for (Bitu i=0;i<cheight;i++) {
00043         MEM_BlockCopy(dest,src,copy);
00044         dest+=nextline;src+=nextline;
00045     }
00046 }
00047 
00048 static void CGA2_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
00049     Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
00050     PhysPt dest=base+((CurMode->twidth*rnew)*(cheight/2)+cleft);
00051     PhysPt src=base+((CurMode->twidth*rold)*(cheight/2)+cleft);
00052     Bitu copy=(Bitu)(cright-cleft);
00053     Bitu nextline=CurMode->twidth;
00054     for (Bitu i=0;i<cheight/2U;i++) {
00055         MEM_BlockCopy(dest,src,copy);
00056         MEM_BlockCopy(dest+8*1024,src+8*1024,copy);
00057         dest+=nextline;src+=nextline;
00058     }
00059 }
00060 
00061 static void CGA4_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
00062     Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
00063     PhysPt dest=base+((CurMode->twidth*rnew)*(cheight/2)+cleft)*2;
00064     PhysPt src=base+((CurMode->twidth*rold)*(cheight/2)+cleft)*2;   
00065     Bitu copy=(Bitu)(cright-cleft)*2u;Bitu nextline=(Bitu)CurMode->twidth*2u;
00066     for (Bitu i=0;i<cheight/2U;i++) {
00067         MEM_BlockCopy(dest,src,copy);
00068         MEM_BlockCopy(dest+8*1024,src+8*1024,copy);
00069         dest+=nextline;src+=nextline;
00070     }
00071 }
00072 
00073 static void TANDY16_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
00074     Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
00075     Bit8u banks=CurMode->twidth/10;
00076     PhysPt dest=base+((CurMode->twidth*rnew)*(cheight/banks)+cleft)*4;
00077     PhysPt src=base+((CurMode->twidth*rold)*(cheight/banks)+cleft)*4;
00078     Bitu copy=(Bitu)(cright-cleft)*4u;Bitu nextline=(Bitu)CurMode->twidth*4u;
00079     for (Bitu i=0;i<static_cast<Bitu>(cheight/banks);i++) {
00080                 for (Bitu b=0;b<banks;b++) MEM_BlockCopy(dest+b*8*1024,src+b*8*1024,copy);
00081         dest+=nextline;src+=nextline;
00082     }
00083 }
00084 
00085 static void EGA16_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
00086     PhysPt src,dest;Bitu copy;
00087     Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
00088     dest=base+(CurMode->twidth*rnew)*cheight+cleft;
00089     src=base+(CurMode->twidth*rold)*cheight+cleft;
00090     Bitu nextline=(Bitu)CurMode->twidth;
00091     /* Setup registers correctly */
00092     IO_Write(0x3ce,5);IO_Write(0x3cf,1);        /* Memory transfer mode */
00093     IO_Write(0x3c4,2);IO_Write(0x3c5,0xf);      /* Enable all Write planes */
00094     /* Do some copying */
00095     Bitu rowsize=(Bitu)(cright-cleft);
00096     copy=cheight;
00097     for (;copy>0;copy--) {
00098         for (Bitu x=0;x<rowsize;x++) mem_writeb(dest+x,mem_readb(src+x));
00099         dest+=nextline;src+=nextline;
00100     }
00101     /* Restore registers */
00102     IO_Write(0x3ce,5);IO_Write(0x3cf,0);        /* Normal transfer mode */
00103 }
00104 
00105 static void VGA_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
00106     PhysPt src,dest;Bitu copy;
00107     Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
00108     dest=base+8u*((CurMode->twidth*rnew)*cheight+cleft);
00109     src=base+8u*((CurMode->twidth*rold)*cheight+cleft);
00110     Bitu nextline=8u*(Bitu)CurMode->twidth;
00111     Bitu rowsize=8u*(Bitu)(cright-cleft);
00112     copy=cheight;
00113     for (;copy>0;copy--) {
00114         for (Bitu x=0;x<rowsize;x++) mem_writeb(dest+x,mem_readb(src+x));
00115         dest+=nextline;src+=nextline;
00116     }
00117 }
00118 
00119 static void TEXT_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
00120     PhysPt src,dest;
00121     src=base+(rold*CurMode->twidth+cleft)*2u;
00122     dest=base+(rnew*CurMode->twidth+cleft)*2u;
00123     MEM_BlockCopy(dest,src,(Bitu)(cright-cleft)*2u);
00124 }
00125 
00126 static void PC98_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
00127     PhysPt src,dest;
00128 
00129     /* character data */
00130     src=base+(rold*CurMode->twidth+cleft)*2;
00131     dest=base+(rnew*CurMode->twidth+cleft)*2;
00132     MEM_BlockCopy(dest,src,(Bitu)(cright-cleft)*2u);
00133 
00134     /* attribute data */
00135     MEM_BlockCopy(dest+0x2000,src+0x2000,(Bitu)(cright-cleft)*2u);
00136 }
00137 
00138 static void MCGA2_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
00139     Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
00140     PhysPt dest=base+((CurMode->twidth*row)*cheight+cleft);
00141     Bitu copy=(Bitu)(cright-cleft);
00142     Bitu nextline=CurMode->twidth;
00143     attr=(attr & 0x3) | ((attr & 0x3) << 2) | ((attr & 0x3) << 4) | ((attr & 0x3) << 6);
00144     for (Bitu i=0;i<cheight;i++) {
00145         for (Bitu x=0;x<copy;x++) {
00146             mem_writeb(dest+x,attr);
00147         }
00148         dest+=nextline;
00149     }
00150 }
00151 
00152 static void CGA2_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
00153     Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
00154     PhysPt dest=base+((CurMode->twidth*row)*(cheight/2)+cleft);
00155     Bitu copy=(Bitu)(cright-cleft);
00156     Bitu nextline=CurMode->twidth;
00157     attr=(attr & 0x3) | ((attr & 0x3) << 2) | ((attr & 0x3) << 4) | ((attr & 0x3) << 6);
00158     for (Bitu i=0;i<cheight/2U;i++) {
00159         for (Bitu x=0;x<copy;x++) {
00160             mem_writeb(dest+x,attr);
00161             mem_writeb(dest+8*1024+x,attr);
00162         }
00163         dest+=nextline;
00164     }
00165 }
00166 
00167 static void CGA4_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
00168     Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
00169     PhysPt dest=base+((CurMode->twidth*row)*(cheight/2)+cleft)*2;
00170     Bitu copy=(Bitu)(cright-cleft)*2u;Bitu nextline=CurMode->twidth*2;
00171     attr=(attr & 0x3) | ((attr & 0x3) << 2) | ((attr & 0x3) << 4) | ((attr & 0x3) << 6);
00172     for (Bitu i=0;i<cheight/2U;i++) {
00173         for (Bitu x=0;x<copy;x++) {
00174             mem_writeb(dest+x,attr);
00175             mem_writeb(dest+8*1024+x,attr);
00176         }
00177         dest+=nextline;
00178     }
00179 }
00180 
00181 static void TANDY16_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
00182     Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
00183     Bit8u banks=CurMode->twidth/10;
00184     PhysPt dest=base+((CurMode->twidth*row)*(cheight/banks)+cleft)*4;
00185     Bitu copy=(Bitu)(cright-cleft)*4u;Bitu nextline=CurMode->twidth*4;
00186     attr=(attr & 0xf) | (attr & 0xf) << 4;
00187     for (Bitu i=0;i<static_cast<Bitu>(cheight/banks);i++) {     
00188         for (Bitu x=0;x<copy;x++) {
00189             for (Bitu b=0;b<banks;b++) mem_writeb(dest+b*8*1024+x,attr);
00190         }
00191         dest+=nextline;
00192     }
00193 }
00194 
00195 static void EGA16_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
00196     /* Set Bitmask / Color / Full Set Reset */
00197     IO_Write(0x3ce,0x8);IO_Write(0x3cf,0xff);
00198     IO_Write(0x3ce,0x0);IO_Write(0x3cf,attr);
00199     IO_Write(0x3ce,0x1);IO_Write(0x3cf,0xf);
00200     /* Enable all Write planes */
00201     IO_Write(0x3c4,2);IO_Write(0x3c5,0xf);
00202     /* Write some bytes */
00203     Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
00204     PhysPt dest=base+(CurMode->twidth*row)*cheight+cleft;   
00205     Bitu nextline=CurMode->twidth;
00206     Bitu copy = cheight;Bitu rowsize=(Bitu)(cright-cleft);
00207     for (;copy>0;copy--) {
00208         for (Bitu x=0;x<rowsize;x++) mem_writeb(dest+x,0xff);
00209         dest+=nextline;
00210     }
00211     IO_Write(0x3cf,0);
00212 }
00213 
00214 static void VGA_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
00215     /* Write some bytes */
00216     Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
00217     PhysPt dest=base+8*((CurMode->twidth*row)*cheight+cleft);
00218     Bitu nextline=8*CurMode->twidth;
00219     Bitu copy = cheight;Bitu rowsize=8u*(Bitu)(cright-cleft);
00220     for (;copy>0;copy--) {
00221         for (Bitu x=0;x<rowsize;x++) mem_writeb(dest+x,attr);
00222         dest+=nextline;
00223     }
00224 }
00225 
00226 static void PC98_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
00227     /* Do some filing */
00228     PhysPt dest;
00229     dest=base+(row*CurMode->twidth+cleft)*2;
00230     Bit16u fill=' ';
00231     Bit16u fattr=attr ? attr : DefaultANSIAttr();
00232     for (Bit8u x=0;x<(Bitu)(cright-cleft);x++) {
00233         mem_writew(dest,fill);
00234         mem_writew(dest+0x2000,fattr);
00235         dest+=2;
00236     }
00237 }
00238 
00239 static void TEXT_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
00240     /* Do some filing */
00241     PhysPt dest;
00242     dest=base+(row*CurMode->twidth+cleft)*2;
00243     Bit16u fill=(attr<<8)+' ';
00244     for (Bit8u x=0;x<(Bitu)(cright-cleft);x++) {
00245         mem_writew(dest,fill);
00246         dest+=2;
00247     }
00248 }
00249 
00250 
00251 void INT10_ScrollWindow(Bit8u rul,Bit8u cul,Bit8u rlr,Bit8u clr,Bit8s nlines,Bit8u attr,Bit8u page) {
00252 /* Do some range checking */
00253     if (CurMode->type!=M_TEXT) page=0xff;
00254     BIOS_NCOLS;BIOS_NROWS;
00255     if(rul>rlr) return;
00256     if(cul>clr) return;
00257     if(rlr>=nrows) rlr=(Bit8u)nrows-1;
00258     if(clr>=ncols) clr=(Bit8u)ncols-1;
00259     clr++;
00260 
00261     /* Get the correct page: current start address for current page (0xFF),
00262        otherwise calculate from page number and page size */
00263     PhysPt base=CurMode->pstart;
00264     if (page==0xff) base+=real_readw(BIOSMEM_SEG,BIOSMEM_CURRENT_START);
00265     else base+=(unsigned int)page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
00266     
00267     if (GCC_UNLIKELY(machine==MCH_PCJR)) {
00268         if (real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_MODE) >= 9) {
00269             // PCJr cannot handle these modes at 0xb800
00270             // See INT10_PutPixel M_TANDY16
00271             Bitu cpupage =
00272                 (real_readb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE) >> 3) & 0x7;
00273 
00274             base = cpupage << 14;
00275             if (page!=0xff)
00276                 base += (unsigned int)page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
00277         }
00278     }
00279 
00280     /* See how much lines need to be copied */
00281     Bit8u start,end;Bits next;
00282     /* Copy some lines */
00283     if (nlines>0) {
00284         start=rlr-nlines+1;
00285         end=rul;
00286         next=-1;
00287     } else if (nlines<0) {
00288         start=rul-nlines-1;
00289         end=rlr;
00290         next=1;
00291     } else {
00292         nlines=rlr-rul+1;
00293         goto filling;
00294     }
00295     while (start!=end) {
00296         start+=next;
00297         switch (CurMode->type) {
00298         case M_PC98:
00299             PC98_CopyRow(cul,clr,start,start+nlines,base);break;
00300         case M_TEXT:
00301             TEXT_CopyRow(cul,clr,start,start+nlines,base);break;
00302         case M_CGA2:
00303             if (machine == MCH_MCGA && CurMode->mode == 0x11)
00304                 MCGA2_CopyRow(cul,clr,start,start+nlines,base);
00305             else
00306                 CGA2_CopyRow(cul,clr,start,start+nlines,base);
00307             break;
00308         case M_CGA4:
00309             CGA4_CopyRow(cul,clr,start,start+nlines,base);break;
00310         case M_TANDY16:
00311             TANDY16_CopyRow(cul,clr,start,start+nlines,base);break;
00312         case M_EGA:     
00313             EGA16_CopyRow(cul,clr,start,start+nlines,base);break;
00314         case M_VGA:     
00315             VGA_CopyRow(cul,clr,start,start+nlines,base);break;
00316         case M_LIN4:
00317             if ((machine==MCH_VGA) && (svgaCard==SVGA_TsengET4K) &&
00318                     (CurMode->swidth<=800)) {
00319                 // the ET4000 BIOS supports text output in 800x600 SVGA
00320                 EGA16_CopyRow(cul,clr,start,start+nlines,base);break;
00321             }
00322             // fall-through
00323         default:
00324             LOG(LOG_INT10,LOG_ERROR)("Unhandled mode %d for scroll",CurMode->type);
00325         }   
00326     } 
00327     /* Fill some lines */
00328 filling:
00329     if (nlines>0) {
00330         start=rul;
00331     } else {
00332         nlines=-nlines;
00333         start=rlr-nlines+1;
00334     }
00335     for (;nlines>0;nlines--) {
00336         switch (CurMode->type) {
00337         case M_PC98:
00338             PC98_FillRow(cul,clr,start,base,attr);break;
00339         case M_TEXT:
00340             TEXT_FillRow(cul,clr,start,base,attr);break;
00341         case M_CGA2:
00342             if (machine == MCH_MCGA && CurMode->mode == 0x11)
00343                 MCGA2_FillRow(cul,clr,start,base,attr);
00344             else
00345                 CGA2_FillRow(cul,clr,start,base,attr);
00346             break;
00347         case M_CGA4:
00348             CGA4_FillRow(cul,clr,start,base,attr);break;
00349         case M_TANDY16:     
00350             TANDY16_FillRow(cul,clr,start,base,attr);break;
00351         case M_EGA:     
00352             EGA16_FillRow(cul,clr,start,base,attr);break;
00353         case M_VGA:     
00354             VGA_FillRow(cul,clr,start,base,attr);break;
00355         case M_LIN4:
00356             if ((machine==MCH_VGA) && (svgaCard==SVGA_TsengET4K) &&
00357                     (CurMode->swidth<=800)) {
00358                 EGA16_FillRow(cul,clr,start,base,attr);break;
00359             }
00360             // fall-through
00361         default:
00362             LOG(LOG_INT10,LOG_ERROR)("Unhandled mode %d for scroll",CurMode->type);
00363         }   
00364         start++;
00365     } 
00366 }
00367 
00368 void INT10_SetActivePage(Bit8u page) {
00369     Bit16u mem_address;
00370     if (page>7) LOG(LOG_INT10,LOG_ERROR)("INT10_SetActivePage page %d",page);
00371 
00372     if (IS_EGAVGA_ARCH && (svgaCard==SVGA_S3Trio)) page &= 7;
00373 
00374     mem_address=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
00375     /* Write the new page start */
00376     real_writew(BIOSMEM_SEG,BIOSMEM_CURRENT_START,mem_address);
00377     if (IS_EGAVGA_ARCH) {
00378         if (CurMode->mode<8) mem_address>>=1;
00379         // rare alternative: if (CurMode->type==M_TEXT)  mem_address>>=1;
00380     } else {
00381         mem_address>>=1;
00382     }
00383     /* Write the new start address in vgahardware */
00384     Bit16u base=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
00385     IO_Write(base,0x0cu);
00386     IO_Write(base+1u,(Bit8u)(mem_address>>8u));
00387     IO_Write(base,0x0du);
00388     IO_Write(base+1u,(Bit8u)mem_address);
00389 
00390     // And change the BIOS page
00391     real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE,page);
00392     Bit8u cur_row=CURSOR_POS_ROW(page);
00393     Bit8u cur_col=CURSOR_POS_COL(page);
00394     // Display the cursor, now the page is active
00395     INT10_SetCursorPos(cur_row,cur_col,page);
00396 }
00397 
00398 void INT10_SetCursorShape(Bit8u first,Bit8u last) {
00399     real_writew(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE,last|(first<<8u));
00400     if (machine==MCH_CGA || machine==MCH_MCGA || machine==MCH_AMSTRAD) goto dowrite;
00401     if (IS_TANDY_ARCH) goto dowrite;
00402     /* Skip CGA cursor emulation if EGA/VGA system is active */
00403     if (!(real_readb(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL) & 0x8)) {
00404         /* Check for CGA type 01, invisible */
00405         if ((first & 0x60) == 0x20) {
00406             first=0x1e;
00407             last=0x00;
00408             goto dowrite;
00409         }
00410         /* Check if we need to convert CGA Bios cursor values */
00411         if (!(real_readb(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL) & 0x1)) { // set by int10 fun12 sub34
00412 //          if (CurMode->mode>0x3) goto dowrite;    //Only mode 0-3 are text modes on cga
00413             if ((first & 0xe0) || (last & 0xe0)) goto dowrite;
00414             Bit8u cheight=real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT)-1;
00415             /* Creative routine i based of the original ibmvga bios */
00416 
00417             if (last<first) {
00418                 if (!last) goto dowrite;
00419                 first=last;
00420                 last=cheight;
00421             /* Test if this might be a cga style cursor set, if not don't do anything */
00422             } else if (((first | last)>=cheight) || !(last==(cheight-1)) || !(first==cheight) ) {
00423                 if (last<=3) goto dowrite;
00424                 if (first+2<last) {
00425                     if (first>2) {
00426                         first=(cheight+1)/2;
00427                         last=cheight;
00428                     } else {
00429                         last=cheight;
00430                     }
00431                 } else {
00432                     first=(first-last)+cheight;
00433                     last=cheight;
00434 
00435                     if (cheight>0xc) { // vgatest sets 15 15 2x where only one should be decremented to 14 14
00436                         first--;     // implementing int10 fun12 sub34 fixed this.
00437                         last--;
00438                     }
00439                 }
00440             }
00441 
00442         }
00443     }
00444 dowrite:
00445     Bit16u base=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
00446     IO_Write(base,0xa);IO_Write(base+1u,first);
00447     IO_Write(base,0xb);IO_Write(base+1u,last);
00448 }
00449 
00450 void vga_pc98_direct_cursor_pos(Bit16u address);
00451 
00452 void INT10_GetScreenColumns(Bit16u *cols)
00453 {
00454     if (IS_PC98_ARCH)
00455         *cols = 80; //TODO
00456     else
00457         *cols = real_readw(BIOSMEM_SEG, BIOSMEM_NB_COLS);
00458 }
00459 
00460 void INT10_GetCursorPos(Bit8u *row, Bit8u*col, const Bit8u page)
00461 {
00462     if (IS_PC98_ARCH) {
00463         *col = real_readb(0x60, 0x11C);
00464         *row = real_readb(0x60, 0x110);
00465     }
00466     else {
00467         *col = real_readb(BIOSMEM_SEG, BIOSMEM_CURSOR_POS + page * 2u);
00468         *row = real_readb(BIOSMEM_SEG, BIOSMEM_CURSOR_POS + page * 2u + 1u);
00469     }
00470 }
00471 
00472 void INT10_SetCursorPos(Bit8u row,Bit8u col,Bit8u page) {
00473     if (page>7) LOG(LOG_INT10,LOG_ERROR)("INT10_SetCursorPos page %d",page);
00474     // Bios cursor pos
00475     if (IS_PC98_ARCH) {
00476         real_writeb(0x60,0x11C,col);
00477         real_writeb(0x60,0x110,row);
00478         page = 0;
00479     }
00480     else {
00481         real_writeb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2u,col);
00482         real_writeb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2u+1u,row);
00483     }
00484     // Set the hardware cursor
00485     Bit8u current=IS_PC98_ARCH ? 0 : real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00486     if(page==current) {
00487         // Get the dimensions
00488         BIOS_NCOLS;
00489         // Calculate the address knowing nbcols nbrows and page num
00490         // NOTE: BIOSMEM_CURRENT_START counts in colour/flag pairs
00491         Bit16u address=(ncols*row)+col+(IS_PC98_ARCH ? 0 : (real_readw(BIOSMEM_SEG,BIOSMEM_CURRENT_START)/2));
00492         if (IS_PC98_ARCH) {
00493             vga_pc98_direct_cursor_pos(address);
00494         }
00495         else {
00496             // CRTC regs 0x0e and 0x0f
00497             Bit16u base=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
00498             IO_Write(base,0x0eu);
00499             IO_Write(base+1u,(Bit8u)(address>>8u));
00500             IO_Write(base,0x0fu);
00501             IO_Write(base+1u,(Bit8u)address);
00502         }
00503     }
00504 }
00505 
00506 void ReadCharAttr(Bit16u col,Bit16u row,Bit8u page,Bit16u * result) {
00507     /* Externally used by the mouse routine */
00508     PhysPt fontdata;
00509     Bit16u cols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
00510     Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
00511     bool split_chr = false;
00512     switch (CurMode->type) {
00513     case M_TEXT:
00514         {   
00515             // Compute the address  
00516             Bit16u address=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
00517             address+=(row*cols+col)*2;
00518             // read the char 
00519             PhysPt where = CurMode->pstart+address;
00520             *result=mem_readw(where);
00521         }
00522         return;
00523     case M_CGA4:
00524     case M_CGA2:
00525     case M_TANDY16:
00526         split_chr = true;
00527         switch (machine) {
00528         case MCH_CGA:
00529         case MCH_HERC:
00530             fontdata=PhysMake(0xf000,0xfa6e);
00531             break;
00532         case TANDY_ARCH_CASE:
00533             fontdata=Real2Phys(RealGetVec(0x44));
00534             break;
00535         default:
00536             fontdata=Real2Phys(RealGetVec(0x43));
00537             break;
00538         }
00539         break;
00540     default:
00541         fontdata=Real2Phys(RealGetVec(0x43));
00542         break;
00543     }
00544 
00545     Bitu x=col*8u,y=(unsigned int)row*cheight*(cols/CurMode->twidth);
00546 
00547     for (Bit16u chr=0;chr<256;chr++) {
00548 
00549         if (chr==128 && split_chr) fontdata=Real2Phys(RealGetVec(0x1f));
00550 
00551         bool error=false;
00552         Bit16u ty=(Bit16u)y;
00553         for (Bit8u h=0;h<cheight;h++) {
00554             Bit8u bitsel=128;
00555             Bit8u bitline=mem_readb(fontdata++);
00556             Bit8u res=0;
00557             Bit8u vidline=0;
00558             Bit16u tx=(Bit16u)x;
00559             while (bitsel) {
00560                 //Construct bitline in memory
00561                 INT10_GetPixel(tx,ty,page,&res);
00562                 if(res) vidline|=bitsel;
00563                 tx++;
00564                 bitsel>>=1;
00565             }
00566             ty++;
00567             if(bitline != vidline){
00568                 /* It's not character 'chr', move on to the next */
00569                 fontdata+=(unsigned int)(cheight-(unsigned int)h-1u);
00570                 error = true;
00571                 break;
00572             }
00573         }
00574         if(!error){
00575             /* We found it */
00576             *result = chr;
00577             return;
00578         }
00579     }
00580     LOG(LOG_INT10,LOG_ERROR)("ReadChar didn't find character");
00581     *result = 0;
00582 }
00583 void INT10_ReadCharAttr(Bit16u * result,Bit8u page) {
00584     if(page==0xFF) page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
00585     Bit8u cur_row=CURSOR_POS_ROW(page);
00586     Bit8u cur_col=CURSOR_POS_COL(page);
00587     ReadCharAttr(cur_col,cur_row,page,result);
00588 }
00589 
00590 void INT10_PC98_CurMode_Relocate(void) {
00591     /* deprecated */
00592 }
00593 
00594 void WriteChar(Bit16u col,Bit16u row,Bit8u page,Bit16u chr,Bit8u attr,bool useattr) {
00595     /* Externally used by the mouse routine */
00596     PhysPt fontdata;
00597     Bit16u cols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
00598     Bit8u back, cheight = IS_PC98_ARCH ? 16 : real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
00599 
00600     if (CurMode->type != M_PC98)
00601         chr &= 0xFF;
00602 
00603     switch (CurMode->type) {
00604     case M_TEXT:
00605         {   
00606             // Compute the address  
00607             Bit16u address=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
00608             address+=(row*cols+col)*2;
00609             // Write the char 
00610             PhysPt where = CurMode->pstart+address;
00611             mem_writeb(where,chr);
00612             if (useattr) mem_writeb(where+1,attr);
00613         }
00614         return;
00615     case M_PC98:
00616         {
00617             // Compute the address  
00618             Bit16u address=((row*80)+col)*2;
00619             // Write the char 
00620             PhysPt where = CurMode->pstart+address;
00621             mem_writew(where,chr);
00622             if (useattr) {
00623                 mem_writeb(where+0x2000,attr);
00624             }
00625 
00626             // some chars are double-wide and need to fill both cells.
00627             // however xx08h-xx0Bh are single-wide encodings such as the
00628             // single-wide box characters used by DOSBox-X's introductory
00629             // message.
00630             if ((chr & 0xFF00u) != 0u && (chr & 0xFCu) != 0x08u) {
00631                 where += 2u;
00632                 mem_writew(where,chr);
00633                 if (useattr) {
00634                     mem_writeb(where+0x2000,attr);
00635                 }
00636             }
00637         }
00638         return;
00639     case M_CGA4:
00640     case M_CGA2:
00641     case M_TANDY16:
00642         if (chr>=128) {
00643             chr-=128;
00644             fontdata=Real2Phys(RealGetVec(0x1f));
00645             break;
00646         }
00647         switch (machine) {
00648         case MCH_CGA:
00649         case MCH_HERC:
00650             fontdata=PhysMake(0xf000,0xfa6e);
00651             break;
00652         case TANDY_ARCH_CASE:
00653             fontdata=Real2Phys(RealGetVec(0x44));
00654             break;
00655         default:
00656             fontdata=Real2Phys(RealGetVec(0x43));
00657             break;
00658         }
00659         break;
00660     default:
00661         fontdata=Real2Phys(RealGetVec(0x43));
00662         break;
00663     }
00664     fontdata+=(unsigned int)chr*(unsigned int)cheight;
00665 
00666     if(GCC_UNLIKELY(!useattr)) { //Set attribute(color) to a sensible value
00667         static bool warned_use = false;
00668         if(GCC_UNLIKELY(!warned_use)){ 
00669             LOG(LOG_INT10,LOG_ERROR)("writechar used without attribute in non-textmode %c %X",chr,chr);
00670             warned_use = true;
00671         }
00672         switch(CurMode->type) {
00673         case M_CGA4:
00674             attr = 0x3;
00675             break;
00676         case M_CGA2:
00677             attr = 0x1;
00678             break;
00679         case M_TANDY16:
00680         case M_EGA:
00681         default:
00682             attr = 0x7;
00683             break;
00684         }
00685     }
00686 
00687     //Attribute behavior of mode 6; mode 11 does something similar but
00688     //it is in INT 10h handler because it only applies to function 09h
00689     if (CurMode->mode==0x06) attr=(attr&0x80)|1;
00690 
00691     switch (CurMode->type) {
00692     case M_VGA:
00693     case M_LIN8:
00694         // 256-color modes have background color instead of page
00695         back=page;
00696         page=0;
00697         break;
00698     case M_EGA:
00699         /* enable all planes for EGA modes (Ultima 1 colour bug) */
00700         /* might be put into INT10_PutPixel but different vga bios
00701            implementations have different opinions about this */
00702         IO_Write(0x3c4,0x2);IO_Write(0x3c5,0xf);
00703         /* fall through */
00704     default:
00705         back=attr&0x80;
00706         break;
00707     }
00708 
00709     Bitu x=col*8u,y=(unsigned int)(row*(unsigned int)cheight*(cols/CurMode->twidth));
00710 
00711     Bit16u ty=(Bit16u)y;
00712     for (Bit8u h=0;h<cheight;h++) {
00713         Bit8u bitsel=128;
00714         Bit8u bitline=mem_readb(fontdata++);
00715         Bit16u tx=(Bit16u)x;
00716         while (bitsel) {
00717             INT10_PutPixel(tx,ty,page,(bitline&bitsel)?attr:back);
00718             tx++;
00719             bitsel>>=1;
00720         }
00721         ty++;
00722     }
00723 }
00724 
00725 void INT10_WriteChar(Bit16u chr,Bit8u attr,Bit8u page,Bit16u count,bool showattr) {
00726     Bit8u pospage=page;
00727     if (CurMode->type!=M_TEXT) {
00728         showattr=true; //Use attr in graphics mode always
00729         switch (machine) {
00730             case EGAVGA_ARCH_CASE:
00731                 switch (CurMode->type) {
00732                 case M_VGA:
00733                 case M_LIN8:
00734                     pospage=0;
00735                     break;
00736                 default:
00737                     page%=CurMode->ptotal;
00738                     pospage=page;
00739                     break;
00740                 }
00741                 break;
00742             case MCH_CGA:
00743             case MCH_MCGA:
00744             case MCH_PCJR:
00745                 page=0;
00746                 pospage=0;
00747                 break;
00748             default:
00749                 break;
00750         }
00751     }
00752 
00753     Bit8u cur_row=CURSOR_POS_ROW(pospage);
00754     Bit8u cur_col=CURSOR_POS_COL(pospage);
00755     BIOS_NCOLS;
00756     while (count>0) {
00757         WriteChar(cur_col,cur_row,page,chr,attr,showattr);
00758         count--;
00759         cur_col++;
00760         if(cur_col==ncols) {
00761             cur_col=0;
00762             cur_row++;
00763         }
00764     }
00765 
00766     if (CurMode->type==M_EGA) {
00767         // Reset write ops for EGA graphics modes
00768         IO_Write(0x3ce,0x3);IO_Write(0x3cf,0x0);
00769     }
00770 }
00771 
00772 static void INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr,Bit8u page) {
00773     BIOS_NCOLS;BIOS_NROWS;
00774     Bit8u cur_row=CURSOR_POS_ROW(page);
00775     Bit8u cur_col=CURSOR_POS_COL(page);
00776     switch (chr) {
00777     case 8:
00778         if(cur_col>0) cur_col--;
00779         break;
00780     case '\r':
00781         cur_col=0;
00782         break;
00783     case '\n':
00784 //      cur_col=0; //Seems to break an old chess game
00785         cur_row++;
00786         break;
00787     case 7: /* Beep */
00788         // Prepare PIT counter 2 for ~900 Hz square wave
00789         IO_Write(0x43, 0xb6);
00790         IO_Write(0x42, 0x28);
00791         IO_Write(0x42, 0x05);
00792         // Speaker on
00793         IO_Write(0x61, IO_Read(0x61) | 0x3);
00794         // Idle for 1/3rd of a second
00795         double start;
00796         start = PIC_FullIndex();
00797         while ((PIC_FullIndex() - start) < 333.0) CALLBACK_Idle();
00798         // Speaker off
00799         IO_Write(0x61, IO_Read(0x61) & ~0x3);
00800         chr=' ';
00801     default:
00802         /* Draw the actual Character */
00803         WriteChar(cur_col,cur_row,page,chr,attr,useattr);
00804         cur_col++;
00805     }
00806     if(cur_col==ncols) {
00807         cur_col=0;
00808         cur_row++;
00809     }
00810     // Do we need to scroll ?
00811     if(cur_row==nrows) {
00812         //Fill with black on non-text modes
00813         Bit8u fill = 0;
00814         if (IS_PC98_ARCH && CurMode->type == M_TEXT) {
00815             //Fill with the default ANSI attribute on textmode
00816             fill = DefaultANSIAttr();
00817         }
00818         else if (CurMode->type==M_TEXT) {
00819             //Fill with attribute at cursor on textmode
00820             Bit16u chat;
00821             INT10_ReadCharAttr(&chat,page);
00822             fill=(Bit8u)(chat>>8);
00823         }
00824         INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,fill,page);
00825         cur_row--;
00826     }
00827     // Set the cursor for the page
00828     INT10_SetCursorPos(cur_row,cur_col,page);
00829 }
00830 
00831 void INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr) {
00832     INT10_TeletypeOutputAttr(chr,attr,useattr,real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE));
00833 }
00834 
00835 void INT10_TeletypeOutput(Bit8u chr,Bit8u attr) {
00836     INT10_TeletypeOutputAttr(chr,attr,CurMode->type!=M_TEXT);
00837 }
00838 
00839 void INT10_WriteString(Bit8u row,Bit8u col,Bit8u flag,Bit8u attr,PhysPt string,Bit16u count,Bit8u page) {
00840     Bit8u cur_row=CURSOR_POS_ROW(page);
00841     Bit8u cur_col=CURSOR_POS_COL(page);
00842     
00843     // if row=0xff special case : use current cursor position
00844     if (row==0xff) {
00845         row=cur_row;
00846         col=cur_col;
00847     }
00848     INT10_SetCursorPos(row,col,page);
00849     while (count>0) {
00850         Bit8u chr=mem_readb(string);
00851         string++;
00852         if (flag&2) {
00853             attr=mem_readb(string);
00854             string++;
00855         }
00856         INT10_TeletypeOutputAttr(chr,attr,true,page);
00857         count--;
00858     }
00859     if (!(flag&1)) {
00860         INT10_SetCursorPos(cur_row,cur_col,page);
00861     }
00862 }
00863 
00864 bool pc98_doskey_insertmode = false;
00865 
00866 bool INT10_GetInsertState()
00867 {
00868     if (IS_PC98_ARCH) {
00869         /* state is internal to DOSKEY */
00870         return pc98_doskey_insertmode;
00871     }
00872     else {
00873         const auto flags = mem_readb(BIOS_KEYBOARD_FLAGS1);
00874         const auto state =static_cast<bool>(flags & BIOS_KEYBOARD_FLAGS1_INSERT_ACTIVE);
00875         return state;
00876     }
00877 }