// license:BSD-3-Clause
// copyright-holders:Dirk Best, Robbbert
/******************************************************************************
Acorn System 1 (Microcomputer Kit)
Skeleton driver
Driver by Dirk Best
2011-06-26 Keyboard & display fixed. [Robbbert]
http://speleotrove.com/acorn/
- (modify) Memory display and modification l (load) Reads a block of bytes from tape
X (go) Run program starting at an address r (return) Resume after a breakpoint
p (point) Inserts or removes breakpoint (up) Increment displayed address
s (store) Writes a block of bytes to tape (down) Decrement displayed address
Pasting:
0-F : as is
(inc) : ^
(dec) : V
M (memory) : -
G (Go) : X
Test Paste:
-0100^11^22^33^44^55^66^77^88^99^-0100^
Now press up-arrow to confirm the data has been entered.
Example usage: Turn on. Press -. Mode letter will show 'A'. Type in an address
(example FE00). Press - (or any command key). Contents will show
on the right. Use Up & Down keys to cycle through addresses.
To save a tape, press S then enter start address, press S, enter end address+1,
start recording and press S. The save only takes a few seconds.
To load a tape, the display must just have dots, (reset if necessary), start
playing tape and immediately press L. The last digit will flicker
as the bytes load. At the end, the dots will show again. There's
no error checking, so if it doesn't work, reset and try again.
Note that left-most digit is not wired up, and therefore will always be blank.
******************************************************************************/
#include "emu.h"
#include "cpu/m6502/m6502.h"
#include "machine/ins8154.h"
#include "machine/74145.h"
#include "machine/timer.h"
#include "imagedev/cassette.h"
#include "sound/wave.h"
#include "speaker.h"
#include "acrnsys1.lh"
class acrnsys1_state : public driver_device
{
public:
acrnsys1_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_maincpu(*this, "maincpu")
, m_ttl74145(*this, "ic8_7445")
, m_cass(*this, "cassette")
, m_display(*this, "digit%u", 0U)
, m_digit(0)
{ }
void acrnsys1(machine_config &config);
private:
virtual void machine_start() override;
DECLARE_READ8_MEMBER(ins8154_b1_port_a_r);
DECLARE_WRITE8_MEMBER(ins8154_b1_port_a_w);
DECLARE_WRITE8_MEMBER(acrnsys1_led_segment_w);
TIMER_DEVICE_CALLBACK_MEMBER(acrnsys1_c);
TIMER_DEVICE_CALLBACK_MEMBER(acrnsys1_p);
void acrnsys1_map(address_map &map);
required_device<cpu_device> m_maincpu;
required_device<ttl74145_device> m_ttl74145;
required_device<cassette_image_device> m_cass;
output_finder<9> m_display;
uint8_t m_digit;
uint8_t m_cass_data[4];
bool m_cass_state;
bool m_cassold;
};
void acrnsys1_state::machine_start()
{
m_display.resolve();
save_item(NAME(m_digit));
save_item(NAME(m_cass_data));
save_item(NAME(m_cass_state));
save_item(NAME(m_cassold));
}
/***************************************************************************
KEYBOARD HANDLING
***************************************************************************/
// bit 7 is cassin
READ8_MEMBER( acrnsys1_state::ins8154_b1_port_a_r )
{
uint8_t data = 0x7f, i, key_line = m_ttl74145->read();
for (i = 0; i < 8; i++)
{
if (BIT(key_line, i))
{
char kbdrow[6];
sprintf(kbdrow,"X%X",i);
data = (ioport(kbdrow)->read() & 0x38) | m_digit;
break;
}
}
data |= m_cass_data[2];
return data;
}
// bit 6 is cassout
WRITE8_MEMBER( acrnsys1_state::ins8154_b1_port_a_w )
{
m_digit = data & 0x47;
m_ttl74145->write(m_digit & 7);
m_cass_state = BIT(data, 6);
}
TIMER_DEVICE_CALLBACK_MEMBER(acrnsys1_state::acrnsys1_c)
{
m_cass_data[3]++;
if (m_cass_state != m_cassold)
{
m_cass_data[3] = 0;
m_cassold = m_cass_state;
}
if (m_cass_state)
m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz
else
m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz
}
TIMER_DEVICE_CALLBACK_MEMBER(acrnsys1_state::acrnsys1_p)
{
/* cassette - turn 1200/2400Hz to a bit */
m_cass_data[1]++;
uint8_t cass_ws = (m_cass->input() > +0.03) ? 1 : 0;
if (cass_ws != m_cass_data[0])
{
m_cass_data[0] = cass_ws;
m_cass_data[2] = ((m_cass_data[1] < 12) ? 128 : 0);
m_cass_data[1] = 0;
}
}
/***************************************************************************
LED DISPLAY
***************************************************************************/
WRITE8_MEMBER( acrnsys1_state::acrnsys1_led_segment_w )
{
uint16_t const key_line = m_ttl74145->read();
for (unsigned i = 0U; 9U > i; ++i)
if (BIT(key_line, i))
m_display[i] = data;
}
/***************************************************************************
ADDRESS MAPS
***************************************************************************/
void acrnsys1_state::acrnsys1_map(address_map &map)
{
map(0x0000, 0x03ff).ram();
map(0x0e00, 0x0e7f).mirror(0x100).rw("b1", FUNC(ins8154_device::ins8154_r), FUNC(ins8154_device::ins8154_w));
map(0x0e80, 0x0eff).mirror(0x100).ram();
map(0xf800, 0xf9ff).mirror(0x600).rom();
}
/***************************************************************************
INPUT PORTS
***************************************************************************/
static INPUT_PORTS_START( acrnsys1 )
PORT_START("X0")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("X1")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_X) PORT_CHAR('X')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1')
PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("X2")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2')
PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("X3")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S) PORT_CHAR('S')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3')
PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("X4")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4')
PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("X5")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5')
PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("X6")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_CHAR('E')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR('^')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6')
PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("X7")
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR('V')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7')
PORT_BIT(0xc7, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("reset")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RST") PORT_CODE(KEYCODE_F3) PORT_CHAR(UCHAR_MAMEKEY(F3))
PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("switch")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Switch") PORT_CODE(KEYCODE_F1) PORT_CHAR(UCHAR_MAMEKEY(F1))
PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("config")
PORT_CONFNAME( 0x03, 0x00, "Switch connected to")
PORT_CONFSETTING( 0x00, "IRQ" )
PORT_CONFSETTING( 0x01, "NMI" )
PORT_CONFSETTING( 0x02, "RST" )
INPUT_PORTS_END
/***************************************************************************
MACHINE DRIVERS
***************************************************************************/
MACHINE_CONFIG_START(acrnsys1_state::acrnsys1)
/* basic machine hardware */
MCFG_DEVICE_ADD("maincpu", M6502, 1.008_MHz_XTAL) /* 1.008 MHz */
MCFG_DEVICE_PROGRAM_MAP(acrnsys1_map)
config.set_default_layout(layout_acrnsys1);
/* sound hardware */
SPEAKER(config, "mono").front_center();
WAVE(config, "wave", "cassette").add_route(ALL_OUTPUTS, "mono", 0.25);
/* devices */
ins8154_device &b1(INS8154(config, "b1"));
b1.in_a().set(FUNC(acrnsys1_state::ins8154_b1_port_a_r));
b1.out_a().set(FUNC(acrnsys1_state::ins8154_b1_port_a_w));
b1.out_b().set(FUNC(acrnsys1_state::acrnsys1_led_segment_w));
TTL74145(config, m_ttl74145, 0);
MCFG_CASSETTE_ADD( "cassette" )
MCFG_TIMER_DRIVER_ADD_PERIODIC("acrnsys1_c", acrnsys1_state, acrnsys1_c, attotime::from_hz(4800))
MCFG_TIMER_DRIVER_ADD_PERIODIC("acrnsys1_p", acrnsys1_state, acrnsys1_p, attotime::from_hz(40000))
MACHINE_CONFIG_END
/***************************************************************************
ROM DEFINITIONS
***************************************************************************/
ROM_START( acrnsys1 )
ROM_REGION(0x10000, "maincpu", 0)
ROM_LOAD("acrnsys1.bin", 0xf800, 0x0200, CRC(43dcfc16) SHA1(b987354c55beb5e2ced761970c3339b895a8c09d))
ROM_END
/***************************************************************************
GAME DRIVERS
***************************************************************************/
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
COMP( 1979, acrnsys1, 0, 0, acrnsys1, acrnsys1, acrnsys1_state, empty_init, "Acorn", "Acorn System 1", MACHINE_SUPPORTS_SAVE )