casio/fp200.cpp: rewrite video section

This commit is contained in:
angelosa 2024-06-29 15:43:12 +02:00
parent f28fba1d5e
commit c5086e6851

View File

@ -1,19 +1,20 @@
// license:BSD-3-Clause // license:BSD-3-Clause
// copyright-holders:Angelo Salese // copyright-holders:Angelo Salese
// thanks-to: Takeda Toshiya
/************************************************************************************************** /**************************************************************************************************
FP-200 (c) 1982 Casio FP-200 (c) 1982 Casio
TODO: TODO:
- Identify LCDC, move to devices
(2x MSM6216-01GS-1K + 1x MSM6215-01GS-K glued together by the gate array);
- backup RAM; - backup RAM;
- cassette i/f; - cassette i/f, glued together by discrete;
- serial i/f;
- FDC (requires test program that Service manual mentions); - FDC (requires test program that Service manual mentions);
- mini plotter printer (FP-1011PL)
- graphic printer (FP-1012PR)
Notes: Notes:
- on start-up there's a "memory illegal" warning. Enter "RESET" command - on start-up there's a "memory illegal" warning. Issue a "RESET" command to initialize it.
to initialize it (thanks to Takeda Toshiya for pointing this out).
**************************************************************************************************/ **************************************************************************************************/
@ -38,6 +39,7 @@ public:
, m_rtc(*this, "rtc") , m_rtc(*this, "rtc")
, m_ioview(*this, "ioview") , m_ioview(*this, "ioview")
, m_key(*this, "KEY%X", 0U) , m_key(*this, "KEY%X", 0U)
, m_gfxrom(*this, "chargen")
{ } { }
void fp200(machine_config &config); void fp200(machine_config &config);
@ -50,26 +52,24 @@ private:
required_device<rp5c01_device> m_rtc; required_device<rp5c01_device> m_rtc;
memory_view m_ioview; memory_view m_ioview;
required_ioport_array<16> m_key; required_ioport_array<16> m_key;
required_memory_region m_gfxrom;
uint8_t *m_chargen = nullptr; uint8_t *m_chargen = nullptr;
uint8_t m_keyb_matrix = 0; uint8_t m_keyb_matrix = 0;
struct{ std::unique_ptr<uint8_t[]> m_lcd_vram[2];
uint8_t x = 0; u16 m_lcd_yoffset[2]{};
uint8_t y = 0; u16 m_lcd_address = 0;
uint8_t status = 0; u8 m_lcd_status = 0;
std::unique_ptr<uint8_t[]> vram; bool m_lcd_text_mode = false;
std::unique_ptr<uint8_t[]> attr;
}m_lcd;
uint8_t read_lcd_attr(uint16_t X, uint16_t Y);
uint8_t read_lcd_vram(uint16_t X, uint16_t Y);
void write_lcd_attr(uint16_t X, uint16_t Y,uint8_t data);
void write_lcd_vram(uint16_t X, uint16_t Y,uint8_t data);
// screen updates
uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
uint8_t lcd_r(offs_t offset); template <unsigned N> u8 lcd_data_r(offs_t offset);
void lcd_w(offs_t offset, uint8_t data); template <unsigned N> void lcd_data_w(offs_t offset, u8 data);
void lcd_map(address_map &map);
uint8_t keyb_r(offs_t offset); uint8_t keyb_r(offs_t offset);
void keyb_w(offs_t offset, uint8_t data); void keyb_w(offs_t offset, uint8_t data);
@ -89,239 +89,120 @@ private:
void fp200_state::video_start() void fp200_state::video_start()
{ {
m_lcd.vram = make_unique_clear<uint8_t[]>(20*64); m_lcd_vram[0] = make_unique_clear<uint8_t[]>(0x400);
m_lcd.attr = make_unique_clear<uint8_t[]>(20*64); m_lcd_vram[1] = make_unique_clear<uint8_t[]>(0x400);
save_pointer(NAME(m_lcd_vram[0]), 0x400);
save_pointer(NAME(m_lcd_vram[1]), 0x400);
} }
// TODO: rewrite, don't loop 4 times
uint32_t fp200_state::screen_update( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect ) uint32_t fp200_state::screen_update( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect )
{ {
uint16_t l_offs, r_offs; for(int y = cliprect.min_x; y <= cliprect.max_y; y ++)
bitmap.fill(0, cliprect);
l_offs = 0;
r_offs = 0;
for(int y = cliprect.top(); y <= cliprect.bottom(); y++)
{ {
for(int x = 0; x < 80;x++) for(int x = cliprect.min_x; x <= cliprect.max_x; x ++)
{ {
if(m_lcd.attr[x / 8 + y * 20] == 0x50) const u8 which = x < 80;
{ const u8 x_tile = x >> 3;
l_offs = (y & ~7); const u8 y_tile = y >> 3;
break; const u16 base_addr = ((x_tile % 10 + y_tile * 0x80) + m_lcd_yoffset[which]) & 0x3ff;
} const u8 xi = x & 7;
} const u8 yi = y & 7;
}
for(int y = cliprect.top(); y <= cliprect.bottom(); y++) const u8 vram = m_lcd_vram[which][(base_addr + yi * 0x10) & 0x3ff];
{ uint8_t const pix = BIT(vram, xi);
for(int x=80;x<160;x++) bitmap.pix(y, x) = pix;
{
if(m_lcd.attr[x/8+y*20] == 0x50)
{
r_offs = (y & ~7);
break;
}
}
}
for(int y = cliprect.top(); y <= cliprect.bottom(); y++)
{
for(int x=0;x<80;x++)
{
uint16_t yoffs;
yoffs = y + l_offs;
if(yoffs >= 64)
yoffs -= 64;
if(m_lcd.attr[x/8+yoffs*20] == 0x60 || m_lcd.attr[x/8+yoffs*20] == 0x50)
{
uint8_t const vram = m_lcd.vram[x/8+yoffs*20];
uint8_t const pix = ((m_chargen[vram*8+(x & 7)]) >> (7-(yoffs & 7))) & 1;
bitmap.pix(y,x) = pix;
}
/*
else if(m_lcd.attr[x/8+yoffs*20] == 0x40)
{
uint8_t const vram = m_lcd.vram[x/8+yoffs*20];
uint8_t const pix = (vram) >> (7-(yoffs & 7)) & 1;
bitmap.pix(y,x) = pix;
}*/
}
}
for(int y = cliprect.top(); y <= cliprect.bottom(); y++)
{
for(int x=80;x<160;x++)
{
uint16_t yoffs;
yoffs = y + r_offs;
if(yoffs >= 64)
yoffs -= 64;
if(m_lcd.attr[x/8+yoffs*20] == 0x60 || m_lcd.attr[x/8+yoffs*20] == 0x50)
{
uint8_t const vram = m_lcd.vram[x/8+yoffs*20];
uint8_t const pix = ((m_chargen[vram*8+(x & 7)]) >> (7-(yoffs & 7))) & 1;
bitmap.pix(y,x) = pix;
}
/*else if(m_lcd.attr[x/8+yoffs*20] == 0x40)
{
uint8_t const vram = m_lcd.vram[x/8+yoffs*20];
uint8_t const pix = (vram) >> (7-(yoffs & 7)) & 1;
bitmap.pix(y,x) = pix;
}*/
} }
} }
return 0; return 0;
} }
template <unsigned N> u8 fp200_state::lcd_data_r(offs_t offset)
{
return m_lcd_vram[N][m_lcd_address & 0x3ff];
}
template <unsigned N> void fp200_state::lcd_data_w(offs_t offset, u8 data)
{
switch (m_lcd_status)
{
// select mode
case 0xb:
if (BIT(data, 6))
{
if (BIT(data, 4))
m_lcd_yoffset[N] = m_lcd_address;
else
m_lcd_text_mode = bool(BIT(data, 5));
}
if (data & 0x8f)
logerror("Warning: LCD write with unknown mode %02x\n", data);
break;
// data write
case 0x1:
if (m_lcd_text_mode)
{
// The handling doesn't make a whole lot of sense for being practically usable ...
const u8 tile_address = bitswap<8>(data, 3, 2, 1, 0, 7, 6, 5, 4);
for (int yi = 0; yi < 8; yi ++)
{
u8 tile = 0;
for (int xi = 0; xi < 8; xi ++)
tile |= BIT(m_gfxrom->base()[tile_address * 8 + xi], 7 - yi) << xi;
m_lcd_vram[N][(m_lcd_address + 0x10 * yi) & 0x3ff] = tile;
}
}
else
m_lcd_vram[N][m_lcd_address & 0x3ff] = data;
break;
default:
logerror("Warning: LCD write with unknown status type %02x\n", m_lcd_status);
break;
}
}
/* /*
[1] DDDD DDDD vram data/attr (left half) * video section is 2x MSM6216-01GS-1K + 1x MSM6215-01GS-K glued together by the gate array
[2] DDDD DDDD vram data/attr (right half) *
[8] SSSS --YY Status code (1=vram type/0xb=attr type) / upper part of Y address * [1] DDDD DDDD vram data/mode select (right half)
[9] YYYY XXXX lower part of Y address / X address * [2] DDDD DDDD vram data/mode select (left half)
*/ * [8] SSSS ---- Status code (1=vram type/0xb=attr type)
uint8_t fp200_state::read_lcd_attr(uint16_t X, uint16_t Y) * ---- --YY upper part of Y address
* [9] YYYY XXXX lower part of Y address / X address
*/
void fp200_state::lcd_map(address_map &map)
{ {
uint16_t base_offs; map(0x1, 0x1).rw(FUNC(fp200_state::lcd_data_r<1>), FUNC(fp200_state::lcd_data_w<1>));
uint8_t res = 0; map(0x2, 0x2).rw(FUNC(fp200_state::lcd_data_r<0>), FUNC(fp200_state::lcd_data_w<0>));
map(0x8, 0x8).lrw8(
for(int yi=0;yi<8;yi++) NAME([this] (offs_t offset) {
{ u8 res = (m_lcd_status & 0xf) << 4 | (m_lcd_address & 0x300) >> 4;
base_offs = X+(Y+yi)*20; return res;
}),
if(base_offs >= 20*64) NAME([this] (offs_t offset, u8 data) {
return 0xff; m_lcd_status = (data & 0xf0) >> 4;
if (m_lcd_status == 0xb)
res = m_lcd.attr[base_offs]; {
} m_lcd_address &= 0xff;
m_lcd_address |= (data & 0x3) << 8;
return res; }
} })
);
uint8_t fp200_state::read_lcd_vram(uint16_t X, uint16_t Y) map(0x9, 0x9).lrw8(
{ NAME([this] (offs_t offset) {
uint16_t base_offs; u8 res = m_lcd_address & 0xff;
uint8_t res = 0; return res;
}),
for(int yi=0;yi<8;yi++) NAME([this] (offs_t offset, u8 data) {
{ m_lcd_address &= 0x300;
base_offs = X+(Y+yi)*20; m_lcd_address |= data;
})
if(base_offs >= 20*64) );
return 0xff;
res = m_lcd.vram[base_offs];
}
return res;
}
uint8_t fp200_state::lcd_r(offs_t offset)
{
uint8_t res;
res = 0;
switch(offset)
{
case 1:
//logerror("%d %d -> (L) %02x\n",m_lcd.x,m_lcd.y,m_lcd.status);
if(m_lcd.status == 0xb)
res = read_lcd_attr(m_lcd.x,m_lcd.y);
else if(m_lcd.status == 1)
res = read_lcd_vram(m_lcd.x,m_lcd.y);
break;
case 2:
//logerror("%d %d -> (R) %02x\n",m_lcd.x,m_lcd.y,m_lcd.status);
if(m_lcd.status == 0xb)
res = read_lcd_attr(m_lcd.x + 10,m_lcd.y);
else if(m_lcd.status == 1)
res = read_lcd_vram(m_lcd.x + 10,m_lcd.y);
break;
case 8:
res = (m_lcd.status & 0xf) << 4;
res |= (m_lcd.y & 0x30) >> 4;
break;
case 9:
res = (m_lcd.y & 0xf) << 4;
res |= (m_lcd.x & 0xf);
break;
}
return res;
}
void fp200_state::write_lcd_attr(uint16_t X, uint16_t Y,uint8_t data)
{
uint16_t base_offs;
for(int yi=0;yi<8;yi++)
{
base_offs = X+(Y+yi)*20;
if(base_offs >= 20*64)
return;
//if(data != 0x60)
// logerror("%d %d %02x\n",X,Y,data);
m_lcd.attr[base_offs] = data;
}
}
void fp200_state::write_lcd_vram(uint16_t X, uint16_t Y,uint8_t data)
{
uint16_t base_offs;
for(int yi=0;yi<8;yi++)
{
base_offs = X+(Y+yi)*20;
if(base_offs >= 20*64)
return;
m_lcd.vram[base_offs] = data;
}
}
void fp200_state::lcd_w(offs_t offset, uint8_t data)
{
switch(offset)
{
case 1:
//logerror("%d %d -> %02x (%c) (L %02x)\n",m_lcd.x,m_lcd.y,data,data,m_lcd.status);
if(m_lcd.status == 0xb)
write_lcd_attr(m_lcd.x,m_lcd.y,data);
else if(m_lcd.status == 1)
write_lcd_vram(m_lcd.x,m_lcd.y,data);
break;
case 2:
//logerror("%d %d -> %02x (%c) (R %02x)\n",m_lcd.x + 10,m_lcd.y,data,data,m_lcd.status);
if(m_lcd.status == 0xb)
write_lcd_attr(m_lcd.x + 10,m_lcd.y,data);
else if(m_lcd.status == 1)
write_lcd_vram(m_lcd.x + 10,m_lcd.y,data);
break;
case 8:
m_lcd.status = (data & 0xf0) >> 4;
if(m_lcd.status == 0x0b)
m_lcd.y = (m_lcd.y & 0xf) | ((data & 3) << 4);
break;
case 9:
m_lcd.y = (m_lcd.y & 0x30) | ((data & 0xf0) >> 4);
m_lcd.x = data & 0xf;
break;
}
} }
uint8_t fp200_state::keyb_r(offs_t offset) uint8_t fp200_state::keyb_r(offs_t offset)
@ -353,10 +234,11 @@ void fp200_state::main_io(address_map &map)
// m_ioview[0](0x20, 0x2f) AUTO-POWER OFF // m_ioview[0](0x20, 0x2f) AUTO-POWER OFF
// m_ioview[0](0x40, 0x4f) FDC Device ID Code (5 for "FP-1021FD") // m_ioview[0](0x40, 0x4f) FDC Device ID Code (5 for "FP-1021FD")
// m_ioview[0](0x80, 0xff) FDD (unknown type) // m_ioview[0](0x80, 0xff) FDD (unknown type)
m_ioview[1](0x00, 0x0f).rw(FUNC(fp200_state::lcd_r), FUNC(fp200_state::lcd_w)); m_ioview[1](0x00, 0x0f).m(*this, FUNC(fp200_state::lcd_map));
// m_ioview[1](0x10, 0x10) I/O control (w/o), D1 selects CMT or RS-232C // m_ioview[1](0x10, 0x10) I/O control (w/o), D1 selects CMT or RS-232C
// m_ioview[1](0x11, 0x11) I/O control (w/o), uPD65010G gate array control // m_ioview[1](0x11, 0x11) I/O control (w/o), uPD65010G gate array control
m_ioview[1](0x20, 0x20).r(FUNC(fp200_state::keyb_r)); // TODO: writes are undocumented, just before reads PC=35C (strobe for update?)
m_ioview[1](0x20, 0x20).r(FUNC(fp200_state::keyb_r)).nopw();
m_ioview[1](0x21, 0x21).w(FUNC(fp200_state::keyb_w)); m_ioview[1](0x21, 0x21).w(FUNC(fp200_state::keyb_w));
// m_ioview[1](0x40, 0x4f) CMT & RS-232C control // m_ioview[1](0x40, 0x4f) CMT & RS-232C control
// m_ioview[1](0x80, 0x8f) [Centronics] printer // m_ioview[1](0x80, 0x8f) [Centronics] printer
@ -364,7 +246,6 @@ void fp200_state::main_io(address_map &map)
INPUT_CHANGED_MEMBER(fp200_state::keyb_irq) INPUT_CHANGED_MEMBER(fp200_state::keyb_irq)
{ {
/* a keyboard stroke causes a rst7.5 */
m_maincpu->set_input_line(I8085_RST75_LINE, (newval) ? ASSERT_LINE : CLEAR_LINE); m_maincpu->set_input_line(I8085_RST75_LINE, (newval) ? ASSERT_LINE : CLEAR_LINE);
} }
@ -477,7 +358,8 @@ static INPUT_PORTS_START( fp200 )
PORT_START("KEYMOD") PORT_START("KEYMOD")
PORT_BIT( 0x01f, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_BIT( 0x01f, IP_ACTIVE_LOW, IPT_UNUSED )
PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CETL") PORT_TOGGLE // positional switch on keyboard
PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Basic / CETL Mode") PORT_TOGGLE
PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT)
PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BREAK") PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BREAK")
PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GRAPH") PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GRAPH")
@ -505,12 +387,6 @@ GFXDECODE_END
void fp200_state::machine_start() void fp200_state::machine_start()
{ {
uint8_t *raw_gfx = memregion("raw_gfx")->base();
m_chargen = memregion("chargen")->base();
// HACK: convert GFX to a more usable format
for(int i = 0; i < 0x800; i++)
m_chargen[i] = raw_gfx[bitswap<16>(i, 15, 14, 13, 12, 11, 6, 5, 4, 3, 10, 9, 8, 7, 2, 1, 0)];
} }
void fp200_state::machine_reset() void fp200_state::machine_reset()
@ -572,10 +448,8 @@ ROM_START( fp200 )
ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 )
ROM_LOAD( "fp200rom.bin", 0x0000, 0x8000, CRC(dba6e41b) SHA1(c694fa19172eb56585a9503997655bcf9d369c34) ) ROM_LOAD( "fp200rom.bin", 0x0000, 0x8000, CRC(dba6e41b) SHA1(c694fa19172eb56585a9503997655bcf9d369c34) )
ROM_REGION( 0x800, "raw_gfx", ROMREGION_ERASE00 )
ROM_LOAD( "chr.bin", 0x0000, 0x800, CRC(2e6501a5) SHA1(6186e25feabe6db851ee7d61dad11e182a6d3a4a) )
ROM_REGION( 0x800, "chargen", ROMREGION_ERASE00 ) ROM_REGION( 0x800, "chargen", ROMREGION_ERASE00 )
ROM_LOAD( "chr.bin", 0x0000, 0x800, CRC(2e6501a5) SHA1(6186e25feabe6db851ee7d61dad11e182a6d3a4a) )
ROM_END ROM_END
} // anonymous namespace } // anonymous namespace