lnw80: used derived class to reduce number of duplicate functions.

This commit is contained in:
Robbbert 2021-04-19 03:06:10 +10:00
parent 92ebbbaee1
commit 1da36f8ce5

View File

@ -3,8 +3,7 @@
/***************************************************************************
Memory map
0000-2fff ROM R D0-D7
3000-37ff ROM R D0-D7
0000-37ff ROM R D0-D7
37de UART status R/W D0-D7
37df UART data R/W D0-D7
37e0 for the realtime clock
@ -79,50 +78,17 @@ To Do / Status:
*******************************************************************************************************/
#include "emu.h"
#include "bus/centronics/ctronics.h"
#include "cpu/z80/z80.h"
#include "includes/trs80.h"
#include "machine/bankdev.h"
#include "imagedev/cassette.h"
#include "imagedev/floppy.h"
#include "imagedev/snapquik.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
#include "bus/rs232/rs232.h"
#include "machine/buffer.h"
#include "machine/wd_fdc.h"
#include "sound/spkrdev.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "formats/td0_dsk.h"
#include "formats/trs80_dsk.h"
class lnw80_state : public driver_device
class lnw80_state : public trs80_state
{
public:
lnw80_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_maincpu(*this, "maincpu")
, m_region_maincpu(*this, "maincpu")
, m_p_chargen(*this, "chargen")
, m_p_videoram(*this, "videoram")
: trs80_state(mconfig, type, tag)
, m_p_gfxram(*this, "gfxram")
, m_lnw_bank(*this, "lnw_banked_mem")
, m_centronics(*this, "centronics")
, m_cent_data_out(*this, "cent_data_out")
, m_cent_status_in(*this, "cent_status_in")
, m_uart(*this, "uart")
, m_uart_clock(*this, "uart_clock")
, m_fdc(*this, "fdc")
, m_floppy0(*this, "fdc:0")
, m_floppy1(*this, "fdc:1")
, m_floppy2(*this, "fdc:2")
, m_floppy3(*this, "fdc:3")
, m_speaker(*this, "speaker")
, m_cassette(*this, "cassette")
, m_io_baud(*this, "BAUD")
, m_io_config(*this, "CONFIG")
, m_io_keyboard(*this, "LINE%u", 0)
{ }
void lnw80(machine_config &config);
@ -133,66 +99,18 @@ protected:
private:
static void floppy_formats(format_registration &fr);
void port_ff_w(uint8_t data);
void lnw80_fe_w(uint8_t data);
void port_ea_w(uint8_t data);
void port_e8_w(uint8_t data);
uint8_t lnw80_fe_r();
uint8_t port_ff_r();
uint8_t port_ea_r();
uint8_t port_e8_r();
uint8_t irq_status_r();
uint8_t printer_r();
void printer_w(uint8_t data);
void motor_w(uint8_t data);
uint8_t keyboard_r(offs_t offset);
u8 fdc_r(offs_t offset);
void fdc_w(offs_t offset, u8 data);
INTERRUPT_GEN_MEMBER(rtc_interrupt);
INTERRUPT_GEN_MEMBER(fdc_interrupt);
TIMER_CALLBACK_MEMBER(cassette_data_callback);
DECLARE_WRITE_LINE_MEMBER(intrq_w);
DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb);
void lnw80_palette(palette_device &palette) const;
uint32_t screen_update_lnw80(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
void lnw80_io(address_map &map);
void lnw80_mem(address_map &map);
void lnw_banked_mem(address_map &map);
bool m_mode;
uint8_t m_irq;
uint8_t m_mask;
bool m_reg_load;
u8 m_lnw_mode;
bool m_cassette_data;
emu_timer *m_cassette_data_timer;
double m_old_cassette_val;
uint8_t m_size_store;
uint16_t m_timeout;
floppy_image_device *m_floppy;
required_device<cpu_device> m_maincpu;
required_memory_region m_region_maincpu;
required_region_ptr<u8> m_p_chargen;
required_shared_ptr<u8> m_p_videoram;
required_shared_ptr<u8> m_p_gfxram;
required_device<address_map_bank_device> m_lnw_bank;
required_device<centronics_device> m_centronics;
required_device<output_latch_device> m_cent_data_out;
required_device<input_buffer_device> m_cent_status_in;
required_device<ay31015_device> m_uart;
required_device<clock_device> m_uart_clock;
required_device<fd1771_device> m_fdc;
required_device<floppy_connector> m_floppy0;
required_device<floppy_connector> m_floppy1;
required_device<floppy_connector> m_floppy2;
required_device<floppy_connector> m_floppy3;
required_device<speaker_sound_device> m_speaker;
required_device<cassette_image_device> m_cassette;
required_ioport m_io_baud;
required_ioport m_io_config;
required_ioport_array<8> m_io_keyboard;
};
@ -363,21 +281,6 @@ static INPUT_PORTS_START( lnw80 )
PORT_DIPSETTING( 0x07, "19200")
INPUT_PORTS_END
#define IRQ_M1_RTC 0x80 /* RTC on Model I */
#define IRQ_M1_FDC 0x40 /* FDC on Model I */
TIMER_CALLBACK_MEMBER(lnw80_state::cassette_data_callback)
{
double new_val = (m_cassette->input());
/* Check for HI-LO transition */
if ( m_old_cassette_val > -0.2 && new_val < -0.2 )
m_cassette_data = true;
m_old_cassette_val = new_val;
}
/*************************************
*
@ -386,98 +289,11 @@ TIMER_CALLBACK_MEMBER(lnw80_state::cassette_data_callback)
*************************************/
uint8_t lnw80_state::port_e8_r()
{
/* not emulated
d7 Clear-to-Send (CTS), Pin 5
d6 Data-Set-Ready (DSR), pin 6
d5 Carrier Detect (CD), pin 8
d4 Ring Indicator (RI), pin 22
d3,d2,d0 Not used
d1 UART Receiver Input, pin 20 (pin 20 is also DTR) */
return 0;
}
uint8_t lnw80_state::port_ea_r()
{
/* UART Status Register
d7 Data Received ('1'=condition true)
d6 Transmitter Holding Register empty ('1'=condition true)
d5 Overrun Error ('1'=condition true)
d4 Framing Error ('1'=condition true)
d3 Parity Error ('1'=condition true)
d2..d0 Not used */
uint8_t data=7;
m_uart->write_swe(0);
data |= m_uart->tbmt_r() ? 0x40 : 0;
data |= m_uart->dav_r( ) ? 0x80 : 0;
data |= m_uart->or_r( ) ? 0x20 : 0;
data |= m_uart->fe_r( ) ? 0x10 : 0;
data |= m_uart->pe_r( ) ? 0x08 : 0;
m_uart->write_swe(1);
return data;
}
void lnw80_state::port_e8_w(uint8_t data)
{
m_reg_load = BIT(data, 1);
}
void lnw80_state::port_ea_w(uint8_t data)
{
if (m_reg_load)
/* d2..d0 not emulated
d7 Even Parity Enable ('1'=even, '0'=odd)
d6='1',d5='1' for 8 bits
d6='0',d5='1' for 7 bits
d6='1',d5='0' for 6 bits
d6='0',d5='0' for 5 bits
d4 Stop Bit Select ('1'=two stop bits, '0'=one stop bit)
d3 Parity Inhibit ('1'=disable; No parity, '0'=parity enabled)
d2 Break ('0'=disable transmit data; continuous RS232 'SPACE' condition)
d1 Request-to-Send (RTS), pin 4
d0 Data-Terminal-Ready (DTR), pin 20 */
{
m_uart->write_cs(0);
m_uart->write_nb1(BIT(data, 6));
m_uart->write_nb2(BIT(data, 5));
m_uart->write_tsb(BIT(data, 4));
m_uart->write_eps(BIT(data, 7));
m_uart->write_np(BIT(data, 3));
m_uart->write_cs(1);
}
else
{
/* not emulated
d7,d6 Not used
d5 Secondary Unassigned, pin 18
d4 Secondary Transmit Data, pin 14
d3 Secondary Request-to-Send, pin 19
d2 Break ('0'=disable transmit data; continuous RS232 'SPACE' condition)
d1 Data-Terminal-Ready (DTR), pin 20
d0 Request-to-Send (RTS), pin 4 */
}
}
uint8_t lnw80_state::lnw80_fe_r()
{
return m_lnw_mode;
}
uint8_t lnw80_state::port_ff_r()
{
/* ModeSel and cassette data
d7 cassette data from tape
d6 modesel setting */
return (m_mode ? 0 : 0x40) | (m_cassette_data ? 0x80 : 0) | 0x3f;
}
/* lnw80 can switch out all the devices, roms and video ram to be replaced by graphics ram. */
void lnw80_state::lnw80_fe_w(uint8_t data)
@ -493,147 +309,6 @@ void lnw80_state::lnw80_fe_w(uint8_t data)
m_lnw_bank->set_bank(BIT(data, 3));
}
void lnw80_state::port_ff_w(uint8_t data)
{
/* Standard output port of Model I
d3 ModeSel bit
d2 Relay
d1, d0 Cassette output */
static const double levels[4] = { 0.0, 1.0, -1.0, 0.0 };
m_cassette->change_state(BIT(data, 2) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR );
m_cassette->output(levels[data & 3]);
m_cassette_data = false;
m_mode = BIT(data, 3);
static const double speaker_levels[4] = { 0.0, -1.0, 0.0, 1.0 };
m_speaker->set_levels(4, speaker_levels);
m_speaker->level_w(data & 3);
}
/*************************************
*
* Interrupt handlers.
*
*************************************/
INTERRUPT_GEN_MEMBER(lnw80_state::rtc_interrupt)
{
/* This enables the processing of interrupts for the clock and the flashing cursor.
The OS counts one tick for each interrupt. It is called 40 times per second. */
m_irq |= IRQ_M1_RTC;
m_maincpu->set_input_line(0, HOLD_LINE);
// While we're here, let's countdown the motor timeout too.
// Let's not... LDOS often freezes
// if (m_timeout)
// {
// m_timeout--;
// if (m_timeout == 0)
// if (m_floppy)
// m_floppy->mon_w(1); // motor off
// }
}
WRITE_LINE_MEMBER(lnw80_state::intrq_w)
{
if (state)
{
m_irq |= IRQ_M1_FDC;
m_maincpu->set_input_line(0, HOLD_LINE);
}
else
m_irq &= ~IRQ_M1_FDC;
}
/*************************************
* *
* Memory handlers *
* *
*************************************/
u8 lnw80_state::fdc_r(offs_t offset)
{
if ((offset == 0) && (!BIT(m_io_config->read(), 7)))
return 0xff;
else
return m_fdc->read(offset) ^ 0xff;
}
void lnw80_state::fdc_w(offs_t offset, u8 data)
{
m_fdc->write(offset, data ^ 0xff);
}
uint8_t lnw80_state::printer_r()
{
return m_cent_status_in->read();
}
void lnw80_state::printer_w(uint8_t data)
{
m_cent_data_out->write(data);
m_centronics->write_strobe(0);
m_centronics->write_strobe(1);
}
uint8_t lnw80_state::irq_status_r()
{
/* (trs80l2) Whenever an interrupt occurs, 37E0 is read to see what devices require service.
d7 = RTC
d6 = FDC
d2 = Communications (not emulated)
All interrupting devices are serviced in a single interrupt. There is a mask byte,
which is dealt with by the DOS. We take the opportunity to reset the cpu INT line. */
u8 result = m_irq;
m_maincpu->set_input_line(0, CLEAR_LINE);
m_irq = 0;
return result;
}
void lnw80_state::motor_w(uint8_t data)
{
m_floppy = nullptr;
if (BIT(data, 0)) m_floppy = m_floppy0->get_device();
if (BIT(data, 1)) m_floppy = m_floppy1->get_device();
if (BIT(data, 2)) m_floppy = m_floppy2->get_device();
if (BIT(data, 3)) m_floppy = m_floppy3->get_device();
m_fdc->set_floppy(m_floppy);
if (m_floppy)
{
m_floppy->mon_w(0);
m_floppy->ss_w(BIT(data, 4));
m_timeout = 200;
}
// switch to fm
m_fdc->dden_w(1);
}
/*************************************
* Keyboard *
*************************************/
uint8_t lnw80_state::keyboard_r(offs_t offset)
{
u8 i, result = 0;
for (i = 0; i < 8; i++)
if (BIT(offset, i))
result |= m_io_keyboard[i]->read();
return result;
}
/*************************************
* Machine *
@ -671,95 +346,8 @@ void lnw80_state::machine_reset()
lnw80_fe_w(0);
}
/***************************************************************************
PARAMETERS
***************************************************************************/
#define LOG 1
#define CMD_TYPE_OBJECT_CODE 0x01
#define CMD_TYPE_TRANSFER_ADDRESS 0x02
#define CMD_TYPE_END_OF_PARTITIONED_DATA_SET_MEMBER 0x04
#define CMD_TYPE_LOAD_MODULE_HEADER 0x05
#define CMD_TYPE_PARTITIONED_DATA_SET_HEADER 0x06
#define CMD_TYPE_PATCH_NAME_HEADER 0x07
#define CMD_TYPE_ISAM_DIRECTORY_ENTRY 0x08
#define CMD_TYPE_END_OF_ISAM_DIRECTORY_ENTRY 0x0a
#define CMD_TYPE_PDS_DIRECTORY_ENTRY 0x0c
#define CMD_TYPE_END_OF_PDS_DIRECTORY_ENTRY 0x0e
#define CMD_TYPE_YANKED_LOAD_BLOCK 0x10
#define CMD_TYPE_COPYRIGHT_BLOCK 0x1f
/***************************************************************************
IMPLEMENTATION
***************************************************************************/
QUICKLOAD_LOAD_MEMBER(lnw80_state::quickload_cb)
{
address_space &program = m_maincpu->space(AS_PROGRAM);
uint8_t type, length;
uint8_t data[0x100];
uint8_t addr[2];
void *ptr;
while (!image.image_feof())
{
image.fread( &type, 1);
image.fread( &length, 1);
switch (type)
{
case CMD_TYPE_OBJECT_CODE: // 01 - block of data
{
length -= 2;
u16 block_length = length ? length : 256;
image.fread( &addr, 2);
u16 address = (addr[1] << 8) | addr[0];
if (LOG) logerror("/CMD object code block: address %04x length %u\n", address, block_length);
if (address < 0x3c00)
{
image.message("Attempting to write outside of RAM");
return image_init_result::FAIL;
}
ptr = program.get_write_ptr(address);
image.fread( ptr, block_length);
}
break;
case CMD_TYPE_TRANSFER_ADDRESS: // 02 - go address
{
image.fread( &addr, 2);
u16 address = (addr[1] << 8) | addr[0];
if (LOG) logerror("/CMD transfer address %04x\n", address);
m_maincpu->set_state_int(Z80_PC, address);
}
return image_init_result::PASS;
case CMD_TYPE_LOAD_MODULE_HEADER: // 05 - name
image.fread( &data, length);
if (LOG) logerror("/CMD load module header '%s'\n", data);
break;
case CMD_TYPE_COPYRIGHT_BLOCK: // 1F - copyright info
image.fread( &data, length);
if (LOG) logerror("/CMD copyright block '%s'\n", data);
break;
default:
image.fread( &data, length);
logerror("/CMD unsupported block type %u!\n", type);
image.message("Unsupported or invalid block type");
return image_init_result::FAIL;
}
}
return image_init_result::PASS;
}
/* 8-bit video, 64/80 characters per line = lnw80 */
uint32_t lnw80_state::screen_update_lnw80(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
uint32_t lnw80_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
static const uint16_t rows[] = { 0, 0x200, 0x100, 0x300, 1, 0x201, 0x101, 0x301 };
uint16_t sy=0,ma=0;
@ -996,7 +584,7 @@ void lnw80_state::lnw80(machine_config &config)
screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
// LNW80 Theory of Operations gives H and V periods as 15.750kHz and 59.66Hz, probably due to rounding the calculated ~15.7468kHz to 4 figures
screen.set_raw(3.579545_MHz_XTAL * 3, 682, 0, 480, 264, 0, 192); // 10.738MHz generated by tank circuit (top left of page 2 of schematics)
screen.set_screen_update(FUNC(lnw80_state::screen_update_lnw80));
screen.set_screen_update(FUNC(lnw80_state::screen_update));
screen.set_palette("palette");
PALETTE(config, "palette", FUNC(lnw80_state::lnw80_palette), 8);
GFXDECODE(config, "gfxdecode", "palette", gfx_lnw80);