apple1: completely rewrote the driver in modern idioms. All functionality should be the same. [R. Belmont]

This commit is contained in:
arbee 2016-02-07 21:11:28 -05:00
parent 54a53b4dba
commit 9205d549a7
4 changed files with 500 additions and 1103 deletions

View File

@ -1,135 +1,78 @@
// license:???
// copyright-holders:Paul Daniels, Colin Howell, R. Belmont
/**********************************************************************
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************
Apple I
apple1.cpp - Apple I
CPU: 6502 @ 1.023 MHz
(Effective speed with RAM refresh waits is 0.960 MHz.)
Next generation driver written in February 2016 by R. Belmont.
Thanks to the original crew.
Apple I has:
6502 @ 1.023 MHz (~0.960 MHz with RAM refresh)
4 or 8 KB RAM on-board
256 byte Monitor ROM
No IRQs, no sound, dumb terminal video
6820 PIA for keyboard / terminal interface
-------------------------------------------------------------------
How to use cassettes:
The system has no error checking or checksums, and the cassette
has no header.
Therefore, you must know the details, and pass these to the
interface yourself.
BASIC has no cassette handling. You must enter the monitor
with: CALL -151
then when finished, re-enter BASIC with: E2B3R
RAM: 4-8 KB on main board (4 KB standard)
Examples:
Additional memory could be added via the expansion
connector, but the user was responsible for making sure
the extra memory was properly interfaced.
Some users replaced the onboard 4-kilobit RAM chips with
16-kilobit RAM chips, increasing on-board memory to 32 KB,
but this required modifying the RAM interface circuitry.
ROM: 256 bytes for Monitor program
Optional cassette interface included 256 bytes for
cassette routines.
Interrupts: None.
(The system board had jumpers to allow interrupts, but
these were not connected in a standard system.)
Video: Dumb terminal, based on 7 1K-bit shift registers
Sound: None
Hardware: Motorola 6820 PIA for keyboard and display interface
Memory map:
$0000-$1FFF: RAM address space
$0000-$00FF: 6502 zero page
$0024-$002B: Zero page locations used by the Monitor
$0100-$01FF: 6502 processor stack
$0200-$027F: Keyboard input buffer storage used by the Monitor
$0280-$0FFF: RAM space available for a program in a 4 KB system
$1000-$1FFF: Extra RAM space available for a program in an 8 KB system
not using cassette BASIC
$2000-$BFFF: Unused address space, available for RAM in systems larger
than 8 KB.
$C000-$CFFF: Address space for optional cassette interface
$C000-$C0FF: Cassette interface I/O range
$C100-$C1FF: Cassette interface ROM
$D000-$DFFF: I/O address space
$D010-$D013: Motorola 6820 PIA registers.
$D010: Keyboard input port
$D011: Control register for keyboard input port, with
key-available flag.
$D012: Display output port (bit 7 is a status input)
$D013: Control register for display output port
(PIA registers also mirrored at $D014-$D017, $D018-$D01B, $D01C-$D01F,
$D030-$D03F, $D050-$D05F, ... , $DFD0-$DFDF, $DFF0-$DFFF.)
$E000-$EFFF: Extra RAM space available for a program in an 8 KB system
modified to use cassette BASIC
(The system simulated here always includes this RAM.)
If you wanted to load the BASIC as rom, here are the details:
ROM_LOAD("basic.bin", 0xE000, 0x1000, CRC(d5e86efc) SHA1(04269c1c66e7d5b4aa5035462c6e612bf2ae9b91) )
A machine-language program will typically be like this:
C100R (enter the interface)
0300.0FFFR (enter the load and end addresses, then load the tape)
You start the tape.
When the prompt returns you stop the tape.
0300R (run your program)
$F000-$FFFF: ROM address space
$FF00-$FFFF: Apple Monitor ROM
To Load Tape Basic:
C100R
E000.EFFFR
You start the tape.
When the prompt returns you stop the tape.
E000R (It must say 4C - if not, your tape is no good).
The BASIC prompt will appear
>@
How to use cassettes:
The system has no error checking or checksums, and the cassette
has no header.
Therefore, you must know the details, and pass these to the
interface yourself.
BASIC has no cassette handling. You must enter the monitor
with: CALL -151
then when finished, re-enter BASIC with: E2B3R
A BASIC program is split into two areas, one for the scratch pad,
and one for the program proper.
In BASIC you may have to adjust the allowed memory area, such as
LOMEM = 768
Then, go to the monitor: CALL -151
C100R (enter the interface)
00A4.00FFR 0300.0FFFR (load the 2 parts)
You start the tape.
When the prompt returns you stop the tape.
E2B3R (back to BASIC)
You can LIST or RUN now.
Examples:
Saving is almost the same, when you specify the address range, enter
W instead of R. The difficulty is finding out how long your program is.
A machine-language program will typically be like this:
C100R (enter the interface)
0300.0FFFR (enter the load and end addresses, then load the tape)
You start the tape.
When the prompt returns you stop the tape.
0300R (run your program)
To Load Tape Basic:
C100R
E000.EFFFR
You start the tape.
When the prompt returns you stop the tape.
E000R (It must say 4C - if not, your tape is no good).
The BASIC prompt will appear
>@
A BASIC program is split into two areas, one for the scratch pad,
and one for the program proper.
In BASIC you may have to adjust the allowed memory area, such as
LOMEM = 768
Then, go to the monitor: CALL -151
C100R (enter the interface)
00A4.00FFR 0300.0FFFR (load the 2 parts)
You start the tape.
When the prompt returns you stop the tape.
E2B3R (back to BASIC)
You can LIST or RUN now.
Saving is almost the same, when you specify the address range, enter
W instead of R. The difficulty is finding out how long your program is.
Insert a blank tape
C100R
0300.0FFFW
Quickly press Record.
When the prompt returns, press Stop.
Insert a blank tape
C100R
0300.0FFFW
Quickly press Record.
When the prompt returns, press Stop.
**********************************************************************/
#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "machine/6821pia.h"
#include "includes/apple1.h"
#include "imagedev/snapquik.h"
#include "machine/ram.h"
@ -139,59 +82,428 @@ When the prompt returns, press Stop.
#include "softlist.h"
/* port i/o functions */
#define A1_CPU_TAG "maincpu"
#define A1_PIA_TAG "pia6821"
#define A1_BUS_TAG "a1bus"
#define A1_BASICRAM_TAG "basicram"
/* memory w/r functions */
static ADDRESS_MAP_START( apple1_map, AS_PROGRAM, 8, apple1_state )
/* In $D000-$DFFF, PIA is selected by address bit 4 being high,
and PIA registers are addressed with address bits 0-1. All
other address bits are ignored. Thus $D010-$D013 is mirrored
at all $Dxxx addresses with bit 4 high. */
AM_RANGE(0xd010, 0xd013) AM_MIRROR(0x0fec) AM_DEVREADWRITE("pia",pia6821_device, read, write)
/* We always include the remapped RAM for cassette BASIC, both for
simplicity and to allow the running of BASIC programs. */
AM_RANGE(0xe000, 0xefff) AM_RAM
AM_RANGE(0xf000, 0xfeff) AM_NOP
/* Monitor ROM: */
AM_RANGE(0xff00, 0xffff) AM_ROM AM_REGION("maincpu", 0)
ADDRESS_MAP_END
/* graphics output */
const gfx_layout apple1_charlayout =
class apple1_state : public driver_device
{
7, 8, /* character cell is 7 pixels wide by 8 pixels high */
64, /* 64 characters in 2513 character generator ROM */
1, /* 1 bitplane */
{ 0 },
/* 5 visible pixels per row, starting at bit 3, with MSB being 0: */
{ 3, 4, 5, 6, 7 },
/* pixel rows stored from top to bottom: */
{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
8 * 8 /* 8 8-bit pixel rows per character */
public:
apple1_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag),
m_maincpu(*this, A1_CPU_TAG),
m_pia(*this, A1_PIA_TAG),
m_ram(*this, RAM_TAG),
m_basicram(*this, A1_BASICRAM_TAG),
m_kb0(*this, "KEY0"),
m_kb1(*this, "KEY1"),
m_kb2(*this, "KEY2"),
m_kb3(*this, "KEY3"),
m_kbspecial(*this, "KBSPECIAL")
{ }
required_device<cpu_device> m_maincpu;
required_device<pia6821_device> m_pia;
required_device<ram_device> m_ram;
required_shared_ptr<UINT8> m_basicram;
required_ioport m_kb0, m_kb1, m_kb2, m_kb3, m_kbspecial;
virtual void machine_start() override;
virtual void machine_reset() override;
DECLARE_PALETTE_INIT(apple2);
UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
DECLARE_READ8_MEMBER(ram_r);
DECLARE_WRITE8_MEMBER(ram_w);
DECLARE_READ8_MEMBER(pia_keyboard_r);
DECLARE_WRITE8_MEMBER(pia_display_w);
DECLARE_WRITE_LINE_MEMBER(pia_display_gate_w);
DECLARE_SNAPSHOT_LOAD_MEMBER( apple1 );
TIMER_CALLBACK_MEMBER(ready_start_cb);
TIMER_CALLBACK_MEMBER(ready_end_cb);
TIMER_CALLBACK_MEMBER(keyboard_strobe_cb);
private:
UINT8 *m_ram_ptr, *m_char_ptr;
int m_ram_size, m_char_size;
UINT8 m_vram[40*24];
int m_cursx, m_cursy;
bool m_reset_down;
bool m_clear_down;
UINT8 m_transchar;
UINT16 m_lastports[4];
void plot_text_character(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, UINT32 code, const UINT8 *textgfx_data, UINT32 textgfx_datalen);
void poll_keyboard();
emu_timer *m_ready_start_timer, *m_ready_end_timer, *m_kbd_strobe_timer;
};
static GFXDECODE_START( apple1 )
GFXDECODE_ENTRY( "gfx1", 0x0000, apple1_charlayout, 0, 1 )
GFXDECODE_END
static const UINT8 apple1_keymap[] =
{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '=', '[', ']', ';', '\'', // KEY0
',', '.', '/', '\\', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', // KEY1
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '\r', '_', // KEY2
' ', '\x1b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // KEY3
/* keyboard input */
/*
It's very likely that the keyboard assgnments are totally wrong: the code in machine/apple1.c
makes arbitrary assumptions about the mapping of the keys. The schematics that are available
on the web can help revealing the real layout.
The large picture of Woz's Apple I at http://home.earthlink.net/~judgementcall/apple1.jpg
show probably how the real keyboard was meant to be: note how the shifted symbols on the digits
and on some letters are different from the ones produced by current emulation and the presence
of the gray keys.
*/
')', '!', '@', '#', '$', '%', '^', '&', '*', '(', '_', '+', '[', ']', ':', '"', // KEY0 + shift
'<', '>', '?', '\\', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', // KEY1 + shift
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '\r', '_', // KEY2 + shift
' ', '\x1b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // KEY3 + shift
'0', '1', '\x00', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', '8', '9', '\x1f', '=', '\x1b', '\x1d', ';', '\'', // KEY0 + CTRL
',', '.', '/', '\x1c', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0a', '\x0b', '\x0c', // KEY1 + CTRL
'\x0d', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1a', '\r', '_', // KEY2 + CTRL
' ', '\x1b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // KEY3 + CTRL
};
// header is "LOAD:abcdDATA:" where abcd is the starting address
SNAPSHOT_LOAD_MEMBER( apple1_state, apple1 )
{
UINT64 snapsize;
UINT8 *data;
UINT16 start, end;
static const char hd1[6] = "LOAD:";
static const char hd2[6] = "DATA:";
// get the snapshot's size
snapsize = image.length();
if (snapsize < 12)
{
logerror("Snapshot is too short\n");
return IMAGE_INIT_FAIL;
}
if ((snapsize - 12) > 65535)
{
logerror("Snapshot is too long\n");
return IMAGE_INIT_FAIL;
}
data = (UINT8 *)image.ptr();
if (!data)
{
logerror("Internal error loading snapshot\n");
return IMAGE_INIT_FAIL;
}
if ((memcmp(hd1, data, 5)) || (memcmp(hd2, &data[7], 5)))
{
logerror("Snapshot is invalid\n");
return IMAGE_INIT_FAIL;
}
start = (data[5]<<8) | data[6];
end = (snapsize - 12) + start;
// check if this fits in RAM; load below 0xe000 must fit in RAMSIZE,
// load at 0xe000 must fit in 4K
if (((start < 0xe000) && (end > (m_ram_size - 1))) || (end > 0xefff))
{
logerror("Snapshot can't fit in RAM\n");
return IMAGE_INIT_FAIL;
}
if (start < 0xe000)
{
memcpy(m_ram_ptr + start, &data[12], snapsize - 12);
}
else if ((start >= 0xe000) && (start <= 0xefff))
{
memcpy(m_basicram + (start - 0xe000), &data[12], snapsize - 12);
}
else
{
logerror("Snapshot has invalid load address %04x\n", start);
return IMAGE_INIT_FAIL;
}
return IMAGE_INIT_PASS;
}
void apple1_state::poll_keyboard()
{
UINT8 special = m_kbspecial->read();
UINT16 ports[4];
int rawkey = 0;
bool bKeypress = false;
// handle special keys first:
if (special & 0x10) // RESET
{
m_reset_down = true;
m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
m_pia->reset();
}
else if (m_reset_down)
{
m_reset_down = false;
m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
}
if (special & 0x20) // CLEAR SCREEN
{
m_clear_down = true;
memset(m_vram, 0, sizeof(m_vram));
m_cursx = m_cursy = 0;
}
else
{
m_clear_down = false;
}
// lower the keyboard strobe
m_pia->ca1_w(0);
// cache all the rows
ports[0] = m_kb0->read();
ports[1] = m_kb1->read();
ports[2] = m_kb2->read();
ports[3] = m_kb3->read();
for (int port = 0; port < 4; port++)
{
UINT16 ptread = ports[port] ^ m_lastports[port];
for (int bit = 0; bit < 16; bit++)
{
// key changed?
if (ptread & (1 << bit))
{
// key down?
if (ports[port] & (1 << bit))
{
rawkey = (port * 16) + bit;
m_lastports[port] |= (1 << bit);
port = 4; // force outer for loop to quit too
bKeypress = true;
}
else // key up
{
m_lastports[port] &= ~(1 << bit);
}
break;
}
}
}
if (bKeypress)
{
if ((special & 0xc) != 0)
{
m_transchar = apple1_keymap[rawkey + (8*16)];
}
else if ((special & 0x3) != 0)
{
m_transchar = apple1_keymap[rawkey + (4*16)];
}
else
{
m_transchar = apple1_keymap[rawkey];
}
// pulse the strobe line
m_pia->ca1_w(1);
}
}
void apple1_state::plot_text_character(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, UINT32 code,
const UINT8 *textgfx_data, UINT32 textgfx_datalen)
{
int x, y, i;
const UINT8 *chardata;
UINT16 color;
int fg = 1, bg = 0;
int charcode = (code & 0x1f) | (((code ^ 0x40) & 0x40) >> 1);
/* look up the character data */
chardata = &textgfx_data[(charcode * 8)];
for (y = 0; y < 8; y++)
{
for (x = 0; x < 7; x++)
{
color = (chardata[y] & (1 << (6-x))) ? fg : bg;
for (i = 0; i < xscale; i++)
{
bitmap.pix16(ypos + y, xpos + (x * xscale) + i) = color;
}
}
}
}
UINT32 apple1_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
int vramad;
int cursor_blink = 0;
UINT8 curs_save = 0;
poll_keyboard();
// the cursor 555 timer counts 0.52 of a second; the cursor is ON for
// 2 of those counts and OFF for the last one.
if (((int)(machine().time().as_double() / (0.52 / 3.0)) % 3) < 2)
{
curs_save = m_vram[(m_cursy * 40) + m_cursx];
m_vram[(m_cursy * 40) + m_cursx] = 0x40;
cursor_blink = 1;
}
for (int row = 0; row < cliprect.max_y; row += 8)
{
for (int col = 0; col < 40; col++)
{
vramad = ((row/8) * 40) + col;
plot_text_character(bitmap, col * 14, row, 2, m_vram[vramad],
m_char_ptr, m_char_size);
}
}
if (cursor_blink)
{
m_vram[(m_cursy * 40) + m_cursx] = curs_save;
}
return 0;
}
void apple1_state::machine_start()
{
m_ram_ptr = m_ram->pointer();
m_ram_size = m_ram->size();
m_char_ptr = memregion("gfx1")->base();
m_char_size = memregion("gfx1")->bytes();
m_reset_down = m_clear_down = false;
m_ready_start_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(apple1_state::ready_start_cb), this));
m_ready_end_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(apple1_state::ready_end_cb), this));
m_kbd_strobe_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(apple1_state::keyboard_strobe_cb), this));
}
void apple1_state::machine_reset()
{
memset(m_vram, 0, sizeof(m_vram));
m_transchar = 0;
m_cursx = m_cursy = 0;
m_lastports[0] = m_lastports[1] = m_lastports[2] = m_lastports[3] = 0;
}
READ8_MEMBER(apple1_state::ram_r)
{
if (offset < m_ram_size)
{
return m_ram_ptr[offset];
}
return 0xff;
}
WRITE8_MEMBER(apple1_state::ram_w)
{
if (offset < m_ram_size)
{
m_ram_ptr[offset] = data;
}
}
static ADDRESS_MAP_START( apple1_map, AS_PROGRAM, 8, apple1_state )
AM_RANGE(0x0000, 0xbfff) AM_READWRITE(ram_r, ram_w)
AM_RANGE(0xd010, 0xd013) AM_MIRROR(0x0fec) AM_DEVREADWRITE(A1_PIA_TAG, pia6821_device, read, write)
AM_RANGE(0xe000, 0xefff) AM_RAM AM_SHARE(A1_BASICRAM_TAG)
AM_RANGE(0xff00, 0xffff) AM_ROM AM_REGION(A1_CPU_TAG, 0)
ADDRESS_MAP_END
READ8_MEMBER(apple1_state::pia_keyboard_r)
{
return m_transchar | 0x80; // bit 7 is wired high, similar-ish to the Apple II
}
WRITE8_MEMBER(apple1_state::pia_display_w)
{
data &= 0x7f; // D7 is ignored by the video h/w
// ignore characters if CLEAR is down
if (m_clear_down)
{
return;
}
// video h/w rejects control characters except CR
if ((data < 32) && (data != '\r'))
{
return;
}
if (data == '\r')
{
m_cursx = 0;
m_cursy++;
}
else
{
m_vram[(m_cursy * 40) + m_cursx] = data;
m_cursx++;
if (m_cursx > 39)
{
m_cursx = 0;
m_cursy++;
}
}
// scroll the screen if we're at the bottom
if (m_cursy > 23)
{
for (int sy = 0; sy < 23; sy++)
{
memcpy(&m_vram[sy * 40], &m_vram[(sy + 1) * 40], 40);
}
memset(&m_vram[23*40], 0, 40);
m_cursy = 23;
}
}
// CB2 here is connected two places: Port B bit 7 for CPU readback,
// and to the display hardware
WRITE_LINE_MEMBER(apple1_state::pia_display_gate_w)
{
m_pia->portb_w((state << 7) ^ 0x80);
// falling edge means start the display timer
if (state == CLEAR_LINE)
{
m_ready_start_timer->adjust(machine().first_screen()->time_until_pos(m_cursy, m_cursx));
}
}
TIMER_CALLBACK_MEMBER(apple1_state::ready_start_cb)
{
// we're ready, pulse CB1 for 3500 nanoseconds
m_pia->cb1_w(0);
m_ready_end_timer->adjust(attotime::from_nsec(3500));
}
TIMER_CALLBACK_MEMBER(apple1_state::ready_end_cb)
{
m_pia->cb1_w(1);
}
TIMER_CALLBACK_MEMBER(apple1_state::keyboard_strobe_cb)
{
m_pia->ca1_w(0);
}
static INPUT_PORTS_START( apple1 )
PORT_START("KEY0") /* first sixteen keys */
PORT_START("KEY0")
PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR(')')
PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('@')
@ -209,7 +521,7 @@ static INPUT_PORTS_START( apple1 )
PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR(':')
PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR('\'') PORT_CHAR('"')
PORT_START("KEY1") /* second sixteen keys */
PORT_START("KEY1")
PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
@ -227,7 +539,7 @@ static INPUT_PORTS_START( apple1 )
PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('K')
PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('L')
PORT_START("KEY2") /* third sixteen keys */
PORT_START("KEY2")
PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('M')
PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('N')
PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('O')
@ -243,21 +555,19 @@ static INPUT_PORTS_START( apple1 )
PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y')
PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z')
PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backarrow") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR('_')
PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR('_')
PORT_START("KEY3") /* fourth sixteen keys */
PORT_START("KEY3")
PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Escape") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
PORT_START("KEY4") /* shift keys */
PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift (Left)") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift (Right)") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control (Left)") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Control (Right)") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
PORT_START("KEY5") /* RESET and CLEAR SCREEN pushbutton switches */
PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F1))
PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
PORT_START("KBSPECIAL")
PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Left Control") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Right Control") PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Reset") PORT_CODE(KEYCODE_F12) PORT_CHAR(UCHAR_MAMEKEY(F1))
PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_F2) PORT_CHAR(UCHAR_MAMEKEY(F2))
INPUT_PORTS_END
static SLOT_INTERFACE_START(apple1_cards)
@ -265,69 +575,44 @@ static SLOT_INTERFACE_START(apple1_cards)
SLOT_INTERFACE("cffa", A1BUS_CFFA)
SLOT_INTERFACE_END
/* machine definition */
static MACHINE_CONFIG_START( apple1, apple1_state )
/* basic machine hardware */
/* Actual CPU speed is 1.023 MHz, but RAM refresh effectively
slows it to 960 kHz. */
MCFG_CPU_ADD("maincpu", M6502, 960000) /* 1.023 MHz */
MCFG_CPU_ADD(A1_CPU_TAG, M6502, 960000) // effective CPU speed
MCFG_CPU_PROGRAM_MAP(apple1_map)
MCFG_QUANTUM_TIME(attotime::from_hz(60))
// video timings are identical to the Apple II, unsurprisingly
MCFG_SCREEN_ADD("screen", RASTER)
MCFG_SCREEN_REFRESH_RATE(60)
/* Video is blanked for 70 out of 262 scanlines per refresh cycle.
Each scanline is composed of 65 character times, 40 of which
are visible, and each character time is 7 dot times; a dot time
is 2 cycles of the fundamental 14.31818 MHz oscillator. The
total blanking time is about 4450 microseconds. */
MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC((int) (70 * 65 * 7 * 2 / 14.31818)))
/* It would be nice if we could implement some sort of display
overscan here. */
MCFG_SCREEN_SIZE(40 * 7, 24 * 8)
MCFG_SCREEN_VISIBLE_AREA(0, 40 * 7 - 1, 0, 24 * 8 - 1)
MCFG_SCREEN_UPDATE_DRIVER(apple1_state, screen_update_apple1)
MCFG_SCREEN_RAW_PARAMS(XTAL_14_31818MHz, (65*7)*2, 0, (40*7)*2, 262, 0, 192)
MCFG_SCREEN_UPDATE_DRIVER(apple1_state, screen_update)
MCFG_SCREEN_PALETTE("palette")
MCFG_GFXDECODE_ADD("gfxdecode", "palette", apple1)
MCFG_PALETTE_ADD_BLACK_AND_WHITE("palette")
MCFG_DEVICE_ADD( "pia", PIA6821, 0)
MCFG_PIA_READPA_HANDLER(READ8(apple1_state,apple1_pia0_kbdin))
MCFG_PIA_WRITEPB_HANDLER(WRITE8(apple1_state,apple1_pia0_dspout))
MCFG_PIA_CB2_HANDLER(WRITELINE(apple1_state,apple1_pia0_dsp_write_signal))
MCFG_DEVICE_ADD( A1_PIA_TAG, PIA6821, 0)
MCFG_PIA_READPA_HANDLER(READ8(apple1_state, pia_keyboard_r))
MCFG_PIA_WRITEPB_HANDLER(WRITE8(apple1_state, pia_display_w))
MCFG_PIA_CB2_HANDLER(WRITELINE(apple1_state, pia_display_gate_w))
MCFG_DEVICE_ADD("a1bus", A1BUS, 0)
MCFG_DEVICE_ADD(A1_BUS_TAG, A1BUS, 0)
MCFG_A1BUS_CPU("maincpu")
MCFG_A1BUS_SLOT_ADD("a1bus", "exp", apple1_cards, "cassette")
MCFG_A1BUS_SLOT_ADD(A1_BUS_TAG, "exp", apple1_cards, "cassette")
/* snapshot */
MCFG_SNAPSHOT_ADD("snapshot", apple1_state, apple1, "snp", 0)
MCFG_SOFTWARE_LIST_ADD("cass_list","apple1")
MCFG_SOFTWARE_LIST_ADD("cass_list", "apple1")
/* Note that because we always include 4K of RAM at $E000-$EFFF,
the RAM amounts listed here will be 4K below the actual RAM
total. */
/* internal ram */
MCFG_RAM_ADD(RAM_TAG)
MCFG_RAM_DEFAULT_SIZE("48K")
MCFG_RAM_EXTRA_OPTIONS("4K,8K,12K,16K,20K,24K,28K,32K,36K,40K,44K")
MACHINE_CONFIG_END
ROM_START(apple1)
ROM_REGION(0x100, "maincpu",0)
/* 256-byte main monitor ROM, in two 82s129 or mmi6301 256x4 proms at A1 and A2 called APPLE-A1(bits D3-D0) and APPLE-A2(bits D7-D4) */
ROM_LOAD_NIB_HIGH( "apple-a2.a2", 0x0000, 0x0100, CRC(254bfb95) SHA1(b6468b72295b7d8ac288d104d252f24de1f1d611) )
ROM_LOAD_NIB_LOW( "apple-a1.a1", 0x0000, 0x0100, CRC(434f8ce6) SHA1(9deee2d39903209b20c3fc6b58e16372f8efece1) )
/* 512-byte Signetics 2513 character generator ROM at location D2-D3 */
ROM_REGION(0x100, A1_CPU_TAG, 0)
ROM_LOAD_NIB_HIGH("apple-a2.a2", 0x0000, 0x0100, CRC(254bfb95) SHA1(b6468b72295b7d8ac288d104d252f24de1f1d611) )
ROM_LOAD_NIB_LOW("apple-a1.a1", 0x0000, 0x0100, CRC(434f8ce6) SHA1(9deee2d39903209b20c3fc6b58e16372f8efece1) )
ROM_REGION(0x0200, "gfx1",0)
ROM_LOAD("s2513.d2", 0x0000, 0x0200, CRC(a7e567fc) SHA1(b18aae0a2d4f92f5a7e22640719bbc4652f3f4ee)) // apple1.vid
ROM_END
/* YEAR NAME PARENT COMPAT MACHINE INPUT STATE INIT COMPANY FULLNAME */
COMP( 1976, apple1, 0, 0, apple1, apple1, driver_device, 0, "Apple Computer", "Apple I", MACHINE_NO_SOUND_HW )
/* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME */
COMP( 1976, apple1, 0, 0, apple1, apple1, apple1_state, apple1, "Apple Computer", "Apple I" , MACHINE_NO_SOUND )

View File

@ -1,92 +0,0 @@
// license:???
// copyright-holders:Paul Daniels, Colin Howell, R. Belmont
/*****************************************************************************
*
* includes/apple1.h
*
****************************************************************************/
#ifndef APPLE1_H_
#define APPLE1_H_
#include "imagedev/snapquik.h"
#include "machine/ram.h"
typedef short termchar_t;
struct terminal_t
{
tilemap_t *tm;
int gfx;
int blank_char;
int char_bits;
int num_cols;
int num_rows;
int (*getcursorcode)(int original_code);
int cur_offset;
int cur_hidden;
termchar_t mem[1];
};
class apple1_state : public driver_device
{
public:
apple1_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag),
m_maincpu(*this, "maincpu"),
m_ram(*this, RAM_TAG),
m_gfxdecode(*this, "gfxdecode"),
m_screen(*this, "screen") { }
int m_vh_clrscrn_pressed;
int m_kbd_data;
UINT32 m_kbd_last_scan[4];
int m_reset_flag;
terminal_t *m_current_terminal;
terminal_t *m_terminal;
int m_blink_on;
DECLARE_DRIVER_INIT(apple1);
TILE_GET_INFO_MEMBER(terminal_gettileinfo);
virtual void machine_reset() override;
virtual void video_start() override;
UINT32 screen_update_apple1(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
TIMER_CALLBACK_MEMBER(apple1_kbd_poll);
TIMER_CALLBACK_MEMBER(apple1_kbd_strobe_end);
TIMER_CALLBACK_MEMBER(apple1_dsp_ready_start);
TIMER_CALLBACK_MEMBER(apple1_dsp_ready_end);
DECLARE_READ8_MEMBER(apple1_pia0_kbdin);
DECLARE_WRITE8_MEMBER(apple1_pia0_dspout);
DECLARE_WRITE_LINE_MEMBER(apple1_pia0_dsp_write_signal);
required_device<cpu_device> m_maincpu;
void terminal_draw(screen_device &screen, bitmap_ind16 &dest, const rectangle &cliprect, terminal_t *terminal);
void verify_coords(terminal_t *terminal, int x, int y);
void terminal_putchar(terminal_t *terminal, int x, int y, int ch);
int terminal_getchar(terminal_t *terminal, int x, int y);
void terminal_putblank(terminal_t *terminal, int x, int y);
void terminal_dirtycursor(terminal_t *terminal);
void terminal_setcursor(terminal_t *terminal, int x, int y);
void terminal_hidecursor(terminal_t *terminal);
void terminal_showcursor(terminal_t *terminal);
void terminal_getcursor(terminal_t *terminal, int *x, int *y);
void terminal_fill(terminal_t *terminal, int val);
void terminal_clear(terminal_t *terminal);
void apple1_vh_dsp_w (int data);
void apple1_vh_dsp_clr ();
void apple1_vh_cursor_blink ();
int apple1_verify_header (UINT8 *data);
terminal_t *terminal_create(int gfx, int blank_char, int char_bits,int (*getcursorcode)(int original_code),int num_cols, int num_rows);
attotime apple1_vh_dsp_time_to_ready();
DECLARE_SNAPSHOT_LOAD_MEMBER( apple1 );
required_device<ram_device> m_ram;
required_device<gfxdecode_device> m_gfxdecode;
required_device<screen_device> m_screen;
};
/*----------- defined in drivers/apple1.c -----------*/
extern const gfx_layout apple1_charlayout;
#endif /* APPLE1_H_ */

View File

@ -1,404 +0,0 @@
// license:???
// copyright-holders:Paul Daniels, Colin Howell, R. Belmont
/***************************************************************************
machine.c
Functions to emulate general aspects of the machine (RAM, ROM, interrupts,
I/O ports)
The Apple I used a Motorola 6820 PIA for its keyboard and display
I/O. The keyboard was mapped to PIA port A, and the display to port
B.
Port A, the keyboard, was an input port connected to a standard
ASCII-encoded keyboard. The high bit of the port was tied to +5V.
The keyboard strobe signal was connected to the PIA's CA1 control
input so that the keyboard could signal each keypress to the PIA.
The processor could check for a keypress by testing the IRQA1 flag
in the Port A Control Register and then reading the character value
from Port A.
The keyboard connector also had two special lines, RESET and CLEAR
SCREEN, which were meant to be connected to pushbutton switches on
the keyboard. RESET was tied to the reset inputs for the CPU and
PIA; it allowed the user to stop a program and return control to the
Monitor. CLEAR SCREEN was directly tied to the video hardware and
would clear the display.
Port B, the display, was an output port which accepted 7-bit ASCII
characters from the PIA and wrote them on the display. The details
of this are described in video/apple1.c. Control line CB2 served
as an output signal to inform the display of a new character. (CB2
was also connected to line 7 of port B, which was configured as an
input, so that the CPU could more easily check the status of the
write.) The CB1 control input signaled the PIA when the display had
finished writing the character and could accept a new one.
MAME models the 6821 instead of the earlier 6820 used in the Apple
I, but there is no difference in functionality between the two
chips; the 6821 simply has a better ability to drive electrical
loads.
The Apple I had an optional cassette interface which plugged into
the expansion connector. This is described below in the "Cassette
interface I/O" section.
***************************************************************************/
#include "emu.h"
#include "includes/apple1.h"
#include "machine/6821pia.h"
#include "cpu/m6502/m6502.h"
#include "imagedev/cassette.h"
#include "machine/ram.h"
/*****************************************************************************
** Structures
*****************************************************************************/
/* Use the same keyboard mapping as on a modern keyboard. This is not
the same as the keyboard mapping of the actual teletype-style
keyboards used with the Apple I, but it's less likely to cause
confusion for people who haven't memorized that layout.
The Backspace key is mapped to the '_' (underscore) character
because the Apple I ROM Monitor used "back-arrow" to erase
characters, rather than backspace, and back-arrow is an earlier
form of the underscore. */
#define ESCAPE '\x1b'
static const UINT8 apple1_unshifted_keymap[] =
{
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '-', '=', '[', ']', ';', '\'',
',', '.', '/', '\\', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', '\r', '_',
' ', ESCAPE
};
static const UINT8 apple1_shifted_keymap[] =
{
')', '!', '@', '#', '$', '%', '^', '&',
'*', '(', '_', '+', '[', ']', ':', '"',
'<', '>', '?', '\\', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', '\r', '_',
' ', ESCAPE
};
/* Control key mappings, like the other mappings, conform to a modern
keyboard where possible. Note that the Apple I ROM Monitor ignores
most control characters. */
static const UINT8 apple1_control_keymap[] =
{
'0', '1', '\x00', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
'8', '9', '\x1f', '=', '\x1b', '\x1d', ';', '\'',
',', '.', '/', '\x1c', '\x01', '\x02', '\x03', '\x04',
'\x05', '\x06', '\x07', '\x08', '\x09', '\x0a', '\x0b', '\x0c',
'\x0d', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14',
'\x15', '\x16', '\x17', '\x18', '\x19', '\x1a', '\r', '_',
'\x00', ESCAPE
};
/*****************************************************************************
** DRIVER_INIT: driver-specific setup, executed once at MESS startup.
*****************************************************************************/
DRIVER_INIT_MEMBER(apple1_state,apple1)
{
address_space& space = m_maincpu->space(AS_PROGRAM);
/* Set up the handlers for MESS's dynamically-sized RAM. */
space.install_readwrite_bank(0x0000, m_ram->size() - 1, "bank1");
membank("bank1")->set_base(m_ram->pointer());
/* Poll the keyboard input ports periodically. These include both
ordinary keys and the RESET and CLEAR SCREEN pushbutton
switches. We can't handle these switches in a VBLANK_INT or
PERIODIC_INT because both switches need to be monitored even
while the CPU is suspended during RESET; VBLANK_INT and
PERIODIC_INT callbacks aren't run while the CPU is in this
state.
A 120-Hz poll rate seems to be fast enough to ensure no
keystrokes are missed. */
machine().scheduler().timer_pulse(attotime::from_hz(120), timer_expired_delegate(FUNC(apple1_state::apple1_kbd_poll),this));
}
void apple1_state::machine_reset()
{
/* Reset the display hardware. */
apple1_vh_dsp_clr();
}
/*****************************************************************************
** apple1_verify_header
*****************************************************************************/
int apple1_state::apple1_verify_header (UINT8 *data)
{
/* Verify the format for the snapshot */
if ((data[0] == 'L') &&
(data[1] == 'O') &&
(data[2] == 'A') &&
(data[3] == 'D') &&
(data[4] == ':') &&
(data[7] == 'D') &&
(data[8] == 'A') &&
(data[9] == 'T') &&
(data[10]== 'A') &&
(data[11]== ':'))
{
return(IMAGE_VERIFY_PASS);
}
else
{
return(IMAGE_VERIFY_FAIL);
}
}
#define SNAP_HEADER_LEN 12
/*****************************************************************************
** snapshot_load_apple1
**
** Format of the binary snapshot image is:
**
** [ LOAD:xxyyDATA:zzzzzz...]
**
** where xxyy is the binary starting address (in big-endian byte
** order) to load the binary data zzzzzz to.
**
** The image can be of arbitrary length, but it must fit in available
** memory.
*****************************************************************************/
SNAPSHOT_LOAD_MEMBER( apple1_state,apple1)
{
UINT64 filesize, datasize;
UINT8 *snapbuf, *snapptr;
UINT16 start_addr, end_addr, addr;
filesize = image.length();
/* Read the snapshot data into a temporary array */
if (filesize < SNAP_HEADER_LEN)
return IMAGE_INIT_FAIL;
snapbuf = (UINT8*)image.ptr();
if (!snapbuf)
return IMAGE_INIT_FAIL;
/* Verify the snapshot header */
if (apple1_verify_header(snapbuf) == IMAGE_VERIFY_FAIL)
{
logerror("apple1 - Snapshot Header is in incorrect format - needs to be LOAD:xxyyDATA:\n");
return IMAGE_INIT_FAIL;
}
datasize = filesize - SNAP_HEADER_LEN;
/* Extract the starting address to load the snapshot to. */
start_addr = (snapbuf[5] << 8) | (snapbuf[6]);
logerror("apple1 - LoadAddress is 0x%04x\n", start_addr);
end_addr = start_addr + datasize - 1;
if ((start_addr < 0xE000 && end_addr > m_ram->size() - 1)
|| end_addr > 0xEFFF)
{
logerror("apple1 - Snapshot won't fit in this memory configuration;\n"
"needs memory from $%04X to $%04X.\n", start_addr, end_addr);
return IMAGE_INIT_FAIL;
}
/* Copy the data into memory space. */
for (addr = start_addr, snapptr = snapbuf + SNAP_HEADER_LEN;
addr <= end_addr;
addr++, snapptr++)
m_maincpu->space(AS_PROGRAM).write_byte(addr, *snapptr);
return IMAGE_INIT_PASS;
}
/*****************************************************************************
** apple1_kbd_poll
**
** Keyboard polling handles both ordinary keys and the special RESET
** and CLEAR SCREEN switches.
**
** For ordinary keys, this implements 2-key rollover to reduce the
** chance of missed keypresses. If we press a key and then press a
** second key while the first hasn't been completely released, as
** might happen during rapid typing, only the second key is
** registered; the first key is ignored.
**
** If multiple newly-pressed keys are found, the one closest to the
** end of the input ports list is counted; the others are ignored.
*****************************************************************************/
TIMER_CALLBACK_MEMBER(apple1_state::apple1_kbd_poll)
{
int port, bit;
int key_pressed;
UINT32 shiftkeys, ctrlkeys;
pia6821_device *pia = machine().device<pia6821_device>("pia");
static const char *const keynames[] = { "KEY0", "KEY1", "KEY2", "KEY3" };
/* This holds the values of all the input ports for ordinary keys
seen during the last scan. */
/* First we check the RESET and CLEAR SCREEN pushbutton switches. */
/* The RESET switch resets the CPU and the 6820 PIA. */
if (ioport("KEY5")->read() & 0x0001)
{
if (!m_reset_flag) {
m_reset_flag = 1;
/* using PULSE_LINE does not allow us to press and hold key */
m_maincpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
pia->reset();
}
}
else if (m_reset_flag) {
/* RESET released--allow the processor to continue. */
m_reset_flag = 0;
m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
}
/* The CLEAR SCREEN switch clears the video hardware. */
if (ioport("KEY5")->read() & 0x0002)
{
if (!m_vh_clrscrn_pressed)
{
/* Ignore further video writes, and clear the screen. */
m_vh_clrscrn_pressed = 1;
apple1_vh_dsp_clr();
}
}
else if (m_vh_clrscrn_pressed)
{
/* CLEAR SCREEN released--pay attention to video writes again. */
m_vh_clrscrn_pressed = 0;
}
/* Now we scan all the input ports for ordinary keys, recording
new keypresses while ignoring keys that were already pressed in
the last scan. */
m_kbd_data = 0;
key_pressed = 0;
/* The keyboard strobe line should always be low when a scan starts. */
pia->ca1_w(0);
shiftkeys = ioport("KEY4")->read() & 0x0003;
ctrlkeys = ioport("KEY4")->read() & 0x000c;
for (port = 0; port < 4; port++)
{
UINT32 portval, newkeys;
portval = ioport(keynames[port])->read();
newkeys = portval & ~(m_kbd_last_scan[port]);
if (newkeys)
{
key_pressed = 1;
for (bit = 0; bit < 16; bit++) {
if (newkeys & 1)
{
m_kbd_data = (ctrlkeys)
? apple1_control_keymap[port*16 + bit]
: (shiftkeys)
? apple1_shifted_keymap[port*16 + bit]
: apple1_unshifted_keymap[port*16 + bit];
}
newkeys >>= 1;
}
}
m_kbd_last_scan[port] = portval;
}
if (key_pressed)
{
/* The keyboard will pulse its strobe line when a key is
pressed. A 10-usec pulse is typical. */
pia->ca1_w(1);
machine().scheduler().timer_set(attotime::from_usec(10), timer_expired_delegate(FUNC(apple1_state::apple1_kbd_strobe_end),this));
}
}
TIMER_CALLBACK_MEMBER(apple1_state::apple1_kbd_strobe_end)
{
pia6821_device *pia = machine().device<pia6821_device>("pia");
/* End of the keyboard strobe pulse. */
pia->ca1_w(0);
}
/*****************************************************************************
** READ/WRITE HANDLERS
*****************************************************************************/
READ8_MEMBER(apple1_state::apple1_pia0_kbdin)
{
/* Bit 7 of the keyboard input is permanently wired high. This is
what the ROM Monitor software expects. */
return m_kbd_data | 0x80;
}
WRITE8_MEMBER(apple1_state::apple1_pia0_dspout)
{
/* Send an ASCII character to the video hardware. */
apple1_vh_dsp_w(data);
}
WRITE_LINE_MEMBER(apple1_state::apple1_pia0_dsp_write_signal)
{
device_t *device = machine().device("pia");
/* PIA output CB2 is inverted to become the DA signal, used to
signal a display write to the video hardware. */
/* DA is directly connected to PIA input PB7, so the processor can
read bit 7 of port B to test whether the display has completed
a write. */
pia6821_device *pia = downcast<pia6821_device *>(device);
pia->portb_w((!state) << 7);
/* Once DA is asserted, the display will wait until it can perform
the write, when the cursor position is about to be refreshed.
Only then will it assert \RDA to signal readiness for another
write. Thus the write delay depends on the cursor position and
where the display is in the refresh cycle. */
if (!state)
machine().scheduler().timer_set(apple1_vh_dsp_time_to_ready(), timer_expired_delegate(FUNC(apple1_state::apple1_dsp_ready_start),this));
}
TIMER_CALLBACK_MEMBER(apple1_state::apple1_dsp_ready_start)
{
pia6821_device *pia = machine().device<pia6821_device>("pia");
/* When the display asserts \RDA to signal it is ready, it
triggers a 74123 one-shot to send a 3.5-usec low pulse to PIA
input CB1. The end of this pulse will tell the PIA that the
display is ready for another write. */
pia->cb1_w(0);
machine().scheduler().timer_set(attotime::from_nsec(3500), timer_expired_delegate(FUNC(apple1_state::apple1_dsp_ready_end),this));
}
TIMER_CALLBACK_MEMBER(apple1_state::apple1_dsp_ready_end)
{
pia6821_device *pia = machine().device<pia6821_device>("pia");
/* The one-shot pulse has ended; return CB1 to high, so we can do
another display write. */
pia->cb1_w(1);
}

View File

@ -1,392 +0,0 @@
// license:???
// copyright-holders:Paul Daniels, Colin Howell, R. Belmont
/***************************************************************************
apple1.c
Functions to emulate the video hardware of the Apple I.
The Apple I video hardware was basically a dumb video terminal; in
fact it was based on Steve Wozniak's own design for a simple video
terminal. It had 40 columns by 24 lines of uppercase-only text.
Text could only be output at 60 characters per second, one character
per video frame. The cursor (a blinking @) could only be advanced
using spaces or carriage returns. Carriage returns were the only
control characters recognized. Previously written text could not be
altered, only scrolled off the top of the screen.
The video memory used seven 1k-bit dynamic shift registers. Six of
these held the 6-bit visible character codes, and one stored the
cursor location as a simple bitmap--the bit for the cursor position
was set to 0, and all the other bits were 1.
These shift registers were continuously recirculated, completing one
cycle per video frame. As a new line of characters was about to be
scanned by the video beam, that character line would be recirculated
into the shift registers and would simultaneously be stored into a
6x40-bit line buffer (also a shift register). At this point, if the
cursor location was in this line, a new character could be written
into that location in the shift registers and the cursor could be
advanced. (Carriage returns were not written into the shift
registers; they only advanced the cursor.)
The characters in the line buffer were recirculated 7 times to
display the 8 scan lines of the characters, before being replaced by
a new line of characters from the main shift registers.
Cursor blinking was performed by a Signetics 555 timer IC whose
output was gated into the character code signals as they passed into
the line buffer.
Character images were provided by a Signetics 2513 character
generator ROM, a chip also used in computer terminals such as the
ADM-3A. This ROM had 9 address lines and 5 data lines; it contained
64 character images, each 5 pixels wide by 8 pixels high, with one
line of pixels being blank for vertical separation. The video
circuitry added the 2 pixels of horizontal separation for each
character.
A special CLEAR SCREEN switch on the keyboard, directly connected to
the video hardware, could be used to clear the video memory and
return the cursor to the home position. This was completely
independent of the processor.
A schematic of the Apple I video hardware can be found in the
Apple-1 Operation Manual; look for the schematic titled "Terminal
Section". Most of the functionality modeled here was determined by
reading this schematic. Many of the chips used were standard 74xx
TTL chips, but the shift registers used for the video memory and
line buffer were Signetics 25xx PMOS ICs. These were already
becoming obsolete when the Apple I was built, and detailed
information on them is very hard to find today.
***************************************************************************/
#include "emu.h"
#include "includes/apple1.h"
/***************************************************************************
Terminal code
***************************************************************************/
TILE_GET_INFO_MEMBER(apple1_state::terminal_gettileinfo)
{
int ch, gfxfont, code, color;
ch = m_current_terminal->mem[tile_index];
code = ch & ((1 << m_current_terminal->char_bits) - 1);
color = ch >> m_current_terminal->char_bits;
gfxfont = m_current_terminal->gfx;
if ((tile_index == m_current_terminal->cur_offset) && !m_current_terminal->cur_hidden && m_current_terminal->getcursorcode)
code = m_current_terminal->getcursorcode(code);
SET_TILE_INFO_MEMBER(gfxfont, /* gfx */
code, /* character */
color, /* color */
0); /* flags */
}
void apple1_state::terminal_draw(screen_device &screen, bitmap_ind16 &dest, const rectangle &cliprect, terminal_t *terminal)
{
m_current_terminal = terminal;
terminal->tm->draw(screen, dest, cliprect, 0, 0);
m_current_terminal = nullptr;
}
void apple1_state::verify_coords(terminal_t *terminal, int x, int y)
{
assert(x >= 0);
assert(y >= 0);
assert(x < terminal->num_cols);
assert(y < terminal->num_rows);
}
void apple1_state::terminal_putchar(terminal_t *terminal, int x, int y, int ch)
{
int offs;
verify_coords(terminal, x, y);
offs = y * terminal->num_cols + x;
if (terminal->mem[offs] != ch)
{
terminal->mem[offs] = ch;
terminal->tm->mark_tile_dirty(offs);
}
}
int apple1_state::terminal_getchar(terminal_t *terminal, int x, int y)
{
int offs;
verify_coords(terminal, x, y);
offs = y * terminal->num_cols + x;
return terminal->mem[offs];
}
void apple1_state::terminal_putblank(terminal_t *terminal, int x, int y)
{
terminal_putchar(terminal, x, y, terminal->blank_char);
}
void apple1_state::terminal_dirtycursor(terminal_t *terminal)
{
if (terminal->cur_offset >= 0)
terminal->tm->mark_tile_dirty(terminal->cur_offset);
}
void apple1_state::terminal_setcursor(terminal_t *terminal, int x, int y)
{
terminal_dirtycursor(terminal);
terminal->cur_offset = y * terminal->num_cols + x;
terminal_dirtycursor(terminal);
}
void apple1_state::terminal_hidecursor(terminal_t *terminal)
{
terminal->cur_hidden = 1;
terminal_dirtycursor(terminal);
}
void apple1_state::terminal_showcursor(terminal_t *terminal)
{
terminal->cur_hidden = 0;
terminal_dirtycursor(terminal);
}
void apple1_state::terminal_getcursor(terminal_t *terminal, int *x, int *y)
{
*x = terminal->cur_offset % terminal->num_cols;
*y = terminal->cur_offset / terminal->num_cols;
}
void apple1_state::terminal_fill(terminal_t *terminal, int val)
{
int i;
for (i = 0; i < terminal->num_cols * terminal->num_rows; i++)
terminal->mem[i] = val;
terminal->tm->mark_all_dirty();
}
void apple1_state::terminal_clear(terminal_t *terminal)
{
terminal_fill(terminal, terminal->blank_char);
}
terminal_t *apple1_state::terminal_create(
int gfx, int blank_char, int char_bits,
int (*getcursorcode)(int original_code),
int num_cols, int num_rows)
{
terminal_t *term;
int char_width, char_height;
char_width = m_gfxdecode->gfx(gfx)->width();
char_height = m_gfxdecode->gfx(gfx)->height();
term = (terminal_t *) auto_alloc_array(machine(), char, sizeof(terminal_t) - sizeof(term->mem)
+ (num_cols * num_rows * sizeof(termchar_t)));
term->tm = &machine().tilemap().create(m_gfxdecode, tilemap_get_info_delegate(FUNC(apple1_state::terminal_gettileinfo),this), TILEMAP_SCAN_ROWS,
char_width, char_height, num_cols, num_rows);
term->gfx = gfx;
term->blank_char = blank_char;
term->char_bits = char_bits;
term->num_cols = num_cols;
term->num_rows = num_rows;
term->getcursorcode = getcursorcode;
term->cur_offset = -1;
term->cur_hidden = 0;
terminal_clear(term);
return term;
}
/**************************************************************************/
/* The cursor blinking is generated by a free-running timer with a
0.52-second period. It is on for 2/3 of this period and off for
1/3. */
#define CURSOR_OFF_LENGTH (0.52/3)
/**************************************************************************/
static int apple1_getcursorcode(int original_code)
{
/* Cursor uses symbol 0 (an @ sign) in the character generator ROM. */
return 0;
}
/**************************************************************************/
void apple1_state::video_start()
{
m_blink_on = 1; /* cursor is visible initially */
m_terminal = terminal_create(
0, /* graphics font 0 (the only one we have) */
32, /* Blank character is symbol 32 in the ROM */
8, /* use 8 bits for the character code */
apple1_getcursorcode,
40, 24); /* 40 columns, 24 rows */
terminal_setcursor(m_terminal, 0, 0);
}
/* This function handles all writes to the video display. */
void apple1_state::apple1_vh_dsp_w (int data)
{
int x, y;
int cursor_x, cursor_y;
/* While CLEAR SCREEN is being held down, the hardware is forced
to clear the video memory, so video writes have no effect. */
if (m_vh_clrscrn_pressed)
return;
/* The video display port only accepts the 7 lowest bits of the char. */
data &= 0x7f;
terminal_getcursor(m_terminal, &cursor_x, &cursor_y);
if (data == '\r') {
/* Carriage-return moves the cursor to the start of the next
line. */
cursor_x = 0;
cursor_y++;
}
else if (data < ' ') {
/* Except for carriage-return, the video hardware completely
ignores all control characters. */
return;
}
else {
/* For visible characters, only 6 bits of the ASCII code are
used, because the 2513 character generator ROM only
contains 64 symbols. The low 5 bits of the ASCII code are
used directly. Bit 6 is ignored, since it is the same for
all the available characters in the ROM. Bit 7 is inverted
before being used as the high bit of the 6-bit ROM symbol
index, because the block of 32 ASCII symbols containing the
uppercase letters comes first in the ROM. */
int romindx = (data & 0x1f) | (((data ^ 0x40) & 0x40) >> 1);
terminal_putchar(m_terminal, cursor_x, cursor_y, romindx);
if (cursor_x < 39)
{
cursor_x++;
}
else
{
cursor_x = 0;
cursor_y++;
}
}
/* If the cursor went past the bottom line, scroll the text up one line. */
if (cursor_y == 24)
{
for (y = 1; y < 24; y++)
for (x = 0; x < 40; x++)
terminal_putchar(m_terminal, x, y-1,
terminal_getchar(m_terminal, x, y));
for (x = 0; x < 40; x++)
terminal_putblank(m_terminal, x, 23);
cursor_y--;
}
terminal_setcursor(m_terminal, cursor_x, cursor_y);
}
/* This function handles clearing the video display on cold-boot or in
response to a press of the CLEAR SCREEN switch. */
void apple1_state::apple1_vh_dsp_clr ()
{
terminal_setcursor(m_terminal, 0, 0);
terminal_clear(m_terminal);
}
/* Calculate how long it will take for the display to assert the RDA
signal in response to a video display write. This signal indicates
the display has completed the write and is ready to accept another
write. */
attotime apple1_state::apple1_vh_dsp_time_to_ready ()
{
int cursor_x, cursor_y;
int cursor_scanline;
double scanline_period = m_screen->scan_period().as_double();
double cursor_hfrac;
/* The video hardware refreshes the screen by reading the
character codes from its circulating shift-register memory.
Because of the way this memory works, a new character can only
be written into the cursor location at the moment this location
is about to be read. This happens during the first scanline of
the cursor's character line, when the beam reaches the cursor's
horizontal position. */
terminal_getcursor(m_terminal, &cursor_x, &cursor_y);
cursor_scanline = cursor_y * apple1_charlayout.height;
/* Each scanline is composed of 455 pixel times. The first 175 of
these are the horizontal blanking period; the remaining 280 are
for the visible part of the scanline. */
cursor_hfrac = (175 + cursor_x * apple1_charlayout.width) / 455;
if (m_screen->vpos() == cursor_scanline) {
/* video_screen_get_hpos() doesn't account for the horizontal
blanking interval; it acts as if the scanline period is
entirely composed of visible pixel times. However, we can
still use it to find what fraction of the current scanline
period has elapsed. */
double current_hfrac = m_screen->hpos() /
m_screen->width();
if (current_hfrac < cursor_hfrac)
return attotime::from_double(scanline_period * (cursor_hfrac - current_hfrac));
}
return attotime::from_double(
m_screen->time_until_pos(cursor_scanline, 0).as_double() +
scanline_period * cursor_hfrac);
}
/* Blink the cursor on or off, as appropriate. */
void apple1_state::apple1_vh_cursor_blink ()
{
int new_blink_on;
/* The cursor is on for 2/3 of its blink period and off for 1/3.
This is most easily handled by dividing the total elapsed time
by the length of the off-portion of the cycle, giving us the
number of one-third-cycles elapsed, then checking the result
modulo 3. */
if (((int) (machine().time().as_double() / CURSOR_OFF_LENGTH)) % 3 < 2)
new_blink_on = 1;
else
new_blink_on = 0;
if (new_blink_on != m_blink_on) { /* have we changed state? */
if (new_blink_on)
terminal_showcursor(m_terminal);
else
terminal_hidecursor(m_terminal);
m_blink_on = new_blink_on;
}
}
UINT32 apple1_state::screen_update_apple1(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
apple1_vh_cursor_blink();
terminal_draw(screen, bitmap, cliprect, m_terminal);
return 0;
}