ti99: Created bus gromport, split monster file gromport

This commit is contained in:
Michael Zapf 2017-05-25 16:00:05 +02:00
parent 7346e19dfa
commit 347e8a4fb7
16 changed files with 1903 additions and 1789 deletions

View File

@ -2358,6 +2358,7 @@ end
---------------------------------------------------
--
--@src/devices/bus/ti99/colorbus/colorbus.h,BUSES["TI99"] = true
--@src/devices/bus/ti99/gromport/cartridges.h,BUSES["TI99"] = true
--@src/devices/bus/ti99/joyport/joyport.h,BUSES["TI99"] = true
--@src/devices/bus/ti99/peb/peribox.h,BUSES["TI99"] = true
--@src/devices/bus/ti99/internal/genboard.h,BUSES["TI99"] = true
@ -2375,14 +2376,22 @@ if (BUSES["TI99"]~=null) then
MAME_DIR .. "src/devices/bus/ti99/internal/evpcconn.h",
MAME_DIR .. "src/devices/bus/ti99/internal/genboard.cpp",
MAME_DIR .. "src/devices/bus/ti99/internal/genboard.h",
MAME_DIR .. "src/devices/bus/ti99/internal/gromport.cpp",
MAME_DIR .. "src/devices/bus/ti99/internal/gromport.h",
MAME_DIR .. "src/devices/bus/ti99/internal/ioport.cpp",
MAME_DIR .. "src/devices/bus/ti99/internal/ioport.h",
MAME_DIR .. "src/devices/bus/ti99/colorbus/busmouse.cpp",
MAME_DIR .. "src/devices/bus/ti99/colorbus/busmouse.h",
MAME_DIR .. "src/devices/bus/ti99/colorbus/colorbus.cpp",
MAME_DIR .. "src/devices/bus/ti99/colorbus/colorbus.h",
MAME_DIR .. "src/devices/bus/ti99/gromport/gromport.cpp",
MAME_DIR .. "src/devices/bus/ti99/gromport/gromport.h",
MAME_DIR .. "src/devices/bus/ti99/gromport/cartridges.cpp",
MAME_DIR .. "src/devices/bus/ti99/gromport/cartridges.h",
MAME_DIR .. "src/devices/bus/ti99/gromport/gkracker.cpp",
MAME_DIR .. "src/devices/bus/ti99/gromport/gkracker.h",
MAME_DIR .. "src/devices/bus/ti99/gromport/multiconn.cpp",
MAME_DIR .. "src/devices/bus/ti99/gromport/multiconn.h",
MAME_DIR .. "src/devices/bus/ti99/gromport/singleconn.cpp",
MAME_DIR .. "src/devices/bus/ti99/gromport/singleconn.h",
MAME_DIR .. "src/devices/bus/ti99/joyport/handset.cpp",
MAME_DIR .. "src/devices/bus/ti99/joyport/handset.h",
MAME_DIR .. "src/devices/bus/ti99/joyport/joyport.cpp",

View File

@ -0,0 +1,367 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/***************************************************************************
Cartridge emulations
****************************************************************************/
#ifndef MAME_BUS_TI99_GROMPORT_CARTRIDGES_H
#define MAME_BUS_TI99_GROMPORT_CARTRIDGES_H
#pragma once
#include "emuopts.h"
#include "bus/ti99/ti99defs.h"
#include "machine/tmc0430.h"
#include "softlist_dev.h"
#include "gromport.h"
#include "unzip.h"
#include "xmlfile.h"
namespace bus { namespace ti99 { namespace gromport {
class ti99_cartridge_pcb;
enum rpk_open_error
{
RPK_OK,
RPK_NOT_ZIP_FORMAT,
RPK_CORRUPT,
RPK_OUT_OF_MEMORY,
RPK_XML_ERROR,
RPK_INVALID_FILE_REF,
RPK_ZIP_ERROR,
RPK_ZIP_UNSUPPORTED,
RPK_MISSING_RAM_LENGTH,
RPK_INVALID_RAM_SPEC,
RPK_UNKNOWN_RESOURCE_TYPE,
RPK_INVALID_RESOURCE_REF,
RPK_INVALID_LAYOUT,
RPK_MISSING_LAYOUT,
RPK_NO_PCB_OR_RESOURCES,
RPK_UNKNOWN_PCB_TYPE
};
const char *const error_text[16] =
{
"No error",
"Not a RPK (zip) file",
"Module definition corrupt",
"Out of memory",
"XML format error",
"Invalid file reference",
"Zip file error",
"Unsupported zip version",
"Missing RAM length",
"Invalid RAM specification",
"Unknown resource type",
"Invalid resource reference",
"layout.xml not valid",
"Missing layout",
"No pcb or resource found",
"Unknown pcb type"
};
class ti99_cartridge_device : public bus8z_device, public device_image_interface
{
public:
ti99_cartridge_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz);
DECLARE_WRITE8_MEMBER(cruwrite);
DECLARE_WRITE_LINE_MEMBER(ready_line);
DECLARE_WRITE_LINE_MEMBER(romgq_line);
DECLARE_WRITE8_MEMBER(set_gromlines);
DECLARE_WRITE_LINE_MEMBER(gclock_in);
bool is_available() { return m_pcb != nullptr; }
void set_slot(int i);
bool is_grom_idle();
protected:
virtual void device_start() override { }
virtual void device_config_complete() override;
virtual machine_config_constructor device_mconfig_additions() const override;
virtual const tiny_rom_entry* device_rom_region() const override;
// Image handling: implementation of methods which are abstract in the parent
image_init_result call_load() override;
void call_unload() override;
virtual const software_list_loader &get_software_list_loader() const override { return rom_software_list_loader::instance(); }
void prepare_cartridge();
// device_image_interface
iodevice_t image_type() const override { return IO_CARTSLOT; }
bool is_readable() const override { return true; }
bool is_writeable() const override { return false; }
bool is_creatable() const override { return false; }
bool must_be_loaded() const override { return false; }
bool is_reset_on_load() const override { return false; }
const char *image_interface() const override { return "ti99_cart"; }
const char *file_extensions() const override { return "rpk"; }
private:
/***************** RPK support ********************
Actually deprecated, and to be removed as soon as
softlists allow for homebrew cartridges
***************************************************/
class rpk_socket;
class rpk;
class rpk_exception
{
public:
rpk_exception(rpk_open_error value): m_err(value), m_detail(nullptr) { }
rpk_exception(rpk_open_error value, const char* detail) : m_err(value), m_detail(detail) { }
const char* to_string()
{
// FIXME: this leaks memory - in some cases it returns a new buffer, in other cases it returns a pointer to a static string, so the caller can't know whether it needs to be cleaned up
if (m_detail==nullptr) return error_text[(int)m_err];
std::string errormsg = std::string(error_text[(int)m_err]).append(": ").append(m_detail);
return core_strdup(errormsg.c_str());
}
private:
rpk_open_error m_err;
const char* m_detail;
};
class rpk_reader
{
public:
rpk_reader(const pcb_type *types) : m_types(types) { }
rpk *open(emu_options &options, const char *filename, const char *system_name);
private:
int find_file(util::archive_file &zip, const char *filename, uint32_t crc);
std::unique_ptr<rpk_socket> load_rom_resource(util::archive_file &zip, util::xml::data_node const* rom_resource_node, const char* socketname);
std::unique_ptr<rpk_socket> load_ram_resource(emu_options &options, util::xml::data_node const* ram_resource_node, const char* socketname, const char* system_name);
const pcb_type* m_types;
};
class rpk
{
friend class rpk_reader;
public:
rpk(emu_options& options, const char* sysname);
~rpk();
int get_type(void) { return m_type; }
uint8_t* get_contents_of_socket(const char *socket_name);
int get_resource_length(const char *socket_name);
void close();
private:
emu_options& m_options; // need this to find the path to the nvram files
int m_type;
//const char* m_system_name; // need this to find the path to the nvram files
std::unordered_map<std::string,std::unique_ptr<rpk_socket>> m_sockets;
void add_socket(const char* id, std::unique_ptr<rpk_socket> newsock);
};
class rpk_socket
{
public:
rpk_socket(const char *id, int length, uint8_t *contents);
rpk_socket(const char *id, int length, uint8_t *contents, const char *pathname);
~rpk_socket() {}
const char* id() { return m_id; }
int get_content_length() { return m_length; }
uint8_t* get_contents() { return m_contents; }
bool persistent_ram() { return m_pathname != nullptr; }
const char* get_pathname() { return m_pathname; }
void cleanup() { if (m_contents != nullptr) global_free_array(m_contents); }
private:
const char* m_id;
uint32_t m_length;
uint8_t* m_contents;
const char* m_pathname;
};
bool m_readrom;
int m_pcbtype;
int m_slot;
int get_index_from_tagname();
std::unique_ptr<ti99_cartridge_pcb> m_pcb; // inbound
cartridge_connector_device* m_connector; // outbound
// RPK which is associated to this cartridge
// When we close it, the contents are saved to NVRAM if available
rpk *m_rpk;
};
/****************************************************************************/
class ti99_cartridge_pcb
{
friend class ti99_cartridge_device;
public:
ti99_cartridge_pcb();
virtual ~ti99_cartridge_pcb() { }
protected:
virtual DECLARE_READ8Z_MEMBER(readz);
virtual DECLARE_WRITE8_MEMBER(write);
virtual DECLARE_READ8Z_MEMBER(crureadz);
virtual DECLARE_WRITE8_MEMBER(cruwrite);
DECLARE_WRITE_LINE_MEMBER(romgq_line);
virtual DECLARE_WRITE8_MEMBER(set_gromlines);
DECLARE_WRITE_LINE_MEMBER(gclock_in);
DECLARE_READ8Z_MEMBER(gromreadz);
DECLARE_WRITE8_MEMBER(gromwrite);
inline void set_grom_pointer(int number, device_t *dev);
void set_cartridge(ti99_cartridge_device *cart);
const char* tag() { return m_tag; }
void set_tag(const char* tag) { m_tag = tag; }
bool is_grom_idle() { return m_grom_idle; }
ti99_cartridge_device* m_cart;
tmc0430_device* m_grom[5];
bool m_grom_idle;
int m_grom_size;
int m_rom_size;
int m_ram_size;
uint8_t* m_rom_ptr;
uint8_t* m_ram_ptr;
bool m_romspace_selected;
int m_rom_page; // for some cartridge types
uint8_t* m_grom_ptr; // for gromemu
int m_grom_address; // for gromemu
int m_ram_page; // for super
const char* m_tag;
std::vector<uint8_t> m_nvram; // for MiniMemory
std::vector<uint8_t> m_ram; // for MBX
};
/******************** Standard cartridge ******************************/
class ti99_standard_cartridge : public ti99_cartridge_pcb
{
public:
};
/*********** Paged cartridge (like Extended Basic) ********************/
class ti99_paged12k_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
};
/*********** Paged cartridge (others) ********************/
class ti99_paged16k_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
};
/********************** Mini Memory ***********************************/
class ti99_minimem_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
};
/********************* Super Space II *********************************/
class ti99_super_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
};
/************************* MBX ***************************************/
class ti99_mbx_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
};
/********************** Paged 379i ************************************/
class ti99_paged379i_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
private:
int get_paged379i_bank(int rompage);
};
/********************** Paged 378 ************************************/
class ti99_paged378_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
};
/********************** Paged 377 ************************************/
class ti99_paged377_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
};
/********************** Paged CRU ************************************/
class ti99_pagedcru_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
};
/********************** GROM emulation cartridge ************************************/
class ti99_gromemu_cartridge : public ti99_cartridge_pcb
{
public:
ti99_gromemu_cartridge(): m_waddr_LSB(false), m_grom_selected(false), m_grom_read_mode(false), m_grom_address_mode(false)
{ m_grom_address = 0; }
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(gromemureadz);
DECLARE_WRITE8_MEMBER(gromemuwrite);
DECLARE_WRITE8_MEMBER(set_gromlines) override;
private:
bool m_waddr_LSB;
bool m_grom_selected;
bool m_grom_read_mode;
bool m_grom_address_mode;
};
} } } // end namespace bus::ti99::gromport
DECLARE_DEVICE_TYPE_NS(TI99_CART, bus::ti99::gromport, ti99_cartridge_device)
#endif // MAME_BUS_TI99_GROMPORT_CARTRIDGES_H

View File

@ -0,0 +1,473 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/**************************************************************************
The GRAM Kracker was manufactured by Miller's Graphics and designed to
fit into the cartridge slot.
It offers one own cartridge slot at the top side and a row of switches
at its front. It contains buffered SRAM circuits; the base version has
56 KiB, and the extended version has 80 KiB.
The operation of the GRAM Kracker is a bit complex and most likely
bound to fail when you have no manual. Accordingly, this emulation is
neither simpler nor more difficult to use.
Concept of operation:
Loader: The GRAM Kracker contains a small loader utility
which allows you to dump cartridges and to load the contents into the
SRAM of the GK. This loader utility is active when the switch 5 is put
into "Loader on" state. The activated loader hides the TI BASIC
interpreter in the console.
Cartridges: When a cartridge is plugged into the GK the contents may be
dumped and saved to disk by the loader. They cannot be directly copied
into the GK because the memory locations are hidden by the cartridge.
Loading the cartridge into the SRAM: With the cartridge unplugged, dumps
can be loaded into the SRAM using the loader. This is one major use case
of the GK, that is, to load dumps from disk, and in particular modified
dumps. (There is no checksum, so contents may be freely changed.)
Console dump: The GK is also able to dump the console GROMs and also to
load them into the SRAM (only in the extended version). Due to a
peculiarity of the TI console design it is possible to override the
console GROMs with the contents in the cartridge slot.
A standard procedure for use with the GK:
Save cartridge:
- Put switches to [Normal | OpSys | TI BASIC | W/P | Loader On]
- Insert a disk image into disk drive 1
- Plug in a cartridge
- Reset the console (done automatically here)
- Visit the option screen, press 1 for GRAM KRACKER
- In the GK loader, select 2 for Save Module
- Follow the on-screen instructions. Switches are set via the dip switch menu.
- Enter a target file name
- Saving is complete when the Save operation has been unmarked.
Load cartridge:
- Put switches to [Normal | OpSys | TI BASIC | W/P | Loader On]
- Insert a disk image into disk drive 1
- Make sure no cartridge is plugged in
- Press 1 for GRAM KRACKER
- Press 3 for Init Module space; follow instructions
- Press 1 for Load Module; specify file name on disk
- Loading is complete when the Load operation has been unmarked.
Memory organisation:
The console has three GROMs with 6 KiB size and occupying 8 KiB of address
space each. These are called GROMs 0, 1, and 2. GROM 0 contains the common
routines for the computer operation; GROMs 1 and 2 contain TI BASIC.
Memory locations 6000-7fff are assigned to cartridge ROMs; in some
cartridges, a second ROM bank can be used by writing a value to a special
ROM access. This way, instead of 8 KiB we often have 16 KiB at these
locations.
Each cartridge can host up to 5 GROMs (called GROM 3, 4, 5, 6, and 7).
As in the console, each one occupies 6 KiB in an 8 KiB window.
The GRAM Kracker offers
- a loader in an own GROM 1 (which hides the console GROM 1 when active,
so we have no BASIC anymore). The contents of the loader must be found
by the emulator in a file named ti99_gkracker.zip.
- a complete set of 8 (simulated) GRAMs with full 8 KiB each (done by a
simple addressing circuit); the basic version only offered GRAMs 3-7
- 16 KiB of RAM memory space for the 6000-7fff area (called "bank 1" and "bank 2")
Notes:
- it is mandatory to turn off the loader when loading into GRAM 1, but only
after prompted in the on-screen instructions, or the loader will crash
- GRAM0 must be properly loaded if switch 2 is set to GRAM0 and the computer is reset
- Switch 4 must not be in W/P position (write protect) when loading data
into the GK (either other position will do).
***************************************************************************/
#include "emu.h"
#include "gkracker.h"
DEFINE_DEVICE_TYPE_NS(TI99_GROMPORT_GK, bus::ti99::gromport, ti99_gkracker_device, "ti99_gkracker", "Miller's Graphics GRAM Kracker")
namespace bus { namespace ti99 { namespace gromport {
enum
{
GK_OFF = 0,
GK_NORMAL = 1,
GK_GRAM0 = 0,
GK_OPSYS = 1,
GK_GRAM12 = 0,
GK_TIBASIC = 1,
GK_BANK1 = 0,
GK_WP = 1,
GK_BANK2 = 2,
GK_LDON = 0,
GK_LDOFF = 1
};
#define GKSWITCH1_TAG "GKSWITCH1"
#define GKSWITCH2_TAG "GKSWITCH2"
#define GKSWITCH3_TAG "GKSWITCH3"
#define GKSWITCH4_TAG "GKSWITCH4"
#define GKSWITCH5_TAG "GKSWITCH5"
#define TRACE_GKRACKER 0
#define TRACE_CHANGE 0
#define GKRACKER_NVRAM_TAG "gkracker_nvram"
#define GKRACKER_ROM_TAG "gkracker_rom"
ti99_gkracker_device::ti99_gkracker_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: cartridge_connector_device(mconfig, TI99_GROMPORT_GK, tag, owner, clock),
device_nvram_interface(mconfig, *this),
m_romspace_selected(false),
m_ram_page(0),
m_grom_address(0),
m_ram_ptr(nullptr),
m_grom_ptr(nullptr),
m_waddr_LSB(false),
m_cartridge(nullptr)
{
}
WRITE_LINE_MEMBER(ti99_gkracker_device::romgq_line)
{
m_romspace_selected = (state==ASSERT_LINE);
// Propagate to the guest
if (m_cartridge != nullptr) m_cartridge->romgq_line(state);
}
/*
Combined select lines
*/
WRITE8_MEMBER(ti99_gkracker_device::set_gromlines)
{
m_grom_selected = (data != 0);
if (m_cartridge != nullptr) m_cartridge->set_gromlines(space, offset, data);
}
WRITE_LINE_MEMBER(ti99_gkracker_device::gclock_in)
{
if (m_cartridge != nullptr) m_cartridge->gclock_in(state);
}
/*
Check whether the GROMs are idle.
*/
bool ti99_gkracker_device::is_grom_idle()
{
return (m_cartridge != nullptr)? m_cartridge->is_grom_idle() : false;
}
READ8Z_MEMBER(ti99_gkracker_device::readz)
{
if (m_grom_selected)
{
// Reads from the GRAM space of the GRAM Kracker.
int id = ((m_grom_address & 0xe000)>>13)&0x07;
// The GK does not have a readable address counter, but the console
// GROMs and the GROMs of the guest cartridge will keep our address
// counter up to date.
if ((offset & 0x0002)==0)
{
// Reading data
if (((id==0) && (m_gk_switch[2]==GK_GRAM0))
|| ((id==1) && (m_gk_switch[5]==GK_LDOFF) && (m_gk_switch[3]==GK_GRAM12))
|| ((id==2) && (m_gk_switch[3]==GK_GRAM12))
|| ((id>=3) && (m_gk_switch[1]==GK_NORMAL)))
*value = m_ram_ptr[m_grom_address];
if ((id==1) && (m_gk_switch[5]==GK_LDON))
*value = m_grom_ptr[m_grom_address & 0x1fff];
// The GK GROM emulation does not wrap at 8K boundaries.
m_grom_address = (m_grom_address + 1) & 0xffff;
// Reset the write address flipflop.
m_waddr_LSB = false;
if (TRACE_GKRACKER) logerror("GROM read -> %02x\n", *value);
}
}
if (m_romspace_selected)
{
// Reads from the RAM space of the GRAM Kracker.
// RAM is stored behind the GRAM area
// Note that offset is 0000...1fff
// When switch in middle position (WP) do bank select according to page flag
if (m_gk_switch[1] == GK_NORMAL)
{
int base = ((m_gk_switch[4]==GK_BANK1) || ((m_gk_switch[4]==GK_WP) && (m_ram_page==0)))? 0x10000 : 0x12000;
*value = m_ram_ptr[offset | base];
if (TRACE_GKRACKER) logerror("Read %04x -> %02x\n", offset | 0x6000, *value);
}
}
// If the guest has GROMs or ROMs they will override the GK contents
if (m_cartridge != nullptr)
{
// For debugging
uint8_t val1 = *value;
// Read from the guest cartridge.
m_cartridge->readz(space, offset, value, mem_mask);
if (TRACE_GKRACKER)
if (val1 != *value) logerror("Read (from guest) %04x -> %02x\n", offset, *value);
}
}
WRITE8_MEMBER(ti99_gkracker_device::write)
{
// write to the guest cartridge if present
if (m_cartridge != nullptr)
{
m_cartridge->write(space, offset, data, mem_mask);
}
if (m_grom_selected)
{
// Write to the GRAM space of the GRAM Kracker.
if ((offset & 0x0002)==0x0002)
{
// Set address
if (m_waddr_LSB == true)
{
// Accept low address byte (second write)
m_grom_address = (m_grom_address & 0xff00) | data;
m_waddr_LSB = false;
if (TRACE_GKRACKER) logerror("Set GROM address %04x\n", m_grom_address);
}
else
{
// Accept high address byte (first write)
m_grom_address = (m_grom_address & 0x00ff) | (data << 8);
m_waddr_LSB = true;
}
}
else
{
// Write data byte to GRAM area.
if (TRACE_GKRACKER) logerror("GROM write %04x(%04x) <- %02x\n", offset, m_grom_address, data);
// According to manual:
// Writing to GRAM 0: switch 2 set to GRAM 0 + Write protect switch (4) in 1 or 2 position
// Writing to GRAM 1: switch 3 set to GRAM 1-2 + Loader off (5); write prot has no effect
// Writing to GRAM 2: switch 3 set to GRAM 1-2 (write prot has no effect)
// Writing to GRAM 3-7: switch 1 set to GK_NORMAL, no cartridge inserted
// GK_NORMAL switch has no effect on GRAM 0-2
int id = ((m_grom_address & 0xe000)>>13)&0x07;
if ((id==0 && m_gk_switch[2]==GK_GRAM0 && m_gk_switch[4]!=GK_WP)
|| (id==1 && m_gk_switch[3]==GK_GRAM12 && m_gk_switch[5]==GK_LDOFF)
|| (id==2 && m_gk_switch[3]==GK_GRAM12)
|| (id>=3 && m_gk_switch[1]==GK_NORMAL))
m_ram_ptr[m_grom_address] = data;
// The GK GROM emulation does not wrap at 8K boundaries.
m_grom_address = (m_grom_address + 1) & 0xffff;
// Reset the write address flipflop.
m_waddr_LSB = false;
}
}
if (m_romspace_selected)
{
// Write to the RAM space of the GRAM Kracker
if (TRACE_GKRACKER) logerror("Write %04x <- %02x\n", offset | 0x6000, data);
if (m_gk_switch[1] == GK_NORMAL)
{
if (m_gk_switch[4]==GK_BANK1) m_ram_ptr[offset | 0x10000] = data;
else if (m_gk_switch[4]==GK_BANK2) m_ram_ptr[offset | 0x12000] = data;
// Switch in middle position (WP, implies auto-select according to the page flag)
// This is handled like in Extended Basic (using addresses)
else m_ram_page = (offset >> 1) & 1;
}
}
}
READ8Z_MEMBER( ti99_gkracker_device::crureadz )
{
if (m_cartridge != nullptr) m_cartridge->crureadz(space, offset, value);
}
WRITE8_MEMBER( ti99_gkracker_device::cruwrite )
{
if (m_cartridge != nullptr) m_cartridge->cruwrite(space, offset, data);
}
INPUT_CHANGED_MEMBER( ti99_gkracker_device::gk_changed )
{
if (TRACE_GKRACKER) logerror("Input changed %d - %d\n", (int)((uint64_t)param & 0x07), newval);
m_gk_switch[(uint64_t)param & 0x07] = newval;
}
void ti99_gkracker_device::insert(int index, ti99_cartridge_device* cart)
{
if (TRACE_CHANGE) logerror("Insert cartridge\n");
m_cartridge = cart;
// Switch 1 has a third location for resetting. We do the reset by default
// here. It can be turned off in the configuration.
m_gromport->cartridge_inserted();
}
void ti99_gkracker_device::remove(int index)
{
if (TRACE_CHANGE) logerror("Remove cartridge\n");
m_cartridge = nullptr;
}
void ti99_gkracker_device::gk_install_menu(const char* menutext, int len, int ptr, int next, int start)
{
const int base = 0x0000;
m_ram_ptr[base + ptr] = (uint8_t)((next >> 8) & 0xff);
m_ram_ptr[base + ptr+1] = (uint8_t)(next & 0xff);
m_ram_ptr[base + ptr+2] = (uint8_t)((start >> 8) & 0xff);
m_ram_ptr[base + ptr+3] = (uint8_t)(start & 0xff);
m_ram_ptr[base + ptr+4] = (uint8_t)(len & 0xff);
memcpy(m_ram_ptr + base + ptr+5, menutext, len);
}
/*
Define the default for the GRAM Kracker device. The memory is preset with
some sample entries which shall indicate that the memory has been tested
by the manufacturer.
*/
void ti99_gkracker_device::nvram_default()
{
if (TRACE_GKRACKER) logerror("Creating default NVRAM\n");
memset(m_ram_ptr, 0, 81920);
m_ram_ptr[0x6000] = 0xaa;
m_ram_ptr[0x6001] = 0x01;
m_ram_ptr[0x6002] = 0x01;
m_ram_ptr[0x6006] = 0x60;
m_ram_ptr[0x6007] = 0x20;
gk_install_menu("GROM 3 OK", 9, 0x60e0, 0, 0x6100);
gk_install_menu("GROM 4 OK", 9, 0x60c0, 0x60e0, 0x6100);
gk_install_menu("GROM 5 OK", 9, 0x60a0, 0x60c0, 0x6100);
gk_install_menu("GROM 6 OK", 9, 0x6080, 0x60a0, 0x6100);
gk_install_menu("PROM OK", 9, 0x6060, 0x6080, 0x6100);
gk_install_menu("RAMS OK", 9, 0x6040, 0x6060, 0x6100);
gk_install_menu("OPTION GRAMS OK", 15, 0x6020, 0x6040, 0x6100);
m_ram_ptr[0x6100] = 0x0b; // GPL EXIT
}
void ti99_gkracker_device::nvram_read(emu_file &file)
{
int readsize = file.read(m_ram_ptr, 81920);
if (TRACE_GKRACKER) logerror("Reading NVRAM\n");
// If we increased the size, fill the remaining parts with 0
if (readsize < 81920)
{
memset(m_ram_ptr + readsize, 0, 81920-readsize);
}
}
void ti99_gkracker_device::nvram_write(emu_file &file)
{
if (TRACE_GKRACKER) logerror("Writing NVRAM\n");
file.write(m_ram_ptr, 81920);
}
void ti99_gkracker_device::device_start()
{
m_ram_ptr = memregion(GKRACKER_NVRAM_TAG)->base();
m_grom_ptr = memregion(GKRACKER_ROM_TAG)->base();
m_cartridge = nullptr;
for (int i=1; i < 6; i++) m_gk_switch[i] = 0;
save_pointer(NAME(m_gk_switch),6);
save_item(NAME(m_romspace_selected));
save_item(NAME(m_ram_page));
save_item(NAME(m_grom_address));
save_item(NAME(m_waddr_LSB));
}
void ti99_gkracker_device::device_reset()
{
m_gk_switch[1] = ioport(GKSWITCH1_TAG)->read();
m_gk_switch[2] = ioport(GKSWITCH2_TAG)->read();
m_gk_switch[3] = ioport(GKSWITCH3_TAG)->read();
m_gk_switch[4] = ioport(GKSWITCH4_TAG)->read();
m_gk_switch[5] = ioport(GKSWITCH5_TAG)->read();
m_grom_address = 0; // for the GROM emulation
m_ram_page = 0;
m_waddr_LSB = false;
m_grom_selected = false;
}
static MACHINE_CONFIG_START( gkracker_slot )
MCFG_DEVICE_ADD("cartridge", TI99_CART, 0)
MACHINE_CONFIG_END
/*
The GRAMKracker ROM
*/
ROM_START( gkracker_rom )
ROM_REGION(0x14000, GKRACKER_NVRAM_TAG, ROMREGION_ERASE00)
ROM_REGION(0x2000, GKRACKER_ROM_TAG, 0)
ROM_LOAD("gkracker.bin", 0x0000, 0x2000, CRC(86eaaf9f) SHA1(a3bd5257c63e190800921b52dbe3ffa91ad91113))
ROM_END
const tiny_rom_entry *ti99_gkracker_device::device_rom_region() const
{
return ROM_NAME( gkracker_rom );
}
machine_config_constructor ti99_gkracker_device::device_mconfig_additions() const
{
return MACHINE_CONFIG_NAME( gkracker_slot );
}
INPUT_PORTS_START(gkracker)
PORT_START( GKSWITCH1_TAG )
PORT_DIPNAME( 0x01, 0x01, "GK switch 1" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 1)
PORT_DIPSETTING( 0x00, "GK Off" )
PORT_DIPSETTING( 0x01, DEF_STR( Normal ) )
PORT_START( GKSWITCH2_TAG )
PORT_DIPNAME( 0x01, 0x01, "GK switch 2" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 2)
PORT_DIPSETTING( 0x00, "GRAM 0" )
PORT_DIPSETTING( 0x01, "Op Sys" )
PORT_START( GKSWITCH3_TAG )
PORT_DIPNAME( 0x01, 0x01, "GK switch 3" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 3)
PORT_DIPSETTING( 0x00, "GRAM 1-2" )
PORT_DIPSETTING( 0x01, "TI BASIC" )
PORT_START( GKSWITCH4_TAG )
PORT_DIPNAME( 0x03, 0x01, "GK switch 4" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 4)
PORT_DIPSETTING( 0x00, "Bank 1" )
PORT_DIPSETTING( 0x01, "W/P" )
PORT_DIPSETTING( 0x02, "Bank 2" )
PORT_START( GKSWITCH5_TAG )
PORT_DIPNAME( 0x01, 0x00, "GK switch 5" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_gkracker_device, gk_changed, 5)
PORT_DIPSETTING( 0x00, "Loader On" )
PORT_DIPSETTING( 0x01, "Loader Off" )
INPUT_PORTS_END
ioport_constructor ti99_gkracker_device::device_input_ports() const
{
return INPUT_PORTS_NAME(gkracker);
}
} } } // end namespace bus::ti99::gromport

View File

@ -0,0 +1,71 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/****************************************************************************
GRAM Kracker.
*****************************************************************************/
#ifndef MAME_BUS_TI99_GROMPORT_GKRACKER_H
#define MAME_BUS_TI99_GROMPORT_GKRACKER_H
#pragma once
#include "bus/ti99/ti99defs.h"
#include "cartridges.h"
namespace bus { namespace ti99 { namespace gromport {
class ti99_gkracker_device : public cartridge_connector_device, public device_nvram_interface
{
public:
ti99_gkracker_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
DECLARE_WRITE_LINE_MEMBER(romgq_line) override;
DECLARE_WRITE8_MEMBER(set_gromlines) override;
DECLARE_WRITE_LINE_MEMBER(gclock_in) override;
void insert(int index, ti99_cartridge_device* cart) override;
void remove(int index) override;
DECLARE_INPUT_CHANGED_MEMBER( gk_changed );
// We may have a cartridge plugged into the GK
bool is_grom_idle() override;
protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual machine_config_constructor device_mconfig_additions() const override;
virtual const tiny_rom_entry* device_rom_region() const override;
virtual ioport_constructor device_input_ports() const override;
// device_nvram_interface
void nvram_default() override;
void nvram_read(emu_file &file) override;
void nvram_write(emu_file &file) override;
private:
int m_gk_switch[6]; // Used to cache the switch settings.
bool m_romspace_selected;
int m_ram_page;
int m_grom_address;
uint8_t* m_ram_ptr;
uint8_t* m_grom_ptr;
bool m_waddr_LSB;
ti99_cartridge_device *m_cartridge; // guest cartridge
// Just for proper initialization
void gk_install_menu(const char* menutext, int len, int ptr, int next, int start);
};
} } } // end namespace bus::ti99::gromport
DECLARE_DEVICE_TYPE_NS(TI99_GROMPORT_GK, bus::ti99::gromport, ti99_gkracker_device)
#endif // MAME_BUS_TI99_GROMPORT_GKRACKER_H

View File

@ -0,0 +1,297 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/***************************************************************************
GROM port - the cartridge port of the TI-99/4, TI-99/4A, and
TI-99/8 console.
The name refers to the main intended application scenario, that is,
to host cartridges with GROMs. The second, wider port of the console is
called I/O port and connects to the Peripheral Expansion System
(see peribox.h).
LEFT
/RESET 1||2 GND
D7 3||4 CRUCLK
D6 5||6 CRUIN
D5 7||8 A15/CRUOUT
D4 9||10 A13
D3 11||12 A12
D2 13||14 A11
D1 15||16 A10
D0 17||18 A9
+5V 19||20 A8
/GS 21||22 A7
A14 23||24 A3
DBIN 25||26 A6
GRMCLK 27||28 A5
-5V 29||30 A4
READY 31||32 /WE
GND 33||34 /ROMG
GND 35||36 GND
RIGHT
Address bus line ordering, according to TI convention, is A0 (MSB) ... A15 (LSB).
A0, A1, and A2 are not delivered to the port but decoded in the console:
/ROMG is asserted for A0/A1/A2 = 011 (addresses 6000 - 7fff)
/GS is asserted for A0...A5 = 100110 (addresses 9800 - 9bff)
This means that a maximum of 8 KiB of direct memory space can be accessed.
The /GS line is used to enable GROM circuits on the board (serial ROMs with
own address counter, see tmc0430.h).
When a cartridge is inserted the /RESET line is pulled to ground, which
via a R/C component pulls down the /RESET input of the timer circuit for
a short time, which in turn resets the CPU. In order to dump cartridges,
a common procedure was to tape the /RESET line of the cartridge. However,
inserting a cartridge without resetting often caused so much data bus noise
that the console usually locked up.
----------------
The TI-99/4A computer was strictly designed for cartridge usage. The basic
console had only little directly accessible RAM and offered no ways to
write machine language programs; cartridges were intended to add various
capabilities, or just for running games.
Beside the seemingly simple handling, Texas Instruments had own intentions
behind their cartridge strategy. With only 8 KiB of direct access memory, a
major part of the cartridge code had to be stored in GROMs, which had to be
licensed from Texas Instruments. Thus they kept firm control over all
software development.
Over the years, and with the increasingly difficult market situations,
TI's policies seem to have changed. This may be the reason that the built-in
operating system actually allowed for running ROM-only cartridges until TI
clipped out this part in the OS, banning cartridges without GROMs. Consoles
with this modification were produced in 1983, TI's last year in the home
computer business.
Although only 8 KiB were available for direct addressing, clever techniques
were invented by third-party manufacturers. The first extension was utilized
by TI themselves in the Extended Basic cartridge which offers two banks
of ROM contents. Switching between the banks is achieved by writing a value
to the ROM space at 6000 or 6002. Later, cartridges with much more memory
space were created, up to the Super Space II cartridge with 128 KiB of
buffered SRAM.
----------------
From the console case layout the GROM port was intended for a single
cartridge only. Although never officially released, the operating system
of the TI console supported a multi-cartridge extender with software
switching. There were also extenders based on hardware switching (like
the Navarone Widget).
This emulation offers both variants as slot options:
-gromport single : default single cartridge connector
-gromport multi : software-switchable 4-slot cartridge extender
-gromport gkracker : GRAM Kracker
The last option enables another popular device, the GRAM Kracker. This is
a device to be plugged into the cartridge slot with five manual switches
at the front and an own cartridge slot at its top. It contains buffered
SRAM, a built-in ROM, and a GROM simulator which simulates GROM behaviour
when accessing the buffered RAM. Its main use is to provide editable
storage for the read-only cartridge contents. Cartridges can be plugged
into its slot; their contents can be read and written to disk, modified as
needed, and loaded into the buffered RAM. Even the console GROMs can be
copied into the device; despite running in parallel, the GROM simulator
is able to override the console GROMs, thus allowing the user to install
a customized OS.
***************************************************************************/
#include "emu.h"
#include "gromport.h"
#include "emuopts.h"
#include "image.h"
#include "softlist.h"
#include "singleconn.h"
#include "multiconn.h"
#include "gkracker.h"
DEFINE_DEVICE_TYPE_NS(TI99_GROMPORT, bus::ti99::gromport, gromport_device, "gromport", "TI-99 Cartridge port")
namespace bus { namespace ti99 { namespace gromport {
#define TRACE_READ 0
#define TRACE_WRITE 0
gromport_device::gromport_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: bus8z_device(mconfig, TI99_GROMPORT, tag, owner, clock),
device_slot_interface(mconfig, *this),
m_connector(nullptr),
m_reset_on_insert(true),
m_console_ready(*this),
m_console_reset(*this)
{ }
/*
Reading via the GROM port. Only 13 address lines are passed through
on the TI-99/4A, and 14 lines on the TI-99/8.
*/
READ8Z_MEMBER(gromport_device::readz)
{
if (m_connector != nullptr)
{
m_connector->readz(space, offset & m_mask, value);
if (TRACE_READ) if (m_romgq) logerror("Read %04x -> %02x\n", offset | 0x6000, *value);
}
}
/*
Writing via the GROM port. Only 13 address lines are passed through
on the TI-99/4A, and 14 lines on the TI-99/8.
*/
WRITE8_MEMBER(gromport_device::write)
{
if (m_connector != nullptr)
{
if (TRACE_WRITE) if (m_romgq) logerror("Write %04x <- %02x\n", offset | 0x6000, data);
m_connector->write(space, offset & m_mask, data);
}
}
READ8Z_MEMBER(gromport_device::crureadz)
{
if (m_connector != nullptr)
m_connector->crureadz(space, offset, value);
}
WRITE8_MEMBER(gromport_device::cruwrite)
{
if (m_connector != nullptr)
m_connector->cruwrite(space, offset, data);
}
WRITE_LINE_MEMBER(gromport_device::ready_line)
{
m_console_ready(state);
}
/*
Asserted when the console addresses cartridge rom.
*/
WRITE_LINE_MEMBER(gromport_device::romgq_line)
{
m_romgq = state;
if (m_connector != nullptr)
m_connector->romgq_line(state);
}
WRITE_LINE_MEMBER(gromport_device::gclock_in)
{
if (m_connector != nullptr)
m_connector->gclock_in(state);
}
/*
Combined GROM control lines.
*/
WRITE8_MEMBER( gromport_device::set_gromlines )
{
if (m_connector != nullptr)
m_connector->set_gromlines(space, offset, data);
}
void gromport_device::device_start()
{
m_console_ready.resolve();
m_console_reset.resolve();
save_item(NAME(m_romgq));
}
void gromport_device::device_reset()
{
m_reset_on_insert = (ioport("CARTRESET")->read()==0x01);
}
/*
Shall we reset the console when a cartridge has been inserted?
This is triggered by the cartridge by pulling down /RESET via a capacitor.
Accordingly, when we put a tape over the /RESET contact we can avoid the
reset, which is useful when we want to swap the cartridges while a program
is runnning.
*/
void gromport_device::cartridge_inserted()
{
if (m_reset_on_insert)
{
m_console_reset(ASSERT_LINE);
m_console_reset(CLEAR_LINE);
}
}
/*
Find out whether the GROMs in the cartridge are idle. In that case,
cut the clock line.
*/
bool gromport_device::is_grom_idle()
{
if (m_connector != nullptr)
return m_connector->is_grom_idle();
else
return false;
}
void gromport_device::device_config_complete()
{
m_connector = downcast<cartridge_connector_device*>(subdevices().first());
}
INPUT_PORTS_START(gromport)
PORT_START( "CARTRESET" )
PORT_CONFNAME( 0x01, 0x01, "RESET on cartridge insert" )
PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
PORT_CONFSETTING( 0x01, DEF_STR( On ) )
INPUT_PORTS_END
ioport_constructor gromport_device::device_input_ports() const
{
return INPUT_PORTS_NAME(gromport);
}
/***************************************************************************
Different versions of cartridge connections
single: the standard console connector, one cartridge
multi: a multi-cart expander, up to 4 cartridges with software selection
gkracker: GRAMKracker, a device with NVRAM which allows the user to copy
the contents of the cartridge plugged into its slot into the NVRAM
and to modify it.
***************************************************************************/
cartridge_connector_device::cartridge_connector_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: bus8z_device(mconfig, type, tag, owner, clock),
m_gromport(nullptr)
{
}
WRITE_LINE_MEMBER( cartridge_connector_device::ready_line )
{
m_gromport->ready_line(state);
}
void cartridge_connector_device::device_config_complete()
{
m_gromport = static_cast<gromport_device*>(owner());
}
} } } // end namespace bus::ti99::gromport
SLOT_INTERFACE_START( gromport4 )
SLOT_INTERFACE("single", TI99_GROMPORT_SINGLE)
SLOT_INTERFACE("multi", TI99_GROMPORT_MULTI)
SLOT_INTERFACE("gkracker", TI99_GROMPORT_GK)
SLOT_INTERFACE_END
SLOT_INTERFACE_START( gromport8 )
SLOT_INTERFACE("single", TI99_GROMPORT_SINGLE)
SLOT_INTERFACE("multi", TI99_GROMPORT_MULTI)
SLOT_INTERFACE_END

View File

@ -0,0 +1,112 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/***************************************************************************
Gromport (Cartridge port) of the TI-99 consoles
For details see gromport.cpp
Michael Zapf
***************************************************************************/
#ifndef MAME_BUS_TI99_GROMPORT_GROMPORT_H
#define MAME_BUS_TI99_GROMPORT_GROMPORT_H
#pragma once
#include "bus/ti99/ti99defs.h"
namespace bus { namespace ti99 { namespace gromport {
struct pcb_type
{
int id;
const char* name;
};
class ti99_cartridge_device;
class cartridge_connector_device;
class gromport_device : public bus8z_device, public device_slot_interface
{
public:
gromport_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz);
DECLARE_WRITE8_MEMBER(cruwrite);
DECLARE_WRITE_LINE_MEMBER(ready_line);
DECLARE_WRITE_LINE_MEMBER(romgq_line);
DECLARE_WRITE8_MEMBER(set_gromlines); // Combined GROM select lines
DECLARE_WRITE_LINE_MEMBER(gclock_in);
static void set_mask(device_t &device, int mask) { downcast<gromport_device &>(device).m_mask = mask; }
template <class Object> static devcb_base &static_set_ready_callback(device_t &device, Object &&cb) { return downcast<gromport_device &>(device).m_console_ready.set_callback(std::forward<Object>(cb)); }
template <class Object> static devcb_base &static_set_reset_callback(device_t &device, Object &&cb) { return downcast<gromport_device &>(device).m_console_reset.set_callback(std::forward<Object>(cb)); }
void cartridge_inserted();
bool is_grom_idle();
protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_config_complete() override;
virtual ioport_constructor device_input_ports() const override;
private:
cartridge_connector_device* m_connector;
bool m_reset_on_insert;
devcb_write_line m_console_ready;
devcb_write_line m_console_reset;
int m_mask;
int m_romgq;
};
class cartridge_connector_device : public bus8z_device
{
public:
virtual DECLARE_READ8Z_MEMBER(crureadz) = 0;
virtual DECLARE_WRITE8_MEMBER(cruwrite) = 0;
virtual DECLARE_WRITE_LINE_MEMBER(romgq_line) = 0;
virtual DECLARE_WRITE8_MEMBER(set_gromlines) = 0;
virtual DECLARE_WRITE_LINE_MEMBER(gclock_in) = 0;
DECLARE_WRITE_LINE_MEMBER(ready_line);
virtual void insert(int index, bus::ti99::gromport::ti99_cartridge_device* cart) { m_gromport->cartridge_inserted(); }
virtual void remove(int index) { }
virtual bool is_grom_idle() = 0;
protected:
cartridge_connector_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
virtual void device_config_complete() override;
gromport_device* m_gromport;
bool m_grom_selected;
};
} } } // end namespace bus::ti99::gromport
SLOT_INTERFACE_EXTERN(gromport4);
SLOT_INTERFACE_EXTERN(gromport8);
#define MCFG_GROMPORT4_ADD( _tag ) \
MCFG_DEVICE_ADD(_tag, TI99_GROMPORT, 0) \
bus::ti99::gromport::gromport_device::set_mask(*device, 0x1fff); \
MCFG_DEVICE_SLOT_INTERFACE(gromport4, "single", false)
#define MCFG_GROMPORT8_ADD( _tag ) \
MCFG_DEVICE_ADD(_tag, TI99_GROMPORT, 0) \
bus::ti99::gromport::gromport_device::set_mask(*device, 0x3fff); \
MCFG_DEVICE_SLOT_INTERFACE(gromport8, "single", false)
#define MCFG_GROMPORT_READY_HANDLER( _ready ) \
devcb = &bus::ti99::gromport::gromport_device::static_set_ready_callback( *device, DEVCB_##_ready );
#define MCFG_GROMPORT_RESET_HANDLER( _reset ) \
devcb = &bus::ti99::gromport::gromport_device::static_set_reset_callback( *device, DEVCB_##_reset );
DECLARE_DEVICE_TYPE_NS(TI99_GROMPORT, bus::ti99::gromport, gromport_device)
#endif // MAME_BUS_TI99_GROMPORT_GROMPORT_H

View File

@ -0,0 +1,332 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/**************************************************************************
The multi-cartridge extender
This is a somewhat mythical device which was never available for the normal
customer, but there are reports of the existence of such a device
in development labs or demonstrations.
The interesting thing about this is that the OS of the console
fully supports this multi-cartridge extender, providing a selection
option on the screen to switch between different plugged-in
cartridges.
The switching is possible by decoding address lines that are reserved
for GROM access. GROMs are accessed via four separate addresses
9800, 9802, 9C00, 9C02. The addressing scheme looks like this:
1001 1Wxx xxxx xxM0 W = write(1), read(0), M = address(1), data(0)
This leaves 8 bits (256 options) which are not decoded inside the
console. As the complete address is routed to the port, some circuit
just needs to decode the xxx lines and turn on the respective slot.
One catch must be considered: Some cartridges contain ROMs which are
directly accessed and not via ports. This means that the ROMs must
be activated according to the slot that is selected.
Another issue: Each GROM contains an own address counter and an ID.
According to the ID the GROM only delivers data if the address counter
is within the ID area (0 = 0000-1fff, 1=2000-3fff ... 7=e000-ffff).
Thus it is essential that all GROMs stay in sync with their address
counters. We have to route all address settings to all slots and their
GROMs, even when the slot has not been selected before. The selected
just shows its effect when data is read. In this case, only the
data from the selected slot will be delivered.
This may be considered as a design flaw within the complete cartridge system
which eventually led to TI not manufacturing that device for the broad
market.
***************************************************************************/
#include "multiconn.h"
DEFINE_DEVICE_TYPE_NS(TI99_GROMPORT_MULTI, bus::ti99::gromport, ti99_multi_cart_conn_device, "ti99_mcartconn", "TI-99 Multi-cartridge extender")
namespace bus { namespace ti99 { namespace gromport {
#define AUTO -1
#define TRACE_CHANGE 0
ti99_multi_cart_conn_device::ti99_multi_cart_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: cartridge_connector_device(mconfig, TI99_GROMPORT_MULTI, tag, owner, clock),
m_active_slot(0),
m_fixed_slot(0),
m_next_free_slot(0)
{
}
/*
Activates a slot in the multi-cartridge extender.
Setting the slot is done by accessing the GROM ports using a
specific address:
slot 0: 0x9800 (0x9802, 0x9c00, 0x9c02) : cartridge1
slot 1: 0x9804 (0x9806, 0x9c04, 0x9c06) : cartridge2
...
slot 15: 0x983c (0x983e, 0x9c3c, 0x9c3e) : cartridge16
Scheme:
1001 1Wxx xxxx xxM0 (M=mode; M=0: data, M=1: address; W=write)
The following addresses are theoretically available, but the
built-in OS does not use them; i.e. cartridges will not be
included in the selection list, and their features will not be
found by lookup, but they could be accessed directly by user
programs.
slot 16: 0x9840 (0x9842, 0x9c40, 0x9c42)
...
slot 255: 0x9bfc (0x9bfe, 0x9ffc, 0x9ffe)
Setting the GROM base should select one cartridge, but the ROMs in the
CPU space must also be switched. As there is no known special mechanism
we assume that by switching the GROM base, the ROM is automatically
switched.
Caution: This means that cartridges which do not have at least one
GROM cannot be switched with this mechanism.
We assume that the slot number is already calculated in the caller:
slotnumber>=0 && slotnumber<=255
NOTE: The OS will stop searching when it finds slots 1 and 2 empty.
Interestingly, cartridge subroutines are found nevertheless, even when
the cartridge is plugged into a higher slot.
*/
void ti99_multi_cart_conn_device::set_slot(int slotnumber)
{
if (TRACE_CHANGE)
if (m_active_slot != slotnumber) logerror("Setting cartslot to %d\n", slotnumber);
if (m_fixed_slot==AUTO)
m_active_slot = slotnumber;
else
m_active_slot = m_fixed_slot;
}
int ti99_multi_cart_conn_device::get_active_slot(bool changebase, offs_t offset)
{
int slot;
if (changebase)
{
// GROM selected?
if (m_grom_selected) set_slot((offset>>2) & 0x00ff);
}
slot = m_active_slot;
return slot;
}
void ti99_multi_cart_conn_device::insert(int index, ti99_cartridge_device* cart)
{
if (TRACE_CHANGE) logerror("Insert slot %d\n", index);
m_cartridge[index] = cart;
m_gromport->cartridge_inserted();
}
void ti99_multi_cart_conn_device::remove(int index)
{
if (TRACE_CHANGE) logerror("Remove slot %d\n", index);
m_cartridge[index] = nullptr;
}
WRITE_LINE_MEMBER(ti99_multi_cart_conn_device::romgq_line)
{
m_readrom = state;
// Propagate to all slots
for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++)
{
if (m_cartridge[i] != nullptr)
{
m_cartridge[i]->romgq_line(state);
}
}
}
/*
Combined select lines
*/
WRITE8_MEMBER(ti99_multi_cart_conn_device::set_gromlines)
{
// GROM selected?
m_grom_selected = (data != 0);
// Propagate to all slots
for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++)
{
if (m_cartridge[i] != nullptr)
{
m_cartridge[i]->set_gromlines(space, offset, data);
}
}
}
WRITE_LINE_MEMBER(ti99_multi_cart_conn_device::gclock_in)
{
// Propagate to all slots
for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++)
{
if (m_cartridge[i] != nullptr)
{
m_cartridge[i]->gclock_in(state);
}
}
}
READ8Z_MEMBER(ti99_multi_cart_conn_device::readz)
{
int slot = get_active_slot(true, offset);
// If we have a GROM access, we need to send the read request to all
// attached cartridges so the slot is irrelevant here. Each GROM
// contains an internal address counter, and we must make sure they all stay in sync.
if (m_grom_selected)
{
for (int i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++)
{
if (m_cartridge[i] != nullptr)
{
uint8_t newval = *value;
m_cartridge[i]->readz(space, offset, &newval, 0xff);
if (i==slot)
{
*value = newval;
}
}
}
}
else
{
if (slot < NUMBER_OF_CARTRIDGE_SLOTS && m_cartridge[slot] != nullptr)
{
m_cartridge[slot]->readz(space, offset, value, 0xff);
}
}
}
WRITE8_MEMBER(ti99_multi_cart_conn_device::write)
{
// Same issue as above (read)
// We don't have GRAM cartridges, anyway, so it's just used for setting the address.
if (m_grom_selected)
{
for (auto & elem : m_cartridge)
{
if (elem != nullptr)
{
elem->write(space, offset, data, 0xff);
}
}
}
else
{
int slot = get_active_slot(true, offset);
if (slot < NUMBER_OF_CARTRIDGE_SLOTS && m_cartridge[slot] != nullptr)
{
// logerror("writing %04x (slot %d) <- %02x\n", offset, slot, data);
m_cartridge[slot]->write(space, offset, data, 0xff);
}
}
}
READ8Z_MEMBER(ti99_multi_cart_conn_device::crureadz)
{
int slot = get_active_slot(false, offset);
/* Sanity check. Higher slots are always empty. */
if (slot >= NUMBER_OF_CARTRIDGE_SLOTS)
return;
if (m_cartridge[slot] != nullptr)
{
m_cartridge[slot]->crureadz(space, offset, value);
}
}
WRITE8_MEMBER(ti99_multi_cart_conn_device::cruwrite)
{
int slot = get_active_slot(true, offset);
/* Sanity check. Higher slots are always empty. */
if (slot >= NUMBER_OF_CARTRIDGE_SLOTS)
return;
if (m_cartridge[slot] != nullptr)
{
m_cartridge[slot]->cruwrite(space, offset, data);
}
}
/*
Check whether the GROMs are idle. Just ask the currently
active cartridge.
*/
bool ti99_multi_cart_conn_device::is_grom_idle()
{
/* Sanity check. Higher slots are always empty. */
if (m_active_slot >= NUMBER_OF_CARTRIDGE_SLOTS)
return false;
if (m_cartridge[m_active_slot] != nullptr)
return m_cartridge[m_active_slot]->is_grom_idle();
return false;
}
void ti99_multi_cart_conn_device::device_start()
{
m_next_free_slot = 0;
m_active_slot = 0;
for (auto & elem : m_cartridge)
{
elem = nullptr;
}
save_item(NAME(m_readrom));
save_item(NAME(m_active_slot));
save_item(NAME(m_fixed_slot));
save_item(NAME(m_next_free_slot));
}
void ti99_multi_cart_conn_device::device_reset(void)
{
m_active_slot = 0;
m_fixed_slot = ioport("CARTSLOT")->read() - 1;
m_grom_selected = false;
}
static MACHINE_CONFIG_START( multi_slot )
MCFG_DEVICE_ADD("cartridge1", TI99_CART, 0)
MCFG_DEVICE_ADD("cartridge2", TI99_CART, 0)
MCFG_DEVICE_ADD("cartridge3", TI99_CART, 0)
MCFG_DEVICE_ADD("cartridge4", TI99_CART, 0)
MACHINE_CONFIG_END
machine_config_constructor ti99_multi_cart_conn_device::device_mconfig_additions() const
{
return MACHINE_CONFIG_NAME( multi_slot );
}
INPUT_CHANGED_MEMBER( ti99_multi_cart_conn_device::switch_changed )
{
if (TRACE_CHANGE) logerror("Slot changed %d - %d\n", (int)((uint64_t)param & 0x07), newval);
m_active_slot = m_fixed_slot = newval - 1;
}
INPUT_PORTS_START(multi_slot)
PORT_START( "CARTSLOT" )
PORT_DIPNAME( 0x0f, 0x00, "Multi-cartridge slot" ) PORT_CHANGED_MEMBER(DEVICE_SELF, ti99_multi_cart_conn_device, switch_changed, 0)
PORT_DIPSETTING( 0x00, "Auto" )
PORT_DIPSETTING( 0x01, "Slot 1" )
PORT_DIPSETTING( 0x02, "Slot 2" )
PORT_DIPSETTING( 0x03, "Slot 3" )
PORT_DIPSETTING( 0x04, "Slot 4" )
INPUT_PORTS_END
ioport_constructor ti99_multi_cart_conn_device::device_input_ports() const
{
return INPUT_PORTS_NAME(multi_slot);
}
} } } // end namespace bus::ti99::gromport

View File

@ -0,0 +1,66 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/****************************************************************************
Multi cartridge connector.
We set the number of slots to 4, although we may have up to 16. From a
logical point of view we could have 256, but the operating system only checks
the first 16 banks.
*****************************************************************************/
#ifndef MAME_BUS_TI99_GROMPORT_MULTICONN_H
#define MAME_BUS_TI99_GROMPORT_MULTICONN_H
#pragma once
#include "bus/ti99/ti99defs.h"
#include "cartridges.h"
namespace bus { namespace ti99 { namespace gromport {
class ti99_multi_cart_conn_device : public cartridge_connector_device
{
public:
ti99_multi_cart_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
DECLARE_WRITE_LINE_MEMBER(romgq_line) override;
DECLARE_WRITE8_MEMBER(set_gromlines) override;
DECLARE_WRITE_LINE_MEMBER(gclock_in) override;
void insert(int index, ti99_cartridge_device* cart) override;
void remove(int index) override;
DECLARE_INPUT_CHANGED_MEMBER( switch_changed );
bool is_grom_idle() override;
protected:
static constexpr unsigned NUMBER_OF_CARTRIDGE_SLOTS = 4;
virtual void device_start() override;
virtual void device_reset() override;
virtual machine_config_constructor device_mconfig_additions() const override;
virtual ioport_constructor device_input_ports() const override;
private:
bool m_readrom;
int m_active_slot;
int m_fixed_slot;
int m_next_free_slot;
ti99_cartridge_device* m_cartridge[NUMBER_OF_CARTRIDGE_SLOTS];
void set_slot(int slotnumber);
int get_active_slot(bool changebase, offs_t offset);
};
} } } // end namespace bus::ti99::gromport
DECLARE_DEVICE_TYPE_NS(TI99_GROMPORT_MULTI, bus::ti99::gromport, ti99_multi_cart_conn_device)
#endif // MAME_BUS_TI99_GROMPORT_MULTICONN_H

View File

@ -0,0 +1,95 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/***************************************************************************
Single: the standard console connector, one cartridge
***************************************************************************/
#include "singleconn.h"
DEFINE_DEVICE_TYPE_NS(TI99_GROMPORT_SINGLE, bus::ti99::gromport, ti99_single_cart_conn_device, "ti99_scartconn", "TI-99 Standard cartridge connector")
namespace bus { namespace ti99 { namespace gromport {
ti99_single_cart_conn_device::ti99_single_cart_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: cartridge_connector_device(mconfig, TI99_GROMPORT_SINGLE, tag, owner, clock),
m_cartridge(nullptr)
{
}
READ8Z_MEMBER(ti99_single_cart_conn_device::readz)
{
// Pass through
m_cartridge->readz(space, offset, value);
}
WRITE8_MEMBER(ti99_single_cart_conn_device::write)
{
// Pass through
m_cartridge->write(space, offset, data);
}
READ8Z_MEMBER(ti99_single_cart_conn_device::crureadz)
{
// Pass through
m_cartridge->crureadz(space, offset, value);
}
WRITE8_MEMBER(ti99_single_cart_conn_device::cruwrite)
{
// Pass through
m_cartridge->cruwrite(space, offset, data);
}
WRITE_LINE_MEMBER(ti99_single_cart_conn_device::romgq_line)
{
// Pass through
m_cartridge->romgq_line(state);
}
/*
Combined select lines
*/
WRITE8_MEMBER(ti99_single_cart_conn_device::set_gromlines)
{
// Pass through
m_cartridge->set_gromlines(space, offset, data);
}
WRITE_LINE_MEMBER(ti99_single_cart_conn_device::gclock_in)
{
// Pass through
m_cartridge->gclock_in(state);
}
/*
Check whether the GROMs are idle.
*/
bool ti99_single_cart_conn_device::is_grom_idle()
{
return m_cartridge->is_grom_idle();
}
void ti99_single_cart_conn_device::device_start()
{
m_cartridge = static_cast<ti99_cartridge_device*>(subdevices().first());
}
void ti99_single_cart_conn_device::device_reset()
{
m_cartridge->set_slot(0);
}
static MACHINE_CONFIG_START( single_slot )
MCFG_DEVICE_ADD("cartridge", TI99_CART, 0)
MACHINE_CONFIG_END
machine_config_constructor ti99_single_cart_conn_device::device_mconfig_additions() const
{
return MACHINE_CONFIG_NAME( single_slot );
}
} } } // end namespace bus::ti99::gromport

View File

@ -0,0 +1,44 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/****************************************************************************
Single cartridge connector.
*****************************************************************************/
#ifndef MAME_BUS_TI99_GROMPORT_SINGLECONN_H
#define MAME_BUS_TI99_GROMPORT_SINGLECONN_H
#pragma once
#include "bus/ti99/ti99defs.h"
#include "cartridges.h"
namespace bus { namespace ti99 { namespace gromport {
class ti99_single_cart_conn_device : public cartridge_connector_device
{
public:
ti99_single_cart_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
DECLARE_WRITE_LINE_MEMBER(romgq_line) override;
DECLARE_WRITE8_MEMBER(set_gromlines) override;
DECLARE_WRITE_LINE_MEMBER(gclock_in) override;
bool is_grom_idle() override;
protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual machine_config_constructor device_mconfig_additions() const override;
private:
ti99_cartridge_device *m_cartridge;
};
} } } // end namespace bus::ti99::gromport
DECLARE_DEVICE_TYPE_NS(TI99_GROMPORT_SINGLE, bus::ti99::gromport, ti99_single_cart_conn_device)
#endif // MAME_BUS_TI99_GROMPORT_SINGLECONN_H

View File

@ -19,7 +19,7 @@
#pragma once
#include "bus/ti99/ti99defs.h"
#include "gromport.h"
#include "bus/ti99/gromport/gromport.h"
#include "bus/ti99/internal/ioport.h"
#include "machine/ram.h"
@ -442,9 +442,6 @@ public:
DECLARE_WRITE_LINE_MEMBER( speech_ready );
DECLARE_WRITE_LINE_MEMBER( pbox_ready );
// Emulation
// void set_gromport(gromport_device* dev) { m_gromport = dev; }
protected:
void device_start() override;
void device_reset() override;
@ -509,7 +506,7 @@ private:
required_device<tms9928a_device> m_video;
required_device<sn76496_base_device> m_sound;
required_device<cd2501ecd_device> m_speech;
required_device<bus::ti99::internal::gromport_device> m_gromport;
required_device<bus::ti99::gromport::gromport_device> m_gromport;
required_device<bus::ti99::internal::ioport_device> m_ioport;
required_device<ram_device> m_sram;

View File

@ -18,7 +18,7 @@
#include "bus/ti99/ti99defs.h"
#include "machine/tmc0430.h"
#include "gromport.h"
#include "bus/ti99/gromport/gromport.h"
#include "bus/ti99/internal/ioport.h"
#include "sound/sn76496.h"
#include "video/tms9928a.h"
@ -67,7 +67,7 @@ private:
required_device<bus::ti99::internal::ioport_device> m_ioport;
// Link to the cartridge port (aka GROM port)
required_device<gromport_device> m_gromport;
required_device<bus::ti99::gromport::gromport_device> m_gromport;
// Memory expansion (internal, 16 bit)
required_device<ram_device> m_ram16b;

View File

@ -1,468 +0,0 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/***************************************************************************
Gromport (Cartridge port) of the TI-99 consoles
For details see gromport.cpp
Michael Zapf
***************************************************************************/
#ifndef MAME_BUS_TI99_INTERNAL_GROMPORT_H
#define MAME_BUS_TI99_INTERNAL_GROMPORT_H
#pragma once
#include "bus/ti99/ti99defs.h"
#include "machine/tmc0430.h"
#include "softlist_dev.h"
namespace bus { namespace ti99 { namespace internal {
class cartridge_connector_device;
class gromport_device : public bus8z_device, public device_slot_interface
{
public:
gromport_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz);
DECLARE_WRITE8_MEMBER(cruwrite);
DECLARE_WRITE_LINE_MEMBER(ready_line);
DECLARE_WRITE_LINE_MEMBER(romgq_line);
// Combined GROM select lines
DECLARE_WRITE8_MEMBER(set_gromlines);
DECLARE_WRITE_LINE_MEMBER(gclock_in);
static void set_mask(device_t &device, int mask) { downcast<gromport_device &>(device).m_mask = mask; }
template <class Object> static devcb_base &static_set_ready_callback(device_t &device, Object &&cb) { return downcast<gromport_device &>(device).m_console_ready.set_callback(std::forward<Object>(cb)); }
template <class Object> static devcb_base &static_set_reset_callback(device_t &device, Object &&cb) { return downcast<gromport_device &>(device).m_console_reset.set_callback(std::forward<Object>(cb)); }
void cartridge_inserted();
bool is_grom_idle();
protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_config_complete() override;
virtual ioport_constructor device_input_ports() const override;
private:
cartridge_connector_device* m_connector;
bool m_reset_on_insert;
devcb_write_line m_console_ready;
devcb_write_line m_console_reset;
int m_mask;
int m_romgq;
};
/****************************************************************************/
class ti99_cartridge_pcb;
class ti99_cartridge_device : public bus8z_device, public device_image_interface
{
public:
ti99_cartridge_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz);
DECLARE_WRITE8_MEMBER(cruwrite);
DECLARE_WRITE_LINE_MEMBER(ready_line);
DECLARE_WRITE_LINE_MEMBER(romgq_line);
DECLARE_WRITE8_MEMBER(set_gromlines);
DECLARE_WRITE_LINE_MEMBER(gclock_in);
bool is_available() { return m_pcb != nullptr; }
void set_slot(int i);
bool is_grom_idle();
protected:
virtual void device_start() override { }
virtual void device_config_complete() override;
virtual machine_config_constructor device_mconfig_additions() const override;
virtual const tiny_rom_entry* device_rom_region() const override;
// Image handling: implementation of methods which are abstract in the parent
image_init_result call_load() override;
void call_unload() override;
virtual const software_list_loader &get_software_list_loader() const override { return rom_software_list_loader::instance(); }
void prepare_cartridge();
// device_image_interface
iodevice_t image_type() const override { return IO_CARTSLOT; }
bool is_readable() const override { return true; }
bool is_writeable() const override { return false; }
bool is_creatable() const override { return false; }
bool must_be_loaded() const override { return false; }
bool is_reset_on_load() const override { return false; }
const char *image_interface() const override { return "ti99_cart"; }
const char *file_extensions() const override { return "rpk"; }
private:
class rpk_socket;
class rpk_reader;
class rpk;
bool m_readrom;
int m_pcbtype;
int m_slot;
int get_index_from_tagname();
std::unique_ptr<ti99_cartridge_pcb> m_pcb; // inbound
cartridge_connector_device* m_connector; // outbound
// RPK which is associated to this cartridge
// When we close it, the contents are saved to NVRAM if available
rpk *m_rpk;
};
/****************************************************************************/
class cartridge_connector_device : public bus8z_device
{
public:
virtual DECLARE_READ8Z_MEMBER(crureadz) = 0;
virtual DECLARE_WRITE8_MEMBER(cruwrite) = 0;
virtual DECLARE_WRITE_LINE_MEMBER(romgq_line) = 0;
virtual DECLARE_WRITE8_MEMBER(set_gromlines) = 0;
virtual DECLARE_WRITE_LINE_MEMBER(gclock_in) = 0;
DECLARE_WRITE_LINE_MEMBER(ready_line);
virtual void insert(int index, ti99_cartridge_device* cart) { m_gromport->cartridge_inserted(); }
virtual void remove(int index) { }
virtual bool is_grom_idle() = 0;
protected:
cartridge_connector_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
virtual void device_config_complete() override;
gromport_device* m_gromport;
bool m_grom_selected;
};
/*
Single cartridge connector.
*/
class ti99_single_cart_conn_device : public cartridge_connector_device
{
public:
ti99_single_cart_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
DECLARE_WRITE_LINE_MEMBER(romgq_line) override;
DECLARE_WRITE8_MEMBER(set_gromlines) override;
DECLARE_WRITE_LINE_MEMBER(gclock_in) override;
bool is_grom_idle() override;
protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual machine_config_constructor device_mconfig_additions() const override;
private:
ti99_cartridge_device *m_cartridge;
};
/*
Multi cartridge connector.
*/
/* We set the number of slots to 4, although we may have up to 16. From a
logical point of view we could have 256, but the operating system only checks
the first 16 banks. */
class ti99_multi_cart_conn_device : public cartridge_connector_device
{
public:
ti99_multi_cart_conn_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
DECLARE_WRITE_LINE_MEMBER(romgq_line) override;
DECLARE_WRITE8_MEMBER(set_gromlines) override;
DECLARE_WRITE_LINE_MEMBER(gclock_in) override;
void insert(int index, ti99_cartridge_device* cart) override;
void remove(int index) override;
DECLARE_INPUT_CHANGED_MEMBER( switch_changed );
bool is_grom_idle() override;
protected:
static constexpr unsigned NUMBER_OF_CARTRIDGE_SLOTS = 4;
virtual void device_start() override;
virtual void device_reset() override;
virtual machine_config_constructor device_mconfig_additions() const override;
virtual ioport_constructor device_input_ports() const override;
private:
bool m_readrom;
int m_active_slot;
int m_fixed_slot;
int m_next_free_slot;
ti99_cartridge_device* m_cartridge[NUMBER_OF_CARTRIDGE_SLOTS];
void set_slot(int slotnumber);
int get_active_slot(bool changebase, offs_t offset);
};
/*
GRAM Kracker.
*/
class ti99_gkracker_device : public cartridge_connector_device, public device_nvram_interface
{
public:
ti99_gkracker_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
DECLARE_WRITE_LINE_MEMBER(romgq_line) override;
DECLARE_WRITE8_MEMBER(set_gromlines) override;
DECLARE_WRITE_LINE_MEMBER(gclock_in) override;
void insert(int index, ti99_cartridge_device* cart) override;
void remove(int index) override;
DECLARE_INPUT_CHANGED_MEMBER( gk_changed );
// We may have a cartridge plugged into the GK
bool is_grom_idle() override;
protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual machine_config_constructor device_mconfig_additions() const override;
virtual const tiny_rom_entry* device_rom_region() const override;
virtual ioport_constructor device_input_ports() const override;
// device_nvram_interface
void nvram_default() override;
void nvram_read(emu_file &file) override;
void nvram_write(emu_file &file) override;
private:
int m_gk_switch[6]; // Used to cache the switch settings.
bool m_romspace_selected;
int m_ram_page;
int m_grom_address;
uint8_t* m_ram_ptr;
uint8_t* m_grom_ptr;
bool m_waddr_LSB;
ti99_cartridge_device *m_cartridge; // guest cartridge
// Just for proper initialization
void gk_install_menu(const char* menutext, int len, int ptr, int next, int start);
};
/****************************************************************************/
class ti99_cartridge_pcb
{
friend class ti99_cartridge_device;
public:
ti99_cartridge_pcb();
virtual ~ti99_cartridge_pcb() { }
protected:
virtual DECLARE_READ8Z_MEMBER(readz);
virtual DECLARE_WRITE8_MEMBER(write);
virtual DECLARE_READ8Z_MEMBER(crureadz);
virtual DECLARE_WRITE8_MEMBER(cruwrite);
DECLARE_WRITE_LINE_MEMBER(romgq_line);
virtual DECLARE_WRITE8_MEMBER(set_gromlines);
DECLARE_WRITE_LINE_MEMBER(gclock_in);
DECLARE_READ8Z_MEMBER(gromreadz);
DECLARE_WRITE8_MEMBER(gromwrite);
inline void set_grom_pointer(int number, device_t *dev);
void set_cartridge(ti99_cartridge_device *cart);
const char* tag() { return m_tag; }
void set_tag(const char* tag) { m_tag = tag; }
bool is_grom_idle() { return m_grom_idle; }
ti99_cartridge_device* m_cart;
tmc0430_device* m_grom[5];
bool m_grom_idle;
int m_grom_size;
int m_rom_size;
int m_ram_size;
uint8_t* m_rom_ptr;
uint8_t* m_ram_ptr;
bool m_romspace_selected;
int m_rom_page; // for some cartridge types
uint8_t* m_grom_ptr; // for gromemu
int m_grom_address; // for gromemu
int m_ram_page; // for super
const char* m_tag;
std::vector<uint8_t> m_nvram; // for MiniMemory
std::vector<uint8_t> m_ram; // for MBX
};
/******************** Standard cartridge ******************************/
class ti99_standard_cartridge : public ti99_cartridge_pcb
{
public:
};
/*********** Paged cartridge (like Extended Basic) ********************/
class ti99_paged12k_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
};
/*********** Paged cartridge (others) ********************/
class ti99_paged16k_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
};
/********************** Mini Memory ***********************************/
class ti99_minimem_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
};
/********************* Super Space II *********************************/
class ti99_super_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
};
/************************* MBX ***************************************/
class ti99_mbx_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
};
/********************** Paged 379i ************************************/
class ti99_paged379i_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
private:
int get_paged379i_bank(int rompage);
};
/********************** Paged 378 ************************************/
class ti99_paged378_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
};
/********************** Paged 377 ************************************/
class ti99_paged377_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
};
/********************** Paged CRU ************************************/
class ti99_pagedcru_cartridge : public ti99_cartridge_pcb
{
public:
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(crureadz) override;
DECLARE_WRITE8_MEMBER(cruwrite) override;
};
/********************** GROM emulation cartridge ************************************/
class ti99_gromemu_cartridge : public ti99_cartridge_pcb
{
public:
ti99_gromemu_cartridge(): m_waddr_LSB(false), m_grom_selected(false), m_grom_read_mode(false), m_grom_address_mode(false)
{ m_grom_address = 0; }
DECLARE_READ8Z_MEMBER(readz) override;
DECLARE_WRITE8_MEMBER(write) override;
DECLARE_READ8Z_MEMBER(gromemureadz);
DECLARE_WRITE8_MEMBER(gromemuwrite);
DECLARE_WRITE8_MEMBER(set_gromlines) override;
private:
bool m_waddr_LSB;
bool m_grom_selected;
bool m_grom_read_mode;
bool m_grom_address_mode;
};
} } } // end namespace bus::ti99::internal
SLOT_INTERFACE_EXTERN(gromport4);
SLOT_INTERFACE_EXTERN(gromport8);
#define MCFG_GROMPORT4_ADD( _tag ) \
MCFG_DEVICE_ADD(_tag, TI99_GROMPORT, 0) \
bus::ti99::internal::gromport_device::set_mask(*device, 0x1fff); \
MCFG_DEVICE_SLOT_INTERFACE(gromport4, "single", false)
#define MCFG_GROMPORT8_ADD( _tag ) \
MCFG_DEVICE_ADD(_tag, TI99_GROMPORT, 0) \
bus::ti99::internal::gromport_device::set_mask(*device, 0x3fff); \
MCFG_DEVICE_SLOT_INTERFACE(gromport8, "single", false)
#define MCFG_GROMPORT_READY_HANDLER( _ready ) \
devcb = &bus::ti99::internal::gromport_device::static_set_ready_callback( *device, DEVCB_##_ready );
#define MCFG_GROMPORT_RESET_HANDLER( _reset ) \
devcb = &bus::ti99::internal::gromport_device::static_set_reset_callback( *device, DEVCB_##_reset );
DECLARE_DEVICE_TYPE_NS(TI99_GROMPORT, bus::ti99::internal, gromport_device)
DECLARE_DEVICE_TYPE_NS(TI99_CART, bus::ti99::internal, ti99_cartridge_device)
DECLARE_DEVICE_TYPE_NS(TI99_GROMPORT_SINGLE, bus::ti99::internal, ti99_single_cart_conn_device)
DECLARE_DEVICE_TYPE_NS(TI99_GROMPORT_MULTI, bus::ti99::internal, ti99_multi_cart_conn_device)
DECLARE_DEVICE_TYPE_NS(TI99_GROMPORT_GK, bus::ti99::internal, ti99_gkracker_device)
#endif // MAME_BUS_TI99_INTERNAL_GROMPORT_H

View File

@ -48,7 +48,7 @@
#include "bus/ti99/ti99defs.h"
#include "bus/ti99/internal/datamux.h"
#include "bus/ti99/internal/gromport.h"
#include "bus/ti99/gromport/gromport.h"
#include "bus/ti99/internal/evpcconn.h"
#include "bus/ti99/joyport/joyport.h"
@ -166,7 +166,7 @@ private:
// Connected devices
required_device<tms9900_device> m_cpu;
required_device<tms9901_device> m_tms9901;
required_device<bus::ti99::internal::gromport_device> m_gromport;
required_device<bus::ti99::gromport::gromport_device> m_gromport;
required_device<bus::ti99::internal::ioport_device> m_ioport;
required_device<bus::ti99::joyport::joyport_device> m_joyport;
required_device<bus::ti99::internal::datamux_device> m_datamux;

View File

@ -182,7 +182,7 @@ Known Issues (MZ, 2010-11-07)
#include "imagedev/cassette.h"
#include "bus/ti99/internal/998board.h"
#include "bus/ti99/internal/gromport.h"
#include "bus/ti99/gromport/gromport.h"
#include "bus/ti99/joyport/joyport.h"
#include "bus/ti99/internal/ioport.h"
@ -275,7 +275,7 @@ private:
// Connected devices
required_device<tms9995_device> m_cpu;
required_device<tms9901_device> m_tms9901;
required_device<bus::ti99::internal::gromport_device> m_gromport;
required_device<bus::ti99::gromport::gromport_device> m_gromport;
required_device<bus::ti99::internal::ioport_device> m_ioport;
required_device<bus::ti99::internal::mainboard8_device> m_mainboard;
required_device<bus::ti99::joyport::joyport_device> m_joyport;