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