1 /*
2  *  Copyright (C) 2002-2021  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 along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "int10.h"
20 
21 #include "mem.h"
22 #include "inout.h"
23 
24 static Bit8u cga_masks[4]={0x3f,0xcf,0xf3,0xfc};
25 static Bit8u cga_masks2[8]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};
26 
INT10_PutPixel(Bit16u x,Bit16u y,Bit8u page,Bit8u color)27 void INT10_PutPixel(Bit16u x,Bit16u y,Bit8u page,Bit8u color) {
28 	static bool putpixelwarned = false;
29 
30 	switch (CurMode->type) {
31 	case M_CGA4:
32 	{
33 		if (real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE)<=5) {
34 			// this is a 16k mode
35 			Bit16u off=(y>>1)*80+(x>>2);
36 			if (y&1) off+=8*1024;
37 
38 			Bit8u old=real_readb(0xb800,off);
39 			if (color & 0x80) {
40 				color&=3;
41 				old^=color << (2*(3-(x&3)));
42 			} else {
43 				old=(old&cga_masks[x&3])|((color&3) << (2*(3-(x&3))));
44 			}
45 			real_writeb(0xb800,off,old);
46 		} else {
47 			// a 32k mode: PCJr special case (see M_TANDY16)
48 			Bit16u seg;
49 			if (machine==MCH_PCJR) {
50 				Bitu cpupage =
51 					(real_readb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE) >> 3) & 0x7;
52 				seg = cpupage << 10; // A14-16 to addr bits 14-16
53 			} else
54 				seg = 0xb800;
55 
56 			Bit16u off=(y>>2)*160+((x>>2)&(~1));
57 			off+=(8*1024) * (y & 3);
58 
59 			Bit16u old=real_readw(seg,off);
60 			if (color & 0x80) {
61 				old^=(color&1) << (7-(x&7));
62 				old^=((color&2)>>1) << ((7-(x&7))+8);
63 			} else {
64 				old=(old&(~(0x101<<(7-(x&7))))) | ((color&1) << (7-(x&7))) | (((color&2)>>1) << ((7-(x&7))+8));
65 			}
66 			real_writew(seg,off,old);
67 		}
68 	}
69 	break;
70 	case M_CGA2:
71 		{
72 				Bit16u off=(y>>1)*80+(x>>3);
73 				if (y&1) off+=8*1024;
74 				Bit8u old=real_readb(0xb800,off);
75 				if (color & 0x80) {
76 					color&=1;
77 					old^=color << ((7-(x&7)));
78 				} else {
79 					old=(old&cga_masks2[x&7])|((color&1) << ((7-(x&7))));
80 				}
81 				real_writeb(0xb800,off,old);
82 		}
83 		break;
84 	case M_TANDY16:
85 	{
86 		// find out if we are in a 32k mode (0x9 or 0xa)
87 		// This requires special handling on the PCJR
88 		// because only 16k are mapped at 0xB800
89 		bool is_32k = (real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_MODE) >= 9)?
90 			true:false;
91 
92 		Bit16u segment, offset;
93 		if (is_32k) {
94 			if (machine==MCH_PCJR) {
95 				Bitu cpupage =
96 					(real_readb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE) >> 3) & 0x7;
97 				segment = cpupage << 10; // A14-16 to addr bits 14-16
98 			} else
99 				segment = 0xb800;
100 			// bits 1 and 0 of y select the bank
101 			// two pixels per byte (thus x>>1)
102 			offset = (y >> 2) * (CurMode->swidth >> 1) + (x>>1);
103 			// select the scanline bank
104 			offset += (8*1024) * (y & 3);
105 		} else {
106 			segment = 0xb800;
107 			// bit 0 of y selects the bank
108 			offset = (y >> 1) * (CurMode->swidth >> 1) + (x>>1);
109 			offset += (8*1024) * (y & 1);
110 		}
111 
112 		// update the pixel
113 		Bit8u old=real_readb(segment, offset);
114 		Bit8u p[2];
115 		p[1] = (old >> 4) & 0xf;
116 		p[0] = old & 0xf;
117 		Bitu ind = 1-(x & 0x1);
118 
119 		if (color & 0x80) {
120 			// color is to be XORed
121 	 		p[ind]^=(color & 0x7f);
122 		} else {
123 			p[ind]=color;
124 		}
125 		old = (p[1] << 4) | p[0];
126 		real_writeb(segment,offset, old);
127 	}
128 	break;
129 	case M_LIN4:
130 		if ((machine!=MCH_VGA) || (svgaCard!=SVGA_TsengET4K) ||
131 				(CurMode->swidth>800)) {
132 			// the ET4000 BIOS supports text output in 800x600 SVGA
133 			// (Gateway 2)
134 			putpixelwarned = true;
135 			LOG(LOG_INT10, LOG_ERROR)("PutPixel unhandled mode type %d", CurMode->type);
136 			break;
137 		}
138 		[[fallthrough]];
139 	case M_EGA: {
140 		/* Set the correct bitmask for the pixel position */
141 		IO_Write(0x3ce, 0x8);
142 		Bit8u mask = 128 >> (x & 7);
143 		IO_Write(0x3cf, mask);
144 		/* Set the color to set/reset register */
145 		IO_Write(0x3ce, 0x0);
146 		IO_Write(0x3cf, color);
147 		/* Enable all the set/resets */
148 		IO_Write(0x3ce, 0x1);
149 		IO_Write(0x3cf, 0xf);
150 		/* test for xorring */
151 		if (color & 0x80) {
152 			IO_Write(0x3ce, 0x3);
153 			IO_Write(0x3cf, 0x18);
154 		}
155 		// Perhaps also set mode 1
156 		/* Calculate where the pixel is in video memory */
157 		if (CurMode->plength !=
158 		    (Bitu)real_readw(BIOSMEM_SEG, BIOSMEM_PAGE_SIZE))
159 			LOG(LOG_INT10, LOG_ERROR)
160 			("PutPixel_EGA_p: %u != %x", CurMode->plength,
161 			 real_readw(BIOSMEM_SEG, BIOSMEM_PAGE_SIZE));
162 		if (CurMode->swidth !=
163 		    (Bitu)real_readw(BIOSMEM_SEG, BIOSMEM_NB_COLS) * 8)
164 			LOG(LOG_INT10, LOG_ERROR)
165 			("PutPixel_EGA_w: %u!=%x", CurMode->swidth,
166 			 real_readw(BIOSMEM_SEG, BIOSMEM_NB_COLS) * 8);
167 		PhysPt off = 0xa0000 +
168 		             real_readw(BIOSMEM_SEG, BIOSMEM_PAGE_SIZE) * page +
169 		             ((y * real_readw(BIOSMEM_SEG, BIOSMEM_NB_COLS) * 8 + x) >>
170 		              3);
171 		/* Bitmask and set/reset should do the rest */
172 		mem_readb(off);
173 		mem_writeb(off, 0xff);
174 		/* Restore bitmask */
175 		IO_Write(0x3ce, 0x8);
176 		IO_Write(0x3cf, 0xff);
177 		IO_Write(0x3ce, 0x1);
178 		IO_Write(0x3cf, 0);
179 		/* Restore write operating if changed */
180 		if (color & 0x80) {
181 			IO_Write(0x3ce, 0x3);
182 			IO_Write(0x3cf, 0x0);
183 		}
184 		break;
185 	}
186 
187 	case M_VGA:
188 		mem_writeb(PhysMake(0xa000,y*320+x),color);
189 		break;
190 	case M_LIN8: {
191 			if (CurMode->swidth!=(Bitu)real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8)
192 				LOG(LOG_INT10,LOG_ERROR)("PutPixel_VGA_w: %u!=%x",CurMode->swidth,real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
193 			PhysPt off=S3_LFB_BASE+y*real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8+x;
194 			mem_writeb(off,color);
195 			break;
196 		}
197 	default:
198 		if(GCC_UNLIKELY(!putpixelwarned)) {
199 			putpixelwarned = true;
200 			LOG(LOG_INT10,LOG_ERROR)("PutPixel unhandled mode type %d",CurMode->type);
201 		}
202 		break;
203 	}
204 }
205 
INT10_GetPixel(Bit16u x,Bit16u y,Bit8u page,Bit8u * color)206 void INT10_GetPixel(Bit16u x,Bit16u y,Bit8u page,Bit8u * color) {
207 	switch (CurMode->type) {
208 	case M_CGA4:
209 		{
210 			Bit16u off=(y>>1)*80+(x>>2);
211 			if (y&1) off+=8*1024;
212 			Bit8u val=real_readb(0xb800,off);
213 			*color=(val>>(((3-(x&3)))*2)) & 3 ;
214 		}
215 		break;
216 	case M_CGA2:
217 		{
218 			Bit16u off=(y>>1)*80+(x>>3);
219 			if (y&1) off+=8*1024;
220 			Bit8u val=real_readb(0xb800,off);
221 			*color=(val>>(((7-(x&7))))) & 1 ;
222 		}
223 		break;
224 	case M_TANDY16:
225 		{
226 			bool is_32k = (real_readb(BIOSMEM_SEG, BIOSMEM_CURRENT_MODE) >= 9)?true:false;
227 			Bit16u segment, offset;
228 			if (is_32k) {
229 				if (machine==MCH_PCJR) {
230 					Bitu cpupage = (real_readb(BIOSMEM_SEG, BIOSMEM_CRTCPU_PAGE) >> 3) & 0x7;
231 					segment = cpupage << 10;
232 				} else segment = 0xb800;
233 				offset = (y >> 2) * (CurMode->swidth >> 1) + (x>>1);
234 				offset += (8*1024) * (y & 3);
235 			} else {
236 				segment = 0xb800;
237 				offset = (y >> 1) * (CurMode->swidth >> 1) + (x>>1);
238 				offset += (8*1024) * (y & 1);
239 			}
240 			Bit8u val=real_readb(segment,offset);
241 			*color=(val>>((x&1)?0:4)) & 0xf;
242 		}
243 		break;
244 	case M_EGA:
245 		{
246 			/* Calculate where the pixel is in video memory */
247 			if (CurMode->plength!=(Bitu)real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE))
248 				LOG(LOG_INT10,LOG_ERROR)("GetPixel_EGA_p: %u!=%x",CurMode->plength,real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE));
249 			if (CurMode->swidth!=(Bitu)real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8)
250 				LOG(LOG_INT10,LOG_ERROR)("GetPixel_EGA_w: %u!=%x",CurMode->swidth,real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
251 			PhysPt off=0xa0000+real_readw(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE)*page+
252 				((y*real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8+x)>>3);
253 			Bitu shift=7-(x & 7);
254 			/* Set the read map */
255 			*color=0;
256 			IO_Write(0x3ce,0x4);IO_Write(0x3cf,0);
257 			*color|=((mem_readb(off)>>shift) & 1) << 0;
258 			IO_Write(0x3ce,0x4);IO_Write(0x3cf,1);
259 			*color|=((mem_readb(off)>>shift) & 1) << 1;
260 			IO_Write(0x3ce,0x4);IO_Write(0x3cf,2);
261 			*color|=((mem_readb(off)>>shift) & 1) << 2;
262 			IO_Write(0x3ce,0x4);IO_Write(0x3cf,3);
263 			*color|=((mem_readb(off)>>shift) & 1) << 3;
264 			break;
265 		}
266 	case M_VGA:
267 		*color=mem_readb(PhysMake(0xa000,320*y+x));
268 		break;
269 	case M_LIN8: {
270 			if (CurMode->swidth!=(Bitu)real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8)
271 				LOG(LOG_INT10,LOG_ERROR)("GetPixel_VGA_w: %u!=%x",CurMode->swidth,real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
272 			PhysPt off=S3_LFB_BASE+y*real_readw(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8+x;
273 			*color = mem_readb(off);
274 			break;
275 		}
276 	default:
277 		LOG(LOG_INT10,LOG_ERROR)("GetPixel unhandled mode type %d",CurMode->type);
278 		break;
279 	}
280 }
281