summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/isa/amgda.cpp
blob: d2c0226db18d8227653de98b30c9f8521f885ace (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

/*
 * IBM Advanced Monochrome Graphics Display Adapter (also known as All-Points-Addressable-8, or APA8).
 *
 * This adapter is an entry level, bitmap-addressable, monochrome graphics
 * adapter designed for use with the IBM RT PC. It is intended to be paired
 * with an IBM 6153 Advanced Monochrome Graphics Display, a 12", monochrome,
 * white phosphor CRT. The combination produces an interlaced 720x512 pixel
 * image with a 92Hz field rate and 46Hz frame rate.
 *
 * The adapter features 64KiB of video RAM, which can be accessed in several
 * modes applying a variety of masks, shifts and logic functions to data from
 * the host or read from video RAM.
 *
 * The 16 bit ISA data bus is connected byte-swapped to the adapter video RAM
 * and registers. When accessed by the host RT PC, the byte-swapping performed
 * by the IOCC makes these registers appear to be in host byte order (i.e.,
 * big-endian).
 *
 * Sources:
 *  - IBM RT PC Hardware Technical Reference, Volume III (84X0873), Second Edition (September 1986), International Business Machines Corporation 1986.
 *
 * TODO:
 *  - fix failing interrupt diagnostic
 *  - vblank/hblank timing, interlace
 *  - interrupt clear
 *  - undefined functions
 *  - D3 content
 */
#include "emu.h"

#include "amgda.h"
#include "screen.h"

#define LOG_REGR (1U << 1)
#define LOG_REGW (1U << 2)
#define LOG_MODE (1U << 3)
#define LOG_VRAM (1U << 4)

//#define VERBOSE (LOG_GENERAL|LOG_REGR|LOG_REGW|LOG_MODE|LOG_VRAM)
#include "logmacro.h"


namespace {

class isa16_amgda_device
	: public device_t
	, public device_isa16_card_interface
{
public:
	isa16_amgda_device(machine_config const &mconfig, char const *const tag, device_t *owner, u32 clock)
		: device_t(mconfig, ISA16_AMGDA, tag, owner, clock)
		, device_isa16_card_interface(mconfig, *this)
		, m_screen(*this, "screen")
{
}

protected:
	virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
	virtual void device_start() override ATTR_COLD;
	virtual void device_reset() override ATTR_COLD;

	u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);

	void pio_map(address_map &map);
	void mem_map(address_map &map);

	u16 vram_r(offs_t offset);
	void vram_w(offs_t offset, u16 data, u16 mem_mask);

	u16 ras_r();
	u16 btr_r();
	void dcr_w(u16 data);
	void dmr_w(u16 data);

	void irq_w(int state);

	u16 address_step(u16 offset) const;
	u8 alu(unsigned const byte) const;

private:
	required_device<screen_device> m_screen;

	std::unique_ptr<u8[]> m_vram;

	enum ras_mask : u8
	{
		RAS_EF  = 0x01, // even field
		RAS_HS  = 0x02, // horizontal sync
		RAS_VS  = 0x04, // vertical sync
		RAS_SV  = 0x08, // serialized video
		RAS_X   = 0x10, // x stepping
		RAS_IP  = 0x20, // interrupt pending
		RAS_DEC = 0x40, // decrement
		RAS_LD  = 0x80, // load address
	};
	u8 m_ras; // ras status register
	enum dcr_mask : u16
	{
		DCR_ROT  = 0x0007, // rotate count
		DCR_FUNC = 0x0038, // logic function unit control
		DCR_MODE = 0x0300, // memory mode
		DCR_DEC  = 0x0400, // decrement
		DCR_X    = 0x0800, // x stepping
		DCR_BLK  = 0x1000, // block transfer
		DCR_IE   = 0x2000, // interrupt enable
		DCR_SE   = 0x4000, // sync enable
		DCR_VE   = 0x8000, // video enable
	};
	enum dcr_mode : u16
	{
		MODE_SYSTEM  = 0x0000,
		MODE_OVERLAY = 0x0100,
		MODE_ADAPTER = 0x0200,
		MODE_AUTO    = 0x0300,
	};
	u16 m_dcr;  // data control register
	u8 m_dm[2]; // data mask registers
	u8 m_wm[2]; // write mask registers

	// internal state
	u8 m_sy[2]; // system data latches
	u8 m_d[3];  // on-card data latches
	u16 m_ba;   // block address
	bool m_aw;  // automatic write
};

void isa16_amgda_device::device_add_mconfig(machine_config &config)
{
	// monitor has 25.7MHz nominal bandwidth
	// OSC: 44.22MHz
	// H: 24.68KHz, retrace: 8.0us
	// V: 92Hz field, 46Hz frame
	// 720x512, 64Kx8 video ram
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_raw(44'220'000 / 2, 800, 0, 720, 600, 0, 512);
	m_screen->set_screen_update(FUNC(isa16_amgda_device::screen_update));
	m_screen->screen_vblank().set(
		[this](int state)
		{
			if (state)
			{
				m_ras ^= RAS_EF;

				if (m_dcr & DCR_IE)
					irq_w(1);
			}
		});
}

void isa16_amgda_device::device_start()
{
	save_item(NAME(m_ras));
	save_item(NAME(m_dcr));
	save_item(NAME(m_dm));
	save_item(NAME(m_wm));

	save_item(NAME(m_sy));
	save_item(NAME(m_d));
	save_item(NAME(m_ba));
	save_item(NAME(m_aw));

	m_vram = std::make_unique<u8[]>(0x1'0000);
	save_pointer(NAME(m_vram), 0x1'0000);

	set_isa_device();

	m_isa->install_device(0x0160, 0x016f, *this, &isa16_amgda_device::pio_map);
	m_isa->install_memory(0xd0'0000, 0xd1'ffff, *this, &isa16_amgda_device::mem_map);

	// TODO: ISA bus shared interrupt 11 enable
	//m_isa->space(isa16_device::AS_ISA_IO).install_write_tap(0x06f3, 0x06f3, "irq_arm", [this](offs_t offset, u8 &data, u8 mem_mask) { irq_w(0); });
}

void isa16_amgda_device::device_reset()
{
	irq_w(0);

	m_ras = RAS_LD;
	m_dcr = 0;
	m_dm[0] = 0;
	m_dm[1] = 0;
	m_wm[0] = 0;
	m_wm[1] = 0;

	m_sy[0] = 0;
	m_sy[1] = 0;
	m_d[0] = 0;
	m_d[1] = 0;
	m_d[2] = 0;
	m_ba = 0;
	m_aw = false;
}

u32 isa16_amgda_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	if (m_dcr & DCR_VE)
	{
		u8 const *vram = m_vram.get();

		for (s32 y = screen.visible_area().min_y; y <= screen.visible_area().max_y; y++)
		{
			for (s32 x = screen.visible_area().min_x; x <= screen.visible_area().max_x; x += 8)
			{
				u8 const data = *vram++;

				bitmap.pix(y, x + 0) = BIT(data, 7) ? rgb_t::white() : rgb_t::black();
				bitmap.pix(y, x + 1) = BIT(data, 6) ? rgb_t::white() : rgb_t::black();
				bitmap.pix(y, x + 2) = BIT(data, 5) ? rgb_t::white() : rgb_t::black();
				bitmap.pix(y, x + 3) = BIT(data, 4) ? rgb_t::white() : rgb_t::black();
				bitmap.pix(y, x + 4) = BIT(data, 3) ? rgb_t::white() : rgb_t::black();
				bitmap.pix(y, x + 5) = BIT(data, 2) ? rgb_t::white() : rgb_t::black();
				bitmap.pix(y, x + 6) = BIT(data, 1) ? rgb_t::white() : rgb_t::black();
				bitmap.pix(y, x + 7) = BIT(data, 0) ? rgb_t::white() : rgb_t::black();
			}

			vram += 38;
		}
	}
	else
		bitmap.fill(rgb_t::black());

	return 0;
}

void isa16_amgda_device::pio_map(address_map &map)
{
	map(0x0, 0x1).rw(FUNC(isa16_amgda_device::ras_r), FUNC(isa16_amgda_device::dcr_w)).flags(1);
	map(0x2, 0x3).rw(FUNC(isa16_amgda_device::btr_r), FUNC(isa16_amgda_device::dmr_w)).flags(1);
}

void isa16_amgda_device::mem_map(address_map &map)
{
	map(0x00000, 0x1ffff).rw(FUNC(isa16_amgda_device::vram_r), FUNC(isa16_amgda_device::vram_w));
}

u16 isa16_amgda_device::vram_r(offs_t offset)
{
	// treat debugger read as x++ system read without side effects
	if (machine().side_effects_disabled())
		return u16(m_vram[offset + 1]) << 8 | m_vram[offset + 0];

	if (m_dcr & DCR_BLK)
	{
		if (m_ras & RAS_LD)
		{
			m_ba = offset;
			m_ras &= ~RAS_LD;
		}
		else
			offset = m_ba;
	}

	u16 address = offset;

	for (unsigned byte = 0; byte < 2; byte++)
	{
		// latch data
		if ((m_dcr & DCR_MODE) != MODE_ADAPTER)
			m_d[byte] = m_vram[address];

		address = address_step(address);
	}

	if (m_dcr & DCR_BLK)
		m_ba = address;

	m_aw = true;

	LOGMASKED(LOG_VRAM, "%s: vram_r 0x%04x data 0x%02x%02x\n", machine().describe_context(), offset, m_d[0], m_d[1]);

	// external data bus is byte-swapped
	return (u16(m_d[1]) << 8) | m_d[0];
}

void isa16_amgda_device::vram_w(offs_t offset, u16 data, u16 mem_mask)
{
	// external data bus is byte-swapped
	data = swapendian_int16(data);

	if (m_dcr & DCR_BLK)
	{
		if (m_ras & RAS_LD)
		{
			m_ba = offset;
			m_ras &= ~RAS_LD;
		}
		else
			offset = m_ba;
	}

	if (!machine().side_effects_disabled())
		LOGMASKED(LOG_VRAM, "%s: vram_w 0x%04x data 0x%04x\n", machine().describe_context(), offset, data);

	u16 address = offset;

	for (unsigned byte = 0; byte < 2; byte++)
	{
		switch (m_dcr & DCR_MODE)
		{
		case MODE_SYSTEM:
			m_sy[byte] = BIT(data, 8 - byte * 8, 8);
			break;
		case MODE_OVERLAY:
			m_wm[byte] = BIT(data, 8 - byte * 8, 8);
			break;
		}

		if ((m_dcr & DCR_MODE) != MODE_AUTO || m_aw)
			m_vram[address] = (m_vram[address] & m_wm[byte]) | (alu(byte) & ~m_wm[byte]);
		else
			m_d[byte] = m_vram[address];

		address = address_step(address);
	}

	if (m_dcr & DCR_BLK)
		m_ba = address;

	m_aw = !m_aw;
}

u16 isa16_amgda_device::ras_r()
{
	u16 data = m_ras;

	if (m_dcr & DCR_DEC)
		data |= RAS_DEC;

	if (m_dcr & DCR_X)
		data |= RAS_X;

	if (!machine().side_effects_disabled())
	{
		rectangle const &visible = m_screen->visible_area();
		int const hpos = m_screen->hpos();
		int const vpos = m_screen->vpos();

		if (visible.contains(hpos, vpos))
		{
			unsigned const bit = (vpos - visible.min_y) * 1024 + (hpos - visible.min_x);

			if ((m_dcr & DCR_VE) && BIT(m_vram[bit >> 3], 7 - (bit & 7)))
				data |= RAS_SV;
		}
		else if (vpos < visible.min_y || vpos > visible.max_y)
			data |= RAS_VS;
		else if (hpos < visible.min_x || hpos > visible.max_x)
			data |= RAS_HS;

		LOGMASKED(LOG_REGR, "%s: ras_r 0x%02x\n", machine().describe_context(), data);
	}

	// external data bus is byte-swapped
	return swapendian_int16(data);
}

u16 isa16_amgda_device::btr_r()
{
	if (!machine().side_effects_disabled())
	{
		LOGMASKED(LOG_REGR, "%s: btr_r\n", machine().describe_context());

		m_ras |= RAS_LD;
	}

	return 0;
}

void isa16_amgda_device::dcr_w(u16 data)
{
	// external data bus is byte-swapped
	data = swapendian_int16(data);

	if (!machine().side_effects_disabled())
	{
		LOGMASKED(LOG_REGW, "%s: dcr_w 0x%04x\n", machine().describe_context(), data);

		static char const *const func[] = { "B", "1", "A", "3", "~B", "A|B", "~A", "~A|B" };
		static char const *const mode[] = { "system", "overlay", "adapter", "automatic" };

		LOGMASKED(LOG_MODE, "ve=%u, se=%u, ie=%u, blk=%u, %s%s, mode=%s, function=%s, rotate=%u\n",
			BIT(data, 15), BIT(data, 14), BIT(data, 13), BIT(data, 12),
			(data & DCR_X) ? "x" : "y", (data & DCR_DEC) ? "--" : "++",
			mode[BIT(data, 8, 2)], func[BIT(data, 3, 3)], (data & DCR_ROT));
	}

	if ((data ^ m_dcr) & DCR_BLK)
		m_ras |= RAS_LD;

	if (!(data & DCR_IE))
		irq_w(0);

	m_dcr = data;
}

void isa16_amgda_device::dmr_w(u16 data)
{
	// external data bus is byte-swapped
	data = swapendian_int16(data);

	if (!machine().side_effects_disabled())
		LOGMASKED(LOG_REGW, "%s: dmr_w 0x%04x\n", machine().describe_context(), data);

	m_dm[0] = BIT(data, 8, 8);
	m_dm[1] = BIT(data, 0, 8);
}

void isa16_amgda_device::irq_w(int state)
{
	if (state)
	{
		if (!(m_ras & RAS_IP))
		{
			m_ras |= RAS_IP;
			m_isa->irq11_w(state);
		}
	}
	else
	{
		if (m_ras & RAS_IP)
		{
			m_ras &= ~RAS_IP;
			m_isa->irq11_w(state);
		}
	}
}

u16 isa16_amgda_device::address_step(u16 offset) const
{
	switch (BIT(m_dcr, 10, 2))
	{
	case 0: return offset + 128; // y++
	case 1: return offset - 128; // y--
	case 2: return (offset & 0xff80) | ((offset + 1) & 0x7f); // x++
	case 3: return (offset & 0xff80) | ((offset - 1) & 0x7f); // x--
	}

	// can't happen
	abort();
}

u8 isa16_amgda_device::alu(unsigned const byte) const
{
	// apply data masks and shift
	unsigned const shift = m_dcr & DCR_ROT;
	u8 const a = m_dm[0] & m_sy[byte];
	u8 const b = m_dm[1] & ((m_d[byte + 0] << shift) | (m_d[byte + 1] >> (8 - shift)));

	switch (BIT(m_dcr, 3, 3))
	{
	case 0: return b;
	case 2: return a;
	case 4: return ~b;
	case 5: return a | b;
	case 6: return ~a;
	case 7: return ~(a | b);
	}

	// FIXME: undocumented functions 1 and 3
	return 0;
}

} // anonymous namespace

DEFINE_DEVICE_TYPE_PRIVATE(ISA16_AMGDA, device_isa16_card_interface, isa16_amgda_device, "amgda", "IBM Advanced Monochrome Graphics Display Adapter")