ef9340: add support for double width/height characters

This commit is contained in:
hap 2020-08-17 19:16:06 +02:00
parent 548cf5c585
commit deec658426
5 changed files with 485 additions and 384 deletions

View File

@ -368,7 +368,7 @@ The C7420 Home Computer Module contains a Z80, 16K RAM and 16K ROM.
</part>
</software>
<software name="helicopt">
<software name="helicopt" supported="partial">
<description>Helicopter Rescue (Euro)</description>
<year>1983</year>
<publisher>Philips</publisher>

View File

@ -2,11 +2,21 @@
// copyright-holders:Wilbert Pol, hap
/***************************************************************************
Thomson EF9340 + EF9341 teletext graphics chips with 1KB external character ram.
Thomson EF9340 + EF9341 teletext graphics, this device is a combination of chips.
Minimal components:
- Thomson EF9340 "VIN"
- Thomson EF9341 "GEN"
- 2*1KB RAM, A for attributes, B for character codes
There's also an optional extended character memory, it can be RAM or ROM.
This is implemented with a callback. The datasheet explains how to hook up
1KB RAM, but it's possible to have more.
TODO:
- busy state (right now it is immediate)
- character width/height doubling
- internal display timing (on g7400, most of it is done externally)
- read slice from internal ROM
- window boxing
- Y zoom
@ -29,6 +39,8 @@ ef9340_1_device::ef9340_1_device(const machine_config &mconfig, const char *tag,
: device_t(mconfig, EF9340_1, tag, owner, clock)
, device_video_interface(mconfig, *this)
, m_charset(*this, "ef9340_1")
, m_write_exram(*this)
, m_read_exram(*this)
{
}
@ -47,6 +59,9 @@ const tiny_rom_entry *ef9340_1_device::device_rom_region() const
void ef9340_1_device::device_start()
{
m_write_exram.resolve_safe();
m_read_exram.resolve_safe(0xff);
// Let the screen create our temporary bitmap with the screen's dimensions
screen().register_screen_bitmap(m_tmp_bitmap);
@ -68,10 +83,10 @@ void ef9340_1_device::device_start()
m_ef9340.M = 0;
m_ef9340.blink = false;
m_ef9340.blink_prescaler = 0;
m_ef9340.h_parity = false;
memset(m_ram_a, 0, sizeof(m_ram_a));
memset(m_ram_b, 0, sizeof(m_ram_b));
memset(m_ram_b, 0, sizeof(m_ef934x_ext_char_ram));
// register our state
save_item(NAME(m_ef9341.TA));
@ -85,10 +100,10 @@ void ef9340_1_device::device_start()
save_item(NAME(m_ef9340.M));
save_item(NAME(m_ef9340.blink));
save_item(NAME(m_ef9340.blink_prescaler));
save_item(NAME(m_ef9340.h_parity));
save_item(NAME(m_ram_a));
save_item(NAME(m_ram_b));
save_item(NAME(m_ef934x_ext_char_ram));
}
@ -143,20 +158,6 @@ void ef9340_1_device::ef9340_inc_c()
}
uint16_t ef9340_1_device::external_chargen_address(uint8_t b, uint8_t slice)
{
uint8_t cc = b & 0x7f;
if ( slice & 8 )
{
// 0 0 CCE4 CCE3 CCE2 CCE1 CCE0 CCE6 CCE5 ADR0
return ( ( cc << 3 ) & 0xf8 ) | ( ( cc >> 4 ) & 0x06) | ( slice & 0x01 );
}
// CCE6 CCE5 CCE4 CCE3 CCE2 CCE1 CCE0 ADR2 ADR1 ADR0
return ( cc << 3 ) | ( slice & 0x07 );
}
void ef9340_1_device::ef9341_write( uint8_t command, uint8_t b, uint8_t data )
{
LOG("ef9341 %s write, t%s, data %02X\n", command ? "command" : "data", b ? "B" : "A", data );
@ -226,12 +227,10 @@ void ef9340_1_device::ef9341_write( uint8_t command, uint8_t b, uint8_t data )
{
uint8_t a = m_ram_a[addr];
uint8_t b = m_ram_b[addr];
uint8_t slice = ( m_ef9340.M & 0x0f ) % 10;
uint8_t slice = m_ef9340.M & 0x0f;
if ( b >= 0xa0 )
{
m_ef934x_ext_char_ram[ ( ( a & 0x80 ) << 3 ) | external_chargen_address( b, slice ) ] = m_ef9341.TA;
}
if (b >= 0xa0)
m_write_exram(a << 12 | b << 4 | slice, m_ef9341.TA);
// Increment slice number
m_ef9340.M = ( m_ef9340.M & 0xf0) | ( ( slice + 1 ) % 10 );
@ -293,13 +292,15 @@ uint8_t ef9340_1_device::ef9341_read( uint8_t command, uint8_t b )
{
uint8_t a = m_ram_a[addr];
uint8_t b = m_ram_b[addr];
uint8_t slice = ( m_ef9340.M & 0x0f ) % 10;
uint8_t slice = m_ef9340.M & 0x0f;
if ( b >= 0xa0 )
{
m_ef9341.TA = m_ef934x_ext_char_ram[ ( ( a & 0x80 ) << 3 ) | external_chargen_address( b, slice ) ];
m_ef9341.TB = 0;
}
m_ef9341.TA = 0xff;
m_ef9341.TB = 0xff;
if (b >= 0xa0)
m_ef9341.TA = m_read_exram(a << 12 | b << 4 | slice);
else
logerror("ef9341 read slice from internal\n");
// Increment slice number
m_ef9340.M = ( m_ef9340.M & 0xf0) | ( ( slice + 1 ) % 10 );
@ -329,26 +330,32 @@ void ef9340_1_device::ef9340_scanline(int vpos)
if (vpos < 0)
return;
// display automaton active at 40-290, or 32-242
int max_vpos = ( m_ef9340.R & 0x40 ) ? 250 : 210;
int slice = vpos % 10;
bool dh = false;
if (vpos == 0)
m_ef9340.h_parity = false;
if ( m_ef9340.R & 0x01 && vpos < max_vpos )
// display automaton active at 40-290, or 32-242
int max_vpos = (m_ef9340.R & 0x40) ? 250 : 210;
if (m_ef9340.R & 0x01 && vpos < max_vpos)
{
int y = vpos;
int y_row, slice;
int y_row = 0;
uint16_t char_data = 0x00;
uint8_t fg = 0;
uint8_t bg = 0;
bool del = false;
bool underline = false;
bool blank = false;
bool w_parity = false;
if ( y < 10 )
if ( vpos < 10 )
{
// Service row
if ( m_ef9340.R & 0x08 )
if (m_ef9340.R & 0x08)
{
// Service row is enabled
y_row = 31;
slice = y;
}
else
{
@ -361,101 +368,129 @@ void ef9340_1_device::ef9340_scanline(int vpos)
else
{
// Displaying regular row
y_row = ((m_ef9340.Y0 & 0x1f) + (y - 10) / 10) % 24;
slice = (y - 10) % 10;
y_row = ((m_ef9340.Y0 & 0x1f) + (vpos - 10) / 10) % 24;
}
for ( int x = 0; x < 40; x++ )
for (int x = 0; x < 40; x++)
{
uint16_t addr = ef9340_get_c_addr( x, y_row );
int s = slice;
uint16_t addr = ef9340_get_c_addr(x, y_row);
uint8_t a = m_ram_a[addr];
uint8_t b = m_ram_b[addr];
uint8_t char_data = 0x00;
bool blink = m_ef9340.R & 0x80 && m_ef9340.blink;
bool cursor = m_ef9340.R & 0x10 && x == m_ef9340.X && y_row == m_ef9340.Y;
bool invert = cursor && !blink;
bool alpha = !bool(a & 0x80);
bool dw = false;
if (alpha)
if (a & 0x80)
{
// Alphanumeric
if ( b & 0x80 )
// graphics
if ((b & 0xe0) != 0x80)
{
if ( b & 0x60 )
{
// Extension
char_data = m_ef934x_ext_char_ram[ external_chargen_address( b & 0x7f, slice ) ];
fg = a & 0x07;
}
else
{
// Deliminator
alpha = false;
blank = m_ef9340.R & 0x04 && b & 0x01;
underline = bool(b & 0x04);
char_data = 0xff;
fg = a & 0x07;
bg = a >> 4 & 0x07;
}
}
else
{
// Normal
if (slice == 9 && underline)
char_data = 0xff;
else
char_data = m_charset[((b & 0x7f) * 10) + slice];
fg = a & 0x07;
bg = a >> 4 & 0x07;
if (b & 0x80)
char_data = m_read_exram(a << 12 | b << 4 | s);
else
char_data = m_charset[((b | 0x80) * 10) + s];
}
// Inverted
if (alpha && a & 0x40)
{
invert = !invert;
blink = m_ef9340.R & 0x80 && !m_ef9340.blink;
}
// illegal
else
char_data = 0xff;
}
else
{
// Graphics
if ( b & 0x80 )
// alphanumeric
if ((b & 0xe0) != 0x80)
{
if ( b & 0x60 )
// double height
if (a & 0x10)
{
// Extension
char_data = m_ef934x_ext_char_ram[ 0x400 | external_chargen_address( b & 0x7f, slice ) ];
fg = a & 0x07;
bg = a >> 4 & 0x07;
dh = true;
if (m_ef9340.h_parity)
s += 10;
if (s > 0)
s = (s - 1) / 2;
}
fg = a & 0x07;
u16 c = 0;
if (b & 0x80)
c = m_read_exram(a << 12 | b << 4 | s);
else if (s == 9 && underline)
c = 0xff;
else
c = m_charset[((b & 0x7f) * 10) + s];
// double width
dw = bool(a & 0x20);
if (dw)
{
if (!w_parity)
char_data = bitswap<16>(c,7,7,6,6,5,5,4,4,3,3,2,2,1,1,0,0);
}
else
char_data = c;
// inverted
if (a & 0x40)
{
// Illegal
invert = !invert;
blink = m_ef9340.R & 0x80 && !m_ef9340.blink;
}
}
// deliminator
else
{
// Normal
char_data = m_charset[((b | 0x80) * 10) + slice];
fg = a & 0x07;
bg = a >> 4 & 0x07;
char_data = 0xff;
del = true;
}
}
// blink character
if (blink && !cursor && (b & 0xe0) != 0x80 && ~a & 0x08)
char_data = 0;
char_data &= ~0xff;
if (invert)
char_data ^= 0xff;
for ( int i = 0; i < 8; i++ )
if (dw)
w_parity = !w_parity;
else
w_parity = false;
for (int i = 0; i < 8; i++)
{
uint16_t d = blank ? 0 : (char_data & 1) ? fg : bg;
m_tmp_bitmap.pix16(m_offset_y + vpos, m_offset_x + x*8 + i ) = d | 8;
m_tmp_bitmap.pix16(m_offset_y + vpos, m_offset_x + x*8 + i) = d | 8;
char_data >>= 1;
}
if (del)
{
blank = m_ef9340.R & 0x04 && b & 0x01;
underline = bool(b & 0x04);
del = false;
}
}
}
// determine next h parity
if (vpos >= 10 && slice == 9)
{
if (dh)
m_ef9340.h_parity = !m_ef9340.h_parity;
else
m_ef9340.h_parity = false;
}
}

View File

@ -26,6 +26,8 @@ public:
// configuration helpers
ef9340_1_device &set_offsets(int x, int y) { m_offset_x = x; m_offset_y = y; return *this; } // when used with overlay chip
auto write_exram() { return m_write_exram.bind(); } // ADR0-ADR3 in a0-a3, B in a4-a11, A in a12-a19
auto read_exram() { return m_read_exram.bind(); } // "
ef9340_1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
@ -44,9 +46,6 @@ protected:
inline uint16_t ef9340_get_c_addr(uint8_t x, uint8_t y);
inline void ef9340_inc_c();
// Calculate the external chargen address for a character and slice
inline uint16_t external_chargen_address(uint8_t b, uint8_t slice);
void ef9340_scanline(int vpos);
/* timers */
@ -79,11 +78,14 @@ protected:
uint8_t M;
bool blink;
int blink_prescaler;
bool h_parity;
} m_ef9340;
uint8_t m_ram_a[0x400];
uint8_t m_ram_b[0x400];
uint8_t m_ef934x_ext_char_ram[0x800]; // The G7400 has 2KB of external ram hooked up. The datasheet only describes how to hookup 1KB.
devcb_write8 m_write_exram;
devcb_read8 m_read_exram;
};

View File

@ -33,9 +33,6 @@ XTAL notes (differs per model):
- G7400: 5.911MHz + 8.867MHz
TODO:
- verify odyssey3 cpu/video clocks
- odyssey sets 210 line mode in plus graphics, which cuts off the bottom part
of the screen
- backgamm doesn't draw all the sprites, what causes it? It doesn't seem like
it's a 824x bug since it does properly write data in the partial screen updates
- 824x screen resolution is not strictly defined, height(243) is correct, but
@ -48,8 +45,20 @@ TODO:
be correct
- ppp(the tetris game) does not work properly on PAL, is this homebrew NTSC-only,
or is it due to PAL video timing? The game does mid-scanline updates
- g7400 helicopt sometimes locks up at the sea level, timing related?
- g7400 probably has different video timing too (not same as g7000)
- g7400 graphics problems, mostly due to missing features in ef934x
- 4in1 and musician are not supposed to work on g7400, but work fine on MAME,
reason they shouldn't work is probably because they write to P2
- verify odyssey3 cpu/video clocks
- odyssey3 keyboard layout is not the same as g7400, but there is no software
to test the scancodes
BTANB:
- a lot of PAL games have problems on NTSC (the other way around, not so much)
- g7400 games don't look correct on odyssey3: ef934x graphics are placed lower
- Blackjack (Videopac 5) does not work on G7400
Plenty games have minor bugs not worth mentioning here.
***************************************************************************/
@ -68,6 +77,8 @@ TODO:
#include "speaker.h"
namespace {
class odyssey2_state : public driver_device
{
public:
@ -88,6 +99,8 @@ public:
void videopac(machine_config &config);
void videopacf(machine_config &config);
void odyssey2_palette(palette_device &palette) const;
protected:
required_device<i8048_device> m_maincpu;
required_device<i8244_device> m_i8244;
@ -99,7 +112,6 @@ protected:
uint8_t m_p2 = 0xff;
DECLARE_READ_LINE_MEMBER(t1_read);
void odyssey2_palette(palette_device &palette) const;
void odyssey2_io(address_map &map);
void odyssey2_mem(address_map &map);
@ -109,8 +121,8 @@ protected:
required_ioport_array<8> m_keyboard;
required_ioport_array<2> m_joysticks;
uint8_t io_read(offs_t offset);
void io_write(offs_t offset, uint8_t data);
virtual uint8_t io_read(offs_t offset);
virtual void io_write(offs_t offset, uint8_t data);
uint8_t bus_read();
void bus_write(uint8_t data);
uint8_t p1_read();
@ -128,7 +140,7 @@ public:
g7400_state(const machine_config &mconfig, device_type type, const char *tag)
: odyssey2_state(mconfig, type, tag)
, m_i8243(*this, "i8243")
, m_ef9340_1(*this, "ef9340_1")
, m_ef934x(*this, "ef934x")
{ }
void g7400(machine_config &config);
@ -137,24 +149,311 @@ public:
protected:
virtual void machine_start() override;
virtual uint8_t io_read(offs_t offset) override;
virtual void io_write(offs_t offset, uint8_t data) override;
private:
required_device<i8243_device> m_i8243;
required_device<ef9340_1_device> m_ef9340_1;
required_device<ef9340_1_device> m_ef934x;
uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
void p2_write(uint8_t data);
uint8_t io_vpp(offs_t offset, uint8_t data);
uint8_t io_read(offs_t offset);
void io_write(offs_t offset, uint8_t data);
template<int P> void i8243_port_w(uint8_t data);
void g7400_io(address_map &map);
inline offs_t ef934x_extram_address(offs_t offset);
uint8_t ef934x_extram_r(offs_t offset);
void ef934x_extram_w(offs_t offset, uint8_t data);
uint8_t m_mix_in = 0xff;
uint8_t m_mix_out = 0xff;
uint8_t m_mix_i8244 = 0xff;
uint8_t m_mix_ef934x = 0xff;
uint8_t m_ef934x_extram[0x800];
};
void odyssey2_state::machine_start()
{
memset(m_ram, 0, sizeof(m_ram));
save_item(NAME(m_ram));
save_item(NAME(m_p1));
save_item(NAME(m_p2));
}
void g7400_state::machine_start()
{
odyssey2_state::machine_start();
memset(m_ef934x_extram, 0, sizeof(m_ef934x_extram));
save_item(NAME(m_mix_i8244));
save_item(NAME(m_mix_ef934x));
save_item(NAME(m_ef934x_extram));
}
/******************************************************************************
Video
******************************************************************************/
constexpr rgb_t odyssey2_colors[] =
{
// Background,Grid Dim
{ 0x00, 0x00, 0x00 }, /* Black */ // i r g b
{ 0x79, 0x00, 0x00 }, /* Red - Calibrated To Real VideoPac */ // i R g b
{ 0x00, 0x6d, 0x07 }, /* Green - Calibrated To Real VideoPac */ // i r G b
{ 0x77, 0x67, 0x0b }, /* Khaki - Calibrated To Real VideoPac */ // i R g B
{ 0x1a, 0x37, 0xbe }, /* Blue - Calibrated To Real VideoPac */ // i r g B
{ 0x94, 0x30, 0x9f }, /* Violet - Calibrated To Real VideoPac */ // i R g B
{ 0x2a, 0xaa, 0xbe }, /* Blue-Green - Calibrated To Real VideoPac */ // i r G B
{ 0xce, 0xce, 0xce }, /* Lt Grey */ // i R G B
// Background,Grid Bright
{ 0x67, 0x67, 0x67 }, /* Grey - Calibrated To Real VideoPac */ // I R g B
{ 0xc7, 0x51, 0x51 }, /* Lt Red - Calibrated To Real VideoPac */ // I R g b
{ 0x56, 0xc4, 0x69 }, /* Lt Green - Calibrated To Real VideoPac */ // I R g B
{ 0xc6, 0xb8, 0x6a }, /* Lt Yellow - Calibrated To Real VideoPac */ // I R G b
{ 0x5c, 0x80, 0xf6 }, /* Lt Blue - Calibrated To Real VideoPac */ // I R g B
{ 0xdc, 0x84, 0xe8 }, /* Lt Violet - Calibrated To Real VideoPac */ // I R g B
{ 0x77, 0xe6, 0xeb }, /* Lt Blue-Green - Calibrated To Real VideoPac */ // I R g b
{ 0xff, 0xff, 0xff } /* White */ // I R G B
};
void odyssey2_state::odyssey2_palette(palette_device &palette) const
{
palette.set_pen_colors(0, odyssey2_colors);
}
uint32_t odyssey2_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
m_i8244->screen_update(screen, bitmap, cliprect);
u8 lum = ~m_p1 >> 4 & 0x08;
// apply external LUM setting
for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
bitmap.pix16(y, x) |= lum;
return 0;
}
uint32_t g7400_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
u8 lum = ~m_p1 >> 4 & 0x08;
bitmap_ind16 *ef934x_bitmap = m_ef934x->get_bitmap();
// apply external LUM setting
for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
{
rectangle clip = cliprect;
clip.min_y = clip.max_y = y;
m_i8244->screen_update(screen, bitmap, clip);
for (int x = clip.min_x; x <= clip.max_x; x++)
{
uint16_t d = bitmap.pix16(y, x);
uint16_t e = ef934x_bitmap->pix16(y, x);
// I outputs to CX
bool i2 = !BIT(m_mix_ef934x, e & 0x07);
m_i8244->write_cx(x, i2);
if (m_mix_i8244 == 0xff || ((e & 0x08) && BIT(m_mix_i8244, d & 0x07)))
{
// Use i8245 input
bitmap.pix16(y, x) |= lum;
}
else
{
// Use EF934x input
bitmap.pix16(y, x) = i2 ? e | 0x08 : e & 0x07;
}
}
}
return 0;
}
/******************************************************************************
I/O
******************************************************************************/
uint8_t odyssey2_state::io_read(offs_t offset)
{
u8 data = m_cart->io_read(offset);
if (!(m_p1 & 0x10) && ~offset & 0x80)
data &= m_ram[offset];
if ((m_p1 & 0x48) == 0)
data &= m_i8244->read(offset);
return data;
}
void odyssey2_state::io_write(offs_t offset, uint8_t data)
{
if (!(m_p1 & 0x40))
{
m_cart->io_write(offset, data);
if (!(m_p1 & 0x10) && ~offset & 0x80)
m_ram[offset] = data;
}
if (!(m_p1 & 0x08))
m_i8244->write(offset, data);
}
uint8_t odyssey2_state::p1_read()
{
return 0xff;
}
// 8048 ports
void odyssey2_state::p1_write(uint8_t data)
{
// LUM changed
if ((m_p1 ^ data) & 0x80)
m_screen->update_now();
m_p1 = data;
m_cart->write_p1(m_p1 & 0x13);
}
uint8_t odyssey2_state::p2_read()
{
u8 data = 0xff;
if (!(m_p1 & 0x04))
{
// 74148 priority encoder, GS to P24, outputs to P25-P27
u8 inp = count_leading_zeros(m_keyboard[m_p2 & 0x07]->read()) - 24;
if (inp < 8)
data &= inp << 5 | 0xf;
}
return data;
}
void odyssey2_state::p2_write(uint8_t data)
{
m_p2 = data;
m_cart->write_p2(m_p2 & 0x0f);
}
uint8_t odyssey2_state::bus_read()
{
u8 data = 0xff;
if (!(m_p1 & 0x04))
{
u8 sel = m_p2 & 0x07;
if (sel < 2)
data &= ~m_joysticks[sel]->read();
}
return data;
}
void odyssey2_state::bus_write(uint8_t data)
{
}
READ_LINE_MEMBER(odyssey2_state::t1_read)
{
return m_i8244->vblank() | m_i8244->hblank();
}
// G7400-specific
uint8_t g7400_state::io_read(offs_t offset)
{
u8 data = odyssey2_state::io_read(offset);
return io_vpp(offset, data);
}
void g7400_state::io_write(offs_t offset, uint8_t data)
{
odyssey2_state::io_write(offset, data);
io_vpp(offset, data);
}
uint8_t g7400_state::io_vpp(offs_t offset, uint8_t data)
{
if (!(m_p1 & 0x20))
{
// A2 to R/W pin
if (offset & 4)
data &= m_ef934x->ef9341_read( offset & 0x02, offset & 0x01 );
else
m_ef934x->ef9341_write( offset & 0x02, offset & 0x01, data );
}
return data;
}
void g7400_state::p2_write(uint8_t data)
{
odyssey2_state::p2_write(data);
m_i8243->p2_w(m_p2 & 0x0f);
}
template<int P>
void g7400_state::i8243_port_w(uint8_t data)
{
// P4,P5: color mix I8244 side (IC674)
// P6,P7: color mix EF9340 side (IC678)
u8 mask = 0xf;
if (~P & 1)
{
data <<= 4;
mask <<= 4;
}
m_screen->update_now();
if (P & 2)
m_mix_i8244 = (m_mix_i8244 & ~mask) | (data & mask);
else
m_mix_ef934x = (m_mix_ef934x & ~mask) | (data & mask);
}
// EF9341 extended RAM
offs_t g7400_state::ef934x_extram_address(offs_t offset)
{
u8 latch = (offset >> 12 & 0x80) | (offset >> 4 & 0x7f);
u16 address = (latch & 0x1f) | (offset << 9 & 0x200) | (latch << 3 & 0x400);
if (offset & 8)
return address | (latch & 0x60);
else
return address | (offset << 4 & 0x60) | (latch << 2 & 0x180);
}
uint8_t g7400_state::ef934x_extram_r(offs_t offset)
{
return m_ef934x_extram[ef934x_extram_address(offset)];
}
void g7400_state::ef934x_extram_w(offs_t offset, uint8_t data)
{
m_ef934x_extram[ef934x_extram_address(offset)] = data;
}
/******************************************************************************
Address Maps
******************************************************************************/
void odyssey2_state::odyssey2_mem(address_map &map)
{
@ -163,18 +462,16 @@ void odyssey2_state::odyssey2_mem(address_map &map)
map(0x0c00, 0x0fff).r(m_cart, FUNC(o2_cart_slot_device::read_rom0c));
}
void odyssey2_state::odyssey2_io(address_map &map)
{
map(0x00, 0xff).rw(FUNC(odyssey2_state::io_read), FUNC(odyssey2_state::io_write));
}
void g7400_state::g7400_io(address_map &map)
{
map(0x00, 0xff).rw(FUNC(g7400_state::io_read), FUNC(g7400_state::io_write));
}
/******************************************************************************
Input Ports
******************************************************************************/
static INPUT_PORTS_START( odyssey2 )
PORT_START("KEY.0")
@ -335,269 +632,14 @@ static INPUT_PORTS_START( g7400 )
PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Break") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(PAUSE))
PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Cntl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_SHIFT_2)
PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED)
PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED)
PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED)
PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED)
PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CODE(KEYCODE_ESC) PORT_CHAR(27)
INPUT_PORTS_END
constexpr rgb_t odyssey2_colors[] =
{
// Background,Grid Dim
{ 0x00, 0x00, 0x00 }, /* Black */ // i r g b
{ 0x79, 0x00, 0x00 }, /* Red - Calibrated To Real VideoPac */ // i R g b
{ 0x00, 0x6d, 0x07 }, /* Green - Calibrated To Real VideoPac */ // i r G b
{ 0x77, 0x67, 0x0b }, /* Khaki - Calibrated To Real VideoPac */ // i R g B
{ 0x1a, 0x37, 0xbe }, /* Blue - Calibrated To Real VideoPac */ // i r g B
{ 0x94, 0x30, 0x9f }, /* Violet - Calibrated To Real VideoPac */ // i R g B
{ 0x2a, 0xaa, 0xbe }, /* Blue-Green - Calibrated To Real VideoPac */ // i r G B
{ 0xce, 0xce, 0xce }, /* Lt Grey */ // i R G B
// Background,Grid Bright
{ 0x67, 0x67, 0x67 }, /* Grey - Calibrated To Real VideoPac */ // I R g B
{ 0xc7, 0x51, 0x51 }, /* Lt Red - Calibrated To Real VideoPac */ // I R g b
{ 0x56, 0xc4, 0x69 }, /* Lt Green - Calibrated To Real VideoPac */ // I R g B
{ 0xc6, 0xb8, 0x6a }, /* Lt Yellow - Calibrated To Real VideoPac */ // I R G b
{ 0x5c, 0x80, 0xf6 }, /* Lt Blue - Calibrated To Real VideoPac */ // I R g B
{ 0xdc, 0x84, 0xe8 }, /* Lt Violet - Calibrated To Real VideoPac */ // I R g B
{ 0x77, 0xe6, 0xeb }, /* Lt Blue-Green - Calibrated To Real VideoPac */ // I R g b
{ 0xff, 0xff, 0xff } /* White */ // I R G B
};
void odyssey2_state::odyssey2_palette(palette_device &palette) const
{
palette.set_pen_colors(0, odyssey2_colors);
}
void odyssey2_state::machine_start()
{
memset(m_ram, 0, sizeof(m_ram));
save_item(NAME(m_ram));
save_item(NAME(m_p1));
save_item(NAME(m_p2));
}
void g7400_state::machine_start()
{
odyssey2_state::machine_start();
save_item(NAME(m_mix_in));
save_item(NAME(m_mix_out));
}
/****** External RAM ******************************/
uint8_t odyssey2_state::io_read(offs_t offset)
{
u8 data = m_cart->io_read(offset);
if (!(m_p1 & 0x10) && ~offset & 0x80)
data &= m_ram[offset];
if ((m_p1 & 0x48) == 0)
data &= m_i8244->read(offset);
return data;
}
void odyssey2_state::io_write(offs_t offset, uint8_t data)
{
if (!(m_p1 & 0x40))
{
m_cart->io_write(offset, data);
if (!(m_p1 & 0x10) && ~offset & 0x80)
m_ram[offset] = data;
}
if (!(m_p1 & 0x08))
m_i8244->write(offset, data);
}
uint8_t g7400_state::io_vpp(offs_t offset, uint8_t data)
{
if (!(m_p1 & 0x20))
{
// A2 to R/W pin
if (offset & 4)
data &= m_ef9340_1->ef9341_read( offset & 0x02, offset & 0x01 );
else
m_ef9340_1->ef9341_write( offset & 0x02, offset & 0x01, data );
}
return data;
}
uint8_t g7400_state::io_read(offs_t offset)
{
u8 data = odyssey2_state::io_read(offset);
return io_vpp(offset, data);
}
void g7400_state::io_write(offs_t offset, uint8_t data)
{
odyssey2_state::io_write(offset, data);
io_vpp(offset, data);
}
uint32_t odyssey2_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
m_i8244->screen_update(screen, bitmap, cliprect);
u8 lum = ~m_p1 >> 4 & 0x08;
// apply external LUM setting
for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
bitmap.pix16(y, x) |= lum;
return 0;
}
uint32_t g7400_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
u8 lum = ~m_p1 >> 4 & 0x08;
bitmap_ind16 *ef934x_bitmap = m_ef9340_1->get_bitmap();
// apply external LUM setting
for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
{
rectangle clip = cliprect;
clip.min_y = clip.max_y = y;
m_i8244->screen_update(screen, bitmap, clip);
for (int x = clip.min_x; x <= clip.max_x; x++)
{
uint16_t d = bitmap.pix16(y, x);
uint16_t e = ef934x_bitmap->pix16(y, x);
// I outputs to CX
bool i2 = !BIT(m_mix_out, e & 0x07);
m_i8244->write_cx(x, i2);
if (m_mix_in == 0xff || ((e & 0x08) && BIT(m_mix_in, d & 0x07)))
{
// Use i8245 input
bitmap.pix16(y, x) |= lum;
}
else
{
// Use EF934x input
bitmap.pix16(y, x) = i2 ? e | 0x08 : e & 0x07;
}
}
}
return 0;
}
READ_LINE_MEMBER(odyssey2_state::t1_read)
{
return m_i8244->vblank() | m_i8244->hblank();
}
uint8_t odyssey2_state::p1_read()
{
return 0xff;
}
void odyssey2_state::p1_write(uint8_t data)
{
// LUM changed
if ((m_p1 ^ data) & 0x80)
m_screen->update_now();
m_p1 = data;
m_cart->write_p1(m_p1 & 0x13);
}
uint8_t odyssey2_state::p2_read()
{
u8 data = 0xff;
if (!(m_p1 & 0x04))
{
// 74148 priority encoder, GS to P24, outputs to P25-P27
u8 inp = count_leading_zeros(m_keyboard[m_p2 & 0x07]->read()) - 24;
if (inp < 8)
data &= inp << 5 | 0xf;
}
return data;
}
void odyssey2_state::p2_write(uint8_t data)
{
m_p2 = data;
m_cart->write_p2(m_p2 & 0x0f);
}
void g7400_state::p2_write(uint8_t data)
{
odyssey2_state::p2_write(data);
m_i8243->p2_w(m_p2 & 0x0f);
}
uint8_t odyssey2_state::bus_read()
{
u8 data = 0xff;
if (!(m_p1 & 0x04))
{
u8 sel = m_p2 & 0x07;
if (sel < 2)
data &= ~m_joysticks[sel]->read();
}
return data;
}
void odyssey2_state::bus_write(uint8_t data)
{
}
template<int P>
void g7400_state::i8243_port_w(uint8_t data)
{
// P4,P5: color mix out (IC674)
// P6,P7: color mix in (IC678)
u8 mask = 0xf;
if (~P & 1)
{
data <<= 4;
mask <<= 4;
}
m_screen->update_now();
if (P & 2)
m_mix_in = (m_mix_in & ~mask) | (data & mask);
else
m_mix_out = (m_mix_out & ~mask) | (data & mask);
}
/******************************************************************************
Machine Configs
******************************************************************************/
void odyssey2_state::odyssey2(machine_config &config)
{
@ -622,13 +664,14 @@ void odyssey2_state::odyssey2(machine_config &config)
PALETTE(config, "palette", FUNC(odyssey2_state::odyssey2_palette), 16);
SPEAKER(config, "mono").front_center();
I8244(config, m_i8244, XTAL(7'159'090) / 2);
m_i8244->set_screen("screen");
m_i8244->set_screen_size(356, 243);
m_i8244->irq_cb().set_inputline(m_maincpu, MCS48_INPUT_IRQ);
m_i8244->add_route(ALL_OUTPUTS, "mono", 0.40);
SPEAKER(config, "mono").front_center();
/* cartridge */
O2_CART_SLOT(config, m_cart, o2_cart, nullptr);
SOFTWARE_LIST(config, "cart_list").set_original("odyssey2");
@ -664,7 +707,7 @@ void g7400_state::g7400(machine_config &config)
/* basic machine hardware */
I8048(config, m_maincpu, XTAL(5'911'000));
m_maincpu->set_addrmap(AS_PROGRAM, &g7400_state::odyssey2_mem);
m_maincpu->set_addrmap(AS_IO, &g7400_state::g7400_io);
m_maincpu->set_addrmap(AS_IO, &g7400_state::odyssey2_io);
m_maincpu->p1_in_cb().set(FUNC(g7400_state::p1_read));
m_maincpu->p1_out_cb().set(FUNC(g7400_state::p1_write));
m_maincpu->p2_in_cb().set(FUNC(g7400_state::p2_read));
@ -689,16 +732,19 @@ void g7400_state::g7400(machine_config &config)
m_i8243->p6_out_cb().set(FUNC(g7400_state::i8243_port_w<2>));
m_i8243->p7_out_cb().set(FUNC(g7400_state::i8243_port_w<3>));
EF9340_1(config, m_ef9340_1, XTAL(8'867'000)/5 * 2, "screen");
m_ef9340_1->set_offsets(15, 5);
EF9340_1(config, m_ef934x, XTAL(8'867'000)/5 * 2, "screen");
m_ef934x->set_offsets(15, 5);
m_ef934x->read_exram().set(FUNC(g7400_state::ef934x_extram_r));
m_ef934x->write_exram().set(FUNC(g7400_state::ef934x_extram_w));
SPEAKER(config, "mono").front_center();
I8245(config, m_i8244, XTAL(8'867'000)/5 * 2);
m_i8244->set_screen("screen");
m_i8244->set_screen_size(356, 243);
m_i8244->irq_cb().set_inputline(m_maincpu, MCS48_INPUT_IRQ);
m_i8244->add_route(ALL_OUTPUTS, "mono", 0.40);
SPEAKER(config, "mono").front_center();
/* cartridge */
O2_CART_SLOT(config, m_cart, o2_cart, nullptr);
SOFTWARE_LIST(config, "cart_list").set_original("g7400");
@ -716,11 +762,21 @@ void g7400_state::odyssey3(machine_config &config)
m_i8244->irq_cb().set_inputline(m_maincpu, MCS48_INPUT_IRQ);
m_i8244->add_route(ALL_OUTPUTS, "mono", 0.40);
m_ef9340_1->set_clock(XTAL(7'159'090) / 2);
m_ef934x->set_clock(XTAL(7'159'090) / 2);
m_ef934x->set_offsets(15, 15);
m_maincpu->set_clock((XTAL(7'159'090) * 3) / 4);
// same color encoder as O2 (no RGB port)
PALETTE(config.replace(), "palette", FUNC(odyssey2_state::odyssey2_palette), 16);
}
/******************************************************************************
ROM Definitions
******************************************************************************/
ROM_START (odyssey2)
ROM_REGION(0x0400,"maincpu",0)
ROM_LOAD ("o2bios.rom", 0x0000, 0x0400, CRC(8016a315) SHA1(b2e1955d957a475de2411770452eff4ea19f4cee))
@ -752,12 +808,19 @@ ROM_START (odyssey3)
ROM_LOAD ("odyssey3.bin", 0x0000, 0x0400, CRC(e2b23324) SHA1(0a38c5f2cea929d2fe0a23e5e1a60de9155815dc))
ROM_END
} // anonymous namespace
/* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY, FULLNAME, FLAGS */
COMP( 1979, odyssey2, 0, 0, odyssey2, odyssey2, odyssey2_state, empty_init, "Magnavox", "Odyssey 2 (US)", MACHINE_SUPPORTS_SAVE )
COMP( 1978, videopac, odyssey2, 0, videopac, odyssey2, odyssey2_state, empty_init, "Philips", "Videopac G7000 (Europe)", MACHINE_SUPPORTS_SAVE )
COMP( 1979, videopacf, odyssey2, 0, videopacf, odyssey2, odyssey2_state, empty_init, "Philips", "Videopac C52 (France)", MACHINE_SUPPORTS_SAVE )
COMP( 1983, g7400, 0, 0, g7400, g7400, g7400_state, empty_init, "Philips", "Videopac+ G7400 (Europe)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1983, jopac, g7400, 0, g7400, g7400, g7400_state, empty_init, "Philips (Brandt license)", "Jopac JO7400 (France)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
COMP( 1983, odyssey3, g7400, 0, odyssey3, g7400, g7400_state, empty_init, "Magnavox", "Odyssey 3 Command Center (US, prototype)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )
/******************************************************************************
Drivers
******************************************************************************/
// YEAR NAME PARENT CMP MACHINE INPUT STATE INIT COMPANY, FULLNAME, FLAGS
COMP( 1979, odyssey2, 0, 0, odyssey2, odyssey2, odyssey2_state, empty_init, "Magnavox", "Odyssey 2 (US)", MACHINE_SUPPORTS_SAVE )
COMP( 1978, videopac, odyssey2, 0, videopac, odyssey2, odyssey2_state, empty_init, "Philips", "Videopac G7000 (Europe)", MACHINE_SUPPORTS_SAVE )
COMP( 1979, videopacf, odyssey2, 0, videopacf, odyssey2, odyssey2_state, empty_init, "Philips", "Videopac C52 (France)", MACHINE_SUPPORTS_SAVE )
COMP( 1983, g7400, 0, 0, g7400, g7400, g7400_state, empty_init, "Philips", "Videopac+ G7400 (Europe)", MACHINE_SUPPORTS_SAVE )
COMP( 1983, jopac, g7400, 0, g7400, g7400, g7400_state, empty_init, "Philips (Brandt license)", "Jopac JO7400 (France)", MACHINE_SUPPORTS_SAVE )
COMP( 1983, odyssey3, g7400, 0, odyssey3, g7400, g7400_state, empty_init, "Magnavox", "Odyssey 3 Command Center (US, prototype)", MACHINE_SUPPORTS_SAVE )

View File

@ -164,6 +164,7 @@ void intchess_state::vram_w(offs_t offset, u8 data)
}
/******************************************************************************
I/O
******************************************************************************/