mirror of
https://github.com/holub/mame
synced 2025-04-16 05:24:54 +03:00
cps2.cpp: Added TOURNAMENT board communication simulation. (#9699) [Vas Crabb, Darksoft, Gregory Lewandowski]
This commit is contained in:
parent
aadf8cb09f
commit
fe8e132efa
@ -721,6 +721,7 @@ def write_project(options, projectfile, mappings, sources):
|
||||
' MAME_DIR .. "src/lib/netlist",\n' \
|
||||
' MAME_DIR .. "3rdparty",\n' \
|
||||
' GEN_DIR .. "mame/layout",\n' \
|
||||
' ext_includedir("asio"),\n' \
|
||||
' ext_includedir("flac"),\n' \
|
||||
' ext_includedir("glm"),\n' \
|
||||
' ext_includedir("jpeg"),\n' \
|
||||
|
@ -1007,6 +1007,7 @@ function createMAMEProjects(_target, _subtarget, _name)
|
||||
GEN_DIR .. "mame/layout",
|
||||
}
|
||||
includedirs {
|
||||
ext_includedir("asio"),
|
||||
ext_includedir("flac"),
|
||||
ext_includedir("glm"),
|
||||
ext_includedir("jpeg"),
|
||||
@ -1543,6 +1544,8 @@ files {
|
||||
MAME_DIR .. "src/mame/drivers/cps1bl_pic.cpp",
|
||||
MAME_DIR .. "src/mame/drivers/kenseim.cpp",
|
||||
MAME_DIR .. "src/mame/drivers/cps2.cpp",
|
||||
MAME_DIR .. "src/mame/machine/cps2comm.cpp",
|
||||
MAME_DIR .. "src/mame/machine/cps2comm.h",
|
||||
MAME_DIR .. "src/mame/machine/cps2crypt.cpp",
|
||||
MAME_DIR .. "src/mame/machine/cps2crypt.h",
|
||||
MAME_DIR .. "src/mame/drivers/cps3.cpp",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -11,18 +11,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sound/msm5205.h"
|
||||
#include "sound/qsound.h"
|
||||
#include "sound/okim6295.h"
|
||||
#include "machine/gen_latch.h"
|
||||
#include "cpu/m68000/m68000.h"
|
||||
#include "machine/74157.h"
|
||||
#include "machine/gen_latch.h"
|
||||
#include "machine/timekpr.h"
|
||||
#include "machine/timer.h"
|
||||
#include "cpu/m68000/m68000.h"
|
||||
#include "sound/msm5205.h"
|
||||
#include "sound/okim6295.h"
|
||||
#include "sound/qsound.h"
|
||||
|
||||
#include "emupal.h"
|
||||
#include "screen.h"
|
||||
#include "tilemap.h"
|
||||
|
||||
|
||||
// Video raw params
|
||||
// measured clocks:
|
||||
// CPS2(Guru): V = 59.6376Hz, H = 15,4445kHz *H is probably measured too low!
|
||||
@ -314,109 +316,6 @@ protected:
|
||||
output_finder<3> m_led_cboard;
|
||||
};
|
||||
|
||||
class cps2_state : public cps_state
|
||||
{
|
||||
public:
|
||||
cps2_state(const machine_config &mconfig, device_type type, const char *tag)
|
||||
: cps_state(mconfig, type, tag, 2)
|
||||
, m_decrypted_opcodes(*this, "decrypted_opcodes")
|
||||
, m_region_key(*this, "key")
|
||||
, m_qsound(*this, "qsound")
|
||||
, m_objram1(*this, "objram1")
|
||||
, m_objram2(*this, "objram2")
|
||||
, m_output(*this, "output")
|
||||
, m_io_in0(*this, "IN0")
|
||||
, m_io_in1(*this, "IN1")
|
||||
, m_cps2_dial_type(0)
|
||||
, m_ecofghtr_dial_direction0(0)
|
||||
, m_ecofghtr_dial_direction1(0)
|
||||
, m_ecofghtr_dial_last0(0)
|
||||
, m_ecofghtr_dial_last1(0)
|
||||
{ }
|
||||
|
||||
void cps2(machine_config &config);
|
||||
void gigaman2(machine_config &config);
|
||||
void dead_cps2(machine_config &config);
|
||||
void init_cps2_video();
|
||||
void init_cps2();
|
||||
void init_cps2nc();
|
||||
void init_cps2crypt();
|
||||
void init_gigaman2();
|
||||
void init_ssf2tb();
|
||||
void init_pzloop2();
|
||||
void init_singbrd();
|
||||
void init_ecofghtr();
|
||||
|
||||
private:
|
||||
void init_digital_volume();
|
||||
uint16_t gigaman2_dummyqsound_r(offs_t offset);
|
||||
void gigaman2_dummyqsound_w(offs_t offset, uint16_t data);
|
||||
void gigaman2_gfx_reorder();
|
||||
void cps2_eeprom_port_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
|
||||
uint16_t cps2_qsound_volume_r();
|
||||
uint16_t kludge_r();
|
||||
uint16_t joy_or_paddle_r();
|
||||
uint16_t joy_or_paddle_ecofghtr_r();
|
||||
TIMER_DEVICE_CALLBACK_MEMBER(cps2_interrupt);
|
||||
TIMER_CALLBACK_MEMBER(cps2_update_digital_volume);
|
||||
|
||||
void cps2_objram_bank_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
|
||||
uint16_t cps2_objram1_r(offs_t offset);
|
||||
uint16_t cps2_objram2_r(offs_t offset);
|
||||
void cps2_objram1_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
|
||||
void cps2_objram2_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
|
||||
|
||||
void unshuffle(uint64_t *buf, int len);
|
||||
void cps2_gfx_decode();
|
||||
virtual void find_last_sprite() override;
|
||||
void cps2_render_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int *primasks);
|
||||
void cps2_set_sprite_priorities();
|
||||
void cps2_objram_latch();
|
||||
uint16_t *cps2_objbase();
|
||||
virtual void render_layers(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) override;
|
||||
uint32_t screen_update_cps2(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
|
||||
|
||||
DECLARE_MACHINE_START(cps2);
|
||||
virtual void video_start() override;
|
||||
|
||||
void cps2_map(address_map &map);
|
||||
void dead_cps2_map(address_map &map);
|
||||
void decrypted_opcodes_map(address_map &map);
|
||||
|
||||
optional_shared_ptr<uint16_t> m_decrypted_opcodes;
|
||||
optional_memory_region m_region_key;
|
||||
|
||||
optional_device<qsound_device> m_qsound;
|
||||
|
||||
required_shared_ptr<uint16_t> m_objram1;
|
||||
required_shared_ptr<uint16_t> m_objram2;
|
||||
required_shared_ptr<uint16_t> m_output;
|
||||
|
||||
optional_ioport m_io_in0;
|
||||
optional_ioport m_io_in1;
|
||||
|
||||
std::unique_ptr<uint16_t[]> m_cps2_buffered_obj;
|
||||
std::unique_ptr<uint16_t[]> m_gigaman2_dummyqsound_ram;
|
||||
|
||||
/* video-related */
|
||||
int m_cps2_last_sprite_offset = 0; /* Offset of the last sprite */
|
||||
int m_pri_ctrl = 0; /* Sprite layer priorities */
|
||||
int m_objram_bank = 0;
|
||||
int m_cps2_obj_size = 0;
|
||||
|
||||
/* misc */
|
||||
int m_readpaddle = 0; // pzloop2
|
||||
int m_cps2networkpresent = 0;
|
||||
int m_cps2digitalvolumelevel = 0;
|
||||
int m_cps2disabledigitalvolume = 0;
|
||||
emu_timer *m_digital_volume_timer = nullptr;
|
||||
int m_cps2_dial_type = 0;
|
||||
int m_ecofghtr_dial_direction0 = 0;
|
||||
int m_ecofghtr_dial_direction1 = 0;
|
||||
int m_ecofghtr_dial_last0 = 0;
|
||||
int m_ecofghtr_dial_last1 = 0;
|
||||
};
|
||||
|
||||
|
||||
/*----------- defined in drivers/cps1.cpp -----------*/
|
||||
|
||||
|
846
src/mame/machine/cps2comm.cpp
Normal file
846
src/mame/machine/cps2comm.cpp
Normal file
@ -0,0 +1,846 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Vas Crabb
|
||||
/***********************************************************************
|
||||
|
||||
High-level simulation of communication using the TOURNAMENT board.
|
||||
|
||||
(Thanks to Darksoft for tracing out the circuit board, and Gregory
|
||||
Lewandowski for writing a proof of concept implementation.)
|
||||
|
||||
This board has two sections, TOURNAMENT and REGISTER, but has only
|
||||
ever been seen with the TOURNAMENT section populated. There are two
|
||||
mini-DIN connectors: a black one labelled "IN" and grey one labelled
|
||||
"OUT". Each connector provides an RS-232 input and output, all of
|
||||
which are connected to a MAX232 providing level translation.
|
||||
Systems are daisy-chained, out to in, with a loopback connector on
|
||||
the IN port at the end of the chain.
|
||||
|
||||
A NEC µPD71051C USART handles serialisation/framing. The game
|
||||
always selects asynchronous mode, x16 transmit/receive clock, 8 data
|
||||
bits, no parity, and one transmitted stop bit. The transmit/receive
|
||||
clock is generated by dividing down the 8 MHz clock with a pair of
|
||||
cascaded counters counters.
|
||||
|
||||
A PAL16L8BCN is used for routing signals. It's purely combinatorial
|
||||
with the three control bits latched separately. It controls the
|
||||
transmit/receive clock, the output signals on the IN and OUT ports,
|
||||
and the RxD input to the USART. D4 controls the transmit/receive
|
||||
clock (halves the data rate when set). D1 ad D0 control how the
|
||||
serial data is routed. The equations are effectively:
|
||||
|
||||
RxCLK/TxCLK <= D4 ? Qd : Qc
|
||||
RxD <=
|
||||
Tin1 <= !D0 ? 1 : D1 ? TxD : (TxD & Rout2)
|
||||
Tin2 <= (!D1 & D0) ? Rout1 : (D1 & !D0) ? TxD : 1
|
||||
|
||||
In tabular form:
|
||||
|
||||
D1 D0 RxD Tin1 Tin2
|
||||
0 0 1 1 1
|
||||
0 1 Rout1 TxD & Rout2 Rout1
|
||||
1 0 Rout2 1 TxD
|
||||
1 1 Rout1 TxD 1
|
||||
|
||||
Mode 00 disables communication. Mode 01 connects the USART to the
|
||||
IN port, also forwarding signals between the IN and OUT ports. Mode
|
||||
10 connects the USART to the OUT port and disconnects the IN port.
|
||||
Mode 11 connects the USART to the IN port and disconnects the OUT
|
||||
port.
|
||||
|
||||
Mode 11 is used by the game while discovering its position in the
|
||||
daisy chain. Mode 10 is used during normal operation on the system
|
||||
at the head of the chain (with the loopback connector on its IN
|
||||
port). Mode 01 is used during normal operation on the remaining
|
||||
systems in the daisy chain.
|
||||
|
||||
There's no flow control or collision detection in the upstream
|
||||
direction. It relies on the fact that the games only transmit
|
||||
briefly so collisions are statistically unlikely.
|
||||
|
||||
To use this, enable communication in the Machine Configuration menu
|
||||
and use the communication settings to daisy-chain for systems. The
|
||||
system at the head of the daisy chain must have the comm_remotehost
|
||||
option set to an empty string. For example the four instances could
|
||||
be run as follows on a single machine - change the comm_remotehost
|
||||
options as necessary to connect multiple machines:
|
||||
|
||||
mame ssf2tb -comm_localhost 0.0.0.0 -comm_localport 1234 -comm_remotehost ""
|
||||
mame ssf2tb -comm_localhost 0.0.0.0 -comm_localport 1235 -comm_remotehost 127.0.0.1 -comm_remoteport 1234
|
||||
mame ssf2tb -comm_localhost 0.0.0.0 -comm_localport 1236 -comm_remotehost 127.0.0.1 -comm_remoteport 1235
|
||||
mame ssf2tb -comm_localhost "" -comm_remotehost 127.0.0.1 -comm_remoteport 1236
|
||||
|
||||
TODO:
|
||||
* When the USART's RxRDY output is asserted, CPU-IPL2 will be
|
||||
asserted (via CPSB-OBJUP). The game doesn't seem to require this
|
||||
to work.
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "cps2comm.h"
|
||||
|
||||
#include "emuopts.h"
|
||||
|
||||
#include "asio.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <locale>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
//#define VERBOSE 1
|
||||
|
||||
#include "logmacro.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
INPUT_PORTS_START(cps2comm)
|
||||
PORT_START("CFG")
|
||||
PORT_CONFNAME(0x01, 0x00, "Communication")
|
||||
PORT_CONFSETTING( 0x00, "Disable")
|
||||
PORT_CONFSETTING( 0x01, "Enable")
|
||||
INPUT_PORTS_END
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
|
||||
class cps2_comm_device::context
|
||||
{
|
||||
public:
|
||||
context(
|
||||
cps2_comm_device &device,
|
||||
std::optional<asio::ip::tcp::endpoint> const &local,
|
||||
std::optional<asio::ip::tcp::endpoint> const &remote) :
|
||||
m_device(device),
|
||||
m_acceptor(m_ioctx),
|
||||
m_in_sock(m_ioctx),
|
||||
m_out_sock(m_ioctx),
|
||||
m_connect_timeout(m_ioctx),
|
||||
m_local(local),
|
||||
m_remote(remote),
|
||||
m_route(0U),
|
||||
m_stopping(false),
|
||||
m_in_connected(false),
|
||||
m_usart_wp(0U),
|
||||
m_usart_rp(0U)
|
||||
{
|
||||
}
|
||||
|
||||
std::error_code start()
|
||||
{
|
||||
std::error_code err;
|
||||
|
||||
if (m_remote)
|
||||
{
|
||||
m_in_sock.open(m_remote->protocol(), err);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (m_local)
|
||||
{
|
||||
m_acceptor.open(m_local->protocol(), err);
|
||||
if (!err)
|
||||
m_acceptor.bind(*m_local, err);
|
||||
if (!err)
|
||||
m_acceptor.listen(1, err);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
m_thread = std::thread(
|
||||
[this] ()
|
||||
{
|
||||
if (m_local)
|
||||
start_accept();
|
||||
if (m_remote)
|
||||
start_connect();
|
||||
m_ioctx.run();
|
||||
LOG("Network thread completed\n");
|
||||
});
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
m_ioctx.post(
|
||||
[this] ()
|
||||
{
|
||||
m_stopping = true;
|
||||
std::error_code err;
|
||||
if (m_acceptor.is_open())
|
||||
m_acceptor.close(err);
|
||||
if (m_in_sock.is_open())
|
||||
m_in_sock.close(err);
|
||||
if (m_out_sock.is_open())
|
||||
m_out_sock.close(err);
|
||||
m_connect_timeout.cancel();
|
||||
});
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
void set_routing(u8 val)
|
||||
{
|
||||
m_ioctx.post(
|
||||
[this, val] ()
|
||||
{
|
||||
if (BIT(val, 0, 2) == 0U)
|
||||
{
|
||||
unsigned rp = m_usart_rp.load(std::memory_order_relaxed);
|
||||
m_usart_wp.store(rp, std::memory_order_release);
|
||||
}
|
||||
m_route = val;
|
||||
});
|
||||
}
|
||||
|
||||
bool usart_get(u8 &data)
|
||||
{
|
||||
unsigned const rp = m_usart_rp.load(std::memory_order_relaxed);
|
||||
unsigned const wp = m_usart_wp.load(std::memory_order_acquire);
|
||||
if (rp == wp)
|
||||
return false;
|
||||
data = m_usart_buf[rp];
|
||||
m_usart_rp.store((rp + 1) % m_usart_buf.size(), std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
void send_in(u8 data)
|
||||
{
|
||||
m_ioctx.post(
|
||||
[this, data] ()
|
||||
{
|
||||
if (m_in_connected)
|
||||
{
|
||||
bool const sending = !m_in_snd_buf.empty();
|
||||
m_in_snd_buf.append(&data, 1U);
|
||||
if (!sending)
|
||||
start_send_in();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void send_out(u8 data)
|
||||
{
|
||||
m_ioctx.post(
|
||||
[this, data] ()
|
||||
{
|
||||
if (m_out_sock.is_open())
|
||||
{
|
||||
bool const sending = !m_out_snd_buf.empty();
|
||||
m_out_snd_buf.append(&data, 1U);
|
||||
if (!sending)
|
||||
start_send_out();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
class buffer
|
||||
{
|
||||
public:
|
||||
std::size_t append(u8 const *src, std::size_t len)
|
||||
{
|
||||
std::size_t used = 0U;
|
||||
if (!m_full)
|
||||
{
|
||||
if (m_wp >= m_rp)
|
||||
{
|
||||
used = std::min<std::size_t>(m_buf.size() - m_wp, len);
|
||||
std::copy_n(&src[0], used, &m_buf[m_wp]);
|
||||
m_wp = (m_wp + used) % m_buf.size();
|
||||
}
|
||||
std::size_t const block = std::min<std::size_t>(len - used, m_rp - m_wp);
|
||||
if (block)
|
||||
{
|
||||
std::copy_n(&src[used], block, &m_buf[m_wp]);
|
||||
used += block;
|
||||
m_wp += block;
|
||||
}
|
||||
m_full = m_wp == m_rp;
|
||||
}
|
||||
return used;
|
||||
}
|
||||
|
||||
std::size_t consume(std::size_t len)
|
||||
{
|
||||
std::size_t const delta = std::min<std::size_t>(
|
||||
m_full ? m_buf.size() : ((m_wp + m_buf.size() - m_rp) % m_buf.size()),
|
||||
len);
|
||||
m_rp = (m_rp + delta) % m_buf.size();
|
||||
if (delta)
|
||||
m_full = false;
|
||||
return delta;
|
||||
}
|
||||
|
||||
auto content() const
|
||||
{
|
||||
return asio::buffer(
|
||||
&m_buf[m_rp],
|
||||
((m_full || (m_wp < m_rp)) ? m_buf.size() : m_wp) - m_rp);
|
||||
}
|
||||
|
||||
void clear() { m_wp = m_rp = 0U; }
|
||||
|
||||
bool empty() const { return !m_full && (m_wp == m_rp); }
|
||||
|
||||
private:
|
||||
unsigned m_wp = 0U, m_rp = 0U;
|
||||
bool m_full = false;
|
||||
std::array<u8, 128> m_buf;
|
||||
};
|
||||
|
||||
template <typename Format, typename... Params>
|
||||
void logerror(Format &&fmt, Params &&... args) const
|
||||
{
|
||||
util::stream_format(
|
||||
std::cerr,
|
||||
"[%s] %s",
|
||||
m_device.tag(),
|
||||
util::string_format(std::forward<Format>(fmt), std::forward<Params>(args)...));
|
||||
}
|
||||
|
||||
bool forwarding() const
|
||||
{
|
||||
return BIT(m_route, 0, 2) == 1U;
|
||||
}
|
||||
|
||||
void usart_queue(u8 const *src, std::size_t len)
|
||||
{
|
||||
unsigned wp = m_usart_wp.load(std::memory_order_relaxed);
|
||||
unsigned const rp = m_usart_rp.load(std::memory_order_acquire);
|
||||
if (wp >= rp)
|
||||
{
|
||||
std::size_t const block = std::min<std::size_t>(m_usart_buf.size() - wp - (rp ? 0 : 1), len);
|
||||
std::copy_n(src, block, &m_usart_buf[wp]);
|
||||
src += block;
|
||||
len -= block;
|
||||
wp = (wp + block) % m_usart_buf.size();
|
||||
}
|
||||
if (wp < rp)
|
||||
{
|
||||
std::size_t const block = std::min<std::size_t>(rp - wp - 1, len);
|
||||
std::copy_n(src, block, &m_usart_buf[wp]);
|
||||
len -= block;
|
||||
wp += block;
|
||||
}
|
||||
m_usart_wp.store(wp, std::memory_order_release);
|
||||
if (len)
|
||||
LOG("USART buffer full, dropping %u bytes\n", len);
|
||||
}
|
||||
|
||||
void start_accept()
|
||||
{
|
||||
LOG("Accepting OUT port connection on %s\n", *m_local);
|
||||
m_acceptor.async_accept(
|
||||
[this] (std::error_code const &err, asio::ip::tcp::socket sock)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
LOG("Error accepting OUT port connection: %s\n", err.message());
|
||||
if (!m_stopping)
|
||||
start_accept();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Accepted OUT port connection from %s\n", sock.remote_endpoint());
|
||||
m_out_sock = std::move(sock);
|
||||
start_receive_out();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void start_connect()
|
||||
{
|
||||
LOG("Initiating IN port connection to %s\n", *m_remote);
|
||||
m_connect_timeout.expires_after(std::chrono::seconds(10));
|
||||
m_connect_timeout.async_wait(
|
||||
[this] (std::error_code const &err)
|
||||
{
|
||||
if (!err && !m_stopping && !m_in_connected && m_in_sock.is_open())
|
||||
{
|
||||
LOG("IN port connection timed out\n");
|
||||
schedule_reconnect();
|
||||
}
|
||||
});
|
||||
m_in_sock.async_connect(
|
||||
*m_remote,
|
||||
[this] (std::error_code const &err)
|
||||
{
|
||||
m_connect_timeout.cancel();
|
||||
if (err)
|
||||
{
|
||||
LOG("IN port connection error: %s\n", err.message());
|
||||
if (!m_stopping)
|
||||
schedule_reconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("IN port connection established\n");
|
||||
m_in_connected = true;
|
||||
start_receive_in();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void schedule_reconnect()
|
||||
{
|
||||
LOG("Scheduling IN port reconnection\n");
|
||||
std::error_code err;
|
||||
if (m_in_sock.is_open())
|
||||
m_in_sock.close(err);
|
||||
m_connect_timeout.cancel();
|
||||
m_connect_timeout.expires_after(std::chrono::seconds(10));
|
||||
m_connect_timeout.async_wait(
|
||||
[this] (std::error_code const &err)
|
||||
{
|
||||
if (!err && !m_stopping)
|
||||
{
|
||||
std::error_code e;
|
||||
m_in_sock.open(m_remote->protocol(), e);
|
||||
if (e)
|
||||
{
|
||||
LOG("Error opening IN port socket: %s\n", e.message());
|
||||
schedule_reconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
start_connect();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void start_receive_in()
|
||||
{
|
||||
m_in_sock.async_read_some(
|
||||
asio::buffer(m_in_rcv_buf),
|
||||
[this] (std::error_code const &err, std::size_t length)
|
||||
{
|
||||
if (err || !length)
|
||||
{
|
||||
if (err)
|
||||
LOG("Error receiving from IN port connection: %s\n", err.message());
|
||||
else
|
||||
LOG("IN port connection lost\n");
|
||||
m_in_connected = false;
|
||||
m_in_snd_buf.clear();
|
||||
if (!m_stopping)
|
||||
schedule_reconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (BIT(m_route, 0))
|
||||
usart_queue(&m_in_rcv_buf[0], length);
|
||||
if (forwarding() && m_out_sock.is_open())
|
||||
{
|
||||
bool const sending = !m_out_snd_buf.empty();
|
||||
m_out_snd_buf.append(&m_in_rcv_buf[0], length);
|
||||
if (!sending)
|
||||
start_send_out();
|
||||
}
|
||||
start_receive_in();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void start_receive_out()
|
||||
{
|
||||
m_out_sock.async_read_some(
|
||||
asio::buffer(m_out_rcv_buf),
|
||||
[this] (std::error_code const &err, std::size_t length)
|
||||
{
|
||||
if (err || !length)
|
||||
{
|
||||
if (err)
|
||||
LOG("Error receiving from OUT port connection: %s\n", err.message());
|
||||
else
|
||||
LOG("OUT port connection lost\n");
|
||||
m_out_snd_buf.clear();
|
||||
if (!m_stopping)
|
||||
{
|
||||
std::error_code e;
|
||||
m_out_sock.close(e);
|
||||
start_accept();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (BIT(m_route, 0, 2) == 2U)
|
||||
usart_queue(&m_out_rcv_buf[0], length);
|
||||
if (forwarding() && m_in_connected)
|
||||
{
|
||||
bool const sending = !m_in_snd_buf.empty();
|
||||
m_in_snd_buf.append(&m_out_rcv_buf[0], length);
|
||||
if (!sending)
|
||||
start_send_in();
|
||||
}
|
||||
start_receive_out();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void start_send_in()
|
||||
{
|
||||
m_in_sock.async_write_some(
|
||||
m_in_snd_buf.content(),
|
||||
[this] (std::error_code const &err, std::size_t length)
|
||||
{
|
||||
m_in_snd_buf.consume(length);
|
||||
if (err)
|
||||
{
|
||||
LOG("Error sending to IN port connection: %s\n", err.message().c_str());
|
||||
m_in_connected = false;
|
||||
m_in_snd_buf.clear();
|
||||
m_in_sock.close();
|
||||
}
|
||||
else if (!m_in_snd_buf.empty())
|
||||
{
|
||||
start_send_in();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void start_send_out()
|
||||
{
|
||||
m_out_sock.async_write_some(
|
||||
m_out_snd_buf.content(),
|
||||
[this] (std::error_code const &err, std::size_t length)
|
||||
{
|
||||
m_out_snd_buf.consume(length);
|
||||
if (err)
|
||||
{
|
||||
LOG("Error sending to OUT port connection: %s\n", err.message().c_str());
|
||||
m_out_snd_buf.clear();
|
||||
m_out_sock.close();
|
||||
}
|
||||
else if (!m_out_snd_buf.empty())
|
||||
{
|
||||
start_send_out();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cps2_comm_device &m_device;
|
||||
std::thread m_thread;
|
||||
asio::io_context m_ioctx;
|
||||
asio::ip::tcp::acceptor m_acceptor;
|
||||
asio::ip::tcp::socket m_in_sock;
|
||||
asio::ip::tcp::socket m_out_sock;
|
||||
asio::steady_timer m_connect_timeout;
|
||||
std::optional<asio::ip::tcp::endpoint> const m_local;
|
||||
std::optional<asio::ip::tcp::endpoint> const m_remote;
|
||||
u8 m_route;
|
||||
bool m_stopping;
|
||||
bool m_in_connected;
|
||||
std::atomic_uint m_usart_wp;
|
||||
std::atomic_uint m_usart_rp;
|
||||
buffer m_in_snd_buf;
|
||||
buffer m_out_snd_buf;
|
||||
std::array<u8, 128> m_in_rcv_buf;
|
||||
std::array<u8, 128> m_out_rcv_buf;
|
||||
std::array<u8, 128> m_usart_buf;
|
||||
};
|
||||
|
||||
|
||||
|
||||
DEFINE_DEVICE_TYPE(CAPCOM_CPS2_COMM, cps2_comm_device, "cps2comm", "Capcom CPS-2 communication board")
|
||||
|
||||
|
||||
cps2_comm_device::cps2_comm_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
|
||||
device_t(mconfig, CAPCOM_CPS2_COMM, tag, owner, clock),
|
||||
m_config(*this, "CFG")
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
u16 cps2_comm_device::usart_data_r(offs_t offset, u16 mem_mask)
|
||||
{
|
||||
if (!BIT(m_usart_status, 1) && m_context && !machine().side_effects_disabled() && m_context->usart_get(m_usart_rx_data))
|
||||
m_usart_status |= 0x02U;
|
||||
LOG("%s: Read USART data %04X & 0x%04X\n", machine().describe_context(), m_usart_rx_data, mem_mask);
|
||||
if (ACCESSING_BITS_0_7 && !machine().side_effects_disabled())
|
||||
m_usart_status &= ~0x02U;
|
||||
return m_usart_rx_data;
|
||||
}
|
||||
|
||||
|
||||
u16 cps2_comm_device::usart_status_r(offs_t offset, u16 mem_mask)
|
||||
{
|
||||
// 0: TxRDY transmit ready
|
||||
// 1: RxRDY receive ready
|
||||
// 2: TxEMP transmit buffers empty (TxD idle)
|
||||
// 3: PE receive parity error
|
||||
// 4: OVE receive buffer overrun error
|
||||
// 5: FE framing error
|
||||
// 6: SYNC/BRK synchronisation/break detect
|
||||
// 7: DSR inverted input
|
||||
if (!BIT(m_usart_status, 1) && m_context && !machine().side_effects_disabled() && m_context->usart_get(m_usart_rx_data))
|
||||
m_usart_status |= 0x02U;
|
||||
LOG("%s: Read USART status %04X & 0x%04X\n", machine().describe_context(), m_usart_status, mem_mask);
|
||||
return m_usart_status;
|
||||
}
|
||||
|
||||
|
||||
void cps2_comm_device::usart_data_w(offs_t offset, u16 data, u16 mem_mask)
|
||||
{
|
||||
LOG("%s: Write USART data 0x%04X & 0x%04X\n", machine().describe_context(), data, mem_mask);
|
||||
if (ACCESSING_BITS_0_7 && m_context)
|
||||
{
|
||||
switch (BIT(m_route, 0, 2))
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 3:
|
||||
if (m_is_head)
|
||||
{
|
||||
m_usart_rx_data = data & 0x00ffU;
|
||||
if (BIT(m_usart_status, 1))
|
||||
m_usart_status |= 0x10U; // set OVE
|
||||
else
|
||||
m_usart_status |= 0x02U; // set RxRDY
|
||||
}
|
||||
else
|
||||
{
|
||||
m_context->send_in(data & 0x00ffU);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
m_context->send_out(data & 0x00ffU);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cps2_comm_device::usart_control_w(offs_t offset, u16 data, u16 mem_mask)
|
||||
{
|
||||
if (ACCESSING_BITS_0_7)
|
||||
{
|
||||
switch (m_usart_control_phase)
|
||||
{
|
||||
case CTRL_MODE:
|
||||
m_usart_mode = data & 0x00ffU;
|
||||
if (!BIT(m_usart_mode, 0, 2))
|
||||
{
|
||||
LOG("%s: USART mode = 0x%02X: Synchronous, %d-bit, %s Parity, %s Sync Detect, %d Sync Characters\n",
|
||||
machine().describe_context(),
|
||||
m_usart_mode,
|
||||
std::array{ 5, 6, 7, 8 }[BIT(m_usart_mode, 2, 2)],
|
||||
std::array{ "No", "Odd", "No", "Even" }[BIT(m_usart_mode, 4, 2)],
|
||||
BIT(m_usart_mode, 6) ? "External" : "Internal",
|
||||
BIT(m_usart_mode, 7) ? 1 : 2);
|
||||
m_usart_control_phase = CTRL_SYNC1;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("%s: USART mode = 0x%02X: Asynchronous, x %d Clock, %d-bit, %s Parity, %s Transmit Stop Bits\n",
|
||||
machine().describe_context(),
|
||||
m_usart_mode,
|
||||
std::array{ -1, 1, 16, 64 }[BIT(m_usart_mode, 0, 2)],
|
||||
std::array{ 5, 6, 7, 8 }[BIT(m_usart_mode, 2, 2)],
|
||||
std::array{ "No", "Odd", "No", "Even" }[BIT(m_usart_mode, 4, 2)],
|
||||
std::array{ "Illegal", "1", "1-1/2", "2" }[BIT(m_usart_mode, 6, 2)]);
|
||||
m_usart_control_phase = CTRL_COMMAND;
|
||||
m_usart_status |= 0x01U;
|
||||
}
|
||||
break;
|
||||
|
||||
case CTRL_SYNC1:
|
||||
m_usart_sync_char[0] = data & 0x00ffU;
|
||||
LOG("%s: USART sync character 1 = 0x%02X\n", machine().describe_context(), m_usart_sync_char[0]);
|
||||
if (BIT(m_usart_mode, 7))
|
||||
{
|
||||
m_usart_control_phase = CTRL_COMMAND;
|
||||
m_usart_status |= 0x01U;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_usart_control_phase = CTRL_SYNC2;
|
||||
}
|
||||
break;
|
||||
|
||||
case CTRL_SYNC2:
|
||||
m_usart_sync_char[1] = data & 0x00ffU;
|
||||
LOG("%s: USART sync character 1 = 0x%02X\n", machine().describe_context(), m_usart_sync_char[1]);
|
||||
m_usart_control_phase = CTRL_COMMAND;
|
||||
m_usart_status |= 0x01U;
|
||||
break;
|
||||
|
||||
case CTRL_COMMAND:
|
||||
LOG("%s: USART command 0x%02X%s /DTR=%u%s%s%s /RTS=%u%s%s\n",
|
||||
machine().describe_context(),
|
||||
data & 0x00ff,
|
||||
BIT(data, 0) ? " TxEN" : "",
|
||||
BIT(~data, 1),
|
||||
BIT(data, 2) ? " RxEN" : "",
|
||||
BIT(data, 3) ? " SBRK" : "",
|
||||
BIT(data, 4) ? " ECL" : "",
|
||||
BIT(~data, 5),
|
||||
BIT(data, 6) ? " SRES" : "",
|
||||
BIT(data, 7) ? " EH" : "");
|
||||
if (BIT(data, 4))
|
||||
m_usart_status &= ~0x38U;
|
||||
if (BIT(data, 6))
|
||||
{
|
||||
m_usart_control_phase = CTRL_MODE;
|
||||
m_usart_status = 0x00U;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cps2_comm_device::route_w(offs_t offset, u16 data, u16 mem_mask)
|
||||
{
|
||||
if (ACCESSING_BITS_0_7)
|
||||
{
|
||||
m_route = data & 0x00ffU;
|
||||
if (m_context)
|
||||
m_context->set_routing(m_route);
|
||||
LOG("%s: Write routing control 0x%04X: %s data rate, %s -> IN, %s -> OUT, %s -> RxD\n",
|
||||
machine().describe_context(),
|
||||
m_route,
|
||||
BIT(m_route, 4) ? "low" : "high",
|
||||
std::array{ "1", "TxD & OUT", "1", "TxD" }[BIT(m_route, 0, 2)],
|
||||
std::array{ "1", "IN", "TxD", "1" }[BIT(m_route, 0, 2)],
|
||||
std::array{ "1", "IN", "OUT", "IN" }[BIT(m_route, 0, 2)]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ioport_constructor cps2_comm_device::device_input_ports() const
|
||||
{
|
||||
return INPUT_PORTS_NAME(cps2comm);
|
||||
}
|
||||
|
||||
|
||||
void cps2_comm_device::device_start()
|
||||
{
|
||||
m_usart_mode = 0x00U;
|
||||
std::fill(std::begin(m_usart_sync_char), std::end(m_usart_sync_char), 0U);
|
||||
m_usart_rx_data = 0U;
|
||||
|
||||
m_is_head = false;
|
||||
|
||||
save_item(NAME(m_usart_control_phase));
|
||||
save_item(NAME(m_usart_mode));
|
||||
save_item(NAME(m_usart_sync_char));
|
||||
save_item(NAME(m_usart_status));
|
||||
save_item(NAME(m_usart_rx_data));
|
||||
save_item(NAME(m_route));
|
||||
}
|
||||
|
||||
|
||||
void cps2_comm_device::device_reset()
|
||||
{
|
||||
if (!m_context)
|
||||
start_comm();
|
||||
|
||||
m_usart_control_phase = CTRL_MODE;
|
||||
m_usart_status = 0x00U;
|
||||
m_route = 0x00U;
|
||||
if (m_context)
|
||||
m_context->set_routing(m_route);
|
||||
}
|
||||
|
||||
|
||||
void cps2_comm_device::device_stop()
|
||||
{
|
||||
if (m_context)
|
||||
{
|
||||
m_context->stop();
|
||||
m_context.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cps2_comm_device::start_comm()
|
||||
{
|
||||
if (!BIT(m_config->read(), 0))
|
||||
return;
|
||||
|
||||
auto const &opts = mconfig().options();
|
||||
std::error_code err;
|
||||
std::istringstream parsestr;
|
||||
parsestr.imbue(std::locale::classic());
|
||||
|
||||
std::optional<asio::ip::tcp::endpoint> local;
|
||||
if (*opts.comm_localhost())
|
||||
{
|
||||
asio::ip::address const bindaddr = asio::ip::make_address(opts.comm_localhost(), err);
|
||||
if (err)
|
||||
{
|
||||
osd_printf_error("[%s] invalid local IP address %s, disabling communication.\n", tag(), opts.comm_localhost());
|
||||
return;
|
||||
}
|
||||
|
||||
parsestr.str(opts.comm_localport());
|
||||
parsestr.seekg(0, std::ios_base::beg);
|
||||
asio::ip::port_type bindport;
|
||||
parsestr >> bindport;
|
||||
if (!parsestr || !bindport)
|
||||
{
|
||||
osd_printf_error("[%s] invalid local TCP port %s, disabling communication.\n", tag(), opts.comm_localport());
|
||||
return;
|
||||
}
|
||||
|
||||
local.emplace(bindaddr, bindport);
|
||||
}
|
||||
|
||||
std::optional<asio::ip::tcp::endpoint> remote;
|
||||
if (*opts.comm_remotehost())
|
||||
{
|
||||
asio::ip::address const connaddr = asio::ip::make_address(opts.comm_remotehost(), err);
|
||||
if (err)
|
||||
{
|
||||
osd_printf_error("[%s] invalid remote address %s, disabling communication.\n", tag(), opts.comm_remotehost());
|
||||
return;
|
||||
}
|
||||
|
||||
parsestr.str(opts.comm_remoteport());
|
||||
parsestr.seekg(0, std::ios_base::beg);
|
||||
asio::ip::port_type connport;
|
||||
parsestr >> connport;
|
||||
if (!parsestr || !connport)
|
||||
{
|
||||
osd_printf_error("[%s] invalid remote TCP port %s, disabling communication.\n", tag(), opts.comm_remoteport());
|
||||
return;
|
||||
}
|
||||
|
||||
remote.emplace(connaddr, connport);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_is_head = true;
|
||||
}
|
||||
|
||||
if (!local && !remote)
|
||||
{
|
||||
osd_printf_error("[%s] no TCP addresses configured, disabling communication.\n", tag());
|
||||
return;
|
||||
}
|
||||
|
||||
auto ctx = std::make_unique<context>(*this, local, remote);
|
||||
err = ctx->start();
|
||||
if (err)
|
||||
{
|
||||
osd_printf_error("[%s] error opening/binding sockets, disabling communication (%s).\n", tag(), err.message());
|
||||
return;
|
||||
}
|
||||
|
||||
m_context = std::move(ctx);
|
||||
}
|
58
src/mame/machine/cps2comm.h
Normal file
58
src/mame/machine/cps2comm.h
Normal file
@ -0,0 +1,58 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Vas Crabb
|
||||
#ifndef MAME_MACHINE_CPS2COMM_H
|
||||
#define MAME_MACHINE_CPS2COMM_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
class cps2_comm_device : public device_t
|
||||
{
|
||||
public:
|
||||
cps2_comm_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
|
||||
|
||||
u16 usart_data_r(offs_t offset, u16 mem_mask);
|
||||
u16 usart_status_r(offs_t offset, u16 mem_mask);
|
||||
void usart_data_w(offs_t offset, u16 data, u16 mem_mask);
|
||||
void usart_control_w(offs_t offset, u16 data, u16 mem_mask);
|
||||
void route_w(offs_t offset, u16 data, u16 mem_mask);
|
||||
|
||||
bool comm_enabled() const { return bool(m_context); }
|
||||
|
||||
protected:
|
||||
enum
|
||||
{
|
||||
CTRL_MODE,
|
||||
CTRL_SYNC1,
|
||||
CTRL_SYNC2,
|
||||
CTRL_COMMAND
|
||||
};
|
||||
|
||||
class context;
|
||||
|
||||
virtual ioport_constructor device_input_ports() const override ATTR_COLD;
|
||||
virtual void device_start() override ATTR_COLD;
|
||||
virtual void device_stop() override ATTR_COLD;
|
||||
virtual void device_reset() override ATTR_COLD;
|
||||
|
||||
void start_comm() ATTR_COLD;
|
||||
|
||||
std::unique_ptr<context> m_context;
|
||||
required_ioport m_config;
|
||||
|
||||
bool m_is_head;
|
||||
|
||||
u8 m_usart_control_phase;
|
||||
u8 m_usart_mode;
|
||||
u8 m_usart_sync_char[2];
|
||||
u8 m_usart_status;
|
||||
u8 m_usart_rx_data;
|
||||
|
||||
u8 m_route;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(CAPCOM_CPS2_COMM, cps2_comm_device)
|
||||
|
||||
#endif // MAME_MACHINE_CPS2COMM_H
|
@ -113,18 +113,22 @@ the decryption keys.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "cpu/m68000/m68000.h"
|
||||
#include "ui/uimain.h"
|
||||
#include "includes/cps1.h"
|
||||
|
||||
#include "cpu/m68000/m68000.h"
|
||||
|
||||
#include "ui/uimain.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static const int fn1_groupA[8] = { 10, 4, 6, 7, 2, 13, 15, 14 };
|
||||
static const int fn1_groupB[8] = { 0, 1, 3, 5, 8, 9, 11, 12 };
|
||||
const int fn1_groupA[8] = { 10, 4, 6, 7, 2, 13, 15, 14 };
|
||||
const int fn1_groupB[8] = { 0, 1, 3, 5, 8, 9, 11, 12 };
|
||||
|
||||
static const int fn2_groupA[8] = { 6, 0, 2, 13, 1, 4, 14, 7 };
|
||||
static const int fn2_groupB[8] = { 3, 5, 9, 10, 8, 15, 12, 11 };
|
||||
const int fn2_groupA[8] = { 6, 0, 2, 13, 1, 4, 14, 7 };
|
||||
const int fn2_groupB[8] = { 3, 5, 9, 10, 8, 15, 12, 11 };
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
@ -134,6 +138,19 @@ static const int fn2_groupB[8] = { 3, 5, 9, 10, 8, 15, 12, 11 };
|
||||
|
||||
struct sbox
|
||||
{
|
||||
int extract_inputs(uint32_t val) const
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
if (inputs[i] >= 0)
|
||||
res |= BIT(val, inputs[i]) << i;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const uint8_t table[64];
|
||||
const int inputs[6]; // positions of the inputs bits, -1 means no input except from key
|
||||
const int outputs[2]; // positions of the output bits
|
||||
@ -142,14 +159,40 @@ struct sbox
|
||||
// the above struct better defines how the hardware works, however
|
||||
// to speed up the decryption at run time we convert it to the
|
||||
// following one
|
||||
struct optimised_sbox
|
||||
class optimised_sbox
|
||||
{
|
||||
public:
|
||||
void optimise(sbox const &in)
|
||||
{
|
||||
// precalculate the input lookup
|
||||
for (int i = 0; i < 256; ++i)
|
||||
input_lookup[i] = in.extract_inputs(i);
|
||||
|
||||
// precalculate the output masks
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
int const o = in.table[i];
|
||||
|
||||
output[i] = 0;
|
||||
if (o & 1)
|
||||
output[i] |= 1 << in.outputs[0];
|
||||
if (o & 2)
|
||||
output[i] |= 1 << in.outputs[1];
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t fn(uint8_t in, uint32_t key) const
|
||||
{
|
||||
return output[input_lookup[in] ^ (key & 0x3f)];
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t input_lookup[256];
|
||||
uint8_t output[64];
|
||||
};
|
||||
|
||||
|
||||
static const struct sbox fn1_r1_boxes[4] =
|
||||
const sbox fn1_r1_boxes[4] =
|
||||
{
|
||||
{ // subkey bits 0- 5
|
||||
{
|
||||
@ -185,7 +228,7 @@ static const struct sbox fn1_r1_boxes[4] =
|
||||
},
|
||||
};
|
||||
|
||||
static const struct sbox fn1_r2_boxes[4] =
|
||||
const sbox fn1_r2_boxes[4] =
|
||||
{
|
||||
{ // subkey bits 24-29
|
||||
{
|
||||
@ -221,7 +264,7 @@ static const struct sbox fn1_r2_boxes[4] =
|
||||
},
|
||||
};
|
||||
|
||||
static const struct sbox fn1_r3_boxes[4] =
|
||||
const sbox fn1_r3_boxes[4] =
|
||||
{
|
||||
{ // subkey bits 48-53
|
||||
{
|
||||
@ -257,7 +300,7 @@ static const struct sbox fn1_r3_boxes[4] =
|
||||
},
|
||||
};
|
||||
|
||||
static const struct sbox fn1_r4_boxes[4] =
|
||||
const sbox fn1_r4_boxes[4] =
|
||||
{
|
||||
{ // subkey bits 72-77
|
||||
{
|
||||
@ -295,7 +338,7 @@ static const struct sbox fn1_r4_boxes[4] =
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static const struct sbox fn2_r1_boxes[4] =
|
||||
const sbox fn2_r1_boxes[4] =
|
||||
{
|
||||
{ // subkey bits 0- 5
|
||||
{
|
||||
@ -331,7 +374,7 @@ static const struct sbox fn2_r1_boxes[4] =
|
||||
},
|
||||
};
|
||||
|
||||
static const struct sbox fn2_r2_boxes[4] =
|
||||
const sbox fn2_r2_boxes[4] =
|
||||
{
|
||||
{ // subkey bits 24-29
|
||||
{
|
||||
@ -367,7 +410,7 @@ static const struct sbox fn2_r2_boxes[4] =
|
||||
},
|
||||
};
|
||||
|
||||
static const struct sbox fn2_r3_boxes[4] =
|
||||
const sbox fn2_r3_boxes[4] =
|
||||
{
|
||||
{ // subkey bits 48-53
|
||||
{
|
||||
@ -403,7 +446,7 @@ static const struct sbox fn2_r3_boxes[4] =
|
||||
},
|
||||
};
|
||||
|
||||
static const struct sbox fn2_r4_boxes[4] =
|
||||
const sbox fn2_r4_boxes[4] =
|
||||
{
|
||||
{ // subkey bits 72-77
|
||||
{
|
||||
@ -442,25 +485,20 @@ static const struct sbox fn2_r4_boxes[4] =
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
static uint8_t fn(uint8_t in, const struct optimised_sbox *sboxes, uint32_t key)
|
||||
uint8_t fn(uint8_t in, const optimised_sbox *sboxes, uint32_t key)
|
||||
{
|
||||
const struct optimised_sbox *sbox1 = &sboxes[0];
|
||||
const struct optimised_sbox *sbox2 = &sboxes[1];
|
||||
const struct optimised_sbox *sbox3 = &sboxes[2];
|
||||
const struct optimised_sbox *sbox4 = &sboxes[3];
|
||||
|
||||
return
|
||||
sbox1->output[sbox1->input_lookup[in] ^ ((key >> 0) & 0x3f)] |
|
||||
sbox2->output[sbox2->input_lookup[in] ^ ((key >> 6) & 0x3f)] |
|
||||
sbox3->output[sbox3->input_lookup[in] ^ ((key >> 12) & 0x3f)] |
|
||||
sbox4->output[sbox4->input_lookup[in] ^ ((key >> 18) & 0x3f)];
|
||||
sboxes[0].fn(in, key >> 0) |
|
||||
sboxes[1].fn(in, key >> 6) |
|
||||
sboxes[2].fn(in, key >> 12) |
|
||||
sboxes[3].fn(in, key >> 18);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// srckey is the 64-bit master key (2x32 bits)
|
||||
// dstkey will contain the 96-bit key for the 1st FN (4x24 bits)
|
||||
static void expand_1st_key(uint32_t *dstkey, const uint32_t *srckey)
|
||||
void expand_1st_key(uint32_t *dstkey, const uint32_t *srckey)
|
||||
{
|
||||
static const int bits[96] =
|
||||
{
|
||||
@ -474,28 +512,27 @@ static void expand_1st_key(uint32_t *dstkey, const uint32_t *srckey)
|
||||
20, 8, 55, 54, 59, 60,
|
||||
27, 33, 35, 18, 8, 15,
|
||||
63, 1, 50, 44, 16, 46,
|
||||
5, 4, 45, 51, 38, 25,
|
||||
5, 4, 45, 51, 38, 25,
|
||||
13, 11, 62, 29, 48, 2,
|
||||
59, 61, 62, 56, 51, 57,
|
||||
54, 9, 24, 63, 22, 7,
|
||||
26, 42, 45, 40, 23, 14,
|
||||
2, 31, 52, 28, 44, 17,
|
||||
2, 31, 52, 28, 44, 17,
|
||||
};
|
||||
int i;
|
||||
|
||||
dstkey[0] = 0;
|
||||
dstkey[1] = 0;
|
||||
dstkey[2] = 0;
|
||||
dstkey[3] = 0;
|
||||
|
||||
for (i = 0; i < 96; ++i)
|
||||
for (int i = 0; i < 96; ++i)
|
||||
dstkey[i / 24] |= BIT(srckey[bits[i] / 32], bits[i] % 32) << (i % 24);
|
||||
}
|
||||
|
||||
|
||||
// srckey is the 64-bit master key (2x32 bits) XORed with the subkey
|
||||
// dstkey will contain the 96-bit key for the 2nd FN (4x24 bits)
|
||||
static void expand_2nd_key(uint32_t *dstkey, const uint32_t *srckey)
|
||||
void expand_2nd_key(uint32_t *dstkey, const uint32_t *srckey)
|
||||
{
|
||||
static const int bits[96] =
|
||||
{
|
||||
@ -532,14 +569,14 @@ static void expand_2nd_key(uint32_t *dstkey, const uint32_t *srckey)
|
||||
// seed is the 16-bit seed generated by the first FN
|
||||
// subkey will contain the 64-bit key to be XORed with the master key
|
||||
// for the 2nd FN (2x32 bits)
|
||||
static void expand_subkey(uint32_t* subkey, uint16_t seed)
|
||||
void expand_subkey(uint32_t* subkey, uint16_t seed)
|
||||
{
|
||||
// Note that each row of the table is a permutation of the seed bits.
|
||||
static const int bits[64] =
|
||||
{
|
||||
5, 10, 14, 9, 4, 0, 15, 6, 1, 8, 3, 2, 12, 7, 13, 11,
|
||||
5, 12, 7, 2, 13, 11, 9, 14, 4, 1, 6, 10, 8, 0, 15, 3,
|
||||
4, 10, 2, 0, 6, 9, 12, 1, 11, 7, 15, 8, 13, 5, 14, 3,
|
||||
5, 10, 14, 9, 4, 0, 15, 6, 1, 8, 3, 2, 12, 7, 13, 11,
|
||||
5, 12, 7, 2, 13, 11, 9, 14, 4, 1, 6, 10, 8, 0, 15, 3,
|
||||
4, 10, 2, 0, 6, 9, 12, 1, 11, 7, 15, 8, 13, 5, 14, 3,
|
||||
14, 11, 12, 7, 4, 5, 2, 10, 1, 15, 0, 9, 8, 6, 13, 3,
|
||||
};
|
||||
int i;
|
||||
@ -553,8 +590,8 @@ static void expand_subkey(uint32_t* subkey, uint16_t seed)
|
||||
|
||||
|
||||
|
||||
static uint16_t feistel(uint16_t val, const int *bitsA, const int *bitsB,
|
||||
const struct optimised_sbox* boxes1, const struct optimised_sbox* boxes2, const struct optimised_sbox* boxes3, const struct optimised_sbox* boxes4,
|
||||
uint16_t feistel(uint16_t val, const int *bitsA, const int *bitsB,
|
||||
const optimised_sbox* boxes1, const optimised_sbox* boxes2, const optimised_sbox* boxes3, const optimised_sbox* boxes4,
|
||||
uint32_t key1, uint32_t key2, uint32_t key3, uint32_t key4)
|
||||
{
|
||||
uint8_t l = bitswap<8>(val, bitsB[7],bitsB[6],bitsB[5],bitsB[4],bitsB[3],bitsB[2],bitsB[1],bitsB[0]);
|
||||
@ -586,63 +623,24 @@ static uint16_t feistel(uint16_t val, const int *bitsA, const int *bitsB,
|
||||
|
||||
|
||||
|
||||
static int extract_inputs(uint32_t val, const int *inputs)
|
||||
void optimise_sboxes(optimised_sbox* out, const sbox* in)
|
||||
{
|
||||
int i;
|
||||
int res = 0;
|
||||
|
||||
for (i = 0; i < 6; ++i)
|
||||
{
|
||||
if (inputs[i] != -1)
|
||||
res |= BIT(val, inputs[i]) << i;
|
||||
}
|
||||
|
||||
return res;
|
||||
for (int box = 0; box < 4; ++box)
|
||||
out[box].optimise(in[box]);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
static void optimise_sboxes(struct optimised_sbox* out, const struct sbox* in)
|
||||
void cps2_decrypt(running_machine &machine, uint16_t *rom, uint16_t *dec, int length, const uint32_t *master_key, uint32_t lower_limit, uint32_t upper_limit)
|
||||
{
|
||||
int box;
|
||||
|
||||
for (box = 0; box < 4; ++box)
|
||||
{
|
||||
int i;
|
||||
|
||||
// precalculate the input lookup
|
||||
for (i = 0; i < 256; ++i)
|
||||
{
|
||||
out[box].input_lookup[i] = extract_inputs(i, in[box].inputs);
|
||||
}
|
||||
|
||||
// precalculate the output masks
|
||||
for (i = 0; i < 64; ++i)
|
||||
{
|
||||
int o = in[box].table[i];
|
||||
|
||||
out[box].output[i] = 0;
|
||||
if (o & 1)
|
||||
out[box].output[i] |= 1 << in[box].outputs[0];
|
||||
if (o & 2)
|
||||
out[box].output[i] |= 1 << in[box].outputs[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void cps2_decrypt(running_machine &machine, uint16_t *rom, uint16_t *dec, int length, const uint32_t *master_key, uint32_t lower_limit, uint32_t upper_limit)
|
||||
{
|
||||
int i;
|
||||
uint32_t key1[4];
|
||||
struct optimised_sbox sboxes1[4*4];
|
||||
struct optimised_sbox sboxes2[4*4];
|
||||
|
||||
optimised_sbox sboxes1[4*4];
|
||||
optimise_sboxes(&sboxes1[0*4], fn1_r1_boxes);
|
||||
optimise_sboxes(&sboxes1[1*4], fn1_r2_boxes);
|
||||
optimise_sboxes(&sboxes1[2*4], fn1_r3_boxes);
|
||||
optimise_sboxes(&sboxes1[3*4], fn1_r4_boxes);
|
||||
|
||||
optimised_sbox sboxes2[4*4];
|
||||
optimise_sboxes(&sboxes2[0*4], fn2_r1_boxes);
|
||||
optimise_sboxes(&sboxes2[1*4], fn2_r2_boxes);
|
||||
optimise_sboxes(&sboxes2[2*4], fn2_r3_boxes);
|
||||
@ -650,6 +648,7 @@ static void cps2_decrypt(running_machine &machine, uint16_t *rom, uint16_t *dec,
|
||||
|
||||
|
||||
// expand master key to 1st FN 96-bit key
|
||||
uint32_t key1[4];
|
||||
expand_1st_key(key1, master_key);
|
||||
|
||||
// add extra bits for s-boxes with less than 6 inputs
|
||||
@ -661,28 +660,24 @@ static void cps2_decrypt(running_machine &machine, uint16_t *rom, uint16_t *dec,
|
||||
key1[2] ^= BIT(key1[2], 1) << 5;
|
||||
key1[2] ^= BIT(key1[2], 8) << 11;
|
||||
|
||||
for (i = 0; i < 0x10000; ++i)
|
||||
for (int i = 0; i < 0x10000; ++i)
|
||||
{
|
||||
int a;
|
||||
uint16_t seed;
|
||||
uint32_t subkey[2];
|
||||
uint32_t key2[4];
|
||||
|
||||
if ((i & 0xff) == 0)
|
||||
{
|
||||
char loadingMessage[256]; // for displaying with UI
|
||||
sprintf(loadingMessage, "Decrypting %d%%", i*100/0x10000);
|
||||
machine.ui().set_startup_text(loadingMessage,false);
|
||||
machine.ui().set_startup_text(loadingMessage, false);
|
||||
}
|
||||
|
||||
|
||||
// pass the address through FN1
|
||||
seed = feistel(i, fn1_groupA, fn1_groupB,
|
||||
uint16_t const seed = feistel(i, fn1_groupA, fn1_groupB,
|
||||
&sboxes1[0*4], &sboxes1[1*4], &sboxes1[2*4], &sboxes1[3*4],
|
||||
key1[0], key1[1], key1[2], key1[3]);
|
||||
|
||||
|
||||
// expand the result to 64-bit
|
||||
uint32_t subkey[2];
|
||||
expand_subkey(subkey, seed);
|
||||
|
||||
// XOR with the master key
|
||||
@ -690,6 +685,7 @@ static void cps2_decrypt(running_machine &machine, uint16_t *rom, uint16_t *dec,
|
||||
subkey[1] ^= master_key[1];
|
||||
|
||||
// expand key to 2nd FN 96-bit key
|
||||
uint32_t key2[4];
|
||||
expand_2nd_key(key2, subkey);
|
||||
|
||||
// add extra bits for s-boxes with less than 6 inputs
|
||||
@ -704,7 +700,7 @@ static void cps2_decrypt(running_machine &machine, uint16_t *rom, uint16_t *dec,
|
||||
|
||||
|
||||
// decrypt the opcodes
|
||||
for (a = i; a < length/2; a += 0x10000)
|
||||
for (int a = i; a < length/2; a += 0x10000)
|
||||
{
|
||||
if (a >= lower_limit && a <= upper_limit)
|
||||
{
|
||||
@ -719,66 +715,3 @@ static void cps2_decrypt(running_machine &machine, uint16_t *rom, uint16_t *dec,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct game_keys
|
||||
{
|
||||
const char *name; /* game driver name */
|
||||
const uint32_t keys[2];
|
||||
uint32_t upper_limit;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cps2_state::init_cps2crypt()
|
||||
{
|
||||
if (m_region_key)
|
||||
{
|
||||
unsigned short decoded[10] = { 0 };
|
||||
for (int b = 0; b < 10 * 16; b++)
|
||||
{
|
||||
int bit = (317 - b) % 160;
|
||||
if ((m_region_key->base()[bit / 8] >> ((bit ^ 7) % 8)) & 1)
|
||||
{
|
||||
decoded[b / 16] |= (0x8000 >> (b % 16));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t key[2] = { ((uint32_t)decoded[0] << 16) | decoded[1], ((uint32_t)decoded[2] << 16) | decoded[3] };
|
||||
// decoded[4] == watchdog instruction third word
|
||||
// decoded[5] == watchdog instruction second word
|
||||
// decoded[6] == watchdog instruction first word
|
||||
// decoded[7] == 0x4000 (bits 8 to 23 of CPS2 object output address)
|
||||
// decoded[8] == 0x0900
|
||||
|
||||
uint32_t lower, upper;
|
||||
if (decoded[9] == 0xffff)
|
||||
{
|
||||
// On a dead board, the only encrypted range is actually FF0000-FFFFFF.
|
||||
// It doesn't start from 0, and it's the upper half of a 128kB bank.
|
||||
upper = 0xffffff;
|
||||
lower = 0xff0000;
|
||||
}
|
||||
else
|
||||
{
|
||||
upper = (((~decoded[9] & 0x3ff) << 14) | 0x3fff) + 1;
|
||||
lower = 0;
|
||||
}
|
||||
|
||||
logerror("cps2 decrypt 0x%08x,0x%08x,0x%08x,0x%08x\n", key[0], key[1], lower, upper);
|
||||
|
||||
// we have a proper key so use it to decrypt
|
||||
cps2_decrypt(machine(), (uint16_t *)memregion("maincpu")->base(), m_decrypted_opcodes, memregion("maincpu")->bytes(), key, lower / 2, upper / 2);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,15 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Paul Leaman, Andreas Naive, Nicola Salmoria,Charles MacDonald
|
||||
/******************************************************************************
|
||||
|
||||
CPS-2 Encryption
|
||||
|
||||
*/
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef MAME_MACHINE_CPS2CRYPT_H
|
||||
#define MAME_MACHINE_CPS2CRYPT_H
|
||||
|
||||
#pragma once
|
||||
|
||||
void cps2_decrypt(running_machine &machine, uint16_t *rom, uint16_t *dec, int length, const uint32_t *master_key, uint32_t lower_limit, uint32_t upper_limit);
|
||||
|
||||
#endif // MAME_MACHINE_CPS2CRYPT_H
|
||||
|
@ -2019,12 +2019,6 @@ static const struct CPS1config cps1_config_table[]=
|
||||
CPS1 VIDEO RENDERER
|
||||
|
||||
*/
|
||||
#define CPS2_OBJ_BASE 0x00 /* Unknown (not base address of objects). Could be bass address of bank used when object swap bit set? */
|
||||
#define CPS2_OBJ_UK1 0x02 /* Unknown (nearly always 0x807d, or 0x808e when screen flipped) */
|
||||
#define CPS2_OBJ_PRI 0x04 /* Layers priorities */
|
||||
#define CPS2_OBJ_UK2 0x06 /* Unknown (usually 0x0000, 0x1101 in ssf2, 0x0001 in 19XX) */
|
||||
#define CPS2_OBJ_XOFFS 0x08 /* X offset (usually 0x0040) */
|
||||
#define CPS2_OBJ_YOFFS 0x0a /* Y offset (always 0x0010) */
|
||||
|
||||
|
||||
MACHINE_RESET_MEMBER(cps_state,cps)
|
||||
@ -2233,41 +2227,6 @@ void cps_state::cps1_cps_b_w(offs_t offset, uint16_t data, uint16_t mem_mask)
|
||||
}
|
||||
|
||||
|
||||
void cps2_state::unshuffle( uint64_t *buf, int len )
|
||||
{
|
||||
int i;
|
||||
uint64_t t;
|
||||
|
||||
if (len == 2)
|
||||
return;
|
||||
|
||||
assert(len % 4 == 0); /* must not happen */
|
||||
|
||||
len /= 2;
|
||||
|
||||
unshuffle(buf, len);
|
||||
unshuffle(buf + len, len);
|
||||
|
||||
for (i = 0; i < len / 2; i++)
|
||||
{
|
||||
t = buf[len / 2 + i];
|
||||
buf[len / 2 + i] = buf[len + i];
|
||||
buf[len + i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cps2_state::cps2_gfx_decode()
|
||||
{
|
||||
const int banksize = 0x200000;
|
||||
int size = memregion("gfx")->bytes();
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i += banksize)
|
||||
unshuffle((uint64_t *)(memregion("gfx")->base() + i), banksize / 8);
|
||||
}
|
||||
|
||||
|
||||
void cps_state::init_cps1()
|
||||
{
|
||||
m_scanline1 = 0;
|
||||
@ -2277,21 +2236,6 @@ void cps_state::init_cps1()
|
||||
}
|
||||
|
||||
|
||||
|
||||
void cps2_state::init_cps2_video()
|
||||
{
|
||||
cps2_gfx_decode();
|
||||
|
||||
m_scanline1 = 262;
|
||||
m_scanline2 = 262;
|
||||
m_scancalls = 0;
|
||||
m_last_sprite_offset = 0;
|
||||
m_cps2_last_sprite_offset = 0;
|
||||
m_pri_ctrl = 0;
|
||||
m_objram_bank = 0;
|
||||
}
|
||||
|
||||
|
||||
void cps_state::cps1_get_video_base()
|
||||
{
|
||||
int layercontrol=0, videocontrol=0, scroll1xoff=0, scroll2xoff=0, scroll3xoff=0;
|
||||
@ -2672,22 +2616,6 @@ void cps_state::video_start()
|
||||
machine().save().register_postload(save_prepost_delegate(FUNC(cps_state::cps1_get_video_base), this));
|
||||
}
|
||||
|
||||
void cps2_state::video_start()
|
||||
{
|
||||
cps_state::video_start();
|
||||
|
||||
m_cps2_obj_size = 0x2000;
|
||||
m_cps2_buffered_obj = make_unique_clear<uint16_t[]>(m_cps2_obj_size / 2);
|
||||
|
||||
memset(m_objram1, 0, m_cps2_obj_size);
|
||||
memset(m_objram2, 0, m_cps2_obj_size);
|
||||
|
||||
save_item(NAME(m_cps2_last_sprite_offset));
|
||||
save_pointer(NAME(m_cps2_buffered_obj), m_cps2_obj_size / 2);
|
||||
save_item(NAME(m_pri_ctrl));
|
||||
save_item(NAME(m_objram_bank));
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
|
||||
Build palette from palette RAM
|
||||
@ -2964,231 +2892,6 @@ void cps_state::cps1_render_sprites( screen_device &screen, bitmap_ind16 &bitmap
|
||||
|
||||
|
||||
|
||||
void cps2_state::cps2_objram_bank_w(offs_t offset, uint16_t data, uint16_t mem_mask)
|
||||
{
|
||||
if (ACCESSING_BITS_0_7)
|
||||
m_objram_bank = data & 1;
|
||||
}
|
||||
|
||||
uint16_t cps2_state::cps2_objram1_r(offs_t offset)
|
||||
{
|
||||
if (m_objram_bank & 1)
|
||||
return m_objram2[offset];
|
||||
else
|
||||
return m_objram1[offset];
|
||||
}
|
||||
|
||||
uint16_t cps2_state::cps2_objram2_r(offs_t offset)
|
||||
{
|
||||
if (m_objram_bank & 1)
|
||||
return m_objram1[offset];
|
||||
else
|
||||
return m_objram2[offset];
|
||||
}
|
||||
|
||||
void cps2_state::cps2_objram1_w(offs_t offset, uint16_t data, uint16_t mem_mask)
|
||||
{
|
||||
if (m_objram_bank & 1)
|
||||
COMBINE_DATA(&m_objram2[offset]);
|
||||
else
|
||||
COMBINE_DATA(&m_objram1[offset]);
|
||||
}
|
||||
|
||||
void cps2_state::cps2_objram2_w(offs_t offset, uint16_t data, uint16_t mem_mask)
|
||||
{
|
||||
if (m_objram_bank & 1)
|
||||
COMBINE_DATA(&m_objram1[offset]);
|
||||
else
|
||||
COMBINE_DATA(&m_objram2[offset]);
|
||||
}
|
||||
|
||||
uint16_t *cps2_state::cps2_objbase()
|
||||
{
|
||||
int baseptr;
|
||||
baseptr = 0x7000;
|
||||
|
||||
if (m_objram_bank & 1)
|
||||
baseptr ^= 0x0080;
|
||||
|
||||
//popmessage("%04x %d", cps2_port(machine, CPS2_OBJ_BASE), m_objram_bank & 1);
|
||||
|
||||
if (baseptr == 0x7000)
|
||||
return m_objram1;
|
||||
else //if (baseptr == 0x7080)
|
||||
return m_objram2;
|
||||
}
|
||||
|
||||
|
||||
void cps2_state::find_last_sprite() /* Find the offset of last sprite */
|
||||
{
|
||||
cps_state::find_last_sprite();
|
||||
|
||||
int offset = 0;
|
||||
uint16_t *base = m_cps2_buffered_obj.get();
|
||||
|
||||
/* Locate the end of table marker */
|
||||
while (offset < m_cps2_obj_size / 2)
|
||||
{
|
||||
if (base[offset + 1] >= 0x8000 || base[offset + 3] >= 0xff00)
|
||||
{
|
||||
/* Marker found. This is the last sprite. */
|
||||
m_cps2_last_sprite_offset = offset - 4;
|
||||
return;
|
||||
}
|
||||
|
||||
offset += 4;
|
||||
}
|
||||
/* Sprites must use full sprite RAM */
|
||||
m_cps2_last_sprite_offset = m_cps2_obj_size / 2 - 4;
|
||||
}
|
||||
|
||||
void cps2_state::cps2_render_sprites( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int *primasks )
|
||||
{
|
||||
#define DRAWSPRITE(CODE,COLOR,FLIPX,FLIPY,SX,SY) \
|
||||
{ \
|
||||
if (flip_screen()) \
|
||||
m_gfxdecode->gfx(2)->prio_transpen(bitmap,\
|
||||
cliprect, \
|
||||
CODE, \
|
||||
COLOR, \
|
||||
!(FLIPX),!(FLIPY), \
|
||||
512-16-(SX),256-16-(SY), screen.priority(),primasks[priority],15); \
|
||||
else \
|
||||
m_gfxdecode->gfx(2)->prio_transpen(bitmap,\
|
||||
cliprect, \
|
||||
CODE, \
|
||||
COLOR, \
|
||||
FLIPX,FLIPY, \
|
||||
SX,SY, screen.priority(),primasks[priority],15); \
|
||||
}
|
||||
|
||||
int i;
|
||||
uint16_t *base = m_cps2_buffered_obj.get();
|
||||
int xoffs = 64 - m_output[CPS2_OBJ_XOFFS /2];
|
||||
int yoffs = 16 - m_output[CPS2_OBJ_YOFFS /2];
|
||||
|
||||
#ifdef MAME_DEBUG
|
||||
if (machine().input().code_pressed(KEYCODE_Z) && machine().input().code_pressed(KEYCODE_R))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (i = m_cps2_last_sprite_offset; i >= 0; i -= 4)
|
||||
{
|
||||
int x = base[i + 0];
|
||||
int y = base[i + 1];
|
||||
int priority = (x >> 13) & 0x07;
|
||||
int code = base[i + 2] + ((y & 0x6000) << 3);
|
||||
int colour = base[i + 3];
|
||||
int col = colour & 0x1f;
|
||||
|
||||
if (colour & 0x80)
|
||||
{
|
||||
x += m_output[CPS2_OBJ_XOFFS /2]; /* fix the offset of some games */
|
||||
y += m_output[CPS2_OBJ_YOFFS /2]; /* like Marvel vs. Capcom ending credits */
|
||||
}
|
||||
|
||||
if (colour & 0xff00)
|
||||
{
|
||||
/* handle blocked sprites */
|
||||
int nx = (colour & 0x0f00) >> 8;
|
||||
int ny = (colour & 0xf000) >> 12;
|
||||
int nxs, nys, sx, sy;
|
||||
nx++;
|
||||
ny++;
|
||||
|
||||
if (colour & 0x40)
|
||||
{
|
||||
/* Y flip */
|
||||
if (colour & 0x20)
|
||||
{
|
||||
for (nys = 0; nys < ny; nys++)
|
||||
{
|
||||
for (nxs = 0; nxs < nx; nxs++)
|
||||
{
|
||||
sx = (x + nxs * 16 + xoffs) & 0x3ff;
|
||||
sy = (y + nys * 16 + yoffs) & 0x3ff;
|
||||
DRAWSPRITE(
|
||||
code + (nx - 1) - nxs + 0x10 * (ny - 1 - nys),
|
||||
(col & 0x1f),
|
||||
1,1,
|
||||
sx,sy);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (nys = 0; nys < ny; nys++)
|
||||
{
|
||||
for (nxs = 0; nxs < nx; nxs++)
|
||||
{
|
||||
sx = (x + nxs * 16 + xoffs) & 0x3ff;
|
||||
sy = (y + nys * 16 + yoffs) & 0x3ff;
|
||||
|
||||
DRAWSPRITE(
|
||||
code + nxs + 0x10 * (ny - 1 - nys),
|
||||
(col & 0x1f),
|
||||
0,1,
|
||||
sx,sy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (colour & 0x20)
|
||||
{
|
||||
for (nys = 0; nys < ny; nys++)
|
||||
{
|
||||
for (nxs = 0; nxs < nx; nxs++)
|
||||
{
|
||||
sx = (x + nxs * 16 + xoffs) & 0x3ff;
|
||||
sy = (y + nys * 16 + yoffs) & 0x3ff;
|
||||
|
||||
DRAWSPRITE(
|
||||
code + (nx - 1) - nxs + 0x10 * nys,
|
||||
(col & 0x1f),
|
||||
1,0,
|
||||
sx,sy);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (nys = 0; nys < ny; nys++)
|
||||
{
|
||||
for (nxs = 0; nxs < nx; nxs++)
|
||||
{
|
||||
sx = (x + nxs * 16 + xoffs) & 0x3ff;
|
||||
sy = (y + nys * 16 + yoffs) & 0x3ff;
|
||||
|
||||
DRAWSPRITE(
|
||||
// code + nxs + 0x10 * nys,
|
||||
(code & ~0xf) + ((code + nxs) & 0xf) + 0x10 * nys, // pgear fix
|
||||
(col & 0x1f),
|
||||
0,0,
|
||||
sx,sy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Simple case... 1 sprite */
|
||||
DRAWSPRITE(
|
||||
code,
|
||||
(col & 0x1f),
|
||||
colour&0x20,colour&0x40,
|
||||
(x+xoffs) & 0x3ff,(y+yoffs) & 0x3ff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cps_state::cps1_render_stars( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect )
|
||||
{
|
||||
uint8_t const *const stars_rom = m_region_stars->base();
|
||||
@ -3323,68 +3026,6 @@ void cps_state::render_layers(screen_device &screen, bitmap_ind16 &bitmap, const
|
||||
cps1_render_layer(screen, bitmap, cliprect, l3, 0);
|
||||
}
|
||||
|
||||
void cps2_state::render_layers(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
|
||||
{
|
||||
/* Draw layers (0 = sprites, 1-3 = tilemaps) */
|
||||
int layercontrol = m_cps_b_regs[m_game_config->layer_control / 2];
|
||||
int l0 = (layercontrol >> 0x06) & 0x03;
|
||||
int l1 = (layercontrol >> 0x08) & 0x03;
|
||||
int l2 = (layercontrol >> 0x0a) & 0x03;
|
||||
int l3 = (layercontrol >> 0x0c) & 0x03;
|
||||
screen.priority().fill(0, cliprect);
|
||||
|
||||
int primasks[8], i;
|
||||
int l0pri = (m_pri_ctrl >> 4 * l0) & 0x0f;
|
||||
int l1pri = (m_pri_ctrl >> 4 * l1) & 0x0f;
|
||||
int l2pri = (m_pri_ctrl >> 4 * l2) & 0x0f;
|
||||
int l3pri = (m_pri_ctrl >> 4 * l3) & 0x0f;
|
||||
|
||||
#if 0
|
||||
if ( (m_output[CPS2_OBJ_BASE /2] != 0x7080 && m_output[CPS2_OBJ_BASE /2] != 0x7000) ||
|
||||
m_output[CPS2_OBJ_UK1 /2] != 0x807d ||
|
||||
(m_output[CPS2_OBJ_UK2 /2] != 0x0000 && m_output[CPS2_OBJ_UK2 /2] != 0x1101 && m_output[CPS2_OBJ_UK2 /2] != 0x0001))
|
||||
popmessage("base %04x uk1 %04x uk2 %04x",
|
||||
m_output[CPS2_OBJ_BASE /2],
|
||||
m_output[CPS2_OBJ_UK1 /2],
|
||||
m_output[CPS2_OBJ_UK2 /2]);
|
||||
|
||||
if (0 && machine().input().code_pressed(KEYCODE_Z))
|
||||
popmessage("order: %d (%d) %d (%d) %d (%d) %d (%d)",l0,l0pri,l1,l1pri,l2,l2pri,l3,l3pri);
|
||||
#endif
|
||||
|
||||
/* take out the CPS1 sprites layer */
|
||||
if (l0 == 0) { l0 = l1; l1 = 0; l0pri = l1pri; }
|
||||
if (l1 == 0) { l1 = l2; l2 = 0; l1pri = l2pri; }
|
||||
if (l2 == 0) { l2 = l3; l3 = 0; l2pri = l3pri; }
|
||||
|
||||
{
|
||||
int mask0 = 0xaa;
|
||||
int mask1 = 0xcc;
|
||||
if (l0pri > l1pri) mask0 &= ~0x88;
|
||||
if (l0pri > l2pri) mask0 &= ~0xa0;
|
||||
if (l1pri > l2pri) mask1 &= ~0xc0;
|
||||
|
||||
primasks[0] = 0xff;
|
||||
for (i = 1; i < 8; i++)
|
||||
{
|
||||
if (i <= l0pri && i <= l1pri && i <= l2pri)
|
||||
{
|
||||
primasks[i] = 0xfe;
|
||||
continue;
|
||||
}
|
||||
primasks[i] = 0;
|
||||
if (i <= l0pri) primasks[i] |= mask0;
|
||||
if (i <= l1pri) primasks[i] |= mask1;
|
||||
if (i <= l2pri) primasks[i] |= 0xf0;
|
||||
}
|
||||
}
|
||||
|
||||
cps1_render_layer(screen, bitmap, cliprect, l0, 1);
|
||||
cps1_render_layer(screen, bitmap, cliprect, l1, 2);
|
||||
cps1_render_layer(screen, bitmap, cliprect, l2, 4);
|
||||
cps2_render_sprites(screen, bitmap, cliprect, primasks);
|
||||
}
|
||||
|
||||
uint32_t cps_state::screen_update_cps1(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
|
||||
{
|
||||
int videocontrol = m_cps_a_regs[CPS1_VIDEOCONTROL];
|
||||
@ -3465,21 +3106,3 @@ WRITE_LINE_MEMBER(cps_state::screen_vblank_cps1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t cps2_state::screen_update_cps2(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
|
||||
{
|
||||
cps2_set_sprite_priorities();
|
||||
return screen_update_cps1(screen, bitmap, cliprect);
|
||||
}
|
||||
|
||||
void cps2_state::cps2_set_sprite_priorities()
|
||||
{
|
||||
m_pri_ctrl = m_output[CPS2_OBJ_PRI /2];
|
||||
}
|
||||
|
||||
void cps2_state::cps2_objram_latch()
|
||||
{
|
||||
cps2_set_sprite_priorities();
|
||||
memcpy(m_cps2_buffered_obj.get(), cps2_objbase(), m_cps2_obj_size);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user