1 // license:BSD-3-Clause
2 // copyright-holders:R. Belmont
3 /***************************************************************************
4 
5     apple1.cpp - Apple I
6 
7     Next generation driver written in February 2016 by R. Belmont.
8     Thanks to the original crew.
9 
10     Apple I has:
11         6502 @ 1.023 MHz (~0.960 MHz with RAM refresh)
12         4 or 8 KB RAM on-board
13         256 byte Monitor ROM
14         No IRQs, no sound, dumb terminal video
15         6820 PIA for keyboard / terminal interface
16 
17     -------------------------------------------------------------------
18 
19     How to use cassettes:
20     The system has no error checking or checksums, and the cassette
21     has no header.
22     Therefore, you must know the details, and pass these to the
23     interface yourself.
24 
25     BASIC has no cassette handling. You must enter the monitor
26     with: CALL -151
27     then when finished, re-enter BASIC with: E2B3R
28 
29     Examples:
30 
31     A machine-language program will typically be like this:
32     C100R    (enter the interface)
33     0300.0FFFR  (enter the load and end addresses, then load the tape)
34     You start the tape.
35     When the prompt returns you stop the tape.
36     0300R  (run your program)
37 
38 
39     To Load Tape Basic:
40     C100R
41     E000.EFFFR
42     You start the tape.
43     When the prompt returns you stop the tape.
44     E000R  (It must say 4C - if not, your tape is no good).
45     The BASIC prompt will appear
46     >@
47 
48 
49     A BASIC program is split into two areas, one for the scratch pad,
50     and one for the program proper.
51     In BASIC you may have to adjust the allowed memory area, such as
52     LOMEM = 768
53     Then, go to the monitor: CALL -151
54     C100R    (enter the interface)
55     00A4.00FFR 0300.0FFFR   (load the 2 parts)
56     You start the tape.
57     When the prompt returns you stop the tape.
58     E2B3R    (back to BASIC)
59     You can LIST or RUN now.
60 
61 
62     Saving is almost the same, when you specify the address range, enter
63     W instead of R. The difficulty is finding out how long your program is.
64 
65     Insert a blank tape
66     C100R
67     0300.0FFFW
68     Quickly press Record.
69     When the prompt returns, press Stop.
70 
71 **********************************************************************/
72 
73 #include "emu.h"
74 #include "cpu/m6502/m6502.h"
75 #include "machine/6821pia.h"
76 #include "imagedev/snapquik.h"
77 #include "machine/ram.h"
78 
79 #include "bus/a1bus/a1bus.h"
80 #include "bus/a1bus/a1cassette.h"
81 #include "bus/a1bus/a1cffa.h"
82 
83 #include "emupal.h"
84 #include "screen.h"
85 #include "softlist.h"
86 
87 #define A1_CPU_TAG  "maincpu"
88 #define A1_PIA_TAG  "pia6821"
89 #define A1_BUS_TAG  "a1bus"
90 #define A1_BASICRAM_TAG "basicram"
91 
92 class apple1_state : public driver_device
93 {
94 public:
apple1_state(const machine_config & mconfig,device_type type,const char * tag)95 	apple1_state(const machine_config &mconfig, device_type type, const char *tag)
96 		: driver_device(mconfig, type, tag),
97 		m_maincpu(*this, A1_CPU_TAG),
98 		m_pia(*this, A1_PIA_TAG),
99 		m_ram(*this, RAM_TAG),
100 		m_screen(*this, "screen"),
101 		m_basicram(*this, A1_BASICRAM_TAG),
102 		m_kb0(*this, "KEY0"),
103 		m_kb1(*this, "KEY1"),
104 		m_kb2(*this, "KEY2"),
105 		m_kb3(*this, "KEY3"),
106 		m_kbspecial(*this, "KBSPECIAL")
107 	{ }
108 
109 	void apple1(machine_config &config);
110 
111 protected:
112 	virtual void machine_start() override;
113 	virtual void machine_reset() override;
114 
115 private:
116 	required_device<cpu_device> m_maincpu;
117 	required_device<pia6821_device> m_pia;
118 	required_device<ram_device> m_ram;
119 	required_device<screen_device> m_screen;
120 	required_shared_ptr<uint8_t> m_basicram;
121 	required_ioport m_kb0, m_kb1, m_kb2, m_kb3, m_kbspecial;
122 
123 	uint8_t *m_ram_ptr, *m_char_ptr;
124 	int m_ram_size, m_char_size;
125 
126 	uint8_t m_vram[40*24];
127 	int m_cursx, m_cursy;
128 
129 	bool m_reset_down;
130 	bool m_clear_down;
131 
132 	uint8_t m_transchar;
133 	uint16_t m_lastports[4];
134 
135 	emu_timer *m_ready_start_timer, *m_ready_end_timer, *m_kbd_strobe_timer;
136 
137 	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
138 
139 	uint8_t ram_r(offs_t offset);
140 	void ram_w(offs_t offset, uint8_t data);
141 	uint8_t pia_keyboard_r();
142 	void pia_display_w(uint8_t data);
143 	DECLARE_WRITE_LINE_MEMBER(pia_display_gate_w);
144 	DECLARE_SNAPSHOT_LOAD_MEMBER(snapshot_cb);
145 	TIMER_CALLBACK_MEMBER(ready_start_cb);
146 	TIMER_CALLBACK_MEMBER(ready_end_cb);
147 	TIMER_CALLBACK_MEMBER(keyboard_strobe_cb);
148 
149 	void apple1_map(address_map &map);
150 
151 	void plot_text_character(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code, const uint8_t *textgfx_data, uint32_t textgfx_datalen);
152 	void poll_keyboard();
153 };
154 
155 static const uint8_t apple1_keymap[] =
156 {
157 	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '=', '[', ']', ';', '\'',    // KEY0
158 	',', '.', '/', '\\', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',    // KEY1
159 	'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '\r', '_',    // KEY2
160 	' ', '\x1b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                              // KEY3
161 
162 	')', '!', '@', '#', '$', '%', '^', '&', '*', '(', '_', '+', '[', ']', ':', '"',     // KEY0 + shift
163 	'<', '>', '?', '\\', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',    // KEY1 + shift
164 	'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '\r', '_',    // KEY2 + shift
165 	' ', '\x1b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                              // KEY3 + shift
166 
167 	'0', '1', '\x00', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', '8', '9', '\x1f', '=', '\x1b', '\x1d', ';', '\'', // KEY0 + CTRL
168 	',', '.', '/', '\x1c', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0a', '\x0b', '\x0c',  // KEY1 + CTRL
169 	'\x0d', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1a', '\r', '_',  // KEY2 + CTRL
170 	' ', '\x1b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                              // KEY3 + CTRL
171 
172 };
173 
174 // header is "LOAD:abcdDATA:" where abcd is the starting address
SNAPSHOT_LOAD_MEMBER(apple1_state::snapshot_cb)175 SNAPSHOT_LOAD_MEMBER(apple1_state::snapshot_cb)
176 {
177 	uint64_t snapsize;
178 	uint8_t *data;
179 	uint16_t start, end;
180 	static const char hd1[6] = "LOAD:";
181 	static const char hd2[6] = "DATA:";
182 
183 	// get the snapshot's size
184 	snapsize = image.length();
185 
186 	if (snapsize < 12)
187 	{
188 		logerror("Snapshot is too short\n");
189 		return image_init_result::FAIL;
190 	}
191 
192 	if ((snapsize - 12) > 65535)
193 	{
194 		logerror("Snapshot is too long\n");
195 		return image_init_result::FAIL;
196 	}
197 
198 	data = (uint8_t *)image.ptr();
199 	if (!data)
200 	{
201 		logerror("Internal error loading snapshot\n");
202 		return image_init_result::FAIL;
203 	}
204 
205 	if ((memcmp(hd1, data, 5)) || (memcmp(hd2, &data[7], 5)))
206 	{
207 		logerror("Snapshot is invalid\n");
208 		return image_init_result::FAIL;
209 	}
210 
211 	start = (data[5]<<8) | data[6];
212 	end = (snapsize - 12) + start;
213 
214 	// check if this fits in RAM; load below 0xe000 must fit in RAMSIZE,
215 	// load at 0xe000 must fit in 4K
216 	if (((start < 0xe000) && (end > (m_ram_size - 1))) || (end > 0xefff))
217 	{
218 		logerror("Snapshot can't fit in RAM\n");
219 		return image_init_result::FAIL;
220 	}
221 
222 	if (start < 0xe000)
223 	{
224 		memcpy(m_ram_ptr + start, &data[12], snapsize - 12);
225 	}
226 	else if ((start >= 0xe000) && (start <= 0xefff))
227 	{
228 		memcpy(m_basicram + (start - 0xe000), &data[12], snapsize - 12);
229 	}
230 	else
231 	{
232 		logerror("Snapshot has invalid load address %04x\n", start);
233 		return image_init_result::FAIL;
234 	}
235 
236 	return image_init_result::PASS;
237 }
238 
poll_keyboard()239 void apple1_state::poll_keyboard()
240 {
241 	uint8_t special = m_kbspecial->read();
242 	uint16_t ports[4];
243 	int rawkey = 0;
244 	bool bKeypress = false;
245 
246 	// handle special keys first:
247 	if (special & 0x10) // RESET
248 	{
249 		m_reset_down = true;
250 		m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
251 		m_pia->reset();
252 	}
253 	else if (m_reset_down)
254 	{
255 		m_reset_down = false;
256 		m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
257 	}
258 
259 	if (special & 0x20) // CLEAR SCREEN
260 	{
261 		m_clear_down = true;
262 		memset(m_vram, 0, sizeof(m_vram));
263 		m_cursx = m_cursy = 0;
264 	}
265 	else
266 	{
267 		m_clear_down = false;
268 	}
269 
270 	// lower the keyboard strobe
271 	m_pia->ca1_w(0);
272 
273 	// cache all the rows
274 	ports[0] = m_kb0->read();
275 	ports[1] = m_kb1->read();
276 	ports[2] = m_kb2->read();
277 	ports[3] = m_kb3->read();
278 
279 	for (int port = 0; port < 4; port++)
280 	{
281 		uint16_t ptread = ports[port] ^ m_lastports[port];
282 
283 		for (int bit = 0; bit < 16; bit++)
284 		{
285 			// key changed?
286 			if (ptread & (1 << bit))
287 			{
288 				// key down?
289 				if (ports[port] & (1 << bit))
290 				{
291 					rawkey = (port * 16) + bit;
292 					m_lastports[port] |= (1 << bit);
293 					port = 4;   // force outer for loop to quit too
294 					bKeypress = true;
295 				}
296 				else    // key up
297 				{
298 					m_lastports[port] &= ~(1 << bit);
299 				}
300 				break;
301 			}
302 		}
303 	}
304 
305 	if (bKeypress)
306 	{
307 		if ((special & 0xc) != 0)
308 		{
309 			m_transchar = apple1_keymap[rawkey + (8*16)];
310 		}
311 		else if ((special & 0x3) != 0)
312 		{
313 			m_transchar = apple1_keymap[rawkey + (4*16)];
314 		}
315 		else
316 		{
317 			m_transchar = apple1_keymap[rawkey];
318 		}
319 		// pulse the strobe line
320 		m_pia->ca1_w(1);
321 	}
322 }
323 
plot_text_character(bitmap_ind16 & bitmap,int xpos,int ypos,int xscale,uint32_t code,const uint8_t * textgfx_data,uint32_t textgfx_datalen)324 void apple1_state::plot_text_character(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code,
325 	const uint8_t *textgfx_data, uint32_t textgfx_datalen)
326 {
327 	int fg = 1, bg = 0;
328 	int const charcode = (code & 0x1f) | (((code ^ 0x40) & 0x40) >> 1);
329 
330 	/* look up the character data */
331 	uint8_t const *const chardata = &textgfx_data[(charcode * 8)];
332 
333 	for (int y = 0; y < 8; y++)
334 	{
335 		for (int x = 0; x < 7; x++)
336 		{
337 			uint16_t const color = (chardata[y] & (1 << (6-x))) ? fg : bg;
338 
339 			for (int i = 0; i < xscale; i++)
340 			{
341 				bitmap.pix(ypos + y, xpos + (x * xscale) + i) = color;
342 			}
343 		}
344 	}
345 }
346 
screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)347 uint32_t apple1_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
348 {
349 	int vramad;
350 	int cursor_blink = 0;
351 	uint8_t curs_save = 0;
352 
353 	poll_keyboard();
354 
355 	// the cursor 555 timer counts 0.52 of a second; the cursor is ON for
356 	// 2 of those counts and OFF for the last one.
357 	if ((int(machine().time().as_double() / (0.52 / 3.0)) % 3) < 2)
358 	{
359 		curs_save = m_vram[(m_cursy * 40) + m_cursx];
360 		m_vram[(m_cursy * 40) + m_cursx] = 0x40;
361 		cursor_blink = 1;
362 	}
363 
364 	for (int row = 0; row < cliprect.bottom(); row += 8)
365 	{
366 		for (int col = 0; col < 40; col++)
367 		{
368 			vramad = ((row/8) * 40) + col;
369 
370 			plot_text_character(bitmap, col * 14, row, 2, m_vram[vramad],
371 				m_char_ptr, m_char_size);
372 		}
373 	}
374 
375 	if (cursor_blink)
376 	{
377 		m_vram[(m_cursy * 40) + m_cursx] = curs_save;
378 	}
379 
380 	return 0;
381 }
382 
machine_start()383 void apple1_state::machine_start()
384 {
385 	m_ram_ptr = m_ram->pointer();
386 	m_ram_size = m_ram->size();
387 	m_char_ptr = memregion("gfx1")->base();
388 	m_char_size = memregion("gfx1")->bytes();
389 
390 	m_reset_down = m_clear_down = false;
391 
392 	m_ready_start_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(apple1_state::ready_start_cb), this));
393 	m_ready_end_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(apple1_state::ready_end_cb), this));
394 	m_kbd_strobe_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(apple1_state::keyboard_strobe_cb), this));
395 
396 	// setup save states
397 	save_item(NAME(m_vram));
398 	save_item(NAME(m_cursx));
399 	save_item(NAME(m_cursy));
400 	save_item(NAME(m_reset_down));
401 	save_item(NAME(m_clear_down));
402 	save_item(NAME(m_transchar));
403 	save_item(NAME(m_lastports));
404 }
405 
machine_reset()406 void apple1_state::machine_reset()
407 {
408 	memset(m_vram, 0, sizeof(m_vram));
409 	m_transchar = 0;
410 	m_cursx = m_cursy = 0;
411 	m_lastports[0] = m_lastports[1] = m_lastports[2] = m_lastports[3] = 0;
412 }
413 
ram_r(offs_t offset)414 uint8_t apple1_state::ram_r(offs_t offset)
415 {
416 	if (offset < m_ram_size)
417 	{
418 		return m_ram_ptr[offset];
419 	}
420 
421 	return 0xff;
422 }
423 
ram_w(offs_t offset,uint8_t data)424 void apple1_state::ram_w(offs_t offset, uint8_t data)
425 {
426 	if (offset < m_ram_size)
427 	{
428 		m_ram_ptr[offset] = data;
429 	}
430 }
431 
apple1_map(address_map & map)432 void apple1_state::apple1_map(address_map &map)
433 {
434 	map(0x0000, 0xbfff).rw(FUNC(apple1_state::ram_r), FUNC(apple1_state::ram_w));
435 	map(0xd010, 0xd013).mirror(0x0fec).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
436 	map(0xe000, 0xefff).ram().share(A1_BASICRAM_TAG);
437 	map(0xff00, 0xffff).rom().region(A1_CPU_TAG, 0);
438 }
439 
pia_keyboard_r()440 uint8_t apple1_state::pia_keyboard_r()
441 {
442 	return m_transchar | 0x80;  // bit 7 is wired high, similar-ish to the Apple II
443 }
444 
pia_display_w(uint8_t data)445 void apple1_state::pia_display_w(uint8_t data)
446 {
447 	data &= 0x7f;   // D7 is ignored by the video h/w
448 
449 	// ignore characters if CLEAR is down
450 	if (m_clear_down)
451 	{
452 		return;
453 	}
454 
455 	// video h/w rejects control characters except CR
456 	if ((data < 32) && (data != '\r'))
457 	{
458 		return;
459 	}
460 
461 	if (data == '\r')
462 	{
463 		m_cursx = 0;
464 		m_cursy++;
465 	}
466 	else
467 	{
468 		m_vram[(m_cursy * 40) + m_cursx] = data;
469 
470 		m_cursx++;
471 		if (m_cursx > 39)
472 		{
473 			m_cursx = 0;
474 			m_cursy++;
475 		}
476 	}
477 
478 	// scroll the screen if we're at the bottom
479 	if (m_cursy > 23)
480 	{
481 		for (int sy = 0; sy < 23; sy++)
482 		{
483 			memcpy(&m_vram[sy * 40], &m_vram[(sy + 1) * 40], 40);
484 		}
485 		memset(&m_vram[23*40], 0, 40);
486 		m_cursy = 23;
487 	}
488 }
489 
490 // CB2 here is connected two places: Port B bit 7 for CPU readback,
491 // and to the display hardware
WRITE_LINE_MEMBER(apple1_state::pia_display_gate_w)492 WRITE_LINE_MEMBER(apple1_state::pia_display_gate_w)
493 {
494 	m_pia->portb_w((state << 7) ^ 0x80);
495 
496 	// falling edge means start the display timer
497 	if (state == CLEAR_LINE)
498 	{
499 		m_ready_start_timer->adjust(m_screen->time_until_pos(m_cursy, m_cursx));
500 	}
501 }
502 
TIMER_CALLBACK_MEMBER(apple1_state::ready_start_cb)503 TIMER_CALLBACK_MEMBER(apple1_state::ready_start_cb)
504 {
505 	// we're ready, pulse CB1 for 3500 nanoseconds
506 	m_pia->cb1_w(0);
507 	m_ready_end_timer->adjust(attotime::from_nsec(3500));
508 }
509 
TIMER_CALLBACK_MEMBER(apple1_state::ready_end_cb)510 TIMER_CALLBACK_MEMBER(apple1_state::ready_end_cb)
511 {
512 	m_pia->cb1_w(1);
513 }
514 
TIMER_CALLBACK_MEMBER(apple1_state::keyboard_strobe_cb)515 TIMER_CALLBACK_MEMBER(apple1_state::keyboard_strobe_cb)
516 {
517 	m_pia->ca1_w(0);
518 }
519 
520 static INPUT_PORTS_START( apple1 )
521 	PORT_START("KEY0")
PORT_CODE(KEYCODE_0)522 	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0)        PORT_CHAR('0') PORT_CHAR(')')
523 	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)        PORT_CHAR('1') PORT_CHAR('!')
524 	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)        PORT_CHAR('2') PORT_CHAR('@')
525 	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)        PORT_CHAR('3') PORT_CHAR('#')
526 	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)        PORT_CHAR('4') PORT_CHAR('$')
527 	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)        PORT_CHAR('5') PORT_CHAR('%')
528 	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)        PORT_CHAR('6') PORT_CHAR('^')
529 	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)        PORT_CHAR('7') PORT_CHAR('&')
530 	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)        PORT_CHAR('8') PORT_CHAR('*')
531 	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)        PORT_CHAR('9') PORT_CHAR('(')
532 	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS)    PORT_CHAR('-') PORT_CHAR('_')
533 	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS)   PORT_CHAR('=') PORT_CHAR('+')
534 	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('[')
535 	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR(']')
536 	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON)    PORT_CHAR(';') PORT_CHAR(':')
537 	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE)    PORT_CHAR('\'') PORT_CHAR('"')
538 
539 	PORT_START("KEY1")
540 	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA)    PORT_CHAR(',') PORT_CHAR('<')
541 	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP)     PORT_CHAR('.') PORT_CHAR('>')
542 	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH)    PORT_CHAR('/') PORT_CHAR('?')
543 	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR('\\')
544 	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_A)        PORT_CHAR('A')
545 	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_B)        PORT_CHAR('B')
546 	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_C)        PORT_CHAR('C')
547 	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_D)        PORT_CHAR('D')
548 	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_E)        PORT_CHAR('E')
549 	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F)        PORT_CHAR('F')
550 	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_G)        PORT_CHAR('G')
551 	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_H)        PORT_CHAR('H')
552 	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_I)        PORT_CHAR('I')
553 	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_J)        PORT_CHAR('J')
554 	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K)        PORT_CHAR('K')
555 	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L)        PORT_CHAR('L')
556 
557 	PORT_START("KEY2")
558 	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M)        PORT_CHAR('M')
559 	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N)        PORT_CHAR('N')
560 	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O)        PORT_CHAR('O')
561 	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_P)        PORT_CHAR('P')
562 	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q)        PORT_CHAR('Q')
563 	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_R)        PORT_CHAR('R')
564 	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_S)        PORT_CHAR('S')
565 	PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_T)        PORT_CHAR('T')
566 	PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_U)        PORT_CHAR('U')
567 	PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_V)        PORT_CHAR('V')
568 	PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_W)        PORT_CHAR('W')
569 	PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_X)        PORT_CHAR('X')
570 	PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y)        PORT_CHAR('Y')
571 	PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z)        PORT_CHAR('Z')
572 	PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER)    PORT_CHAR(13)
573 	PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR('_')
574 
575 	PORT_START("KEY3")
576 	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
577 	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Escape") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
578 
579 	PORT_START("KBSPECIAL")
580 	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
581 	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
582 	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Control") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
583 	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Control") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
584 	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F1))
585 	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
586 INPUT_PORTS_END
587 
588 static void apple1_cards(device_slot_interface &device)
589 {
590 	device.option_add("cassette", A1BUS_CASSETTE);
591 	device.option_add("cffa", A1BUS_CFFA);
592 }
593 
apple1(machine_config & config)594 void apple1_state::apple1(machine_config &config)
595 {
596 	M6502(config, m_maincpu, 960000);        // effective CPU speed
597 	m_maincpu->set_addrmap(AS_PROGRAM, &apple1_state::apple1_map);
598 
599 	// video timings are identical to the Apple II, unsurprisingly
600 	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
601 	m_screen->set_raw(XTAL(14'318'181), (65*7)*2, 0, (40*7)*2, 262, 0, 192);
602 	m_screen->set_screen_update(FUNC(apple1_state::screen_update));
603 	m_screen->set_palette("palette");
604 
605 	PALETTE(config, "palette", palette_device::MONOCHROME);
606 
607 	PIA6821(config, m_pia, 0);
608 	m_pia->readpa_handler().set(FUNC(apple1_state::pia_keyboard_r));
609 	m_pia->writepb_handler().set(FUNC(apple1_state::pia_display_w));
610 	m_pia->cb2_handler().set(FUNC(apple1_state::pia_display_gate_w));
611 
612 	A1BUS(config, A1_BUS_TAG, 0).set_space(m_maincpu, AS_PROGRAM);
613 	A1BUS_SLOT(config, "exp", 0, A1_BUS_TAG, apple1_cards, "cassette");
614 
615 	SNAPSHOT(config, "snapshot", "snp").set_load_callback(FUNC(apple1_state::snapshot_cb));
616 
617 	SOFTWARE_LIST(config, "cass_list").set_original("apple1");
618 
619 	RAM(config, RAM_TAG).set_default_size("48K").set_extra_options("4K,8K,12K,16K,20K,24K,28K,32K,36K,40K,44K");
620 }
621 
622 ROM_START(apple1)
623 	ROM_REGION(0x100, A1_CPU_TAG, 0)
624 	ROM_LOAD_NIB_HIGH("apple-a2.a2", 0x0000, 0x0100, CRC(254bfb95) SHA1(b6468b72295b7d8ac288d104d252f24de1f1d611) )
625 	ROM_LOAD_NIB_LOW("apple-a1.a1", 0x0000, 0x0100, CRC(434f8ce6) SHA1(9deee2d39903209b20c3fc6b58e16372f8efece1) )
626 	ROM_REGION(0x0200, "gfx1",0)
627 	ROM_LOAD("s2513.d2", 0x0000, 0x0200, CRC(a7e567fc) SHA1(b18aae0a2d4f92f5a7e22640719bbc4652f3f4ee)) // apple1.vid
628 ROM_END
629 
630 /*    YEAR  NAME    PARENT  COMPAT  MACHINE  INPUT   CLASS         INIT        COMPANY           FULLNAME */
631 COMP( 1976, apple1, 0,      0,      apple1,  apple1, apple1_state, empty_init, "Apple Computer", "Apple I", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE )
632