Skip to content

Commit

Permalink
hitachi/bmjr.cpp: implement MP-1710 color features
Browse files Browse the repository at this point in the history
  • Loading branch information
angelosa committed Sep 17, 2024
1 parent 912e32f commit 3170abd
Showing 1 changed file with 86 additions and 48 deletions.
134 changes: 86 additions & 48 deletions src/mame/hitachi/bmjr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Basic Master Jr. (MB-6885) (c) 1982? Hitachi
TODO:
- Memory view control at $efd0;
- Color adapter (Jr specific);
- Convert MP-1710 color adapter to device (bmjr specific);
- Sound DAC;
- Keyboard eats inputs if typed relatively fast (verify, particularly with emu.keypost);
- Break key is unemulated (tied with the NMI);
Expand All @@ -18,7 +18,7 @@ Basic Master Jr. (MB-6885) (c) 1982? Hitachi
#include "cpu/m6800/m6800.h"
#include "imagedev/cassette.h"
#include "machine/timer.h"
#include "sound/beep.h"

#include "emupal.h"
#include "screen.h"
#include "speaker.h"
Expand All @@ -33,14 +33,16 @@ class bmjr_state : public driver_device
: driver_device(mconfig, type, tag)
, m_maincpu(*this, "maincpu")
, m_cass(*this, "cassette")
, m_beep(*this, "beeper")
, m_workram(*this, "work_ram")
, m_work_ram(*this, "work_ram")
, m_p_chargen(*this, "chargen")
, m_io_keyboard(*this, "KEY%d", 0U)
{ }

void bmjr(machine_config &config);

protected:
virtual void video_start() override;
virtual void video_reset() override;
private:
u8 key_r();
void key_w(u8 data);
Expand All @@ -50,11 +52,11 @@ class bmjr_state : public driver_device
void tape_w(u8 data);
u8 tape_stop_r();
u8 tape_start_r();
void xor_display_w(u8 data);
void screen_reverse_w(u8 data);
u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
void main_map(address_map &map);
bool m_tape_switch = 0;
u8 m_xor_display = 0U;
u8 m_screen_reverse = 0U;
u8 m_key_select = 0U;
u16 m_casscnt = 0U;
bool m_cassold = 0, m_cassbit = 0;
Expand All @@ -63,43 +65,58 @@ class bmjr_state : public driver_device
virtual void machine_reset() override;
required_device<cpu_device> m_maincpu;
required_device<cassette_image_device> m_cass;
required_device<beep_device> m_beep;
required_shared_ptr<u8> m_workram;
required_shared_ptr<u8> m_work_ram;
required_region_ptr<u8> m_p_chargen;
required_ioport_array<16> m_io_keyboard;

std::unique_ptr<u8[]> m_color_ram;
u8 m_color_mode = 0;
u8 m_tile_latch = 0;
u8 m_screen_mode = 0;
void mp1710_map(address_map &map);
void screen_mode_w(u8 data);
};

void bmjr_state::video_start()
{
m_color_ram = std::make_unique<u8[]>(0x300);
save_item(NAME(m_screen_mode));
save_item(NAME(m_screen_reverse));
save_item(NAME(m_color_mode));
save_pointer(NAME(m_color_ram), 0x300);
}

void bmjr_state::video_reset()
{
m_tile_latch = 0x07;
m_screen_reverse = 0;
m_screen_mode = 0;
}

u32 bmjr_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
u8 fg=4;
u16 sy=0,ma=0x100;
u8 const inv = (m_xor_display) ? 0xff : 0;
const u8 fg_shift = m_screen_reverse ? 4 : 0;
const u8 bg_shift = m_screen_reverse ? 0 : 4;
const u16 screen_bank_offset = 0x900 + ((m_screen_mode & 0xf) << 9);

for(u8 y = 0; y < 24; y++ )
// TODO: convert to scanline based renderer
for(int y = cliprect.min_y; y <= cliprect.max_y; y++ )
{
for (u8 ra = 0; ra < 8; ra++)
for (int x = cliprect.min_x; x <= cliprect.max_x; x+= 8)
{
u16 *p = &bitmap.pix(sy++);

for (u16 x = ma; x < ma + 32; x++)
const u16 tile_offset = (x >> 3) + ((y >> 3) * 32);
const u16 tile = m_work_ram[0x100 + tile_offset] << 3;
const u8 gfx_data = BIT(m_screen_mode, 7) ? m_work_ram[screen_bank_offset + (x >> 3) + (y * 32)] : m_p_chargen[tile | (y & 7)];
const u8 attr = BIT(m_color_mode, 0) ? m_color_ram[tile_offset] : 0x07;
const u8 fg_color = (attr >> fg_shift) & 7;
const u8 bg_color = (attr >> bg_shift) & 7;

for (int xi = 0; xi < 8; xi++)
{
u8 const chr = m_workram[x];
u8 const gfx = m_p_chargen[(chr<<3) | ra] ^ inv;

/* Display a scanline of a character */
*p++ = BIT(gfx, 7) ? fg : 0;
*p++ = BIT(gfx, 6) ? fg : 0;
*p++ = BIT(gfx, 5) ? fg : 0;
*p++ = BIT(gfx, 4) ? fg : 0;
*p++ = BIT(gfx, 3) ? fg : 0;
*p++ = BIT(gfx, 2) ? fg : 0;
*p++ = BIT(gfx, 1) ? fg : 0;
*p++ = BIT(gfx, 0) ? fg : 0;
const u8 pen = BIT(gfx_data, 7 - xi) ? fg_color : bg_color;
bitmap.pix(y, x + xi) = pen;
}
}
ma+=32;
}

return 0;
Expand Down Expand Up @@ -140,8 +157,7 @@ TIMER_DEVICE_CALLBACK_MEMBER( bmjr_state::kansas_r )
m_cassbit = (m_casscnt < 12) ? 1 : 0;
m_casscnt = 0;
}
else
if (m_casscnt > 32)
else if (m_casscnt > 32)
{
m_casscnt = 32;
m_cassbit = 0;
Expand All @@ -160,7 +176,7 @@ void bmjr_state::tape_w(u8 data)
if(!m_tape_switch)
{
// TODO: to five bit DAC, --xx xxx-
m_beep->set_state(!BIT(data, 7));
//m_beep->set_state(!BIT(data, 7));
}
else
{
Expand All @@ -184,9 +200,27 @@ u8 bmjr_state::tape_start_r()
return 0x01;
}

void bmjr_state::xor_display_w(u8 data)
void bmjr_state::screen_reverse_w(u8 data)
{
m_screen_reverse = data;
}

void bmjr_state::screen_mode_w(u8 data)
{
m_xor_display = data;
m_screen_mode = data;
}

void bmjr_state::mp1710_map(address_map &map)
{
map(0x00, 0x00).lrw8(
NAME([this] (offs_t offset) { return m_tile_latch; }),
NAME([this] (offs_t offset, u8 data) { m_tile_latch = data; })
);
//map(0x01, 0x01) border color?
map(0x02, 0x02).lrw8(
NAME([this] (offs_t offset) { return m_color_mode; }),
NAME([this] (offs_t offset, u8 data) { m_color_mode = data; })
);
}

void bmjr_state::main_map(address_map &map)
Expand All @@ -196,6 +230,14 @@ void bmjr_state::main_map(address_map &map)
//0x0900, 0x20ff vram, modes 0x40 / 0xc0
//0x2100, 0x38ff vram, modes 0x44 / 0xcc
map(0x0000, 0xafff).ram().share("work_ram");
// overlay for MP-1710 tile latches
map(0x0100, 0x03ff).lrw8(
NAME([this] (offs_t offset) { return m_work_ram[offset + 0x100]; }),
NAME([this] (offs_t offset, u8 data) {
m_work_ram[offset + 0x100] = data;
m_color_ram[offset] = m_tile_latch;
})
);
map(0xb000, 0xdfff).rom().region("basic", 0);
map(0xe000, 0xe7ff).rom().region("printer", 0);
// 0xe800-0xedff expansion I/O
Expand All @@ -204,17 +246,18 @@ void bmjr_state::main_map(address_map &map)
// map(0xe890, 0xe890) W MP-1710 tile color
// map(0xe891, 0xe891) W MP-1710 background color
// map(0xe892, 0xe892) W MP-1710 monochrome / color setting
map(0xe890, 0xe89f).m(*this, FUNC(bmjr_state::mp1710_map));
// 0xee00-0xefff system I/O
map(0xee00, 0xee00).r(FUNC(bmjr_state::tape_stop_r)); //R stop tape
map(0xee20, 0xee20).r(FUNC(bmjr_state::tape_start_r)); //R start tape
map(0xee40, 0xee40).w(FUNC(bmjr_state::xor_display_w)); //W Picture reverse
map(0xee40, 0xee40).w(FUNC(bmjr_state::screen_reverse_w)); //W Picture reverse
map(0xee80, 0xee80).rw(FUNC(bmjr_state::tape_r), FUNC(bmjr_state::tape_w));//RW tape input / output
map(0xeec0, 0xeec0).rw(FUNC(bmjr_state::key_r), FUNC(bmjr_state::key_w));//RW keyboard
map(0xef00, 0xef00).r(FUNC(bmjr_state::ff_r)); // R timer
map(0xef40, 0xef40).r(FUNC(bmjr_state::ff_r)); // R unknown
map(0xef80, 0xef80).r(FUNC(bmjr_state::unk_r)); // TODO: to break key
// map(0xefd0, 0xefd0) memory bank + timer control
// map(0xefe0, 0xefe0) W screen mode
map(0xefe0, 0xefe0).w(FUNC(bmjr_state::screen_mode_w));
map(0xf000, 0xffff).rom().region("monitor", 0);
}

Expand Down Expand Up @@ -343,7 +386,6 @@ GFXDECODE_END
void bmjr_state::machine_start()
{
save_item(NAME(m_tape_switch));
save_item(NAME(m_xor_display));
save_item(NAME(m_key_select));
save_item(NAME(m_casscnt));
save_item(NAME(m_cassold));
Expand All @@ -352,9 +394,8 @@ void bmjr_state::machine_start()

void bmjr_state::machine_reset()
{
m_beep->set_state(0);
//m_beep->set_state(0);
m_tape_switch = 0;
m_xor_display = 0;
m_key_select = 0;
m_cass->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR);
}
Expand All @@ -366,7 +407,10 @@ void bmjr_state::bmjr(machine_config &config)
m_maincpu->set_addrmap(AS_PROGRAM, &bmjr_state::main_map);
m_maincpu->set_vblank_int("screen", FUNC(bmjr_state::irq0_line_hold));

/* video hardware */
CASSETTE(config, m_cass);
m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
TIMER(config, "kansas_r").configure_periodic(FUNC(bmjr_state::kansas_r), attotime::from_hz(40000));

screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
screen.set_refresh_hz(60);
screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
Expand All @@ -378,14 +422,8 @@ void bmjr_state::bmjr(machine_config &config)
PALETTE(config, "palette", palette_device::BRG_3BIT);
GFXDECODE(config, "gfxdecode", "palette", gfx_bmjr);

/* Audio */
SPEAKER(config, "mono").front_center();
BEEP(config, "beeper", 1200).add_route(ALL_OUTPUTS, "mono", 0.50); // guesswork

/* Devices */
CASSETTE(config, m_cass);
m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
TIMER(config, "kansas_r").configure_periodic(FUNC(bmjr_state::kansas_r), attotime::from_hz(40000));
// TODO: DAC_5BIT_?
}

/* ROM definition */
Expand All @@ -409,4 +447,4 @@ ROM_END
// 1979 Basic Master MB-6880 (retroactively Level 1)
// 1979 Basic Master Level 2 MB-6880L2
// 1980 Basic Master Level 2 II MB-6881
COMP( 1981, bmjr, 0, 0, bmjr, bmjr, bmjr_state, empty_init, "Hitachi", "Basic Master Jr. (MB-6885)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )
COMP( 1981, bmjr, 0, 0, bmjr, bmjr, bmjr_state, empty_init, "Hitachi", "Basic Master Jr. (MB-6885)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE )

0 comments on commit 3170abd

Please sign in to comment.