1 /*
2  *  Copyright (C) 2002-2015  The DOSBox Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 
20 /* Character displaying moving functions */
21 
22 #include "dosbox.h"
23 #include "bios.h"
24 #include "mem.h"
25 #include "inout.h"
26 #include "int10.h"
27 #include "pic.h"
28 #include "callback.h"
29 
CGA2_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base)30 static void CGA2_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
31 	Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
32 	PhysPt dest=base+((CurMode->twidth*rnew)*(cheight/2)+cleft);
33 	PhysPt src=base+((CurMode->twidth*rold)*(cheight/2)+cleft);
34 	Bitu copy=(cright-cleft);
35 	Bitu nextline=CurMode->twidth;
36 	for (Bitu i=0;i<cheight/2U;i++) {
37 		MEM_BlockCopy(dest,src,copy);
38 		MEM_BlockCopy(dest+8*1024,src+8*1024,copy);
39 		dest+=nextline;src+=nextline;
40 	}
41 }
42 
CGA4_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base)43 static void CGA4_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
44 	Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
45 	PhysPt dest=base+((CurMode->twidth*rnew)*(cheight/2)+cleft)*2;
46 	PhysPt src=base+((CurMode->twidth*rold)*(cheight/2)+cleft)*2;
47 	Bitu copy=(cright-cleft)*2;Bitu nextline=CurMode->twidth*2;
48 	for (Bitu i=0;i<cheight/2U;i++) {
49 		MEM_BlockCopy(dest,src,copy);
50 		MEM_BlockCopy(dest+8*1024,src+8*1024,copy);
51 		dest+=nextline;src+=nextline;
52 	}
53 }
54 
TANDY16_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base)55 static void TANDY16_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
56 	Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
57 	Bit8u banks=CurMode->twidth/10;
58 	PhysPt dest=base+((CurMode->twidth*rnew)*(cheight/banks)+cleft)*4;
59 	PhysPt src=base+((CurMode->twidth*rold)*(cheight/banks)+cleft)*4;
60 	Bitu copy=(cright-cleft)*4;Bitu nextline=CurMode->twidth*4;
61 	for (Bitu i=0;i<cheight/banks;i++) {
62 		for (Bitu b=0;b<banks;b++) MEM_BlockCopy(dest+b*8*1024,src+b*8*1024,copy);
63 		dest+=nextline;src+=nextline;
64 	}
65 }
66 
EGA16_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base)67 static void EGA16_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
68 	PhysPt src,dest;Bitu copy;
69 	Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
70 	dest=base+(CurMode->twidth*rnew)*cheight+cleft;
71 	src=base+(CurMode->twidth*rold)*cheight+cleft;
72 	Bitu nextline=CurMode->twidth;
73 	/* Setup registers correctly */
74 	IO_Write(0x3ce,5);IO_Write(0x3cf,1);		/* Memory transfer mode */
75 	IO_Write(0x3c4,2);IO_Write(0x3c5,0xf);		/* Enable all Write planes */
76 	/* Do some copying */
77 	Bitu rowsize=(cright-cleft);
78 	copy=cheight;
79 	for (;copy>0;copy--) {
80 		for (Bitu x=0;x<rowsize;x++) mem_writeb(dest+x,mem_readb(src+x));
81 		dest+=nextline;src+=nextline;
82 	}
83 	/* Restore registers */
84 	IO_Write(0x3ce,5);IO_Write(0x3cf,0);		/* Normal transfer mode */
85 }
86 
VGA_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base)87 static void VGA_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
88 	PhysPt src,dest;Bitu copy;
89 	Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
90 	dest=base+8*((CurMode->twidth*rnew)*cheight+cleft);
91 	src=base+8*((CurMode->twidth*rold)*cheight+cleft);
92 	Bitu nextline=8*CurMode->twidth;
93 	Bitu rowsize=8*(cright-cleft);
94 	copy=cheight;
95 	for (;copy>0;copy--) {
96 		for (Bitu x=0;x<rowsize;x++) mem_writeb(dest+x,mem_readb(src+x));
97 		dest+=nextline;src+=nextline;
98 	}
99 }
100 
TEXT_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base)101 static void TEXT_CopyRow(Bit8u cleft,Bit8u cright,Bit8u rold,Bit8u rnew,PhysPt base) {
102 	PhysPt src,dest;
103 	src=base+(rold*CurMode->twidth+cleft)*2;
104 	dest=base+(rnew*CurMode->twidth+cleft)*2;
105 	MEM_BlockCopy(dest,src,(cright-cleft)*2);
106 }
107 
CGA2_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr)108 static void CGA2_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
109 	Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
110 	PhysPt dest=base+((CurMode->twidth*row)*(cheight/2)+cleft);
111 	Bitu copy=(cright-cleft);
112 	Bitu nextline=CurMode->twidth;
113 	attr=(attr & 0x3) | ((attr & 0x3) << 2) | ((attr & 0x3) << 4) | ((attr & 0x3) << 6);
114 	for (Bitu i=0;i<cheight/2U;i++) {
115 		for (Bitu x=0;x<copy;x++) {
116 			mem_writeb(dest+x,attr);
117 			mem_writeb(dest+8*1024+x,attr);
118 		}
119 		dest+=nextline;
120 	}
121 }
122 
CGA4_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr)123 static void CGA4_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
124 	Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
125 	PhysPt dest=base+((CurMode->twidth*row)*(cheight/2)+cleft)*2;
126 	Bitu copy=(cright-cleft)*2;Bitu nextline=CurMode->twidth*2;
127 	attr=(attr & 0x3) | ((attr & 0x3) << 2) | ((attr & 0x3) << 4) | ((attr & 0x3) << 6);
128 	for (Bitu i=0;i<cheight/2U;i++) {
129 		for (Bitu x=0;x<copy;x++) {
130 			mem_writeb(dest+x,attr);
131 			mem_writeb(dest+8*1024+x,attr);
132 		}
133 		dest+=nextline;
134 	}
135 }
136 
TANDY16_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr)137 static void TANDY16_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
138 	Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
139 	Bit8u banks=CurMode->twidth/10;
140 	PhysPt dest=base+((CurMode->twidth*row)*(cheight/banks)+cleft)*4;
141 	Bitu copy=(cright-cleft)*4;Bitu nextline=CurMode->twidth*4;
142 	attr=(attr & 0xf) | (attr & 0xf) << 4;
143 	for (Bitu i=0;i<cheight/banks;i++) {
144 		for (Bitu x=0;x<copy;x++) {
145 			for (Bitu b=0;b<banks;b++) mem_writeb(dest+b*8*1024+x,attr);
146 		}
147 		dest+=nextline;
148 	}
149 }
150 
EGA16_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr)151 static void EGA16_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
152 	/* Set Bitmask / Color / Full Set Reset */
153 	IO_Write(0x3ce,0x8);IO_Write(0x3cf,0xff);
154 	IO_Write(0x3ce,0x0);IO_Write(0x3cf,attr);
155 	IO_Write(0x3ce,0x1);IO_Write(0x3cf,0xf);
156 	/* Enable all Write planes */
157 	IO_Write(0x3c4,2);IO_Write(0x3c5,0xf);
158 	/* Write some bytes */
159 	Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
160 	PhysPt dest=base+(CurMode->twidth*row)*cheight+cleft;
161 	Bitu nextline=CurMode->twidth;
162 	Bitu copy = cheight;Bitu rowsize=(cright-cleft);
163 	for (;copy>0;copy--) {
164 		for (Bitu x=0;x<rowsize;x++) mem_writeb(dest+x,0xff);
165 		dest+=nextline;
166 	}
167 	IO_Write(0x3cf,0);
168 }
169 
VGA_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr)170 static void VGA_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
171 	/* Write some bytes */
172 	Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
173 	PhysPt dest=base+8*((CurMode->twidth*row)*cheight+cleft);
174 	Bitu nextline=8*CurMode->twidth;
175 	Bitu copy = cheight;Bitu rowsize=8*(cright-cleft);
176 	for (;copy>0;copy--) {
177 		for (Bitu x=0;x<rowsize;x++) mem_writeb(dest+x,attr);
178 		dest+=nextline;
179 	}
180 }
181 
TEXT_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr)182 static void TEXT_FillRow(Bit8u cleft,Bit8u cright,Bit8u row,PhysPt base,Bit8u attr) {
183 	/* Do some filing */
184 	PhysPt dest;
185 	dest=base+(row*CurMode->twidth+cleft)*2;
186 	Bit16u fill=(attr<<8)+' ';
187 	for (Bit8u x=0;x<(cright-cleft);x++) {
188 		mem_writew(dest,fill);
189 		dest+=2;
190 	}
191 }
192 
193 
INT10_ScrollWindow(Bit8u rul,Bit8u cul,Bit8u rlr,Bit8u clr,Bit8s nlines,Bit8u attr,Bit8u page)194 void INT10_ScrollWindow(Bit8u rul,Bit8u cul,Bit8u rlr,Bit8u clr,Bit8s nlines,Bit8u attr,Bit8u page) {
195 /* Do some range checking */
196 	if (CurMode->type!=M_TEXT) page=0xff;
197 	BIOS_NCOLS;BIOS_NROWS;
198 	if(rul>rlr) return;
199 	if(cul>clr) return;
200 	if(rlr>=nrows) rlr=(Bit8u)nrows-1;
201 	if(clr>=ncols) clr=(Bit8u)ncols-1;
202 	clr++;
203 
204 	/* Get the correct page: current start address for current page (0xFF),
205 	   otherwise calculate from page number and page size */
206 	PhysPt base=CurMode->pstart;
207 	if (page==0xff) base+=real_readw(BIOSMEM_SEG,BIOSMEM_CURRENT_START);
208 	else base+=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
209 
210 	if (GCC_UNLIKELY(machine==MCH_PCJR)) {
211 		if (real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_MODE) >= 9) {
212 			// PCJr cannot handle these modes at 0xb800
213 			// See INT10_PutPixel M_TANDY16
214 			Bitu cpupage =
215 				(real_readb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE) >> 3) & 0x7;
216 
217 			base = cpupage << 14;
218 			if (page!=0xff)
219 				base += page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
220 		}
221 	}
222 
223 	/* See how much lines need to be copied */
224 	Bit8u start,end;Bits next;
225 	/* Copy some lines */
226 	if (nlines>0) {
227 		start=rlr-nlines+1;
228 		end=rul;
229 		next=-1;
230 	} else if (nlines<0) {
231 		start=rul-nlines-1;
232 		end=rlr;
233 		next=1;
234 	} else {
235 		nlines=rlr-rul+1;
236 		goto filling;
237 	}
238 	while (start!=end) {
239 		start+=next;
240 		switch (CurMode->type) {
241 		case M_TEXT:
242 			TEXT_CopyRow(cul,clr,start,start+nlines,base);break;
243 		case M_CGA2:
244 			CGA2_CopyRow(cul,clr,start,start+nlines,base);break;
245 		case M_CGA4:
246 			CGA4_CopyRow(cul,clr,start,start+nlines,base);break;
247 		case M_TANDY16:
248 			TANDY16_CopyRow(cul,clr,start,start+nlines,base);break;
249 		case M_EGA:
250 			EGA16_CopyRow(cul,clr,start,start+nlines,base);break;
251 		case M_VGA:
252 			VGA_CopyRow(cul,clr,start,start+nlines,base);break;
253 		case M_LIN4:
254 			if ((machine==MCH_VGA) && (svgaCard==SVGA_TsengET4K) &&
255 					(CurMode->swidth<=800)) {
256 				// the ET4000 BIOS supports text output in 800x600 SVGA
257 				EGA16_CopyRow(cul,clr,start,start+nlines,base);break;
258 			}
259 			// fall-through
260 		default:
261 			LOG(LOG_INT10,LOG_ERROR)("Unhandled mode %d for scroll",CurMode->type);
262 		}
263 	}
264 	/* Fill some lines */
265 filling:
266 	if (nlines>0) {
267 		start=rul;
268 	} else {
269 		nlines=-nlines;
270 		start=rlr-nlines+1;
271 	}
272 	for (;nlines>0;nlines--) {
273 		switch (CurMode->type) {
274 		case M_TEXT:
275 			TEXT_FillRow(cul,clr,start,base,attr);break;
276 		case M_CGA2:
277 			CGA2_FillRow(cul,clr,start,base,attr);break;
278 		case M_CGA4:
279 			CGA4_FillRow(cul,clr,start,base,attr);break;
280 		case M_TANDY16:
281 			TANDY16_FillRow(cul,clr,start,base,attr);break;
282 		case M_EGA:
283 			EGA16_FillRow(cul,clr,start,base,attr);break;
284 		case M_VGA:
285 			VGA_FillRow(cul,clr,start,base,attr);break;
286 		case M_LIN4:
287 			if ((machine==MCH_VGA) && (svgaCard==SVGA_TsengET4K) &&
288 					(CurMode->swidth<=800)) {
289 				EGA16_FillRow(cul,clr,start,base,attr);break;
290 			}
291 			// fall-through
292 		default:
293 			LOG(LOG_INT10,LOG_ERROR)("Unhandled mode %d for scroll",CurMode->type);
294 		}
295 		start++;
296 	}
297 }
298 
INT10_SetActivePage(Bit8u page)299 void INT10_SetActivePage(Bit8u page) {
300 	Bit16u mem_address;
301 	if (page>7) LOG(LOG_INT10,LOG_ERROR)("INT10_SetActivePage page %d",page);
302 
303 	if (IS_EGAVGA_ARCH && (svgaCard==SVGA_S3Trio)) page &= 7;
304 
305 	mem_address=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
306 	/* Write the new page start */
307 	real_writew(BIOSMEM_SEG,BIOSMEM_CURRENT_START,mem_address);
308 	if (IS_EGAVGA_ARCH) {
309 		if (CurMode->mode<8) mem_address>>=1;
310 		// rare alternative: if (CurMode->type==M_TEXT)  mem_address>>=1;
311 	} else {
312 		mem_address>>=1;
313 	}
314 	/* Write the new start address in vgahardware */
315 	Bit16u base=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
316 	IO_Write(base,0x0c);
317 	IO_Write(base+1,(Bit8u)(mem_address>>8));
318 	IO_Write(base,0x0d);
319 	IO_Write(base+1,(Bit8u)mem_address);
320 
321 	// And change the BIOS page
322 	real_writeb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE,page);
323 	Bit8u cur_row=CURSOR_POS_ROW(page);
324 	Bit8u cur_col=CURSOR_POS_COL(page);
325 	// Display the cursor, now the page is active
326 	INT10_SetCursorPos(cur_row,cur_col,page);
327 }
328 
INT10_SetCursorShape(Bit8u first,Bit8u last)329 void INT10_SetCursorShape(Bit8u first,Bit8u last) {
330 	real_writew(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE,last|(first<<8));
331 	if (machine==MCH_CGA) goto dowrite;
332 	if (IS_TANDY_ARCH) goto dowrite;
333 	/* Skip CGA cursor emulation if EGA/VGA system is active */
334 	if (!(real_readb(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL) & 0x8)) {
335 		/* Check for CGA type 01, invisible */
336 		if ((first & 0x60) == 0x20) {
337 			first=0x1e;
338 			last=0x00;
339 			goto dowrite;
340 		}
341 		/* Check if we need to convert CGA Bios cursor values */
342 		if (!(real_readb(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL) & 0x1)) { // set by int10 fun12 sub34
343 //			if (CurMode->mode>0x3) goto dowrite;	//Only mode 0-3 are text modes on cga
344 			if ((first & 0xe0) || (last & 0xe0)) goto dowrite;
345 			Bit8u cheight=real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT)-1;
346 			/* Creative routine i based of the original ibmvga bios */
347 
348 			if (last<first) {
349 				if (!last) goto dowrite;
350 				first=last;
351 				last=cheight;
352 			/* Test if this might be a cga style cursor set, if not don't do anything */
353 			} else if (((first | last)>=cheight) || !(last==(cheight-1)) || !(first==cheight) ) {
354 				if (last<=3) goto dowrite;
355 				if (first+2<last) {
356 					if (first>2) {
357 						first=(cheight+1)/2;
358 						last=cheight;
359 					} else {
360 						last=cheight;
361 					}
362 				} else {
363 					first=(first-last)+cheight;
364 					last=cheight;
365 
366 					if (cheight>0xc) { // vgatest sets 15 15 2x where only one should be decremented to 14 14
367 						first--;     // implementing int10 fun12 sub34 fixed this.
368 						last--;
369 					}
370 				}
371 			}
372 
373 		}
374 	}
375 dowrite:
376 	Bit16u base=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
377 	IO_Write(base,0xa);IO_Write(base+1,first);
378 	IO_Write(base,0xb);IO_Write(base+1,last);
379 }
380 
INT10_SetCursorPos(Bit8u row,Bit8u col,Bit8u page)381 void INT10_SetCursorPos(Bit8u row,Bit8u col,Bit8u page) {
382 	Bit16u address;
383 
384 	if (page>7) LOG(LOG_INT10,LOG_ERROR)("INT10_SetCursorPos page %d",page);
385 	// Bios cursor pos
386 	real_writeb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2,col);
387 	real_writeb(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2+1,row);
388 	// Set the hardware cursor
389 	Bit8u current=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
390 	if(page==current) {
391 		// Get the dimensions
392 		BIOS_NCOLS;
393 		// Calculate the address knowing nbcols nbrows and page num
394 		// NOTE: BIOSMEM_CURRENT_START counts in colour/flag pairs
395 		address=(ncols*row)+col+real_readw(BIOSMEM_SEG,BIOSMEM_CURRENT_START)/2;
396 		// CRTC regs 0x0e and 0x0f
397 		Bit16u base=real_readw(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
398 		IO_Write(base,0x0e);
399 		IO_Write(base+1,(Bit8u)(address>>8));
400 		IO_Write(base,0x0f);
401 		IO_Write(base+1,(Bit8u)address);
402 	}
403 }
404 
ReadCharAttr(Bit16u col,Bit16u row,Bit8u page,Bit16u * result)405 void ReadCharAttr(Bit16u col,Bit16u row,Bit8u page,Bit16u * result) {
406 	/* Externally used by the mouse routine */
407 	PhysPt fontdata;
408 	Bit16u cols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
409 	Bit8u cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
410 	bool split_chr = false;
411 	switch (CurMode->type) {
412 	case M_TEXT:
413 		{
414 			// Compute the address
415 			Bit16u address=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
416 			address+=(row*cols+col)*2;
417 			// read the char
418 			PhysPt where = CurMode->pstart+address;
419 			*result=mem_readw(where);
420 		}
421 		return;
422 	case M_CGA4:
423 	case M_CGA2:
424 	case M_TANDY16:
425 		split_chr = true;
426 		switch (machine) {
427 		case MCH_CGA:
428 		case MCH_HERC:
429 			fontdata=PhysMake(0xf000,0xfa6e);
430 			break;
431 		case TANDY_ARCH_CASE:
432 			fontdata=Real2Phys(RealGetVec(0x44));
433 			break;
434 		default:
435 			fontdata=Real2Phys(RealGetVec(0x43));
436 			break;
437 		}
438 		break;
439 	default:
440 		fontdata=Real2Phys(RealGetVec(0x43));
441 		break;
442 	}
443 
444 	Bitu x=col*8,y=row*cheight*(cols/CurMode->twidth);
445 
446 	for (Bit16u chr=0;chr<256;chr++) {
447 
448 		if (chr==128 && split_chr) fontdata=Real2Phys(RealGetVec(0x1f));
449 
450 		bool error=false;
451 		Bit16u ty=(Bit16u)y;
452 		for (Bit8u h=0;h<cheight;h++) {
453 			Bit8u bitsel=128;
454 			Bit8u bitline=mem_readb(fontdata++);
455 			Bit8u res=0;
456 			Bit8u vidline=0;
457 			Bit16u tx=(Bit16u)x;
458 			while (bitsel) {
459 				//Construct bitline in memory
460 				INT10_GetPixel(tx,ty,page,&res);
461 				if(res) vidline|=bitsel;
462 				tx++;
463 				bitsel>>=1;
464 			}
465 			ty++;
466 			if(bitline != vidline){
467 				/* It's not character 'chr', move on to the next */
468 				fontdata+=(cheight-h-1);
469 				error = true;
470 				break;
471 			}
472 		}
473 		if(!error){
474 			/* We found it */
475 			*result = chr;
476 			return;
477 		}
478 	}
479 	LOG(LOG_INT10,LOG_ERROR)("ReadChar didn't find character");
480 	*result = 0;
481 }
INT10_ReadCharAttr(Bit16u * result,Bit8u page)482 void INT10_ReadCharAttr(Bit16u * result,Bit8u page) {
483 	if(page==0xFF) page=real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
484 	Bit8u cur_row=CURSOR_POS_ROW(page);
485 	Bit8u cur_col=CURSOR_POS_COL(page);
486 	ReadCharAttr(cur_col,cur_row,page,result);
487 }
WriteChar(Bit16u col,Bit16u row,Bit8u page,Bit8u chr,Bit8u attr,bool useattr)488 void WriteChar(Bit16u col,Bit16u row,Bit8u page,Bit8u chr,Bit8u attr,bool useattr) {
489 	/* Externally used by the mouse routine */
490 	PhysPt fontdata;
491 	Bit16u cols = real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS);
492 	Bit8u back,cheight = real_readb(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
493 	switch (CurMode->type) {
494 	case M_TEXT:
495 		{
496 			// Compute the address
497 			Bit16u address=page*real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE);
498 			address+=(row*cols+col)*2;
499 			// Write the char
500 			PhysPt where = CurMode->pstart+address;
501 			mem_writeb(where,chr);
502 			if (useattr) mem_writeb(where+1,attr);
503 		}
504 		return;
505 	case M_CGA4:
506 	case M_CGA2:
507 	case M_TANDY16:
508 		if (chr>=128) {
509 			chr-=128;
510 			fontdata=Real2Phys(RealGetVec(0x1f));
511 			break;
512 		}
513 		switch (machine) {
514 		case MCH_CGA:
515 		case MCH_HERC:
516 			fontdata=PhysMake(0xf000,0xfa6e);
517 			break;
518 		case TANDY_ARCH_CASE:
519 			fontdata=Real2Phys(RealGetVec(0x44));
520 			break;
521 		default:
522 			fontdata=Real2Phys(RealGetVec(0x43));
523 			break;
524 		}
525 		break;
526 	default:
527 		fontdata=Real2Phys(RealGetVec(0x43));
528 		break;
529 	}
530 	fontdata+=chr*cheight;
531 
532 	if(GCC_UNLIKELY(!useattr)) { //Set attribute(color) to a sensible value
533 		static bool warned_use = false;
534 		if(GCC_UNLIKELY(!warned_use)){
535 			LOG(LOG_INT10,LOG_ERROR)("writechar used without attribute in non-textmode %c %X",chr,chr);
536 			warned_use = true;
537 		}
538 		switch(CurMode->type) {
539 		case M_CGA4:
540 			attr = 0x3;
541 			break;
542 		case M_CGA2:
543 			attr = 0x1;
544 			break;
545 		case M_TANDY16:
546 		case M_EGA:
547 		default:
548 			attr = 0xf;
549 			break;
550 		}
551 	}
552 
553 	//Attribute behavior of mode 6; mode 11 does something similar but
554 	//it is in INT 10h handler because it only applies to function 09h
555 	if (CurMode->mode==0x06) attr=(attr&0x80)|1;
556 
557 	switch (CurMode->type) {
558 	case M_VGA:
559 	case M_LIN8:
560 		// 256-color modes have background color instead of page
561 		back=page;
562 		page=0;
563 		break;
564 	case M_EGA:
565 		/* enable all planes for EGA modes (Ultima 1 colour bug) */
566 		/* might be put into INT10_PutPixel but different vga bios
567 		   implementations have different opinions about this */
568 		IO_Write(0x3c4,0x2);IO_Write(0x3c5,0xf);
569 		// fall-through
570 	default:
571 		back=attr&0x80;
572 		break;
573 	}
574 
575 	Bitu x=col*8,y=row*cheight*(cols/CurMode->twidth);
576 
577 	Bit16u ty=(Bit16u)y;
578 	for (Bit8u h=0;h<cheight;h++) {
579 		Bit8u bitsel=128;
580 		Bit8u bitline=mem_readb(fontdata++);
581 		Bit16u tx=(Bit16u)x;
582 		while (bitsel) {
583 			INT10_PutPixel(tx,ty,page,(bitline&bitsel)?attr:back);
584 			tx++;
585 			bitsel>>=1;
586 		}
587 		ty++;
588 	}
589 }
590 
INT10_WriteChar(Bit8u chr,Bit8u attr,Bit8u page,Bit16u count,bool showattr)591 void INT10_WriteChar(Bit8u chr,Bit8u attr,Bit8u page,Bit16u count,bool showattr) {
592 	Bit8u pospage=page;
593 	if (CurMode->type!=M_TEXT) {
594 		showattr=true; //Use attr in graphics mode always
595 		switch (machine) {
596 			case EGAVGA_ARCH_CASE:
597 				switch (CurMode->type) {
598 				case M_VGA:
599 				case M_LIN8:
600 					pospage=0;
601 					break;
602 				default:
603 					page%=CurMode->ptotal;
604 					pospage=page;
605 					break;
606 				}
607 				break;
608 			case MCH_CGA:
609 			case MCH_PCJR:
610 				page=0;
611 				pospage=0;
612 				break;
613 		}
614 	}
615 
616 	Bit8u cur_row=CURSOR_POS_ROW(pospage);
617 	Bit8u cur_col=CURSOR_POS_COL(pospage);
618 	BIOS_NCOLS;
619 	while (count>0) {
620 		WriteChar(cur_col,cur_row,page,chr,attr,showattr);
621 		count--;
622 		cur_col++;
623 		if(cur_col==ncols) {
624 			cur_col=0;
625 			cur_row++;
626 		}
627 	}
628 }
629 
INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr,Bit8u page)630 static void INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr,Bit8u page) {
631 	BIOS_NCOLS;BIOS_NROWS;
632 	Bit8u cur_row=CURSOR_POS_ROW(page);
633 	Bit8u cur_col=CURSOR_POS_COL(page);
634 	switch (chr) {
635 	case 7: /* Beep */
636 		// Prepare PIT counter 2 for ~900 Hz square wave
637 		IO_Write(0x43,0xb6);
638 		IO_Write(0x42,0x28);
639 		IO_Write(0x42,0x05);
640 		// Speaker on
641 		IO_Write(0x61,IO_Read(0x61)|3);
642 		// Idle for 1/3rd of a second
643 		double start;
644 		start=PIC_FullIndex();
645 		while ((PIC_FullIndex()-start)<333.0) CALLBACK_Idle();
646 		// Speaker off
647 		IO_Write(0x61,IO_Read(0x61)&~3);
648 		// No change in position
649 		return;
650 	case 8:
651 		if(cur_col>0) cur_col--;
652 		break;
653 	case '\r':
654 		cur_col=0;
655 		break;
656 	case '\n':
657 //		cur_col=0; //Seems to break an old chess game
658 		cur_row++;
659 		break;
660 	default:
661 		/* Draw the actual Character */
662 		WriteChar(cur_col,cur_row,page,chr,attr,useattr);
663 		cur_col++;
664 	}
665 	if(cur_col==ncols) {
666 		cur_col=0;
667 		cur_row++;
668 	}
669 	// Do we need to scroll ?
670 	if(cur_row==nrows) {
671 		//Fill with black on non-text modes and with attribute at cursor on textmode
672 		Bit8u fill=0;
673 		if (CurMode->type==M_TEXT) {
674 			Bit16u chat;
675 			INT10_ReadCharAttr(&chat,page);
676 			fill=(Bit8u)(chat>>8);
677 		}
678 		INT10_ScrollWindow(0,0,(Bit8u)(nrows-1),(Bit8u)(ncols-1),-1,fill,page);
679 		cur_row--;
680 	}
681 	// Set the cursor for the page
682 	INT10_SetCursorPos(cur_row,cur_col,page);
683 }
684 
INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr)685 void INT10_TeletypeOutputAttr(Bit8u chr,Bit8u attr,bool useattr) {
686 	INT10_TeletypeOutputAttr(chr,attr,useattr,real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE));
687 }
688 
INT10_TeletypeOutput(Bit8u chr,Bit8u attr)689 void INT10_TeletypeOutput(Bit8u chr,Bit8u attr) {
690 	INT10_TeletypeOutputAttr(chr,attr,CurMode->type!=M_TEXT);
691 }
692 
INT10_WriteString(Bit8u row,Bit8u col,Bit8u flag,Bit8u attr,PhysPt string,Bit16u count,Bit8u page)693 void INT10_WriteString(Bit8u row,Bit8u col,Bit8u flag,Bit8u attr,PhysPt string,Bit16u count,Bit8u page) {
694 	Bit8u cur_row=CURSOR_POS_ROW(page);
695 	Bit8u cur_col=CURSOR_POS_COL(page);
696 
697 	// if row=0xff special case : use current cursor position
698 	if (row==0xff) {
699 		row=cur_row;
700 		col=cur_col;
701 	}
702 	INT10_SetCursorPos(row,col,page);
703 	while (count>0) {
704 		Bit8u chr=mem_readb(string);
705 		string++;
706 		if (flag&2) {
707 			attr=mem_readb(string);
708 			string++;
709 		};
710 		INT10_TeletypeOutputAttr(chr,attr,true,page);
711 		count--;
712 	}
713 	if (!(flag&1)) {
714 		INT10_SetCursorPos(cur_row,cur_col,page);
715 	}
716 }
717