1 // license:BSD-3-Clause
2 // copyright-holders:Sergey Svishchev
3
4 /***************************************************************************
5
6 IBM Professional Graphics Controller (PGC).
7
8 Designed for IBM by Vermont Microsystems. References:
9
10 IBM Options and Adapters manual
11 http://www.minuszerodegrees.net/oa/OA%20-%20IBM%20Professional%20Graphics%20Controller.pdf
12 http://bitsavers.org/pdf/ibm/pc/cards/Technical_Reference_Options_and_Adapters_Volume_3.pdf
13 IBM Systems Journal white paper
14 http://wayback.archive.org/web/20061015235146/http://www.research.ibm.com/journal/sj/241/ibmsj2401D.pdf
15 John Elliott's page
16 http://www.seasip.info/VintagePC/pgc.html
17
18 To do:
19 - pass IBM diagnostics (currently fail with code 3905)
20 - CGA emulator
21 - what's up with irq 3 (= vblank irq)? (causes soft reset)
22 - "test pin of the microprocessor samples the hsync pulse"
23 - bus state handling?
24 - VRAM address translator ROM?
25 - clones/compatibles? CompuShow apparently was tested with a clone and sends opcode E6, writes 2 to C630C
26
27 ***************************************************************************/
28
29 #include "emu.h"
30 #include "pgc.h"
31
32 #include "screen.h"
33
34
35 //#define LOG_GENERAL (1U << 0) //defined in logmacro.h already
36 #define LOG_VRAM (1U << 1)
37 #define LOG_CMD (1U << 2)
38
39 //#define VERBOSE (LOG_GENERAL | LOG_VRAM)
40 //#define LOG_OUTPUT_STREAM std::cout
41
42 #include "logmacro.h"
43
44 #define LOGV(...) LOGMASKED(LOG_VRAM, __VA_ARGS__)
45 #define LOGCMD(...) LOGMASKED(LOG_CMD, __VA_ARGS__)
46
47
48 #define PGC_SCREEN_NAME "pgc_screen"
49
50 #define PGC_TOTAL_HORZ 820
51 #define PGC_DISP_HORZ 640
52 #define PGC_HORZ_START 80
53
54 #define PGC_TOTAL_VERT 508
55 #define PGC_DISP_VERT 480
56 #define PGC_VERT_START 10
57
58 /*
59 Prototypes
60 */
61
62 ROM_START( pgc )
63 ROM_REGION(0x100000, "maincpu", 0)
64 ROM_DEFAULT_BIOS("1985")
65
66 ROM_SYSTEM_BIOS(0, "1984", "1984 firmware, P/N 6137322/3")
CRC(f564f342)67 ROMX_LOAD("ibm_6137323_pgc_card_27256.bin", 0x00000, 0x8000, CRC(f564f342) SHA1(c5ef17fd1569043cb59f61faf828ea8b0ee95526), ROM_BIOS(0))
68 ROMX_LOAD("ibm_6137322_pgc_card_27256.bin", 0x08000, 0x8000, CRC(5e6cc82f) SHA1(45b3ffb5a9c51986862f8d47b3e03dcaaf4073d5), ROM_BIOS(0))
69
70 ROM_SYSTEM_BIOS(1, "1985", "1985 firmware, P/N 59X7354/5")
71 ROMX_LOAD("pgc_u44.bin", 0x00000, 0x8000, CRC(71280241) SHA1(7042ccd4ebd03f576a256a433b8aa38d1b4fefa8), ROM_BIOS(1))
72 ROMX_LOAD("pgc_u43.bin", 0x08000, 0x8000, CRC(923f5ea3) SHA1(2b2a55d64b20d3a613b00c51443105aa03eca5d6), ROM_BIOS(1))
73
74 ROM_REGION(0x800, "commarea", ROMREGION_ERASE00)
75
76 ROM_REGION(0x1000, "chargen", 0)
77 ROM_LOAD("pgc_u27.bin", 0x0000, 0x1000, CRC(6be256cc) SHA1(deb1195886268dcddce10459911e020f7a9f74f7))
78 ROM_END
79
80 static INPUT_PORTS_START( pgc )
81 PORT_START("DSW")
82 /*
83 PORT_DIPNAME( 0x01, 0x00, "CGA emulator")
84 PORT_DIPSETTING( 0x00, DEF_STR(No) )
85 PORT_DIPSETTING( 0x01, DEF_STR(Yes) )
86 */
87 PORT_DIPNAME( 0x02, 0x00, "Communication area")
88 PORT_DIPSETTING( 0x00, "C6000" )
89 PORT_DIPSETTING( 0x02, "C6400" )
90 INPUT_PORTS_END
91
92 /*
93 write only
94 30000 LUT WR O L
95 30001 LUT WR I L
96 32000 MODE WT L
97 32001 NIBBLE WT L
98 3200A ??
99 34000 FUNCTION WT L
100 34001 STARTADD WT L
101 36001 CURSOR WT L
102
103 read only
104 38000 LUT RD O L
105 38001 LUT RD I L
106 3C001 INIT L/INIT H
107 */
108
109 void isa8_pgc_device::pgc_map(address_map &map)
110 {
111 map.unmap_value_high();
112 map(0x00000, 0x07fff).rom();
113 map(0x08000, 0x0ffff).rom().region("maincpu", 0x8000);
114 map(0x10000, 0x1001f).rw(FUNC(isa8_pgc_device::stateparam_r), FUNC(isa8_pgc_device::stateparam_w));
115 // map(0x18000, 0x18fff).ram(); // ??
116 map(0x28000, 0x287ff).ram().region("commarea", 0).mirror(0x800);
117 map(0x32001, 0x32001).nopw();
118 map(0x32020, 0x3203f).w(FUNC(isa8_pgc_device::accel_w));
119 map(0x3c000, 0x3c001).r(FUNC(isa8_pgc_device::init_r));
120 // map(0x3e000, 0x3efff).ram(); // ??
121 map(0x80000, 0xf7fff).rw(FUNC(isa8_pgc_device::vram_r), FUNC(isa8_pgc_device::vram_w));
122 map(0xf8000, 0xfffff).rom().region("maincpu", 0x8000);
123 }
124
125 static const gfx_layout pgc_charlayout =
126 {
127 8, 16, /* 8x16 pixels */
128 256, /* 256 characters */
129 1, /* 1 bits per pixel */
130 { 0 }, /* no bitplanes */
131 /* x offsets */
132 { 0, 1, 2, 3, 4, 5, 6, 7 },
133 /* y offsets */
134 { 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8, 9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8 },
135 8*16 /* every char takes 10 bytes */
136 };
137
138 static GFXDECODE_START( gfx_pgc )
139 GFXDECODE_REVERSEBITS("chargen", 0, pgc_charlayout, 0, 1)
140 GFXDECODE_END
141
142
143 //**************************************************************************
144 // GLOBAL VARIABLES
145 //**************************************************************************
146
147 DEFINE_DEVICE_TYPE(ISA8_PGC, isa8_pgc_device, "isa_ibm_pgc", "IBM Professional Graphics Controller")
148
149
150 //-------------------------------------------------
151 // device_add_mconfig - add device configuration
152 //-------------------------------------------------
153
device_add_mconfig(machine_config & config)154 void isa8_pgc_device::device_add_mconfig(machine_config &config)
155 {
156 I8088(config, m_cpu, XTAL(24'000'000)/3);
157 m_cpu->set_addrmap(AS_PROGRAM, &isa8_pgc_device::pgc_map);
158 #if 0
159 m_cpu->set_irq_acknowledge_callback(FUNC(isa8_pgc_device::irq_callback));
160 #endif
161
162 SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
163 m_screen->set_raw(XTAL(50'000'000)/2,
164 PGC_TOTAL_HORZ, PGC_HORZ_START, PGC_HORZ_START+PGC_DISP_HORZ,
165 PGC_TOTAL_VERT, PGC_VERT_START, PGC_VERT_START+PGC_DISP_VERT);
166 m_screen->set_screen_update(FUNC(isa8_pgc_device::screen_update));
167 m_screen->set_palette(m_palette);
168 #if 0
169 m_screen->screen_vblank().set(FUNC(isa8_pgc_device::vblank_irq));
170 #endif
171
172 GFXDECODE(config, "gfxdecode", m_palette, gfx_pgc);
173 PALETTE(config, m_palette).set_entries(256);
174 }
175
176 //-------------------------------------------------
177 // rom_region - device-specific ROM region
178 //-------------------------------------------------
179
device_rom_region() const180 const tiny_rom_entry *isa8_pgc_device::device_rom_region() const
181 {
182 return ROM_NAME(pgc);
183 }
184
185 //-------------------------------------------------
186 // input_ports - device-specific input ports
187 //-------------------------------------------------
188
device_input_ports() const189 ioport_constructor isa8_pgc_device::device_input_ports() const
190 {
191 return INPUT_PORTS_NAME(pgc);
192 }
193
194 //**************************************************************************
195 // LIVE DEVICE
196 //**************************************************************************
197
198 //-------------------------------------------------
199 // isa8_pgc_device - constructor
200 //-------------------------------------------------
201
isa8_pgc_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)202 isa8_pgc_device::isa8_pgc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
203 isa8_pgc_device(mconfig, ISA8_PGC, tag, owner, clock)
204 {
205 }
206
isa8_pgc_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)207 isa8_pgc_device::isa8_pgc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
208 device_t(mconfig, type, tag, owner, clock),
209 device_isa8_card_interface(mconfig, *this),
210 m_cpu(*this, "maincpu"),
211 m_screen(*this, PGC_SCREEN_NAME),
212 m_palette(*this, "palette"),
213 m_commarea(nullptr), m_vram(nullptr), m_eram(nullptr)
214 {
215 }
216
device_start()217 void isa8_pgc_device::device_start()
218 {
219 if (m_palette != nullptr && !m_palette->started())
220 throw device_missing_dependencies();
221
222 set_isa_device();
223
224 for (int i = 0; i < 256; i++)
225 {
226 m_palette->set_pen_color(i, 0, 0, 0);
227 }
228
229 m_vram = std::make_unique<uint8_t[]>(0x78000);
230 m_eram = std::make_unique<uint8_t[]>(0x8000);
231
232 machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&isa8_pgc_device::reset_common, this));
233 }
234
reset_common()235 void isa8_pgc_device::reset_common()
236 {
237 address_space &space = m_cpu->space(AS_PROGRAM);
238
239 space.unmap_readwrite(0xf8000, 0xfffff);
240 space.install_rom(0xf8000, 0xfffff, memregion("maincpu")->base() + 0x8000);
241 }
242
device_reset()243 void isa8_pgc_device::device_reset()
244 {
245 memset(m_stateparam, 0, sizeof(m_stateparam));
246 memset(m_lut, 0, sizeof(m_lut));
247 m_accel = 0;
248
249 m_commarea = memregion("commarea")->base();
250 if (BIT(ioport("DSW")->read(), 1))
251 m_isa->install_bank(0xc6400, 0xc67ff, "commarea", m_commarea);
252 else
253 m_isa->install_bank(0xc6000, 0xc63ff, "commarea", m_commarea);
254 }
255
256 //
257
WRITE_LINE_MEMBER(isa8_pgc_device::vblank_irq)258 WRITE_LINE_MEMBER(isa8_pgc_device::vblank_irq)
259 {
260 if (state)
261 {
262 LOGCMD("vblank_irq\n");
263 m_cpu->set_input_line(0, ASSERT_LINE);
264 }
265 }
266
IRQ_CALLBACK_MEMBER(isa8_pgc_device::irq_callback)267 IRQ_CALLBACK_MEMBER(isa8_pgc_device::irq_callback)
268 {
269 LOGCMD("irq_callback\n");
270 m_cpu->set_input_line(0, CLEAR_LINE);
271 return 3;
272 }
273
274 // memory handlers
275
vram_r(offs_t offset)276 uint8_t isa8_pgc_device::vram_r(offs_t offset)
277 {
278 uint8_t ret;
279
280 ret = m_vram[offset];
281 LOGV("vram R @ %02x == %02x\n", offset, ret);
282 return ret;
283 }
284
285 /*
286 * accel modes (decimal)
287 *
288 * 0 - none
289 * 1 - write 4 pixels, starting at offset
290 * 2 - write up to 4 pixels, ending at offset
291 * 3 - write up to 4 pixels, starting at offset
292 * 5 - write 20 pixels, starting at offset
293 * 9 - write up to 5 pixel groups, ending at offset. offset may be in the middle of pixel group.
294 * 13 - write up to 5 pixel groups, starting at offset.
295 */
vram_w(offs_t offset,uint8_t data)296 void isa8_pgc_device::vram_w(offs_t offset, uint8_t data)
297 {
298 bool handled = true;
299
300 switch (m_accel)
301 {
302 case 0:
303 m_vram[offset] = data;
304 break;
305
306 case 1:
307 std::fill(&m_vram[offset], &m_vram[offset + 4], data);
308 break;
309
310 case 2:
311 std::fill(&m_vram[offset & ~3], &m_vram[offset + 1], data);
312 break;
313
314 case 3:
315 std::fill(&m_vram[offset], &m_vram[(offset + 4) & ~3], data);
316 break;
317
318 case 5:
319 std::fill(&m_vram[offset], &m_vram[offset + 20], data);
320 break;
321
322 case 9:
323 std::fill(&m_vram[offset - ((offset % 1024) % 20)], &m_vram[(offset + 4) & ~3], data);
324 break;
325
326 case 13:
327 std::fill(&m_vram[offset], &m_vram[(offset + 20) - ((offset % 1024) + 20) % 20], data);
328 break;
329
330 default:
331 m_vram[offset] = data;
332 handled = false;
333 break;
334 }
335 LOGV("vram W @ %02x <- %02x (accel %d)%s\n", offset, data, m_accel,
336 handled ? "" : " (unsupported)");
337 }
338
accel_w(offs_t offset,uint8_t data)339 void isa8_pgc_device::accel_w(offs_t offset, uint8_t data)
340 {
341 m_accel = offset >> 1;
342 LOGV("accel @ %05x <- %02x (%d)\n", 0x32020 + offset, data, m_accel);
343 }
344
stateparam_r(offs_t offset)345 uint8_t isa8_pgc_device::stateparam_r(offs_t offset)
346 {
347 uint8_t ret;
348
349 ret = m_stateparam[offset >> 1];
350 if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
351 {
352 LOG("stateparam R @ %02x == %02x\n", offset, ret);
353 }
354 return ret;
355 }
356
stateparam_w(offs_t offset,uint8_t data)357 void isa8_pgc_device::stateparam_w(offs_t offset, uint8_t data)
358 {
359 if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
360 {
361 LOG("stateparam W @ %02x <- %02x\n", offset, data);
362 }
363 m_stateparam[offset >> 1] = data;
364 }
365
lut_w(offs_t offset,uint8_t data)366 void isa8_pgc_device::lut_w(offs_t offset, uint8_t data)
367 {
368 uint8_t o = (offset >> 1) * 3;
369
370 if (offset & 1)
371 {
372 m_lut[o + 2] = (data & 15) * 17;
373 m_palette->set_pen_color( offset >> 1, m_lut[o], m_lut[o + 1], m_lut[o + 2] );
374 LOG("lut W @ %02X <- %d %d %d\n",
375 offset >> 1, m_lut[o], m_lut[o + 1], m_lut[o + 2] );
376 } else {
377 m_lut[o ] = (data >> 4) * 17;
378 m_lut[o + 1] = (data & 15) * 17;
379 }
380 }
381
init_r()382 uint8_t isa8_pgc_device::init_r()
383 {
384 if (!machine().side_effects_disabled())
385 {
386 address_space &space = m_cpu->space(AS_PROGRAM);
387
388 LOG("INIT: unmapping ROM\n");
389 space.unmap_read(0xf8000, 0xfffff);
390
391 LOG("INIT: mapping emulator RAM\n");
392 space.install_readwrite_bank(0xf8000, 0xfffff, "eram");
393 membank("eram")->set_base(m_eram.get());
394
395 LOG("INIT: mapping LUT\n");
396 space.install_write_handler(0xf8400, 0xf85ff, write8sm_delegate(*this, FUNC(isa8_pgc_device::lut_w)));
397 }
398
399 return 0; // XXX ignored
400 }
401
screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)402 uint32_t isa8_pgc_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
403 {
404 for (int y = 0; y < PGC_DISP_VERT; y++)
405 {
406 // XXX address translation happens in hardware
407 uint8_t const *v = &m_vram[y * 1024];
408 uint16_t *p = &bitmap.pix(y + PGC_VERT_START, PGC_HORZ_START);
409
410 for (int x = 0; x < PGC_DISP_HORZ; x++)
411 {
412 *p++ = *v++;
413 }
414 }
415 return 0;
416 }
417