brother/lw30.cpp: Added driver for Brother LW-30 word processor. (#10996)

New working systems
---------------------
Brother LW-30
This commit is contained in:
BartmanAbyss 2023-03-19 17:54:11 +01:00 committed by GitHub
parent 1fa76ca697
commit 4ff301b134
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 1190 additions and 0 deletions

View File

@ -1187,6 +1187,18 @@ if opt_tool(FORMATS, "JVC_DSK") then
}
end
--------------------------------------------------
--
--@src/lib/formats/lw30_dsk.h,FORMATS["LW30_DSK"] = true
--------------------------------------------------
if opt_tool(FORMATS, "LW30_DSK") then
files {
MAME_DIR.. "src/lib/formats/lw30_dsk.cpp",
MAME_DIR.. "src/lib/formats/lw30_dsk.h",
}
end
--------------------------------------------------
--
--@src/lib/formats/os9_dsk.h,FORMATS["OS9_DSK"] = true

View File

@ -0,0 +1,211 @@
// license:BSD-3-Clause
// copyright-holders:Bartman/Abyss
/***************************************************************************
Brother LW-30 Disk image format
see https://github.com/BartmanAbyss/brother-diskconv for disk conversion tool
***************************************************************************/
#include "lw30_dsk.h"
#include "ioprocs.h"
#include <array>
#include <cassert>
#include <cstring>
namespace {
constexpr uint16_t sync_table[]{
0xdaef, 0xb7ad, 0xfbbe, 0xeadf, 0xbffa, 0xaeb6, 0xf5d7, 0xdbee, 0xbaab, 0xfdbd,
0xebde, 0xd5f7, 0xafb5, 0xf6d6, 0xdded, 0xbbaa, 0xedbb, 0xd6dd, 0xb5f6, 0xf7af,
0xded5, 0xbdeb, 0xabfd, 0xeeba, 0xd7db, 0xb6f5, 0xfaae, 0xdfbf, 0xbeea, 0xadfb,
0xefb7, 0xdada, 0xb7ef, 0xfbad, 0xeabe, 0xbfdf, 0xaefa, 0xf5b6, 0xdbd7, 0xbaee,
0xfdab, 0xebbd, 0xd5de, 0xaff7, 0xf6b5, 0xddd6, 0xbbed, 0xaadd, 0xedf6, 0xd6af,
0xb5d5, 0xf7eb, 0xdefd, 0xbdba, 0xabdb, 0xeef5, 0xd7ae, 0xb6bf, 0xfaea, 0xdffb,
0xbeb7, 0xadda, 0xefef, 0xdaad, 0xb7be, 0xfbdf, 0xeafa, 0xbfb6, 0xaed7, 0xf5ee,
0xdbab, 0xbabd, 0xfdde, 0xebf7, 0xd5b5, 0xafd6, 0xf6ed, 0xddaa, 0xd6bb, 0xb5dd
};
constexpr uint8_t gcr_table[]{
0xaa, 0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7,
0xba, 0xbb, 0xbd, 0xbe, 0xbf, 0xd5, 0xd6, 0xd7,
0xda, 0xdb, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed,
0xee, 0xef, 0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd,
0xfe, 0xff // FE, FF are reserved
};
// format
constexpr uint8_t sector_prefix[]{
0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xab
};
// write
constexpr uint8_t sector_header[]{
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xed
};
// write
constexpr uint8_t sector_footer[]{
0xf5, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd
};
constexpr uint8_t sector_interleave1[]{ // 1-based
1, 6, 11, 4, 9, 2, 7, 12, 5, 10, 3, 8
};
constexpr uint8_t sector_interleave2[]{ // 1-based
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
};
constexpr uint8_t sector_interleave3[]{ // 1-based
1, 4, 7, 10, 6, 9, 12, 3, 11, 2, 5, 8
};
void gcr_encode_5_to_8(const uint8_t *input, uint8_t *output)
{
// input:
// 76543210
// --------
// 00000111 0
// 11222223 1
// 33334444 2
// 45555566 3
// 66677777 4
output[0] = gcr_table[(input[0] >> 3) & 0x1f];
output[1] = gcr_table[((input[0] << 2) & 0x1f) | ((input[1] >> 6) & 0b00000011)];
output[2] = gcr_table[(input[1] >> 1) & 0x1f];
output[3] = gcr_table[((input[1] << 4) & 0x1f) | ((input[2] >> 4) & 0b00001111)];
output[4] = gcr_table[((input[2] << 1) & 0x1f) | ((input[3] >> 7) & 0b00000001)];
output[5] = gcr_table[(input[3] >> 2) & 0x1f];
output[6] = gcr_table[((input[3] << 3) & 0x1f) | ((input[4] >> 5) & 0b00000111)];
output[7] = gcr_table[input[4] & 0x1f];
}
std::array<uint8_t, 3> checksum_256_bytes(const uint8_t *input)
{
size_t i = 0;
uint8_t a = 0;
uint8_t c = input[i++];
uint8_t d = input[i++];
uint8_t e = input[i++];
for(size_t b = 0; b < 253; b++) {
a = d;
if(c & 0b10000000)
a ^= 1;
d = c;
c = a;
a = (d << 1) ^ e;
e = d;
d = a;
e ^= input[i++];
}
return { c, d, e };
}
std::array<uint8_t, 416> gcr_encode_and_checksum(const uint8_t *input /* 256 bytes */) {
std::array<uint8_t, 416> output;
for(int i = 0; i < 51; i++)
gcr_encode_5_to_8(&input[i * 5], &output[i * 8]);
auto checksum = checksum_256_bytes(input);
std::array<uint8_t, 5> end_and_checksum{ input[255], checksum[0], checksum[1], checksum[2], 0x58 };
gcr_encode_5_to_8(&end_and_checksum[0], &output[408]);
return output;
}
}
static constexpr int TRACKS_PER_DISK = 78;
static constexpr int SECTORS_PER_TRACK = 12;
static constexpr int SECTOR_SIZE = 256;
static constexpr int RPM = 300;
static constexpr int CELLS_PER_REV = 250'000 / (RPM / 60);
// format track: 0xaa (2), 0xaa (48), 12*sector
// format sector: sector_prefix (8), track_sync (2), sector_sync (2), predata (19), payload=0xaa (414), postdata (13), 0xaa (42), should come out to ~4,000 bits
// write sector: (after sector_sync, 0xdd) sector_header (2+14), payload (416), sector_footer (11)
// from write_format, write_sector_data_header_data_footer
static constexpr int raw_sector_size = 8/*sector_prefix*/ + 2/*track_sync*/ + 2/*sector_sync*/ + 1/*0xdd*/ + 16/*sector_header*/ + 416/*payload*/ + 11/*sector_footer*/ + 42/*0xaa*/;
static constexpr int raw_track_size = 2/*0xaa*/ + 48/*0xaa*/ + SECTORS_PER_TRACK * raw_sector_size;
int lw30_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint64_t size = 0;
io.length(size);
if(size == TRACKS_PER_DISK * SECTORS_PER_TRACK * SECTOR_SIZE)
return 50; // identified by size
return 0;
}
bool lw30_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image *image) const
{
uint8_t trackdata[SECTORS_PER_TRACK * SECTOR_SIZE], rawdata[CELLS_PER_REV / 8];
memset(rawdata, 0xaa, sizeof(rawdata));
for(int track = 0; track < TRACKS_PER_DISK; track++) {
size_t actual{};
io.read_at(track * SECTORS_PER_TRACK * SECTOR_SIZE, trackdata, SECTORS_PER_TRACK * SECTOR_SIZE, actual);
if(actual != SECTORS_PER_TRACK * SECTOR_SIZE)
return false;
size_t i = 0;
for(int x = 0; x < 2 + 48; x++)
rawdata[i++] = 0xaa;
auto interleave_offset = (track % 4) * 4;
for(size_t s = interleave_offset; s < interleave_offset + SECTORS_PER_TRACK; s++) {
auto sector = sector_interleave1[s % SECTORS_PER_TRACK] - 1;
// according to check_track_and_sector
for(const auto& d : sector_prefix) // 8 bytes
rawdata[i++] = d;
rawdata[i++] = sync_table[track] & 0xff;
rawdata[i++] = sync_table[track] >> 8;
rawdata[i++] = sync_table[sector] & 0xff;
rawdata[i++] = sync_table[sector] >> 8;
rawdata[i++] = 0xdd;
for(const auto& d : sector_header) // 16 bytes
rawdata[i++] = d;
auto payload = gcr_encode_and_checksum(trackdata + sector * SECTOR_SIZE); // 256 -> 416 bytes
for(const auto &d : payload)
rawdata[i++] = d;
for(const auto &d : sector_footer) // 11 bytes
rawdata[i++] = d;
for(int x = 0; x < 42; x++)
rawdata[i++] = 0xaa;
}
assert(i == raw_track_size);
assert(i <= CELLS_PER_REV / 8);
generate_track_from_bitstream(track, 0, rawdata, CELLS_PER_REV, image);
}
image->set_variant(floppy_image::SSDD);
return true;
}
const char *lw30_format::name() const
{
return "lw30";
}
const char *lw30_format::description() const
{
return "Brother LW-30 floppy disk image";
}
const char *lw30_format::extensions() const
{
return "img";
}
bool lw30_format::supports_save() const
{
// TODO
return false;
}
const lw30_format FLOPPY_LW30_FORMAT;

View File

@ -0,0 +1,29 @@
// license:BSD-3-Clause
// copyright-holders:Bartman/Abyss
/***************************************************************************
Brother LW-30 Disk image format
see https://github.com/BartmanAbyss/brother-diskconv for disk conversion tool
***************************************************************************/
#ifndef MAME_FORMATS_LW30_DSK_H
#define MAME_FORMATS_LW30_DSK_H
#pragma once
#include "flopimg.h"
class lw30_format : public floppy_image_format_t
{
public:
virtual int identify(util::random_read& io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t>& variants, floppy_image *image) const override;
virtual const char *name() const override;
virtual const char *description() const override;
virtual const char *extensions() const override;
virtual bool supports_save() const override;
};
extern const lw30_format FLOPPY_LW30_FORMAT;
#endif // MAME_FORMATS_LW30_DSK_H

935
src/mame/brother/lw30.cpp Normal file
View File

@ -0,0 +1,935 @@
// license:BSD-3-Clause
// copyright-holders:Bartman/Abyss
#include "emu.h"
#include "cpu/z180/z180.h"
#include "imagedev/floppy.h"
#include "machine/timer.h"
#include "sound/beep.h"
#include "video/mc6845.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "formats/lw30_dsk.h"
#include "util/utf8.h"
#define LOG_FLOPPY (1U << 1)
//#define VERBOSE (LOG_GENERAL | LOG_FLOPPY)
//#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"
#define LOGFLOPPY(...) LOGMASKED(LOG_FLOPPY, __VA_ARGS__)
// command line parameters:
// -log -debug -window -intscalex 2 -intscaley 2 lw30 -resolution 960x256 -flop roms\lw30\tetris.img
// floppy: see src\devices\bus\vtech\memexp\floppy.cpp
//////////////////////////////////////////////////////////////////////////
// LW-30
//////////////////////////////////////////////////////////////////////////
// *** Hit Ctrl+Q during bootup to be able to start programs (like Tetris) from disk
/***************************************************************************
Brother LW-30
1991
Hardware:
#7
Hitachi
HD64180RP6
8-bit CMOS Micro Processing Unit
fully compatible with Zilog Z80180 (Z180)
6 MHz, DP-64S, Address Space 512 K Byte
MuRata CST12MTW 12.00 MHz Ceramic Resonator
#8
Mitsubishi
M65122ASP
UA5445-B LC-1
#6
NEC
D23C4001EC-172
UA2849-A
4MBit Mask ROM for Dictionary
#5
LH532H07
UA5362-A
2MBit Mask ROM
#11
Hitachi
HM6264ALP-15
High Speed CMOS Static RAM (8kbit x 8) 150ns
#10
Mitsubishi
M65017FP
UA5498-A
Murata CST4MGW 4.00 MHz Ceramic Resonator
#3, #4
Mitsubishi
HD74LS157P
#1, #2
NEC
D41464C-10
Dynamic NMOS RAM (64kbit x 4) 100ns
QA1, QA2
Mitsubishi
M54587P
#12
Texas Instruments
SN74HC04N
Floppy - custom single sided 3.5" DD
240kb capacity
256 bytes/sector
12 sectors/track
78 tracks
custom 5-to-8 GCR encoding (very similar to Apple II's 5-and-3 encoding)
300 rpm
FF FF FF used as sync-start, AB sync-mark for sector header, DE sync-mark for sector data
FAT12 File System
ROHM
BA6580DK
Read/Write Amplifier for FDD
see https://github.com/BartmanAbyss/brother-hardware/tree/master/1G%20-%20Brother%20LW-30 for datasheets, photos
Emulation Status:
Printer not working
Floppy Disk writing not working
Floppy Disk Sync not implemented (reading works)
Dictionary ROM not working
Cursor shapes not implemented except block cursor
Keyboard not 100% (mostly copied from LW-350)
TODO: find self-test; verify RAM address map
// 8kb SRAM, 64kb DRAM <- where?
***************************************************************************/
class lw30_beep_device : public device_t, public device_sound_interface
{
public:
lw30_beep_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
protected:
// device-level overrides
void device_start() override ATTR_COLD;
// sound stream update overrides
void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
public:
DECLARE_WRITE_LINE_MEMBER(set_state); // enable/disable sound output
void set_clock(uint32_t frequency); // output frequency
private:
sound_stream *m_stream; /* stream number */
uint8_t m_state; /* enable beep */
int m_frequency; /* set frequency - this can be changed using the appropriate function */
int m_incr; /* initial wave state */
int8_t m_signal; /* current signal */
};
DEFINE_DEVICE_TYPE(BROTHER_BEEP, lw30_beep_device, "lw30_beep", "Brother LW-30 Beeper")
static constexpr auto BROTHER_BEEP_RATE = 48000;
lw30_beep_device::lw30_beep_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, BROTHER_BEEP, tag, owner, clock)
, device_sound_interface(mconfig, *this)
, m_stream(nullptr)
, m_state(0xff)
, m_frequency(clock)
{
}
void lw30_beep_device::device_start()
{
m_stream = stream_alloc(0, 1, BROTHER_BEEP_RATE);
m_state = 0xff;
m_signal = 0x01;
// register for savestates
save_item(NAME(m_state));
save_item(NAME(m_frequency));
save_item(NAME(m_incr));
save_item(NAME(m_signal));
}
void lw30_beep_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
auto &buffer = outputs[0];
auto signal = m_signal;
int clock = 0, rate = BROTHER_BEEP_RATE / 2;
/* get progress through wave */
int incr = m_incr;
if(m_frequency > 0)
clock = m_frequency;
/* if we're not enabled, just fill with 0 */
if(m_state == 0xff || clock == 0)
{
buffer.fill(0);
return;
}
/* fill in the sample */
for(int sampindex = 0; sampindex < buffer.samples(); sampindex++)
{
if(BIT(m_state, 7) != 0)
buffer.put(sampindex, signal > 0 ? 1.f : -1.f);
else
buffer.put(sampindex, 0.f);
incr -= clock;
while(incr < 0)
{
incr += rate;
signal = -signal;
}
}
/* store progress through wave */
m_incr = incr;
m_signal = signal;
}
WRITE_LINE_MEMBER(lw30_beep_device::set_state)
{
// only update if new state is not the same as old state
if(m_state == state)
return;
m_stream->update();
if(m_state == 0) {
// restart wave from beginning
m_incr = 0;
m_signal = 0x01;
}
m_state = state;
}
void lw30_beep_device::set_clock(uint32_t frequency)
{
if(m_frequency == frequency)
return;
m_stream->update();
m_frequency = frequency;
m_signal = 0x01;
m_incr = 0;
}
namespace {
class lw30_state : public driver_device
{
public:
lw30_state(const machine_config &mconfig, device_type type, const char *tag) :
driver_device(mconfig, type, tag),
maincpu(*this, "maincpu"),
screen(*this, "screen"),
floppy(*this, "floppy"),
beeper(*this, "beeper"),
m_io_kbrow(*this, "kbrow.%u", 0),
rom(*this, "maincpu"),
font_normal(*this, "font_normal"),
font_bold(*this, "font_bold")
{
//video_control = 0b00000010; // TEST LW-10 screen height
}
void lw30(machine_config& config);
protected:
// driver_device overrides
virtual void machine_start() override ATTR_COLD;
virtual void machine_reset() override ATTR_COLD;
private:
// devices
required_device<hd64180rp_device> maincpu;
required_device<screen_device> screen;
required_device<floppy_connector> floppy;
required_device<lw30_beep_device> beeper;
required_ioport_array<9> m_io_kbrow;
required_region_ptr<uint8_t> rom, font_normal, font_bold;
// floppy
uint8_t floppy_data = 0;
uint8_t io_88 = 0;
uint8_t io_98 = 0;
uint8_t floppy_control = 0; // stepper motor control
uint8_t floppy_steps = 0; // quarter track
uint8_t floppy_shifter = 0, floppy_latch = 0;
bool floppy_read_until_zerobit = false;
// video
uint8_t videoram[0x2000]; // 80 chars * 14 lines; 2 bytes per char (attribute, char)
uint8_t video_cursor_x, video_cursor_y, video_pos_x, video_pos_y, video_control, io_b8;
uint8_t cursor_state;
// screen updates
uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
uint8_t illegal_r(offs_t offset)
{
if(!machine().side_effects_disabled())
LOG("%s: unmapped memory read from %0*X\n", machine().describe_context(), 6, offset);
return 0;
}
void illegal_w(offs_t offset, uint8_t data)
{
LOG("%s: unmapped memory write to %0*X = %0*X\n", machine().describe_context(), 6, offset, 2, data);
}
// IO
void video_cursor_x_w(uint8_t data) // 70
{
video_cursor_x = data;
}
void video_cursor_y_w(uint8_t data) // 71
{
video_cursor_y = data;
}
void video_pos_x_w(uint8_t data) // 72
{
video_pos_x = data;
}
void video_pos_y_w(uint8_t data) // 73
{
video_pos_y = data;
}
uint8_t video_data_r() // 74
{
uint8_t data = 0x00;
if(video_pos_y < 0x20)
data = videoram[video_pos_y * 256 + video_pos_x];
else {
if(!machine().side_effects_disabled())
LOG("%s: video_data_r out of range: x=%u, y=%u\n", machine().describe_context(), video_pos_x, video_pos_y);
}
return data;
}
void video_data_w(uint8_t data) // 74
{
if(video_pos_y < 0x20)
videoram[video_pos_y * 256 + video_pos_x] = data;
else
LOG("%s: video_data_w out of range: x=%u, y=%u\n", machine().describe_context(), video_pos_x, video_pos_y);
video_pos_x++;
if(video_pos_x == 0)
video_pos_y++;
}
uint8_t video_control_r() // 75
{
return video_control;
}
void video_control_w(uint8_t data)
{
video_control = data; // | 0b00000010; // TEST LW-10 screen height
}
// 76
uint8_t io_77_r() // config
{
// TODO: use PORT_CONFNAME, etc
uint8_t out = 0x20; // 14 lines
out |= 0x00; // german
//out |= 0x01; // french
//out |= 0x02; // german
return ~out;
}
// Floppy
TIMER_DEVICE_CALLBACK_MEMBER(floppy_timer_callback)
{
auto floppy_device = floppy->get_device();
if(floppy_device->ready_r() != false)
return;
floppy_latch <<= 1;
attotime now = machine().time();
attotime when = now - attotime::from_usec(4);
attotime reversal = floppy_device->get_next_transition(when);
if(reversal > when && reversal <= now)
floppy_latch |= 1;
floppy_shifter++;
if((floppy_read_until_zerobit && (floppy_latch & 1) == 0) || (!floppy_read_until_zerobit && floppy_shifter == 8)) {
//LOGFLOPPY("%s: floppy_timer_callback: floppy_read_until_zerobit=%d latch=%02X\n", machine().describe_context(), floppy_read_until_zerobit, floppy_latch);
floppy_control |= 0x80; // floppy_data_available = true;
floppy_data = floppy_latch;
floppy_latch = floppy_shifter = 0;
floppy_read_until_zerobit = false;
}
//LOGFLOPPY("%s: floppy_timer_callback: IO_80=%02X, shifter=%u offset=%u\n", machine().describe_context(), io_80, floppy_shifter, floppy_track_offset % cache.size());
//LOGFLOPPY("%s: read_io_80 track=%d,offset=%4x => %02x\n", callstack(), floppy_steps / 4, floppy_track_offset, ret);
}
uint8_t floppy_data_r() // 80
{
if(!machine().side_effects_disabled()) {
floppy_control &= ~0x80; // floppy_data_available = false;
LOGFLOPPY("%s: read %02X from IO 80\n", machine().describe_context(), floppy_data);
}
return floppy_data;
}
void floppy_data_w(uint8_t data)
{
LOGFLOPPY("%s: write %02X to IO 80\n", machine().describe_context(), data);
floppy_data = data;
}
uint8_t io_88_r()
{
// bit 0: set in start_write; cleared in end_write
// bit 1: pulsed after 3*0xFF sync (read next floppydata until zero-bit)
// bit 2: cleared in stepper routines, rst28_06
// bit 3: set in start_write; cleared in end_write
// bit 5: cleared in rst28_06; motor-on?
if(!machine().side_effects_disabled())
LOGFLOPPY("%s: read %02X from IO 88\n", machine().describe_context(), io_88);
return io_88;
}
void io_88_w(uint8_t data)
{
LOGFLOPPY("%s: write %02X to IO 88\n", machine().describe_context(), data);
io_88 = data;
floppy->get_device()->mon_w((io_88 & (1 << 5)) == 0);
}
uint8_t floppy_status_r() // 90
{
// bit 7 set; data ready from floppy
// bit 6 clear; unknown meaning
// bit 5 clear; unknown meaning
// bit 4 clear; unknown meaning
// bit 3-0: stepper motor
if(!machine().side_effects_disabled())
LOGFLOPPY("%s: read %02X from IO 90\n", machine().describe_context(), floppy_control);
return floppy_control;
}
void floppy_stepper_w(uint8_t data)
{
LOGFLOPPY("%s: write %02X to IO 90\n", machine().describe_context(), data);
// write directly to 4-wire bipolar stepper motor (see stepper_table)
// a rotation to the left means decrease quarter-track
// a rotation to the right means increase quarter-track
const auto rol4 = [](uint8_t d) { return ((d << 1) & 0b1111) | ((d >> 3) & 0b0001); };
const auto ror4 = [](uint8_t d) { return ((d >> 1) & 0b0111) | ((d << 3) & 0b1000); };
const auto old_track = floppy_steps / 4;
switch(data & 0xf) {
case 0b0011:
case 0b0110:
case 0b1100:
case 0b1001:
if((data & 0x0f) == rol4(floppy_control))
floppy_steps--;
else if((data & 0x0f) == ror4(floppy_control))
floppy_steps++;
else
LOGFLOPPY("%s: illegal step %02x=>%02x\n", machine().describe_context(), floppy_control, data);
break;
default:
LOGFLOPPY("%s: initial step %02x=>%02x\n", machine().describe_context(), floppy_control, data);
break;
}
const auto new_track = floppy_steps / 4;
auto floppy_device = floppy->get_device();
if(new_track != old_track) {
floppy_device->dir_w(new_track < old_track);
floppy_device->stp_w(true);
floppy_device->stp_w(false);
}
LOGFLOPPY("%s: floppy_steps=%3d => old_track=%2d new_track=%2d cyl=%2d\n", machine().describe_context(), floppy_steps, old_track, new_track, floppy_device->get_cyl());
assert(floppy_device->get_cyl() == new_track);
floppy_control = (floppy_control & 0xf0) | (data & 0x0f);
}
uint8_t io_98_r()
{
// mirrored in RAM
// bit 0: cleared in rst28_06 in mirror
// bit 2: cleared before formatting in mirror; set after formatting
// bit 3: cleared before formatting in mirror
// bit 4: cleared before writing in mirror; set after writing
if(!machine().side_effects_disabled()) {
if(io_88 & 0b10)
floppy_read_until_zerobit = true;
else
floppy_read_until_zerobit = false;
LOGFLOPPY("%s: read %02X from IO 98\n", machine().describe_context(), io_98);
}
return io_98;
}
void io_98_w(uint8_t data)
{
LOGFLOPPY("%s: write %02X to IO 98\n", machine().describe_context(), data);
io_98 = data;
}
uint8_t illegal_io_r(offs_t offset, uint8_t mem_mask = ~0)
{
if(!machine().side_effects_disabled())
LOGFLOPPY("%s: unmapped IO read from %0*X & %0*X\n", machine().describe_context(), 4, offset + 0x40, 2, mem_mask);
return 0;
}
void illegal_io_w(offs_t offset, uint8_t data, uint8_t mem_mask = ~0)
{
LOGFLOPPY("%s: unmapped IO write to %0*X = %0*X & %0*X\n", machine().describe_context(), 4, offset + 0x40, 2, data, 2, mem_mask);
}
uint8_t io_b0_r()
{
// Tetris reads bit 3, needed for correct keyboard layout
return 0b1000;
}
uint8_t io_b8_r() // B8 (keyboard)
{
// keyboard matrix
if(io_b8 <= 8)
return m_io_kbrow[io_b8].read();
return 0x00;
}
void io_b8_w(uint8_t data)
{
io_b8 = data;
}
void beeper_w(uint8_t data) // F0
{
beeper->set_state(data);
}
void irqack_w(uint8_t) // F8
{
maincpu->set_input_line(INPUT_LINE_IRQ1, CLEAR_LINE);
}
TIMER_DEVICE_CALLBACK_MEMBER(int1_timer_callback)
{
maincpu->set_input_line(INPUT_LINE_IRQ1, ASSERT_LINE);
}
TIMER_DEVICE_CALLBACK_MEMBER(cursor_timer_callback)
{
cursor_state = !cursor_state;
}
static void floppy_formats(format_registration &fr)
{
fr.add(FLOPPY_LW30_FORMAT);
}
static void lw30_floppies(device_slot_interface &device) ATTR_COLD
{
device.option_add("35ssdd", FLOPPY_35_SSDD);
}
void map_program(address_map &map) ATTR_COLD
{
map(0x00000, 0x01fff).rom();
map(0x02000, 0x05fff).ram();
map(0x06000, 0x3ffff).rom();
map(0x50000, 0x51fff).ram(); // ???
map(0x61000, 0x61fff).ram();
map(0x42000, 0x45fff).rom().region("maincpu", 0x02000).w(FUNC(lw30_state::illegal_w)); // => ROM 0x02000-0x05fff
map(0x65000, 0x70fff).ram();
}
void map_io(address_map &map) ATTR_COLD
{
map.global_mask(0xff);
map(0x00, 0x3f).noprw(); // Z180 internal registers
// video
map(0x70, 0x70).w(FUNC(lw30_state::video_cursor_x_w));
map(0x71, 0x71).w(FUNC(lw30_state::video_cursor_y_w));
map(0x72, 0x72).w(FUNC(lw30_state::video_pos_x_w));
map(0x73, 0x73).w(FUNC(lw30_state::video_pos_y_w));
map(0x74, 0x74).rw(FUNC(lw30_state::video_data_r), FUNC(lw30_state::video_data_w));
map(0x75, 0x75).rw(FUNC(lw30_state::video_control_r), FUNC(lw30_state::video_control_w));
map(0x76, 0x76).noprw(); // NOP just to shut up the log
map(0x77, 0x77).r(FUNC(lw30_state::io_77_r)).nopw(); // NOP just to shut up the log
// floppy
map(0x80, 0x80).rw(FUNC(lw30_state::floppy_data_r), FUNC(lw30_state::floppy_data_w));
map(0x88, 0x88).rw(FUNC(lw30_state::io_88_r), FUNC(lw30_state::io_88_w));
map(0x90, 0x90).rw(FUNC(lw30_state::floppy_status_r), FUNC(lw30_state::floppy_stepper_w));
map(0x98, 0x98).rw(FUNC(lw30_state::io_98_r), FUNC(lw30_state::io_98_w));
map(0xa8, 0xa8).noprw(); // NOP just to shut up the log
map(0xb0, 0xb0).r(FUNC(lw30_state::io_b0_r));
map(0xb8, 0xb8).rw(FUNC(lw30_state::io_b8_r), FUNC(lw30_state::io_b8_w));
map(0xd8, 0xd8).noprw(); // NOP just to shut up the log
map(0xf0, 0xf0).w(FUNC(lw30_state::beeper_w));
map(0xf8, 0xf8).w(FUNC(lw30_state::irqack_w));
//map(0x40, 0xff).rw(FUNC(lw30_state::illegal_io_r), FUNC(lw30_state::illegal_io_w));
}
};
uint32_t lw30_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
// based on LW-350 ROM draw_char routine @ 6B14
enum attrs : uint8_t {
UNDERLINE = 0b00000001,
OVERLINE = 0b00000010,
BOLD = 0b00000100,
VERTICAL_LINE = 0b00001000,
INVERT_FULL = 0b00010000,
INVERT_UPPER_HALF = 0b00100000,
INVERT_LOWER_HALF = 0b01000000
};
const rgb_t palette[]{
0xffffffff,
0xff000000,
};
enum control : uint8_t {
DISPLAY_ON = 0b00000001,
HALF_HEIGHT = 0b00000010, // 64px height (LW-10/20) instead of 128px height (LW-30)
BITMAP_MODE = 0b00001000,
TILE_MODE = 0b00100000, // 8x8 tiles at videoram[0x1000]
};
if(video_control & DISPLAY_ON) {
if(video_control & TILE_MODE) {
uint8_t pixmap[60 * 128]; // pixel data
for(auto y = 0; y < 16; y++) {
for(auto x = 0; x < 60; x++) {
const auto atr = videoram[y * 256 + x * 2 + 0];
const auto chr = videoram[y * 256 + x * 2 + 1];
const auto fnt = &videoram[0x1000 + chr * 8 + ((atr & BOLD) ? 0x800 : 0)];
uint8_t charbuf[8];
for(int i = 0; i < 8; i++) {
charbuf[i] = fnt[i];
}
if(atr & UNDERLINE) {
charbuf[7] = 0xff;
}
if(atr & VERTICAL_LINE) {
for(int i = 0; i < 8; i++) {
charbuf[i] |= 0b1;
}
}
for(int i = 0; i < 8; i++) {
pixmap[(y * 8 + i) * 60 + x] = charbuf[i];
}
}
}
for(auto y = 0; y < 128; y++) {
uint32_t *p = &bitmap.pix(y);
for(auto x = 0; x < 480; x += 8) {
const auto gfx = pixmap[y * 60 + x / 8];
*p++ = palette[BIT(gfx, 7)];
*p++ = palette[BIT(gfx, 6)];
*p++ = palette[BIT(gfx, 5)];
*p++ = palette[BIT(gfx, 4)];
*p++ = palette[BIT(gfx, 3)];
*p++ = palette[BIT(gfx, 2)];
*p++ = palette[BIT(gfx, 1)];
*p++ = palette[BIT(gfx, 0)];
}
}
} else if(video_control & BITMAP_MODE) {
for(auto y = 0; y < 128; y++) {
uint32_t *p = &bitmap.pix(y);
for(auto x = 0; x < 480; x += 8) {
const auto gfx = videoram[y * 64 + x / 8];
*p++ = palette[BIT(gfx, 7)];
*p++ = palette[BIT(gfx, 6)];
*p++ = palette[BIT(gfx, 5)];
*p++ = palette[BIT(gfx, 4)];
*p++ = palette[BIT(gfx, 3)];
*p++ = palette[BIT(gfx, 2)];
*p++ = palette[BIT(gfx, 1)];
*p++ = palette[BIT(gfx, 0)];
}
}
} else {
// default font
uint8_t pixmap[80 * 128]{}; // pixel data
for(auto y = 0; y < 14; y++) {
for(auto x = 0; x < 80; x++) {
const auto atr = videoram[y * 256 + x * 2 + 0];
const auto chr = videoram[y * 256 + x * 2 + 1];
const auto fnt = (atr & BOLD) ? &font_bold[chr * 8] : &font_normal[chr * 8];
uint8_t charbuf[9];
charbuf[0] = 0x00;
for(int i = 0; i < 8; i++) {
charbuf[i + 1] = fnt[i];
}
if(atr & UNDERLINE) {
charbuf[8] = 0xff;
}
if(atr & OVERLINE) {
charbuf[0] = 0xff;
}
if(atr & VERTICAL_LINE) {
for(int i = 0; i < 9; i++) {
charbuf[i] |= 0b1;
}
}
if(atr & INVERT_FULL) {
for(int i = 0; i < 9; i++) {
charbuf[i] ^= 0xff;
}
}
if(atr & INVERT_LOWER_HALF) {
for(int i = 4; i < 9; i++) {
charbuf[i] ^= 0xff;
}
}
if(atr & INVERT_UPPER_HALF) {
for(int i = 0; i < 5; i++) {
charbuf[i] ^= 0xff;
}
}
for(int i = 0; i < 9; i++) {
pixmap[(y * 9 + i) * 80 + x] = charbuf[i];
}
}
}
// draw cursor; TODO: shape
if(cursor_state) {
const auto cursor_x = video_cursor_x & 0x7f;
const auto cursor_y = (video_cursor_x >> 7) | ((video_cursor_y & 7) << 1);
if(cursor_x < 80 && cursor_y < 14) {
for(int i = 0; i < 9; i++) {
pixmap[(cursor_y * 9 + i) * 80 + cursor_x] ^= 0xff;
}
}
}
for(auto y = 0; y < 128; y++) {
uint32_t *p = &bitmap.pix(y);
for(auto x = 0; x < 640; x += 8) {
const auto gfx = pixmap[y * 80 + x / 8];
*p++ = palette[BIT(gfx, 5)];
*p++ = palette[BIT(gfx, 4)];
*p++ = palette[BIT(gfx, 3)];
*p++ = palette[BIT(gfx, 2)];
*p++ = palette[BIT(gfx, 1)];
*p++ = palette[BIT(gfx, 0)];
}
}
}
} else {
// display off
for(auto y = 0; y < 128; y++) {
uint32_t *p = &bitmap.pix(y);
for(auto x = 0; x < 480; x++) {
*p++ = palette[0];
}
}
}
return 0;
}
void lw30_state::machine_start()
{
screen->set_visible_area(0, 480 - 1, 0, 128 - 1);
// patch out printer init
rom[0x280f4] = 0x00;
// patch out autosave load
rom[0x28c3a] = rom[0x28c3a + 1] = rom[0x28c3a + 2] = 0x00;
// always jump to "zusatzprogramme" (otherwise hit Ctrl+Q during bootup)
//rom[0x28103] = 0xc3;
// floppy debugging
//if(machine().debug_enabled()) {
// machine().debugger().console().execute_command(R"(bp 6a2c,1,{logerror "expect AB; A=%02X\n",a; g})", false);
// machine().debugger().console().execute_command(R"(bp 6617,1,{logerror "expect DE; A=%02X\n",a; g})", false);
//}
}
void lw30_state::machine_reset()
{
cursor_state = 0;
video_cursor_x = video_cursor_y = 0;
video_pos_x = video_pos_y = 0;
video_control = 0;
// TODO more reset variables
memcpy(&videoram[0x1000], font_normal, 0x800);
}
static INPUT_PORTS_START(lw30)
PORT_START("kbrow.0")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR(U'§')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X')
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_TAB) PORT_CHAR(UCHAR_MAMEKEY(TAB))
PORT_START("kbrow.1")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
PORT_START("kbrow.2")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('/')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V')
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))
PORT_START("kbrow.3")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK))
PORT_START("kbrow.4")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N')
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
PORT_START("kbrow.5")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MINUS) PORT_CHAR(U'ß') PORT_CHAR('?')
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_CHAR('=')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR(';')
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_MENU) PORT_CHAR(UCHAR_MAMEKEY(MENU))
PORT_START("kbrow.6")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FILE/SPELL") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_COLON) PORT_CHAR(U'ö') PORT_CHAR(U'Ö')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('+') PORT_CHAR('*')
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR(U'ü') PORT_CHAR(U'Ü')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("kbrow.7")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TW/WP/LAYOUT") PORT_CODE(KEYCODE_PRTSCR)
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CANCEL") PORT_CODE(KEYCODE_PAUSE) PORT_CHAR(UCHAR_MAMEKEY(CANCEL))
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(UCHAR_MAMEKEY(ENTER))
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(UCHAR_MAMEKEY(BACKSPACE))
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) //PORT_CODE(KEYCODE_)
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("WORD OUT/LINE OUT") //PORT_CODE(KEYCODE_)
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SPACE) PORT_CHAR(UCHAR_MAMEKEY(SPACE))
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_LSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
PORT_START("kbrow.8")
PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(U'´') PORT_CHAR(U'`')
PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('K')
PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) //PORT_CODE(KEYCODE_TILDE)
PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K')
PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR(':')
PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('-') PORT_CHAR('_')
PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(U'ä') PORT_CHAR(U'Ä')
PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END
void lw30_state::lw30(machine_config &config)
{
// basic machine hardware
HD64180RP(config, maincpu, 12'000'000 / 2);
maincpu->set_addrmap(AS_PROGRAM, &lw30_state::map_program);
maincpu->set_addrmap(AS_IO, &lw30_state::map_io);
// video hardware
SCREEN(config, screen, SCREEN_TYPE_RASTER);
screen->set_color(rgb_t(6, 245, 206));
screen->set_physical_aspect(480, 128);
screen->set_refresh_hz(78.1);
screen->set_screen_update(FUNC(lw30_state::screen_update));
screen->set_size(480, 128);
// floppy disk
FLOPPY_CONNECTOR(config, floppy, lw30_state::lw30_floppies, "35ssdd", lw30_state::floppy_formats).enable_sound(true);
// sound hardware
SPEAKER(config, "mono").front_center();
BROTHER_BEEP(config, beeper, 4'000).add_route(ALL_OUTPUTS, "mono", 1.0); // 4.0 kHz
// timers
TIMER(config, "timer_1khz").configure_periodic(FUNC(lw30_state::int1_timer_callback), attotime::from_hz(1000));
TIMER(config, "timer_floppy").configure_periodic(FUNC(lw30_state::floppy_timer_callback), attotime::from_usec(4));
TIMER(config, "timer_cursor").configure_periodic(FUNC(lw30_state::cursor_timer_callback), attotime::from_msec(512));
}
/***************************************************************************
Machine driver(s)
***************************************************************************/
ROM_START( lw30 )
ROM_REGION( 0x40000, "maincpu", 0 )
ROM_LOAD("ua5362-a", 0x00000, 0x40000, CRC(DAC77867) SHA1(5c7ab30dec55a24eb1b7f241e5015e3836ebf077))
ROM_REGION(0x80000, "dictionary", 0)
ROM_LOAD("ua2849-a", 0x00000, 0x80000, CRC(FA8712EB) SHA1(2d3454138c79e75604b30229c05ed8fb8e7d15fe))
ROM_REGION(0x800, "font_normal", 0)
ROM_LOAD("font-normal", 0x00000, 0x800, CRC(56A8B45D) SHA1(3f2860667ee56944cf5a79bfd4e80bebf532b51a))
ROM_REGION(0x800, "font_bold", 0)
ROM_LOAD("font-bold", 0x00000, 0x800, CRC(D81B79C4) SHA1(fa6be6f9dd0d7ae6d001802778272ecce8f425bc))
ROM_END
} // anonymous namespace
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
COMP( 1991, lw30, 0, 0, lw30, lw30, lw30_state, empty_init, "Brother", "Brother LW-30", MACHINE_NODEVICE_PRINTER )

View File

@ -14792,6 +14792,9 @@ apexc // 1955(?) APEXC: All-Purpose Electronic X-ray C
@source:brother/ax145.cpp
ax145 // Brother AX-145 (c) 198? Brother Industries, Ltd.
@source:brother/lw30.cpp
lw30 // Brother LW-30 (c) 1991 Brother Industries, Ltd.
@source:brother/lw840.cpp
lw840 // Brother LW-840ic (c) 1997 Brother Industries, Ltd.