From 65c8982510f7e33a23ed373d949097a30bcdc57f Mon Sep 17 00:00:00 2001 From: Andrei Holub Date: Wed, 25 Dec 2024 13:34:24 -0500 Subject: [PATCH] spg format + [tsconf.xml] added 28 items --- hash/tsconf.xml | 442 ++++++++++++++++++++++++++++++ hash/tsconf_betadisc_flop.xml | 30 --- src/mame/sinclair/spec128.cpp | 7 + src/mame/sinclair/spec128.h | 1 + src/mame/sinclair/spec_snqk.cpp | 457 ++++++++++++++++++++++++++++++++ src/mame/sinclair/spec_snqk.h | 9 + src/mame/sinclair/spectrum.cpp | 4 +- src/mame/sinclair/spectrum.h | 5 +- src/mame/sinclair/tsconf.cpp | 2 +- src/mame/sinclair/tsconf.h | 2 + src/mame/sinclair/tsconf_m.cpp | 5 + 11 files changed, 931 insertions(+), 33 deletions(-) create mode 100644 hash/tsconf.xml delete mode 100644 hash/tsconf_betadisc_flop.xml diff --git a/hash/tsconf.xml b/hash/tsconf.xml new file mode 100644 index 00000000000..46ec8a283bd --- /dev/null +++ b/hash/tsconf.xml @@ -0,0 +1,442 @@ + + + + + + + Alter Ego + 2011 + <homebrew> + + + + + + + + + + + + Bomberman + 2012 + <homebrew> + + + + + + + + + + + + + Bruce Lee + 2015 + <homebrew> + + + + + + + + + + Chase + 2012 + <homebrew> + + + + + + + + + + + Copter v0.1 + 2012 + <homebrew> + + + + + + + + + + Digger + 2016 + <homebrew> + + + + + + + + + + + + Edge Grinder v1.01 + 2018 + <homebrew> + + + + + + + + + + + + Jim Power Test + 201? + <homebrew> + + + + + + + + + + Lirus + 201? + <homebrew> + + + + + + + + + + + + + + + Touhou Zero. Lost Donation Box Incident + 201? + <homebrew> + + + + + + + + + + + + + + + MultiDude + 2014 + <homebrew> + + + + + + + + + + + + + Ninja Gaiden + 201? + <homebrew> + + + + + + + + + + Otter & Smoker + 201? + <homebrew> + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ottifants + 201? + <homebrew> + + + + + + + + + + + + + + + + + Once Upon a Time in a Kingdom + 201? + <homebrew> + + + + + + + + + + + + + + + PacPack + 2018 + <homebrew> + + + + + + + + + + Cannon Fodder Parallax + 201? + <homebrew> + + + + + + + + + + Sir Ababol + 201? + <homebrew> + + + + + + + + + + + + Socoban + 2015 + <homebrew> + + + + + + + + + + Sonic the Hedgehog + 201? + <homebrew> + + + + + + + + + + + Synchronization + 201? + Robus + + + + + + + + + + + + + T-circles + 201? + <homebrew> + + + + + + + + + + Tetris + 201? + <homebrew> + + + + + + + + + + TS-TechDemo + 2013 + Wizart/DT + + + + + + + + + + TSolitaire + 201? + ERA Creative Group (Multinational) + + + + + + + + + + + + + + + + + + + + + + + Uwol - Quest for Money + 2012 + <homebrew> + + + + + + + + + + + Wonder Boy + 201? + <homebrew> + + + + + + + + + + Xonix + 2012 + <homebrew> + + + + + + + + + + Zen Loops + 201? + <homebrew> + + + + + + + + + + ZX Battle City v1.4 (NoVDAC) + 2020 + <homebrew> + + + + + + + + + + diff --git a/hash/tsconf_betadisc_flop.xml b/hash/tsconf_betadisc_flop.xml deleted file mode 100644 index d315425839e..00000000000 --- a/hash/tsconf_betadisc_flop.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - Copter v0.1 - 2012 - Wizart/DT - - - - - - - - - ZX Battle City v1.4 (NoVDAC) - 2020 - Marie Slip / n1k-o - - - - - - - - diff --git a/src/mame/sinclair/spec128.cpp b/src/mame/sinclair/spec128.cpp index 8895627ffcb..7247d7ac237 100644 --- a/src/mame/sinclair/spec128.cpp +++ b/src/mame/sinclair/spec128.cpp @@ -238,6 +238,13 @@ void spectrum_128_state::spectrum_128_port_7ffd_w(offs_t offset, uint8_t data) m_exp->iorq_w(offset | 1, data); } +void spectrum_128_state::bank3_set_page(u8 page) +{ + m_port_7ffd_data &= 0xf8; + m_port_7ffd_data |= page & 0x07; + spectrum_128_update_memory(); +} + void spectrum_128_state::spectrum_128_update_memory() { m_bank_rom[0]->set_entry(BIT(m_port_7ffd_data, 4)); diff --git a/src/mame/sinclair/spec128.h b/src/mame/sinclair/spec128.h index 819a7502978..5c6f3ccfada 100644 --- a/src/mame/sinclair/spec128.h +++ b/src/mame/sinclair/spec128.h @@ -33,6 +33,7 @@ protected: virtual void machine_start() override ATTR_COLD; virtual void machine_reset() override ATTR_COLD; + virtual void bank3_set_page(u8 page) override; virtual void spectrum_128_update_memory() override; virtual rectangle get_screen_area() override; diff --git a/src/mame/sinclair/spec_snqk.cpp b/src/mame/sinclair/spec_snqk.cpp index 93db167cb22..08aec44cfd2 100644 --- a/src/mame/sinclair/spec_snqk.cpp +++ b/src/mame/sinclair/spec_snqk.cpp @@ -206,6 +206,15 @@ SNAPSHOT_LOAD_MEMBER(spectrum_state::snapshot_cb) setup_frz(&snapshot_data[0], snapshot_size); } + else if (image.is_filetype("spg")) + { + if (snapshot_data[32] != 'S' || snapshot_data[33] != 'p' || snapshot_data[34] != 'e' || snapshot_data[35] != 'c' + || snapshot_data[36] != 't' || snapshot_data[37] != 'r' || snapshot_data[38] != 'u' || snapshot_data[39] != 'm' + || snapshot_data[40] != 'P' || snapshot_data[41] != 'r' ||snapshot_data[42] != 'o' || snapshot_data[43] != 'g') + return std::make_pair(image_error::INVALIDIMAGE, "Invalid .SPG file header."); + + setup_spg(&snapshot_data[0], snapshot_size); + } else { setup_z80(&snapshot_data[0], snapshot_size); @@ -2396,6 +2405,454 @@ void spectrum_state::setup_z80(const uint8_t *snapdata, uint32_t snapsize) } } +void spectrum_state::hrust_decompress_block(address_space &space, const u8 *source, u16 dest, u16 size) +{ + class bb_stream + { + private: + const u8 *m_base; + const u8 *m_read; + int m_offset; + int m_length; + bool m_eof; + u16 m_buffer; + + public: + bb_stream(const u8 *src, int src_length) + { + m_base = m_read = src; + + m_length = src_length; + m_offset = 0; + m_eof = false; + + m_buffer = get_byte(); + m_buffer += get_byte() << 8; + } + + u8 get_byte() + { + m_eof |= ((m_read - m_base) == m_length); + return m_eof ? 0 : *m_read++; + } + + u8 get_bit() + { + u8 bit = BIT(m_buffer, 15 - m_offset); + if (m_offset == 15) + { + m_buffer = get_byte(); + m_buffer += get_byte() << 8; + } + m_offset = (m_offset + 1) & 0xf; + + return bit; + } + + u8 get_bits(u8 n) + { + u8 r = 0; + do + { + r = (r << 1) + get_bit(); + } while (--n); + + return r; + } + + bool overflow() { return m_eof; } + }; + + constexpr u8 mask[] = { 0, 0, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0 }; + u8 no_bits = 2; + bb_stream bitbuf(source, size); + + space.write_byte(dest++, bitbuf.get_byte()); + + while (!bitbuf.overflow()) + { + while (bitbuf.get_bit()) + { + space.write_byte(dest++, bitbuf.get_byte()); + } + + u16 len = 0; + u8 bb; + do + { + bb = bitbuf.get_bits(2); + len += bb; + + } while (bb == 0x03 && len != 0x0f); + + short offset = 0; + if (len == 0) + { + offset = 0xfff8 + bitbuf.get_bits(3); + space.write_byte(dest, space.read_byte(dest + offset)); + ++dest; + continue; + } + else if (len == 1) + { + const u8 code = bitbuf.get_bits(2); + if (code == 0 || code == 1) + { + offset = bitbuf.get_byte(); + offset += (code ? 0xfe : 0xfd) << 8; + } + else if (code == 2) + { + u8 b = bitbuf.get_byte(); + if (b >= 0xe0) + { + b <<= 1; + ++b; // rlca + b ^= 2; // xor c + + if (b == 0xff) + { + ++no_bits; + continue; + } + + offset = 0xff00 + b - 0x0f; + space.write_byte(dest, space.read_byte(dest + offset)); + ++dest; + space.write_byte(dest++, bitbuf.get_byte()); + space.write_byte(dest, space.read_byte(dest + offset)); + ++dest; + continue; + } + offset = 0xff00 + b; + } + else if (code == 3) + { + offset = 0xffe0 + bitbuf.get_bits(5); + } + + for (auto i = 0; i < 2; ++i) + { + space.write_byte(dest, space.read_byte(dest + offset)); + ++dest; + } + continue; + } + else if (len == 3) + { + if (bitbuf.get_bit()) + { + offset = 0xfff0 + bitbuf.get_bits(4); + space.write_byte(dest, space.read_byte(dest + offset)); + ++dest; + space.write_byte(dest++, bitbuf.get_byte()); + space.write_byte(dest, space.read_byte(dest + offset)); + ++dest; + continue; + } + + if (bitbuf.get_bit()) + { + u8 bytes_no = 6 + bitbuf.get_bits(4); + for (u8 i = 0; i < (bytes_no << 1); ++i) + space.write_byte(dest++, bitbuf.get_byte()); + continue; + } + + len = bitbuf.get_bits(7); + if (len == 0x0f) + { + break; // EOF + } + if (len < 0x0f) + { + len = (len << 8) + bitbuf.get_byte(); + } + } + + if (len == 2) + { + ++len; + } + + + const u8 code = bitbuf.get_bits(2); + if (code == 0) + { + offset = 0xfe00 + bitbuf.get_byte(); + } + else if (code == 1) + { + u8 b = bitbuf.get_byte(); + + if (b >= 0xe0) + { + if (len > 3) + { + // logerror + break; + } + + b <<= 1; + ++b; // rlca + b ^= 3; // xor c + + offset = 0xff00 + b - 0x0f; + + space.write_byte(dest, space.read_byte(dest + offset)); + ++dest; + space.write_byte(dest++, bitbuf.get_byte()); + space.write_byte(dest, space.read_byte(dest + offset)); + ++dest; + continue; + } + offset = 0xff00 + b; + } + else if (code == 2) + { + offset = 0xffe0 + bitbuf.get_bits(5); + } + else if (code == 3) + { + offset = (mask[no_bits] + bitbuf.get_bits(no_bits)) << 8; + offset += bitbuf.get_byte(); + } + + for (u16 i = 0; i < len; ++i) + { + space.write_byte(dest, space.read_byte(dest + offset)); + ++dest; + } + } +} + +void spectrum_state::mlz_decompress_block(address_space &space, const u8 *source, u16 dest, u16 size) +{ + class de_mlz + { + private: + address_space &m_space; + const u8 *m_src; + u16 m_to; + u8 m_buffer; + int m_buffer_size; + + public: + de_mlz(address_space &space, const u8 *src, u16 dst) : m_space(space) + { + m_src = src; + m_to = dst; + } + + void init_bitstream() + { + m_buffer = get_byte(); + m_buffer_size = 8; + } + + u8 get_byte() + { + return *m_src++; + } + + void put_byte(u8 val) + { + m_space.write_byte(m_to++, val); + } + + void repeat(u32 disp, int num) + { + for (int i = 0; i < num; i++) + { + u8 val = m_space.read_byte(m_to - disp); + put_byte(val); + } + } + + // gets specified number of bits from bitstream + // returns them LSB-aligned + u32 get_bits(int count) + { + u32 bits = 0; + while (count--) + { + if (m_buffer_size--) + { + bits <<= 1; + bits |= 1 & (m_buffer >> 7); + m_buffer <<= 1; + } + else + { + init_bitstream(); + count++; // repeat loop once more + } + } + + return bits; + } + + int get_bigdisp() + { + u32 bits; + + // inter displacement + if (get_bits(1)) + { + bits = get_bits(4); + return 0x1100 - (bits << 8) - get_byte(); + } + + // shorter displacement + else + return 256 - get_byte(); + } + }; + + de_mlz s(space, source, dest); + u32 done = 0; + int i; + + // get first byte of packed file and write to output + s.put_byte(s.get_byte()); + + // second byte goes to bitstream + s.init_bitstream(); + + // actual depacking loop! + do + { + // get 1st bit - either OUTBYTE or beginning of LZ code + // OUTBYTE + if (s.get_bits(1)) + s.put_byte(s.get_byte()); + + // LZ code + else + { + switch (s.get_bits(2)) + { + case 0: // 000 + s.repeat(8 - s.get_bits(3), 1); + break; + + case 1: // 001 + s.repeat(256 - s.get_byte(), 2); + break; + + case 2: // 010 + s.repeat(s.get_bigdisp(), 3); + break; + + case 3: // 011 + // extract num of length bits + for (i = 1; !s.get_bits(1); i++) + ; + + // check for exit code + if (i == 9) + { + done = 1; + } + else if (i <= 7) + { + // get length bits itself + int bits = s.get_bits(i); + s.repeat(s.get_bigdisp(), 2 + (1 << i) + bits); + } + break; + } + } + } while (!done); +} + +/* + Load a .SPG (Spectrum Prog) file. + + v1.1: https://raw.githubusercontent.com/tslabs/zx-evo/master/pentevo/docs/Formats/SPGv1_1.txt + v1.0: https://raw.githubusercontent.com/tslabs/zx-evo/master/pentevo/docs/Formats/SPGv1_0.txt + v0.2 https://raw.githubusercontent.com/tslabs/zx-evo/master/pentevo/docs/Formats/SPGv0_2.txt +*/ +void spectrum_state::setup_spg(const u8 *snapdata, u32 snapsize) +{ + u16 data; + address_space &space = m_maincpu->space(AS_PROGRAM); + + data = snapdata[SPG_OFFSET + 0x2c]; + if (BIT(data, 4, 4) != 1 || BIT(data, 0, 4) != 0) // just v1.0 for now + { + logerror("Can't load .SPG file v%d.%d\n", BIT(data, 4, 4), BIT(data, 0, 4)); + return; + } + + m_maincpu->set_state_int(Z80_IY, 0x5c3a); + m_maincpu->set_state_int(Z80_HL2, 0x2758); + m_maincpu->set_state_int(Z80_I, 0x3f); + m_maincpu->set_state_int(Z80_IM, 1); + m_port_7ffd_data = 16; + + data = (snapdata[SPG_OFFSET + 0x31] << 8) | snapdata[SPG_OFFSET + 0x30]; + if (data < 0x4000) + { + logerror("PC(%04x) < 0x4000 is not allowed\n", data); + } + m_maincpu->set_state_int(Z80_PC, data); + + data = (snapdata[SPG_OFFSET + 0x33] << 8) | snapdata[SPG_OFFSET + 0x32]; + m_maincpu->set_state_int(Z80_SP, data); + + const u8 page3 = snapdata[SPG_OFFSET + 0x34]; + + data = snapdata[SPG_OFFSET + 0x35]; + m_maincpu->set_state_int(Z80_IFF1, BIT(data, 2)); + + offs_t data_offset = SPG_DATA; + for (auto b = 0; b < 0x100; b++) + { + data = snapdata[SPG_BLOCK_INFO(b) + 0]; + const u16 offs = BIT(data, 0, 5) * 512; + const bool is_last = BIT(data, 7); + + data = snapdata[SPG_BLOCK_INFO(b) + 1]; + const u16 size = (BIT(data, 0, 5) + 1) * 512; + const u8 compression = BIT(data, 6, 2); + + const u8 page = snapdata[SPG_BLOCK_INFO(b) + 2]; + if (page > 0xdf) + { + logerror("Page %02x is not allowed\n", page); + return; + } + bank3_set_page(page); + + switch (compression) + { + case 0x00: + for (auto i = 0; i < size; i++) + space.write_byte(0xc000 + i, snapdata[data_offset + i]); + break; + + case 0x01: + mlz_decompress_block(space, &snapdata[data_offset], 0xc000 + offs, size); + break; + + case 0x02: + hrust_decompress_block(space, &snapdata[data_offset], 0xc000 + offs, size); + break; + + default: + logerror("Unsupported compression: %d\n", compression); + return; + } + + data_offset += size; + if (is_last) + break; + } + + bank3_set_page(page3); +} + QUICKLOAD_LOAD_MEMBER(spectrum_state::quickload_cb) { size_t quickload_size = image.length(); diff --git a/src/mame/sinclair/spec_snqk.h b/src/mame/sinclair/spec_snqk.h index 419820aba0d..587b7e92460 100644 --- a/src/mame/sinclair/spec_snqk.h +++ b/src/mame/sinclair/spec_snqk.h @@ -119,6 +119,15 @@ #define SNX_COMPRESSED 0xff #define SNX_UNCOMPRESSED 0x00 +/***************************************************************************** + * + * .SPG format (used by TS-Conf, BASECONF, Next) + * + ****************************************************************************/ +#define SPG_OFFSET 0 +#define SPG_BLOCK_INFO(_n) (SPG_OFFSET + 0x100 + (3 * _n)) +#define SPG_DATA SPG_BLOCK_INFO(256) + /***************************************************************************** * * .FRZ format (used by CBSpeccy, ZX-Live and ASp) diff --git a/src/mame/sinclair/spectrum.cpp b/src/mame/sinclair/spectrum.cpp index 25a0b41df8b..9a0cfa3d0a7 100644 --- a/src/mame/sinclair/spectrum.cpp +++ b/src/mame/sinclair/spectrum.cpp @@ -835,7 +835,9 @@ void spectrum_state::spectrum_common(machine_config &config) m_exp->fb_r_handler().set(FUNC(spectrum_state::floating_bus_r)); /* devices */ - SNAPSHOT(config, "snapshot", "ach,frz,plusd,prg,sem,sit,sna,snp,snx,sp,z80,zx").set_load_callback(FUNC(spectrum_state::snapshot_cb)); + snapshot_image_device &snapshot(SNAPSHOT(config, "snapshot", "ach,frz,plusd,prg,sem,sit,sna,snp,snx,sp,spg,z80,zx")); + snapshot.set_load_callback(FUNC(spectrum_state::snapshot_cb)); + snapshot.set_interface("spectrum_snapshot"); QUICKLOAD(config, "quickload", "raw,scr", attotime::from_seconds(2)).set_load_callback(FUNC(spectrum_state::quickload_cb)); // The delay prevents the screen from being cleared by the RAM test at boot CASSETTE(config, m_cassette); diff --git a/src/mame/sinclair/spectrum.h b/src/mame/sinclair/spectrum.h index f14ed0fe4e9..efd1eca56c5 100644 --- a/src/mame/sinclair/spectrum.h +++ b/src/mame/sinclair/spectrum.h @@ -90,6 +90,7 @@ protected: virtual void video_start() override ATTR_COLD; // until machine/spec_snqk.cpp gets somehow disentangled + virtual void bank3_set_page(u8 page) { } virtual void plus3_update_memory() { } virtual void spectrum_128_update_memory() { } virtual void ts2068_update_memory() { } @@ -207,7 +208,9 @@ protected: void setup_frz(const uint8_t *snapdata, uint32_t snapsize); void z80_decompress_block(address_space &space, const uint8_t *source, uint16_t dest, uint16_t size); void setup_z80(const uint8_t *snapdata, uint32_t snapsize); - + void hrust_decompress_block(address_space &space, const u8 *source, u16 dest, u16 size); + void mlz_decompress_block(address_space &space, const u8 *source, u16 dest, u16 size); + void setup_spg(const u8 *snapdata, u32 snapsize); // quickload helpers void log_quickload(const char *type, uint32_t start, uint32_t length, uint32_t exec, const char *exec_format); void setup_scr(const uint8_t *quickdata, uint32_t quicksize); diff --git a/src/mame/sinclair/tsconf.cpp b/src/mame/sinclair/tsconf.cpp index ebe7ec88a71..01ca37317cd 100644 --- a/src/mame/sinclair/tsconf.cpp +++ b/src/mame/sinclair/tsconf.cpp @@ -326,7 +326,7 @@ void tsconf_state::tsconf(machine_config &config) AT_KEYB(config, m_keyboard, pc_keyboard_device::KEYBOARD_TYPE::AT, 3); SOFTWARE_LIST(config, "betadisc_list_pent").set_original("spectrum_betadisc_flop"); - SOFTWARE_LIST(config, "betadisc_list_tsconf").set_original("tsconf_betadisc_flop"); + SOFTWARE_LIST(config, "tsconf_list").set_original("tsconf"); } ROM_START(tsconf) diff --git a/src/mame/sinclair/tsconf.h b/src/mame/sinclair/tsconf.h index 37dfe65b808..890327b6569 100644 --- a/src/mame/sinclair/tsconf.h +++ b/src/mame/sinclair/tsconf.h @@ -56,6 +56,8 @@ protected: virtual void machine_reset() override ATTR_COLD; virtual void device_post_load() override ATTR_COLD; + virtual void bank3_set_page(u8 page) override; + virtual TIMER_CALLBACK_MEMBER(irq_off) override; TIMER_CALLBACK_MEMBER(irq_frame); TIMER_CALLBACK_MEMBER(irq_scanline); diff --git a/src/mame/sinclair/tsconf_m.cpp b/src/mame/sinclair/tsconf_m.cpp index 92f9678fd17..9e1da1dea11 100644 --- a/src/mame/sinclair/tsconf_m.cpp +++ b/src/mame/sinclair/tsconf_m.cpp @@ -477,6 +477,11 @@ u8 tsconf_state::tsconf_port_xx1f_r(offs_t offset) { : 0x00; // TODO kempston read } +void tsconf_state::bank3_set_page(u8 page) +{ + tsconf_port_xxaf_w(PAGE3 << 8, page); +} + void tsconf_state::tsconf_port_7ffd_w(u8 data) { // LOCK? BIT(data, 5);