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