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
20 #include "dosbox.h"
21 #include "setup.h"
22 #include "vga.h"
23 #include "inout.h"
24 #include "mem.h"
25 #include "../ints/int10.h"
26
27 struct SVGA_PVGA1A_DATA {
28 uint8_t PR0A = 0;
29 uint8_t PR0B = 0;
30 uint8_t PR1 = 0;
31 uint8_t PR2 = 0;
32 uint8_t PR3 = 0;
33 uint8_t PR4 = 0;
34 uint8_t PR5 = 0;
35
lockedSVGA_PVGA1A_DATA36 inline bool locked() { return (PR5&7)!=5; }
37
38 uint32_t clockFreq[4] = {0, 0, 0, 0};
39 Bitu biosMode = 0;
40 };
41
42 static SVGA_PVGA1A_DATA pvga1a = {};
43
bank_setup_pvga1a()44 static void bank_setup_pvga1a() {
45 // Note: There is some inconsistency in available documentation. Most sources tell that PVGA1A used
46 // only 7 bits of bank index (VGADOC and Ferraro agree on that) but also point that there are
47 // implementations with 1M of RAM which is simply not possible with 7-bit banks. This implementation
48 // assumes that the eighth bit was actually wired and could be used. This does not conflict with
49 // anything and actually works in WHATVGA just fine.
50 if (pvga1a.PR1 & 0x08) {
51 // TODO: Dual bank function is not supported yet
52 // TODO: Requirements are not compatible with vga_memory implementation.
53 } else {
54 // Single bank config is straightforward
55 vga.svga.bank_read = vga.svga.bank_write = pvga1a.PR0A;
56 vga.svga.bank_size = 4*1024;
57 VGA_SetupHandlers();
58 }
59 }
60
write_p3cf_pvga1a(io_port_t reg,io_val_t value,io_width_t)61 void write_p3cf_pvga1a(io_port_t reg, io_val_t value, io_width_t)
62 {
63 const auto val = check_cast<uint8_t>(value);
64 if (pvga1a.locked() && reg >= 0x09 && reg <= 0x0e)
65 return;
66
67 switch (reg) {
68 case 0x09:
69 // Bank A, 4K granularity, not using bit 7
70 // Maps to A800h-AFFFh if PR1 bit 3 set and 64k config B000h-BFFFh if 128k config. A000h-AFFFh otherwise.
71 pvga1a.PR0A = val;
72 bank_setup_pvga1a();
73 break;
74 case 0x0a:
75 // Bank B, 4K granularity, not using bit 7
76 // Maps to A000h-A7FFh if PR1 bit 3 set and 64k config, A000h-AFFFh if 128k
77 pvga1a.PR0B = val;
78 bank_setup_pvga1a();
79 break;
80 case 0x0b:
81 // Memory size. We only allow to mess with bit 3 here (enable bank B) - this may break some detection schemes
82 pvga1a.PR1 = (pvga1a.PR1 & ~0x08) | (val & 0x08);
83 bank_setup_pvga1a();
84 break;
85 case 0x0c:
86 // Video configuration
87 // TODO: Figure out if there is anything worth implementing here.
88 pvga1a.PR2 = val;
89 break;
90 case 0x0d:
91 // CRT control. Bits 3-4 contain bits 16-17 of CRT start.
92 // TODO: Implement bit 2 (CRT address doubling - this mechanism is present in other chipsets as well,
93 // but not implemented in DosBox core)
94 pvga1a.PR3 = val;
95 vga.config.display_start = (vga.config.display_start & 0xffff) | ((val & 0x18)<<13);
96 vga.config.cursor_start = (vga.config.cursor_start & 0xffff) | ((val & 0x18)<<13);
97 break;
98 case 0x0e:
99 // Video control
100 // TODO: Figure out if there is anything worth implementing here.
101 pvga1a.PR4 = val;
102 break;
103 case 0x0f:
104 // Enable extended registers
105 pvga1a.PR5 = val;
106 break;
107 default:
108 LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:GFX:PVGA1A:Write to illegal index %2x", reg);
109 break;
110 }
111 }
112
read_p3cf_pvga1a(io_port_t reg,io_width_t)113 uint8_t read_p3cf_pvga1a(io_port_t reg, io_width_t)
114 {
115 if (pvga1a.locked() && reg >= 0x09 && reg <= 0x0e)
116 return 0x0;
117
118 switch (reg) {
119 case 0x09:
120 return pvga1a.PR0A;
121 case 0x0a:
122 return pvga1a.PR0B;
123 case 0x0b:
124 return pvga1a.PR1;
125 case 0x0c:
126 return pvga1a.PR2;
127 case 0x0d:
128 return pvga1a.PR3;
129 case 0x0e:
130 return pvga1a.PR4;
131 case 0x0f:
132 return pvga1a.PR5;
133 default:
134 LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:GFX:PVGA1A:Read from illegal index %2x", reg);
135 break;
136 }
137
138 return 0x0;
139 }
140
FinishSetMode_PVGA1A(io_port_t,VGA_ModeExtraData * modeData)141 void FinishSetMode_PVGA1A(io_port_t /*crtc_base*/, VGA_ModeExtraData *modeData)
142 {
143 pvga1a.biosMode = modeData->modeNo;
144
145 // Reset to single bank and set it to 0. May need to unlock first (DPaint locks on exit)
146 IO_Write(0x3ce, 0x0f);
147 const auto oldlock = IO_Read(0x3cf);
148 IO_Write(0x3cf, 0x05);
149 IO_Write(0x3ce, 0x09);
150 IO_Write(0x3cf, 0x00);
151 IO_Write(0x3ce, 0x0a);
152 IO_Write(0x3cf, 0x00);
153 IO_Write(0x3ce, 0x0b);
154 Bit8u val = IO_Read(0x3cf);
155 IO_Write(0x3cf, val & ~0x08);
156 IO_Write(0x3ce, 0x0c);
157 IO_Write(0x3cf, 0x00);
158 IO_Write(0x3ce, 0x0d);
159 IO_Write(0x3cf, 0x00);
160 IO_Write(0x3ce, 0x0e);
161 IO_Write(0x3cf, 0x00);
162 IO_Write(0x3ce, 0x0f);
163 IO_Write(0x3cf, oldlock);
164
165 if (svga.determine_mode)
166 svga.determine_mode();
167
168 if(vga.mode != M_VGA) {
169 vga.config.compatible_chain4 = false;
170 vga.vmemwrap = vga.vmemsize;
171 } else {
172 vga.config.compatible_chain4 = true;
173 vga.vmemwrap = 256*1024;
174 }
175
176 VGA_SetupHandlers();
177 }
178
DetermineMode_PVGA1A()179 void DetermineMode_PVGA1A() {
180 // Close replica from the base implementation. It will stay here
181 // until I figure a way to either distinguish M_VGA and M_LIN8 or
182 // merge them.
183 if (vga.attr.mode_control & 1) {
184 if (vga.gfx.mode & 0x40) VGA_SetMode((pvga1a.biosMode<=0x13)?M_VGA:M_LIN8);
185 else if (vga.gfx.mode & 0x20) VGA_SetMode(M_CGA4);
186 else if ((vga.gfx.miscellaneous & 0x0c)==0x0c) VGA_SetMode(M_CGA2);
187 else VGA_SetMode((pvga1a.biosMode<=0x13)?M_EGA:M_LIN4);
188 } else {
189 VGA_SetMode(M_TEXT);
190 }
191 }
192
SetClock_PVGA1A(Bitu which,const uint32_t target)193 void SetClock_PVGA1A(Bitu which, const uint32_t target)
194 {
195 if (which < 4) {
196 pvga1a.clockFreq[which]=1000*target;
197 VGA_StartResize();
198 }
199 }
200
GetClock_PVGA1A()201 uint32_t GetClock_PVGA1A()
202 {
203 return pvga1a.clockFreq[(vga.misc_output >> 2) & 3];
204 }
205
AcceptsMode_PVGA1A(Bitu mode)206 bool AcceptsMode_PVGA1A(Bitu mode) {
207 return VideoModeMemSize(mode) < vga.vmemsize;
208 }
209
SVGA_Setup_ParadisePVGA1A(void)210 void SVGA_Setup_ParadisePVGA1A(void) {
211 svga.write_p3cf = &write_p3cf_pvga1a;
212 svga.read_p3cf = &read_p3cf_pvga1a;
213
214 svga.set_video_mode = &FinishSetMode_PVGA1A;
215 svga.determine_mode = &DetermineMode_PVGA1A;
216 svga.set_clock = &SetClock_PVGA1A;
217 svga.get_clock = &GetClock_PVGA1A;
218 svga.accepts_mode = &AcceptsMode_PVGA1A;
219
220 VGA_SetClock(0,CLK_25);
221 VGA_SetClock(1,CLK_28);
222 VGA_SetClock(2,32400); // could not find documentation
223 VGA_SetClock(3,35900);
224
225 // Adjust memory, default to 512K
226 if (vga.vmemsize == 0)
227 vga.vmemsize = 512*1024;
228
229 if (vga.vmemsize < 512*1024) {
230 vga.vmemsize = 256*1024;
231 pvga1a.PR1 = 1<<6;
232 } else if (vga.vmemsize > 512*1024) {
233 vga.vmemsize = 1024*1024;
234 pvga1a.PR1 = 3<<6;
235 } else {
236 pvga1a.PR1 = 2<<6;
237 }
238
239 IO_Write(0x3cf, 0x05); // Enable!
240
241 const auto num_modes = ModeList_VGA_Paradise.size();
242 VGA_LogInitialization("Paradise VGA 1A", "DRAM", num_modes);
243 }
244