// license:GPL-2.0+
// copyright-holders:Kevin Thacker
/******************************************************************************
pcw16.c
system driver
Kevin Thacker [MESS driver]
Thankyou to:
- Cliff Lawson @ Amstrad plc for his documentation (Anne ASIC documentation),
and extensive help.
(web.ukonline.co.uk/cliff.lawson/)
(www.amstrad.com)
- John Elliot for his help and tips
(he's written a CP/M implementation for the PCW16)
(www.seasip.deomon.co.uk)
- and others who offered their help (Richard Fairhurst, Richard Wildey)
Hardware:
- 2mb dram max,
- 2mb flash-file memory max (in 2 1mb chips),
- 16MHz Z80 (core combined in Anne ASIC),
- Anne ASIC (keyboard interface, video (colours), dram/flash/rom paging,
real time clock, "glue" logic for Super I/O)
- Winbond Super I/O chip (PC type hardware - FDC, Serial, LPT, Hard-drive)
- PC/AT keyboard - some keys are coloured to identify special functions, but
these are the same as normal PC keys
- PC Serial Mouse - uses Mouse System Mouse protocol
- PC 1.44MB Floppy drive
Primary Purpose:
- built as a successor to the PCW8526/PCW9512 series
- wordprocessor system (also contains spreadsheet and other office applications)
- 16MHz processor used so proportional fonts and enhanced wordprocessing features
are possible, true WYSIWYG wordprocessing.
- flash-file can store documents.
To Do:
- reduce memory usage so it is more MESSD friendly
- different configurations
- implement configuration register
- extract game-port hardware from pc driver - used in any PCW16 progs?
- extract hard-drive code from PC driver and use in this driver
- implement printer
- .. anything else that requires implementing
Info:
- to use this driver you need a OS rescue disc.
(HINT: This also contains the boot-rom)
- the OS will be installed from the OS rescue disc into the Flash-ROM
Uses "MEMCARD" dir to hold flash-file data.
To use the power button, flick the dip switch off/on
From comp.sys.amstrad.8bit FAQ:
"Amstrad made the following PCW systems :
- 1) PCW8256
- 2) PCW8512
- 3) PCW9512
- 4) PCW9512+
- 5) PcW10
- 6) PcW16
1 had 180K drives, 2 had a 180K A drive and a 720K B drive, 3 had only
720K drives. All subsequent models had 3.5" disks using CP/M format at
720K until 6 when it switched to 1.44MB in MS-DOS format. The + of
model 4 was that it had a "real" parallel interface so could be sold
with an external printer such as the Canon BJ10. The PcW10 wasn't
really anything more than 4 in a more modern looking case.
The PcW16 is a radical digression who's sole "raison d'etre" was to
make a true WYSIWYG product but this meant a change in the screen and
processor (to 16MHz) etc. which meant that it could not be kept
compatible with the previous models (though documents ARE compatible)"
TODO:
- Verfiy uart model.
******************************************************************************/
/* PeT 19.October 2000
added/changed printer support
not working reliable, seams to expect parallelport in epp/ecp mode
epp/ecp modes in parallel port not supported yet
so ui disabled */
#include "emu.h"
#include "includes/pcw16.h"
#include "bus/rs232/rs232.h"
#include "bus/rs232/ser_mouse.h"
#include "screen.h"
#include "softlist.h"
#include "speaker.h"
// interrupt counter
/* controls which bank of 2mb address space is paged into memory */
// output of 4-bit port from Anne ASIC
// code defining which INT fdc is connected to
// interrupt bits
// bit 7: ??
// bit 6: fdc
// bit 5: ??
// bit 3,4; Serial
// bit 2: Vsync state
// bit 1: keyboard int
// bit 0: Display ints
// debugging - write ram as seen by cpu
void pcw16_state::pcw16_refresh_ints()
{
/* any bits set excluding vsync */
if ((m_system_status & (~0x04))!=0)
{
m_maincpu->set_input_line(0, HOLD_LINE);
}
else
{
m_maincpu->set_input_line(0, CLEAR_LINE);
}
}
TIMER_DEVICE_CALLBACK_MEMBER(pcw16_state::pcw16_timer_callback)
{
/* do not increment past 15 */
if (m_interrupt_counter!=15)
{
m_interrupt_counter++;
/* display int */
m_system_status |= (1<<0);
}
if (m_interrupt_counter!=0)
{
pcw16_refresh_ints();
}
}
void pcw16_state::pcw16_map(address_map &map)
{
map(0x0000, 0xffff).rw(this, FUNC(pcw16_state::pcw16_mem_r), FUNC(pcw16_state::pcw16_mem_w));
}
WRITE8_MEMBER(pcw16_state::pcw16_palette_w)
{
m_colour_palette[offset & 0x0f] = data & 31;
}
uint8_t pcw16_state::read_bank_data(uint8_t type, uint16_t offset)
{
if(type & 0x80) // DRAM
{
return m_ram->pointer()[((type & 0x7f)*0x4000) + offset];
}
else // ROM / Flash
{
if(type < 4)
{
return m_region_rom->base()[((type & 0x03)*0x4000)+offset+0x10000];
}
if(type < 0x40) // first flash
{
return m_flash0->read(((type & 0x3f)*0x4000)+offset);
}
else // second flash
{
return m_flash1->read(((type & 0x3f)*0x4000)+offset);
}
}
}
void pcw16_state::write_bank_data(uint8_t type, uint16_t offset, uint8_t data)
{
if(type & 0x80) // DRAM
{
m_ram->pointer()[((type & 0x7f)*0x4000) + offset] = data;
}
else // ROM / Flash
{
if(type < 4)
return; // first four sectors are write protected
if(type < 0x40) // first flash
{
m_flash0->write(((type & 0x3f)*0x4000)+offset,data);
}
else // second flash
{
m_flash1->write(((type & 0x3f)*0x4000)+offset,data);
}
}
}
uint8_t pcw16_state::pcw16_read_mem(uint8_t bank, uint16_t offset)
{
switch(bank)
{
case 0:
return read_bank_data(m_banks[bank],offset);
case 1:
return read_bank_data(m_banks[bank],offset);
case 2:
return read_bank_data(m_banks[bank],offset);
case 3:
return read_bank_data(m_banks[bank],offset);
}
return 0xff;
}
void pcw16_state::pcw16_write_mem(uint8_t bank, uint16_t offset, uint8_t data)
{
switch(bank)
{
case 0:
write_bank_data(m_banks[bank],offset,data);
break;
case 1:
write_bank_data(m_banks[bank],offset,data);
break;
case 2:
write_bank_data(m_banks[bank],offset,data);
break;
case 3:
write_bank_data(m_banks[bank],offset,data);
break;
}
}
READ8_MEMBER(pcw16_state::pcw16_mem_r)
{
if(offset < 0x4000)
return pcw16_read_mem(0,offset);
if(offset >= 0x4000 && offset < 0x8000)
return pcw16_read_mem(1,offset-0x4000);
if(offset >= 0x8000 && offset < 0xc000)
return pcw16_read_mem(2,offset-0x8000);
if(offset >= 0xc000 && offset < 0x10000)
return pcw16_read_mem(3,offset-0xc000);
return 0xff;
}
WRITE8_MEMBER(pcw16_state::pcw16_mem_w)
{
if(offset < 0x4000)
pcw16_write_mem(0,offset,data);
if(offset >= 0x4000 && offset < 0x8000)
pcw16_write_mem(1,offset-0x4000,data);
if(offset >= 0x8000 && offset < 0xc000)
pcw16_write_mem(2,offset-0x8000,data);
if(offset >= 0xc000 && offset < 0x10000)
pcw16_write_mem(3,offset-0xc000,data);
}
READ8_MEMBER(pcw16_state::pcw16_bankhw_r)
{
// logerror("bank r: %d \n", offset);
return m_banks[offset];
}
WRITE8_MEMBER(pcw16_state::pcw16_bankhw_w)
{
//logerror("bank w: %d block: %02x\n", offset, data);
m_banks[offset] = data;
}
WRITE8_MEMBER(pcw16_state::pcw16_video_control_w)
{
//logerror("video control w: %02x\n", data);
m_video_control = data;
}
/* PCW16 KEYBOARD */
//unsigned char pcw16_keyboard_status;
#define PCW16_KEYBOARD_PARITY_MASK (1<<7)
#define PCW16_KEYBOARD_STOP_BIT_MASK (1<<6)
#define PCW16_KEYBOARD_START_BIT_MASK (1<<5)
#define PCW16_KEYBOARD_BUSY_STATUS (1<<4)
#define PCW16_KEYBOARD_FORCE_KEYBOARD_CLOCK (1<<1)
#define PCW16_KEYBOARD_TRANSMIT_MODE (1<<0)
#define PCW16_KEYBOARD_RESET_INTERFACE (1<<2)
#define PCW16_KEYBOARD_DATA (1<<1)
#define PCW16_KEYBOARD_CLOCK (1<<0)
/* parity table. Used to set parity bit in keyboard status register */
void pcw16_state::pcw16_keyboard_init()
{
int i;
int b;
/* if sum of all bits in the byte is even, then the data
has even parity, otherwise it has odd parity */
for (i=0; i<256; i++)
{
int data;
int sum;
sum = 0;
data = i;
for (b=0; b<8; b++)
{
sum+=data & 0x01;
data = data>>1;
}
m_keyboard_parity_table[i] = sum & 0x01;
}
/* clear int */
pcw16_keyboard_int(0);
/* reset state */
m_keyboard_state = 0;
/* reset ready for transmit */
pcw16_keyboard_reset();
}
void pcw16_state::pcw16_keyboard_refresh_outputs()
{
/* generate output bits */
m_keyboard_bits_output = m_keyboard_bits;
/* force clock low? */
if (m_keyboard_state & PCW16_KEYBOARD_FORCE_KEYBOARD_CLOCK)
{
m_keyboard_bits_output &= ~PCW16_KEYBOARD_CLOCK;
}
}
void pcw16_state::pcw16_keyboard_set_clock_state(int state)
{
m_keyboard_bits &= ~PCW16_KEYBOARD_CLOCK;
if (state)
{
m_keyboard_bits |= PCW16_KEYBOARD_CLOCK;
}
pcw16_keyboard_refresh_outputs();
}
void pcw16_state::pcw16_keyboard_int(int state)
{
m_system_status &= ~(1<<1);
if (state)
{
m_system_status |= (1<<1);
}
pcw16_refresh_ints();
}
void pcw16_state::pcw16_keyboard_reset()
{
/* clock set to high */
pcw16_keyboard_set_clock_state(1);
}
/* interfaces to a pc-at keyboard */
READ8_MEMBER(pcw16_state::pcw16_keyboard_data_shift_r)
{
//logerror("keyboard data shift r: %02x\n", m_keyboard_data_shift);
m_keyboard_state &= ~(PCW16_KEYBOARD_BUSY_STATUS);
pcw16_keyboard_int(0);
/* reset for reception */
pcw16_keyboard_reset();
/* read byte */
return m_keyboard_data_shift;
}
/* if force keyboard clock is low it is safe to send */
int pcw16_state::pcw16_keyboard_can_transmit()
{
/* clock is not forced low */
/* and not busy - i.e. not already sent a char */
return ((m_keyboard_bits_output & PCW16_KEYBOARD_CLOCK)!=0);
}
#ifdef UNUSED_FUNCTION
/* issue a begin byte transfer */
void ::pcw16_begin_byte_transfer(void)
{
}
#endif
/* signal a code has been received */
void pcw16_state::pcw16_keyboard_signal_byte_received(int data)
{
/* clear clock */
pcw16_keyboard_set_clock_state(0);
/* set code in shift register */
m_keyboard_data_shift = data;
/* busy */
m_keyboard_state |= PCW16_KEYBOARD_BUSY_STATUS;
/* initialise start, stop and parity bits */
m_keyboard_state &= ~PCW16_KEYBOARD_START_BIT_MASK;
m_keyboard_state |=PCW16_KEYBOARD_STOP_BIT_MASK;
/* "Keyboard data has odd parity, so the parity bit in the
status register should only be set when the shift register
data itself has even parity. */
m_keyboard_state &= ~PCW16_KEYBOARD_PARITY_MASK;
/* if data has even parity, set parity bit */
if ((m_keyboard_parity_table[data])==0)
m_keyboard_state |= PCW16_KEYBOARD_PARITY_MASK;
pcw16_keyboard_int(1);
}
WRITE8_MEMBER(pcw16_state::pcw16_keyboard_data_shift_w)
{
//logerror("Keyboard Data Shift: %02x\n", data);
/* writing to shift register clears parity */
/* writing to shift register clears start bit */
m_keyboard_state &= ~(
PCW16_KEYBOARD_PARITY_MASK |
PCW16_KEYBOARD_START_BIT_MASK);
/* writing to shift register sets stop bit */
m_keyboard_state |= PCW16_KEYBOARD_STOP_BIT_MASK;
m_keyboard_data_shift = data;
}
READ8_MEMBER(pcw16_state::pcw16_keyboard_status_r)
{
/* bit 2,3 are bits 8 and 9 of vdu pointer */
return (m_keyboard_state &
(PCW16_KEYBOARD_PARITY_MASK |
PCW16_KEYBOARD_STOP_BIT_MASK |
PCW16_KEYBOARD_START_BIT_MASK |
PCW16_KEYBOARD_BUSY_STATUS |
PCW16_KEYBOARD_FORCE_KEYBOARD_CLOCK |
PCW16_KEYBOARD_TRANSMIT_MODE));
}
WRITE8_MEMBER(pcw16_state::pcw16_keyboard_control_w)
{
//logerror("Keyboard control w: %02x\n",data);
m_keyboard_previous_state = m_keyboard_state;
/* if set, set parity */
if (data & 0x080)
{
m_keyboard_state |= PCW16_KEYBOARD_PARITY_MASK;
}
/* clear read/write bits */
m_keyboard_state &=
~(PCW16_KEYBOARD_FORCE_KEYBOARD_CLOCK |
PCW16_KEYBOARD_TRANSMIT_MODE);
/* set read/write bits from data */
m_keyboard_state |= (data & 0x03);
if (data & PCW16_KEYBOARD_RESET_INTERFACE)
{
pcw16_keyboard_reset();
}
if (data & PCW16_KEYBOARD_TRANSMIT_MODE)
{
/* force clock changed */
if (((m_keyboard_state^m_keyboard_previous_state) & PCW16_KEYBOARD_FORCE_KEYBOARD_CLOCK)!=0)
{
/* just cleared? */
if ((m_keyboard_state & PCW16_KEYBOARD_FORCE_KEYBOARD_CLOCK)==0)
{
/* write */
/* busy */
m_keyboard_state |= PCW16_KEYBOARD_BUSY_STATUS;
/* keyboard takes data */
m_keyboard->write(space, 0, m_keyboard_data_shift);
/* set clock low - no furthur transmissions */
pcw16_keyboard_set_clock_state(0);
/* set int */
pcw16_keyboard_int(1);
}
}
}
if (((m_keyboard_state^m_keyboard_previous_state) & PCW16_KEYBOARD_TRANSMIT_MODE)!=0)
{
if ((m_keyboard_state & PCW16_KEYBOARD_TRANSMIT_MODE)==0)
{
if ((m_system_status & (1<<1))!=0)
{
pcw16_keyboard_int(0);
}
}
}
pcw16_keyboard_refresh_outputs();
}
WRITE_LINE_MEMBER(pcw16_state::pcw16_keyboard_callback)
{
if(!state)
return;
if (pcw16_keyboard_can_transmit())
{
int data;
data = m_keyboard->read(machine().dummy_space(), 0);
if (data)
{
// if (data==4)
// {
// pcw16_dump_cpu_ram();
// }
pcw16_keyboard_signal_byte_received(data);
}
}
}
static const int rtc_days_in_each_month[]=
{
31,/* jan */
28, /* feb */
31, /* march */
30, /* april */
31, /* may */
30, /* june */
31, /* july */
31, /* august */
30, /* september */
31, /* october */
30, /* november */
31 /* december */
};
static const int rtc_days_in_february[] =
{
29, 28, 28, 28
};
void pcw16_state::rtc_setup_max_days()
{
/* february? */
if (m_rtc_months == 2)
{
/* low two bits of year select number of days in february */
m_rtc_days_max = rtc_days_in_february[m_rtc_years & 0x03];
}
else
{
m_rtc_days_max = (unsigned char)rtc_days_in_each_month[m_rtc_months];
}
}
TIMER_DEVICE_CALLBACK_MEMBER(pcw16_state::rtc_timer_callback)
{
int fraction_of_second;
/* halt counter? */
if ((m_rtc_control & 0x01)!=0)
{
/* no */
/* increment 256th's of a second register */
fraction_of_second = m_rtc_256ths_seconds+1;
/* add bit 8 = overflow */
m_rtc_seconds+=(fraction_of_second>>8);
/* ensure counter is in range 0-255 */
m_rtc_256ths_seconds = fraction_of_second & 0x0ff;
}
if (m_rtc_seconds>59)
{
m_rtc_seconds = 0;
m_rtc_minutes++;
if (m_rtc_minutes>59)
{
m_rtc_minutes = 0;
m_rtc_hours++;
if (m_rtc_hours>23)
{
m_rtc_hours = 0;
m_rtc_days++;
if (m_rtc_days > m_rtc_days_max)
{
m_rtc_days = 1;
m_rtc_months++;
if (m_rtc_months>12)
{
m_rtc_months = 1;
/* 7 bit year counter */
m_rtc_years = (m_rtc_years + 1) & 0x07f;
}
rtc_setup_max_days();
}
}
}
}
}
READ8_MEMBER(pcw16_state::rtc_year_invalid_r)
{
/* year in lower 7 bits. RTC Invalid status is m_rtc_control bit 0
inverted */
return (m_rtc_years & 0x07f) | (((m_rtc_control & 0x01)<<7)^0x080);
}
READ8_MEMBER(pcw16_state::rtc_month_r)
{
return m_rtc_months;
}
READ8_MEMBER(pcw16_state::rtc_days_r)
{
return m_rtc_days;
}
READ8_MEMBER(pcw16_state::rtc_hours_r)
{
return m_rtc_hours;
}
READ8_MEMBER(pcw16_state::rtc_minutes_r)
{
return m_rtc_minutes;
}
READ8_MEMBER(pcw16_state::rtc_seconds_r)
{
return m_rtc_seconds;
}
READ8_MEMBER(pcw16_state::rtc_256ths_seconds_r)
{
return m_rtc_256ths_seconds;
}
WRITE8_MEMBER(pcw16_state::rtc_control_w)
{
/* write control */
m_rtc_control = data;
}
WRITE8_MEMBER(pcw16_state::rtc_seconds_w)
{
/* TODO: Writing register could cause next to increment! */
m_rtc_seconds = data;
}
WRITE8_MEMBER(pcw16_state::rtc_minutes_w)
{
/* TODO: Writing register could cause next to increment! */
m_rtc_minutes = data;
}
WRITE8_MEMBER(pcw16_state::rtc_hours_w)
{
/* TODO: Writing register could cause next to increment! */
m_rtc_hours = data;
}
WRITE8_MEMBER(pcw16_state::rtc_days_w)
{
/* TODO: Writing register could cause next to increment! */
m_rtc_days = data;
}
WRITE8_MEMBER(pcw16_state::rtc_month_w)
{
/* TODO: Writing register could cause next to increment! */
m_rtc_months = data;
rtc_setup_max_days();
}
WRITE8_MEMBER(pcw16_state::rtc_year_w)
{
/* TODO: Writing register could cause next to increment! */
m_rtc_hours = data;
rtc_setup_max_days();
}
void pcw16_state::trigger_fdc_int()
{
int state;
state = m_system_status & (1<<6);
switch (m_fdc_int_code)
{
/* nmi */
case 0:
{
/* I'm assuming that the nmi is edge triggered */
/* a interrupt from the fdc will cause a change in line state, and
the nmi will be triggered, but when the state changes because the int
is cleared this will not cause another nmi */
/* I'll emulate it like this to be sure */
if (state!=m_previous_fdc_int_state)
{
if (state)
{
/* I'll pulse it because if I used hold-line I'm not sure
it would clear - to be checked */
m_maincpu->set_input_line(INPUT_LINE_NMI, PULSE_LINE);
}
}
}
break;
/* attach fdc to int */
case 1:
{
pcw16_refresh_ints();
}
break;
/* do not interrupt */
default:
break;
}
m_previous_fdc_int_state = state;
}
READ8_MEMBER(pcw16_state::pcw16_system_status_r)
{
// logerror("system status r: \n");
return m_system_status | (m_io_extra->read() & 0x04);
}
READ8_MEMBER(pcw16_state::pcw16_timer_interrupt_counter_r)
{
int data;
data = m_interrupt_counter;
m_interrupt_counter = 0;
/* clear display int */
m_system_status &= ~(1<<0);
pcw16_refresh_ints();
return data;
}
WRITE8_MEMBER(pcw16_state::pcw16_system_control_w)
{
//logerror("0x0f8: function: %d\n",data);
/* lower 4 bits define function code */
switch (data & 0x0f)
{
/* no effect */
case 0x00:
case 0x09:
case 0x0a:
case 0x0d:
case 0x0e:
break;
/* system reset */
case 0x01:
break;
/* connect IRQ6 input to /NMI */
case 0x02:
{
m_fdc_int_code = 0;
}
break;
/* connect IRQ6 input to /INT */
case 0x03:
{
m_fdc_int_code = 1;
}
break;
/* dis-connect IRQ6 input from /NMI and /INT */
case 0x04:
{
m_fdc_int_code = 2;
}
break;
/* set terminal count */
case 0x05:
{
m_fdc->tc_w(true);
}
break;
/* clear terminal count */
case 0x06:
{
m_fdc->tc_w(false);
}
break;
/* bleeper on */
case 0x0b:
{
m_beeper->set_state(1);
}
break;
/* bleeper off */
case 0x0c:
{
m_beeper->set_state(0);
}
break;
/* drive video outputs */
case 0x07:
{
}
break;
/* float video outputs */
case 0x08:
{
}
break;
/* set 4-bit output port to value X */
case 0x0f:
{
/* bit 7 - ?? */
/* bit 6 - ?? */
/* bit 5 - green/red led (1==green)*/
/* bit 4 - monitor on/off (1==on) */
m_4_bit_port = data>>4;
}
break;
}
}
WRITE_LINE_MEMBER( pcw16_state::fdc_interrupt )
{
/* IRQ6 */
/* bit 6 of PCW16 system status indicates floppy ints */
if (state)
m_system_status |= (1<<6);
else
m_system_status &= ~(1<<6);
trigger_fdc_int();
}
WRITE_LINE_MEMBER(pcw16_state::pcw16_com_interrupt_1)
{
m_system_status &= ~(1 << 4);
if ( state ) {
m_system_status |= (1 << 4);
}
pcw16_refresh_ints();
}
WRITE_LINE_MEMBER(pcw16_state::pcw16_com_interrupt_2)
{
m_system_status &= ~(1 << 3);
if ( state ) {
m_system_status |= (1 << 3);
}
pcw16_refresh_ints();
}
FLOPPY_FORMATS_MEMBER( pcw16_state::floppy_formats )
FLOPPY_PC_FORMAT
FLOPPY_FORMATS_END
static void pcw16_floppies(device_slot_interface &device)
{
device.option_add("35hd", FLOPPY_35_HD);
}
void pcw16_state::pcw16_io(address_map &map)
{
map.global_mask(0xff);
/* super i/o chip */
map(0x018, 0x01f).m(m_fdc, FUNC(pc_fdc_superio_device::map));
map(0x020, 0x027).rw("ns16550_1", FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w));
map(0x028, 0x02f).rw(m_uart2, FUNC(ns16550_device::ins8250_r), FUNC(ns16550_device::ins8250_w));
map(0x038, 0x03a).rw("lpt", FUNC(pc_lpt_device::read), FUNC(pc_lpt_device::write));
/* anne asic */
map(0x0e0, 0x0ef).w(this, FUNC(pcw16_state::pcw16_palette_w));
map(0x0f0, 0x0f3).rw(this, FUNC(pcw16_state::pcw16_bankhw_r), FUNC(pcw16_state::pcw16_bankhw_w));
map(0x0f4, 0x0f4).rw(this, FUNC(pcw16_state::pcw16_keyboard_data_shift_r), FUNC(pcw16_state::pcw16_keyboard_data_shift_w));
map(0x0f5, 0x0f5).rw(this, FUNC(pcw16_state::pcw16_keyboard_status_r), FUNC(pcw16_state::pcw16_keyboard_control_w));
map(0x0f7, 0x0f7).rw(this, FUNC(pcw16_state::pcw16_timer_interrupt_counter_r), FUNC(pcw16_state::pcw16_video_control_w));
map(0x0f8, 0x0f8).rw(this, FUNC(pcw16_state::pcw16_system_status_r), FUNC(pcw16_state::pcw16_system_control_w));
map(0x0f9, 0x0f9).rw(this, FUNC(pcw16_state::rtc_256ths_seconds_r), FUNC(pcw16_state::rtc_control_w));
map(0x0fa, 0x0fa).rw(this, FUNC(pcw16_state::rtc_seconds_r), FUNC(pcw16_state::rtc_seconds_w));
map(0x0fb, 0x0fb).rw(this, FUNC(pcw16_state::rtc_minutes_r), FUNC(pcw16_state::rtc_minutes_w));
map(0x0fc, 0x0fc).rw(this, FUNC(pcw16_state::rtc_hours_r), FUNC(pcw16_state::rtc_hours_w));
map(0x0fd, 0x0fd).rw(this, FUNC(pcw16_state::rtc_days_r), FUNC(pcw16_state::rtc_days_w));
map(0x0fe, 0x0fe).rw(this, FUNC(pcw16_state::rtc_month_r), FUNC(pcw16_state::rtc_month_w));
map(0x0ff, 0x0ff).rw(this, FUNC(pcw16_state::rtc_year_invalid_r), FUNC(pcw16_state::rtc_year_w));
}
void pcw16_state::machine_reset()
{
/* initialise defaults */
m_fdc_int_code = 2;
/* clear terminal count */
m_fdc->tc_w(false);
/* select first rom page */
m_banks[0] = 0;
// pcw16_update_memory(machine);
/* temp rtc setup */
m_rtc_seconds = 0;
m_rtc_minutes = 0;
m_rtc_hours = 0;
m_rtc_days_max = 0;
m_rtc_days = 1;
m_rtc_months = 1;
m_rtc_years = 0;
m_rtc_control = 1;
m_rtc_256ths_seconds = 0;
pcw16_keyboard_init();
m_uart2->ri_w(0);
}
void pcw16_state::machine_start()
{
m_system_status = 0;
m_interrupt_counter = 0;
m_beeper->set_state(0);
}
static INPUT_PORTS_START(pcw16)
PORT_START("EXTRA")
/* vblank */
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM) PORT_VBLANK("screen")
/* power switch - default is on */
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SERVICE) PORT_NAME("Power Switch/Suspend") PORT_WRITE_LINE_DEVICE_MEMBER("ns16550_2", ins8250_uart_device, ri_w) PORT_TOGGLE
PORT_INCLUDE( at_keyboard ) /* IN4 - IN11 */
INPUT_PORTS_END
static void pcw16_com(device_slot_interface &device)
{
device.option_add("msystems_mouse", MSYSTEM_SERIAL_MOUSE);
}
MACHINE_CONFIG_START(pcw16_state::pcw16)
/* basic machine hardware */
MCFG_CPU_ADD("maincpu", Z80, 16000000)
MCFG_CPU_PROGRAM_MAP(pcw16_map)
MCFG_CPU_IO_MAP(pcw16_io)
MCFG_QUANTUM_TIME(attotime::from_hz(60))
MCFG_DEVICE_ADD( "ns16550_1", NS16550, XTAL(1'843'200) ) /* TODO: Verify uart model */
MCFG_INS8250_OUT_TX_CB(DEVWRITELINE("serport1", rs232_port_device, write_txd))
MCFG_INS8250_OUT_DTR_CB(DEVWRITELINE("serport1", rs232_port_device, write_dtr))
MCFG_INS8250_OUT_RTS_CB(DEVWRITELINE("serport1", rs232_port_device, write_rts))
MCFG_INS8250_OUT_INT_CB(WRITELINE(pcw16_state, pcw16_com_interrupt_1))
MCFG_RS232_PORT_ADD( "serport1", pcw16_com, "msystems_mouse" )
MCFG_RS232_RXD_HANDLER(DEVWRITELINE("ns16550_1", ins8250_uart_device, rx_w))
MCFG_RS232_DCD_HANDLER(DEVWRITELINE("ns16550_1", ins8250_uart_device, dcd_w))
MCFG_RS232_DSR_HANDLER(DEVWRITELINE("ns16550_1", ins8250_uart_device, dsr_w))
MCFG_RS232_RI_HANDLER(DEVWRITELINE("ns16550_1", ins8250_uart_device, ri_w))
MCFG_RS232_CTS_HANDLER(DEVWRITELINE("ns16550_1", ins8250_uart_device, cts_w))
MCFG_DEVICE_ADD( "ns16550_2", NS16550, XTAL(1'843'200) ) /* TODO: Verify uart model */
MCFG_INS8250_OUT_TX_CB(DEVWRITELINE("serport2", rs232_port_device, write_txd))
MCFG_INS8250_OUT_DTR_CB(DEVWRITELINE("serport2", rs232_port_device, write_dtr))
MCFG_INS8250_OUT_RTS_CB(DEVWRITELINE("serport2", rs232_port_device, write_rts))
MCFG_INS8250_OUT_INT_CB(WRITELINE(pcw16_state, pcw16_com_interrupt_2))
MCFG_RS232_PORT_ADD( "serport2", pcw16_com, nullptr )
MCFG_RS232_RXD_HANDLER(DEVWRITELINE("ns16550_2", ins8250_uart_device, rx_w))
MCFG_RS232_DCD_HANDLER(DEVWRITELINE("ns16550_2", ins8250_uart_device, dcd_w))
MCFG_RS232_DSR_HANDLER(DEVWRITELINE("ns16550_2", ins8250_uart_device, dsr_w))
MCFG_RS232_CTS_HANDLER(DEVWRITELINE("ns16550_2", ins8250_uart_device, cts_w))
/* video hardware */
MCFG_SCREEN_ADD("screen", RASTER)
MCFG_SCREEN_REFRESH_RATE(50)
MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) /* not accurate */
MCFG_SCREEN_SIZE(PCW16_SCREEN_WIDTH, PCW16_SCREEN_HEIGHT)
MCFG_SCREEN_VISIBLE_AREA(0, PCW16_SCREEN_WIDTH-1, 0, PCW16_SCREEN_HEIGHT-1)
MCFG_SCREEN_UPDATE_DRIVER(pcw16_state, screen_update_pcw16)
MCFG_SCREEN_PALETTE("palette")
MCFG_PALETTE_ADD("palette", PCW16_NUM_COLOURS)
MCFG_PALETTE_INIT_OWNER(pcw16_state, pcw16)
/* sound hardware */
MCFG_SPEAKER_STANDARD_MONO("mono")
MCFG_SOUND_ADD("beeper", BEEP, 3750)
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.00)
/* printer */
MCFG_DEVICE_ADD("lpt", PC_LPT, 0)
MCFG_PC_LPT_IRQ_HANDLER(INPUTLINE("maincpu", 0))
MCFG_PC_FDC_SUPERIO_ADD("fdc")
MCFG_UPD765_INTRQ_CALLBACK(WRITELINE(pcw16_state, fdc_interrupt))
MCFG_FLOPPY_DRIVE_ADD("fdc:0", pcw16_floppies, "35hd", pcw16_state::floppy_formats)
MCFG_FLOPPY_DRIVE_ADD("fdc:1", pcw16_floppies, "35hd", pcw16_state::floppy_formats)
MCFG_SOFTWARE_LIST_ADD("disk_list","pcw16")
/* internal ram */
MCFG_RAM_ADD(RAM_TAG)
MCFG_RAM_DEFAULT_SIZE("2M")
MCFG_INTEL_E28F008SA_ADD("flash0")
MCFG_INTEL_E28F008SA_ADD("flash1")
MCFG_AT_KEYB_ADD("at_keyboard", 3, WRITELINE(pcw16_state, pcw16_keyboard_callback))
/* video ints */
MCFG_TIMER_DRIVER_ADD_PERIODIC("video_timer", pcw16_state, pcw16_timer_callback, attotime::from_usec(5830))
/* rtc timer */
MCFG_TIMER_DRIVER_ADD_PERIODIC("rtc_timer", pcw16_state, rtc_timer_callback, attotime::from_hz(256))
MACHINE_CONFIG_END
/***************************************************************************
Game driver(s)
***************************************************************************/
/* the lower 64k of the flash-file memory is write protected. This contains the boot
rom. The boot rom is also on the OS rescue disc. Handy! */
ROM_START(pcw16)
ROM_REGION((0x010000+524288), "maincpu",0)
ROM_LOAD("pcw045.sys",0x10000, 524288, CRC(c642f498) SHA1(8a5c05de92e7b2c5acdfb038217503ad363285b5))
ROM_END
/* YEAR NAME PARENT COMPAT MACHINE INPUT STATE INIT COMPANY FULLNAME */
COMP( 1995, pcw16, 0, 0, pcw16, pcw16, pcw16_state, 0, "Amstrad plc", "PcW16", MACHINE_NOT_WORKING )