summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Angelo Salese <angelosa@users.noreply.github.com>2011-05-06 10:52:23 +0000
committer Angelo Salese <angelosa@users.noreply.github.com>2011-05-06 10:52:23 +0000
commitf280fd9f990a676e74880125bc84de378555fd43 (patch)
tree7d51581335fd9e11c5cbc01c8a1b6f2e5af2fbf4
parent249c2c640233970fd99240c082a4b012379262d0 (diff)
Ported DC, SMS and SG1000 to main folder, nw
-rw-r--r--.gitattributes24
-rw-r--r--src/emu/emu.mak4
-rw-r--r--src/emu/hashfile.c617
-rw-r--r--src/emu/hashfile.h17
-rw-r--r--src/emu/machine/msm8251.c825
-rw-r--r--src/emu/machine/msm8251.h88
-rw-r--r--src/emu/machine/serial.c733
-rw-r--r--src/emu/machine/serial.h247
-rw-r--r--src/emu/machine/upd765.c2457
-rw-r--r--src/emu/machine/upd765.h161
-rw-r--r--src/mame/drivers/dc.c379
-rw-r--r--src/mame/drivers/segasms.c783
-rw-r--r--src/mame/drivers/sg1000.c1221
-rw-r--r--src/mame/formats/basicdsk.c352
-rw-r--r--src/mame/formats/basicdsk.h35
-rw-r--r--src/mame/includes/segasms.h183
-rw-r--r--src/mame/includes/sg1000.h102
-rw-r--r--src/mame/layout/sms1.lay62
-rw-r--r--src/mame/machine/ctronics.c391
-rw-r--r--src/mame/machine/ctronics.h75
-rw-r--r--src/mame/machine/dccons.c725
-rw-r--r--src/mame/machine/gdrom.c874
-rw-r--r--src/mame/machine/gdrom.h17
-rw-r--r--src/mame/machine/segasms.c2053
-rw-r--r--src/mame/mame.mak18
-rw-r--r--src/mame/video/smsvdp.c1530
-rw-r--r--src/mame/video/smsvdp.h86
27 files changed, 14059 insertions, 0 deletions
diff --git a/.gitattributes b/.gitattributes
index 6e86faea760..d6463eaa155 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -665,6 +665,8 @@ src/emu/fileio.c svneol=native#text/plain
src/emu/fileio.h svneol=native#text/plain
src/emu/hash.c svneol=native#text/plain
src/emu/hash.h svneol=native#text/plain
+src/emu/hashfile.c svneol=native#text/plain
+src/emu/hashfile.h svneol=native#text/plain
src/emu/image.c svneol=native#text/plain
src/emu/image.h svneol=native#text/plain
src/emu/imagedev/bitbngr.c svneol=native#text/plain
@@ -857,6 +859,8 @@ src/emu/machine/msm58321.c svneol=native#text/plain
src/emu/machine/msm58321.h svneol=native#text/plain
src/emu/machine/msm6242.c svneol=native#text/plain
src/emu/machine/msm6242.h svneol=native#text/plain
+src/emu/machine/msm8251.c svneol=native#text/plain
+src/emu/machine/msm8251.h svneol=native#text/plain
src/emu/machine/nmc9306.c svneol=native#text/plain
src/emu/machine/nmc9306.h svneol=native#text/plain
src/emu/machine/nvram.c svneol=native#text/plain
@@ -903,6 +907,8 @@ src/emu/machine/scsihd.c svneol=native#text/plain
src/emu/machine/scsihd.h svneol=native#text/plain
src/emu/machine/secflash.c svneol=native#text/plain
src/emu/machine/secflash.h svneol=native#text/plain
+src/emu/machine/serial.c svneol=native#text/plain
+src/emu/machine/serial.h svneol=native#text/plain
src/emu/machine/smc91c9x.c svneol=native#text/plain
src/emu/machine/smc91c9x.h svneol=native#text/plain
src/emu/machine/timekpr.c svneol=native#text/plain
@@ -917,6 +923,8 @@ src/emu/machine/upd4701.c svneol=native#text/plain
src/emu/machine/upd4701.h svneol=native#text/plain
src/emu/machine/upd7201.c svneol=native#text/plain
src/emu/machine/upd7201.h svneol=native#text/plain
+src/emu/machine/upd765.c svneol=native#text/plain
+src/emu/machine/upd765.h svneol=native#text/plain
src/emu/machine/wd33c93.c svneol=native#text/plain
src/emu/machine/wd33c93.h svneol=native#text/plain
src/emu/machine/x2212.c svneol=native#text/plain
@@ -1783,6 +1791,7 @@ src/mame/drivers/darkseal.c svneol=native#text/plain
src/mame/drivers/dassault.c svneol=native#text/plain
src/mame/drivers/dblewing.c svneol=native#text/plain
src/mame/drivers/dbz.c svneol=native#text/plain
+src/mame/drivers/dc.c svneol=native#text/plain
src/mame/drivers/dcheese.c svneol=native#text/plain
src/mame/drivers/dcon.c svneol=native#text/plain
src/mame/drivers/dday.c svneol=native#text/plain
@@ -2407,6 +2416,7 @@ src/mame/drivers/segas16b.c svneol=native#text/plain
src/mame/drivers/segas18.c svneol=native#text/plain
src/mame/drivers/segas24.c svneol=native#text/plain
src/mame/drivers/segas32.c svneol=native#text/plain
+src/mame/drivers/segasms.c svneol=native#text/plain
src/mame/drivers/segaxbd.c svneol=native#text/plain
src/mame/drivers/segaybd.c svneol=native#text/plain
src/mame/drivers/seibuspi.c svneol=native#text/plain
@@ -2419,6 +2429,7 @@ src/mame/drivers/sf.c svneol=native#text/plain
src/mame/drivers/sfbonus.c svneol=native#text/plain
src/mame/drivers/sfcbox.c svneol=native#text/plain
src/mame/drivers/sfkick.c svneol=native#text/plain
+src/mame/drivers/sg1000.c svneol=native#text/plain
src/mame/drivers/sg1000a.c svneol=native#text/plain
src/mame/drivers/shadfrce.c svneol=native#text/plain
src/mame/drivers/shangha3.c svneol=native#text/plain
@@ -2705,6 +2716,8 @@ src/mame/drivers/zodiack.c svneol=native#text/plain
src/mame/drivers/zr107.c svneol=native#text/plain
src/mame/etc/fd1094dp.c svneol=native#text/plain
src/mame/etc/jrcrypt.c svneol=native#text/plain
+src/mame/formats/basicdsk.c svneol=native#text/plain
+src/mame/formats/basicdsk.h svneol=native#text/plain
src/mame/includes/1942.h svneol=native#text/plain
src/mame/includes/1943.h svneol=native#text/plain
src/mame/includes/20pacgal.h svneol=native#text/plain
@@ -3196,6 +3209,7 @@ src/mame/includes/segamsys.h svneol=native#text/plain
src/mame/includes/segas16.h svneol=native#text/plain
src/mame/includes/segas24.h svneol=native#text/plain
src/mame/includes/segas32.h svneol=native#text/plain
+src/mame/includes/segasms.h svneol=native#text/plain
src/mame/includes/sei_crtc.h svneol=native#text/plain
src/mame/includes/seibuspi.h svneol=native#text/plain
src/mame/includes/seicross.h svneol=native#text/plain
@@ -3203,6 +3217,7 @@ src/mame/includes/senjyo.h svneol=native#text/plain
src/mame/includes/seta.h svneol=native#text/plain
src/mame/includes/seta2.h svneol=native#text/plain
src/mame/includes/sf.h svneol=native#text/plain
+src/mame/includes/sg1000.h svneol=native#text/plain
src/mame/includes/shadfrce.h svneol=native#text/plain
src/mame/includes/shangha3.h svneol=native#text/plain
src/mame/includes/shangkid.h svneol=native#text/plain
@@ -3510,6 +3525,7 @@ src/mame/layout/slots.lay svneol=native#text/plain
src/mame/layout/sltblgpo.lay svneol=native#text/plain
src/mame/layout/sltblgtk.lay svneol=native#text/plain
src/mame/layout/smoto.lay svneol=native#text/plain
+src/mame/layout/sms1.lay svneol=native#text/plain
src/mame/layout/snookr10.lay svneol=native#text/plain
src/mame/layout/solarq.lay svneol=native#text/plain
src/mame/layout/sos.lay svneol=native#text/plain
@@ -3580,6 +3596,8 @@ src/mame/machine/cdislave.c svneol=native#text/plain
src/mame/machine/cdislave.h svneol=native#text/plain
src/mame/machine/chaknpop.c svneol=native#text/plain
src/mame/machine/cps2crpt.c svneol=native#text/plain
+src/mame/machine/ctronics.c svneol=native#text/plain
+src/mame/machine/ctronics.h svneol=native#text/plain
src/mame/machine/cx4data.c svneol=native#text/plain
src/mame/machine/cx4fn.c svneol=native#text/plain
src/mame/machine/cx4oam.c svneol=native#text/plain
@@ -3587,6 +3605,7 @@ src/mame/machine/cx4ops.c svneol=native#text/plain
src/mame/machine/dc-ctrl.c svneol=native#text/plain
src/mame/machine/dc-ctrl.h svneol=native#text/plain
src/mame/machine/dc.c svneol=native#text/plain
+src/mame/machine/dccons.c svneol=native#text/plain
src/mame/machine/dec0.c svneol=native#text/plain
src/mame/machine/deco102.c svneol=native#text/plain
src/mame/machine/deco156.c svneol=native#text/plain
@@ -3609,6 +3628,8 @@ src/mame/machine/gaelcrpt.c svneol=native#text/plain
src/mame/machine/galaxold.c svneol=native#text/plain
src/mame/machine/gaplus.c svneol=native#text/plain
src/mame/machine/gdcrypt.c svneol=native#text/plain
+src/mame/machine/gdrom.c svneol=native#text/plain
+src/mame/machine/gdrom.h svneol=native#text/plain
src/mame/machine/harddriv.c svneol=native#text/plain
src/mame/machine/irem_cpu.c svneol=native#text/plain
src/mame/machine/irem_cpu.h svneol=native#text/plain
@@ -3720,6 +3741,7 @@ src/mame/machine/segaic16.c svneol=native#text/plain
src/mame/machine/segaic16.h svneol=native#text/plain
src/mame/machine/segamsys.c svneol=native#text/plain
src/mame/machine/segas32.c svneol=native#text/plain
+src/mame/machine/segasms.c svneol=native#text/plain
src/mame/machine/seibuspi.c svneol=native#text/plain
src/mame/machine/seibuspi.h svneol=native#text/plain
src/mame/machine/seicop.c svneol=native#text/plain
@@ -4342,6 +4364,8 @@ src/mame/video/skykid.c svneol=native#text/plain
src/mame/video/skyraid.c svneol=native#text/plain
src/mame/video/slapfght.c svneol=native#text/plain
src/mame/video/slapshot.c svneol=native#text/plain
+src/mame/video/smsvdp.c svneol=native#text/plain
+src/mame/video/smsvdp.h svneol=native#text/plain
src/mame/video/snes.c svneol=native#text/plain
src/mame/video/snk.c svneol=native#text/plain
src/mame/video/snk6502.c svneol=native#text/plain
diff --git a/src/emu/emu.mak b/src/emu/emu.mak
index cf8ebfb969a..98bdd28c7e0 100644
--- a/src/emu/emu.mak
+++ b/src/emu/emu.mak
@@ -44,6 +44,7 @@ OBJDIRS += \
#-------------------------------------------------
EMUOBJS = \
+ $(EMUOBJ)/hashfile.o \
$(EMUOBJ)/addrmap.o \
$(EMUOBJ)/attotime.o \
$(EMUOBJ)/audit.o \
@@ -204,6 +205,7 @@ EMUMACHINEOBJS = \
$(EMUMACHINE)/msm5832.o \
$(EMUMACHINE)/msm58321.o \
$(EMUMACHINE)/msm6242.o \
+ $(EMUMACHINE)/msm8251.o \
$(EMUMACHINE)/nmc9306.o \
$(EMUMACHINE)/nvram.o \
$(EMUMACHINE)/pc16552d.o \
@@ -225,6 +227,7 @@ EMUMACHINEOBJS = \
$(EMUMACHINE)/scsidev.o \
$(EMUMACHINE)/scsihd.o \
$(EMUMACHINE)/secflash.o \
+ $(EMUMACHINE)/serial.o \
$(EMUMACHINE)/smc91c9x.o \
$(EMUMACHINE)/timekpr.o \
$(EMUMACHINE)/tmp68301.o \
@@ -232,6 +235,7 @@ EMUMACHINEOBJS = \
$(EMUMACHINE)/upd1990a.o \
$(EMUMACHINE)/upd4701.o \
$(EMUMACHINE)/upd7201.o \
+ $(EMUMACHINE)/upd765.o \
$(EMUMACHINE)/wd33c93.o \
$(EMUMACHINE)/x2212.o \
$(EMUMACHINE)/x76f041.o \
diff --git a/src/emu/hashfile.c b/src/emu/hashfile.c
new file mode 100644
index 00000000000..53c185d4bdf
--- /dev/null
+++ b/src/emu/hashfile.c
@@ -0,0 +1,617 @@
+/*********************************************************************
+
+ hashfile.c
+
+ Code for parsing hash info (*.hsi) files
+
+*********************************************************************/
+
+#include "hashfile.h"
+#include "pool.h"
+#include "expat.h"
+#include "emuopts.h"
+#include "hash.h"
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _hash_info hash_info;
+struct _hash_info
+{
+ hash_collection *hashes;
+ const char *extrainfo;
+};
+
+typedef struct _hash_file hash_file;
+
+typedef void (*hashfile_error_func)(const char *message);
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+/* opens a hash file; if is_preload is non-zero, the entire file is preloaded */
+hash_file *hashfile_open(emu_options &options, const char *sysname, int is_preload, hashfile_error_func error_proc);
+
+/* closes a hash file and associated resources */
+void hashfile_close(hash_file *hashfile);
+
+/* looks up information in a hash file */
+const hash_info *hashfile_lookup(hash_file *hashfile, const hash_collection *hashes);
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+struct _hash_file
+{
+ emu_file *file;
+ object_pool *pool;
+ astring functions[IO_COUNT];
+
+ hash_info **preloaded_hashes;
+ int preloaded_hash_count;
+
+ void (*error_proc)(const char *message);
+};
+
+
+
+enum hash_parse_position
+{
+ HASH_POS_ROOT,
+ HASH_POS_MAIN,
+ HASH_POS_HASH
+};
+
+
+
+struct hash_parse_state
+{
+ XML_Parser parser;
+ hash_file *hashfile;
+ int done;
+
+ int (*selector_proc)(hash_file *hashfile, void *param, const char *name, const hash_collection *hashes);
+ void (*use_proc)(hash_file *hashfile, void *param, hash_info *hi);
+ void (*error_proc)(const char *message);
+ void *param;
+
+ enum hash_parse_position pos;
+ char **text_dest;
+ hash_info *hi;
+};
+
+
+
+/***************************************************************************
+ PROTOTYPES
+***************************************************************************/
+
+static void *expat_malloc(size_t size);
+static void *expat_realloc(void *ptr, size_t size);
+static void expat_free(void *ptr);
+
+
+
+/***************************************************************************
+ CORE IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ parse_error
+-------------------------------------------------*/
+
+static void ATTR_PRINTF(2,3) parse_error(struct hash_parse_state *state, const char *fmt, ...)
+{
+ char buf[256];
+ va_list va;
+
+ if (state->error_proc)
+ {
+ va_start(va, fmt);
+ vsnprintf(buf, ARRAY_LENGTH(buf), fmt, va);
+ va_end(va);
+ (*state->error_proc)(buf);
+ }
+}
+
+
+
+/*-------------------------------------------------
+ unknown_tag
+-------------------------------------------------*/
+
+static void unknown_tag(struct hash_parse_state *state, const char *tagname)
+{
+ parse_error(state, "[%lu:%lu]: Unknown tag: %s\n",
+ XML_GetCurrentLineNumber(state->parser),
+ XML_GetCurrentColumnNumber(state->parser),
+ tagname);
+}
+
+
+
+/*-------------------------------------------------
+ unknown_attribute
+-------------------------------------------------*/
+
+static void unknown_attribute(struct hash_parse_state *state, const char *attrname)
+{
+ parse_error(state, "[%lu:%lu]: Unknown attribute: %s\n",
+ XML_GetCurrentLineNumber(state->parser),
+ XML_GetCurrentColumnNumber(state->parser),
+ attrname);
+}
+
+
+
+/*-------------------------------------------------
+ unknown_attribute_value
+-------------------------------------------------*/
+
+static void unknown_attribute_value(struct hash_parse_state *state,
+ const char *attrname, const char *attrvalue)
+{
+ parse_error(state, "[%lu:%lu]: Unknown attribute value: %s\n",
+ XML_GetCurrentLineNumber(state->parser),
+ XML_GetCurrentColumnNumber(state->parser),
+ attrvalue);
+}
+
+
+
+/*-------------------------------------------------
+ start_handler
+-------------------------------------------------*/
+
+static void start_handler(void *data, const char *tagname, const char **attributes)
+{
+ struct hash_parse_state *state = (struct hash_parse_state *) data;
+ const char *name;
+ hash_info *hi;
+ char **text_dest;
+ hash_collection hashes;
+ astring all_functions;
+ char functions;
+ iodevice_t device;
+ int i;
+
+ switch(state->pos)
+ {
+ case HASH_POS_ROOT:
+ if (!strcmp(tagname, "hashfile"))
+ {
+ }
+ else
+ {
+ unknown_tag(state, tagname);
+ }
+ break;
+
+ case HASH_POS_MAIN:
+ if (!strcmp(tagname, "hash"))
+ {
+ // we are now examining a hash tag
+ name = NULL;
+ device = IO_COUNT;
+
+ while(attributes[0])
+ {
+ functions = 0;
+ if (!strcmp(attributes[0], "name"))
+ {
+ /* name attribute */
+ name = attributes[1];
+ }
+ else if (!strcmp(attributes[0], "crc32"))
+ {
+ /* crc32 attribute */
+ functions = hash_collection::HASH_CRC;
+ }
+ else if (!strcmp(attributes[0], "md5"))
+ {
+ /* md5 attribute */
+ functions = hash_collection::HASH_MD5;
+ }
+ else if (!strcmp(attributes[0], "sha1"))
+ {
+ /* sha1 attribute */
+ functions = hash_collection::HASH_SHA1;
+ }
+ else if (!strcmp(attributes[0], "type"))
+ {
+ /* type attribute */
+ i = 0;//device_typeid(attributes[1]);
+ if (i < 0)
+ unknown_attribute_value(state, attributes[0], attributes[1]);
+ else
+ device = (iodevice_t) i;
+ }
+ else
+ {
+ /* unknown attribute */
+ unknown_attribute(state, attributes[0]);
+ }
+
+ if (functions)
+ {
+ hashes.add_from_string(functions, attributes[1], strlen(attributes[1]));
+ all_functions.cat(functions);
+ }
+
+ attributes += 2;
+ }
+
+ /* do we use this hash? */
+ if (!state->selector_proc || state->selector_proc(state->hashfile, state->param, name, &hashes))
+ {
+ hi = (hash_info*)pool_malloc_lib(state->hashfile->pool, sizeof(hash_info));
+ if (!hi)
+ return;
+ memset(hi, 0, sizeof(*hi));
+
+ hi->hashes = &hashes;
+ state->hi = hi;
+ }
+ }
+ else
+ {
+ unknown_tag(state, tagname);
+ }
+ break;
+
+ case HASH_POS_HASH:
+ text_dest = NULL;
+
+ if (!strcmp(tagname, "year")) {
+ }
+ else if (!strcmp(tagname, "manufacturer")){
+ }
+ else if (!strcmp(tagname, "status")){
+ }
+ else if (!strcmp(tagname, "pcb")){
+ }
+ else if (!strcmp(tagname, "extrainfo")) {
+ text_dest = (char **) &state->hi->extrainfo;
+ }
+ else
+ unknown_tag(state, tagname);
+
+ if (text_dest && state->hi)
+ state->text_dest = text_dest;
+ break;
+ }
+ state->pos = (hash_parse_position) (state->pos + 1);
+}
+
+
+
+/*-------------------------------------------------
+ end_handler
+-------------------------------------------------*/
+
+static void end_handler(void *data, const char *name)
+{
+ struct hash_parse_state *state = (struct hash_parse_state *) data;
+ state->text_dest = NULL;
+
+ state->pos = (hash_parse_position) (state->pos - 1);
+ switch(state->pos)
+ {
+ case HASH_POS_ROOT:
+ case HASH_POS_HASH:
+ break;
+
+ case HASH_POS_MAIN:
+ if (state->hi)
+ {
+ if (state->use_proc)
+ (*state->use_proc)(state->hashfile, state->param, state->hi);
+ state->hi = NULL;
+ }
+ break;
+ }
+}
+
+
+
+/*-------------------------------------------------
+ data_handler
+-------------------------------------------------*/
+
+static void data_handler(void *data, const XML_Char *s, int len)
+{
+ struct hash_parse_state *state = (struct hash_parse_state *) data;
+ int text_len;
+ char *text;
+
+ if (state->text_dest)
+ {
+ text = *state->text_dest;
+
+ text_len = text ? strlen(text) : 0;
+ text = (char*)pool_realloc_lib(state->hashfile->pool, text, text_len + len + 1);
+ if (!text)
+ return;
+
+ memcpy(&text[text_len], s, len);
+ text[text_len + len] = '\0';
+ *state->text_dest = text;
+ }
+}
+
+
+
+/*-------------------------------------------------
+ hashfile_parse
+-------------------------------------------------*/
+
+static void hashfile_parse(hash_file *hashfile,
+ int (*selector_proc)(hash_file *hashfile, void *param, const char *name, const hash_collection *hashes),
+ void (*use_proc)(hash_file *hashfile, void *param, hash_info *hi),
+ void (*error_proc)(const char *message),
+ void *param)
+{
+ struct hash_parse_state state;
+ char buf[1024];
+ UINT32 len;
+ XML_Memory_Handling_Suite memcallbacks;
+
+ hashfile->file->seek(0, SEEK_SET);
+
+ memset(&state, 0, sizeof(state));
+ state.hashfile = hashfile;
+ state.selector_proc = selector_proc;
+ state.use_proc = use_proc;
+ state.error_proc = error_proc;
+ state.param = param;
+
+ /* create the XML parser */
+ memcallbacks.malloc_fcn = expat_malloc;
+ memcallbacks.realloc_fcn = expat_realloc;
+ memcallbacks.free_fcn = expat_free;
+ state.parser = XML_ParserCreate_MM(NULL, &memcallbacks, NULL);
+ if (!state.parser)
+ goto done;
+
+ XML_SetUserData(state.parser, &state);
+ XML_SetElementHandler(state.parser, start_handler, end_handler);
+ XML_SetCharacterDataHandler(state.parser, data_handler);
+
+ while(!state.done)
+ {
+ len = hashfile->file->read(buf, sizeof(buf));
+ state.done = hashfile->file->eof();
+ if (XML_Parse(state.parser, buf, len, state.done) == XML_STATUS_ERROR)
+ {
+ parse_error(&state, "[%lu:%lu]: %s\n",
+ XML_GetCurrentLineNumber(state.parser),
+ XML_GetCurrentColumnNumber(state.parser),
+ XML_ErrorString(XML_GetErrorCode(state.parser)));
+ goto done;
+ }
+ }
+
+done:
+ if (state.parser)
+ XML_ParserFree(state.parser);
+}
+
+
+
+/*-------------------------------------------------
+ preload_use_proc
+-------------------------------------------------*/
+
+static void preload_use_proc(hash_file *hashfile, void *param, hash_info *hi)
+{
+ hash_info **new_preloaded_hashes;
+
+ new_preloaded_hashes = (hash_info **)pool_realloc_lib(hashfile->pool, hashfile->preloaded_hashes,
+ (hashfile->preloaded_hash_count + 1) * sizeof(*new_preloaded_hashes));
+ if (!new_preloaded_hashes)
+ return;
+
+ hashfile->preloaded_hashes = new_preloaded_hashes;
+ hashfile->preloaded_hashes[hashfile->preloaded_hash_count++] = hi;
+}
+
+
+
+/*-------------------------------------------------
+ hashfile_open
+-------------------------------------------------*/
+
+hash_file *hashfile_open(emu_options &options, const char *sysname, int is_preload,
+ void (*error_proc)(const char *message))
+{
+ hash_file *hashfile = NULL;
+ object_pool *pool = NULL;
+ file_error filerr;
+
+ /* create a pool for this hash file */
+ pool = pool_alloc_lib(error_proc);
+ if (!pool)
+ goto error;
+
+ /* allocate space for this hash file */
+ hashfile = (hash_file *) pool_malloc_lib(pool, sizeof(*hashfile));
+ if (!hashfile)
+ goto error;
+
+ /* set up the hashfile structure */
+ memset(hashfile, 0, sizeof(*hashfile));
+ hashfile->pool = pool;
+ hashfile->error_proc = error_proc;
+
+ /* open a file */
+ hashfile->file = global_alloc(emu_file(options.hash_path(), OPEN_FLAG_READ));
+ filerr = hashfile->file->open(sysname, ".hsi");
+ if (filerr != FILERR_NONE)
+ {
+ global_free(hashfile->file);
+ hashfile->file = NULL;
+ goto error;
+ }
+
+ if (is_preload)
+ hashfile_parse(hashfile, NULL, preload_use_proc, hashfile->error_proc, NULL);
+
+ return hashfile;
+
+error:
+ if (hashfile != NULL)
+ hashfile_close(hashfile);
+ return NULL;
+}
+
+
+
+/*-------------------------------------------------
+ hashfile_close
+-------------------------------------------------*/
+
+void hashfile_close(hash_file *hashfile)
+{
+ global_free(hashfile->file);
+ pool_free_lib(hashfile->pool);
+}
+
+
+
+/*-------------------------------------------------
+ singular_selector_proc
+-------------------------------------------------*/
+
+struct hashlookup_params
+{
+ const hash_collection *hashes;
+ hash_info *hi;
+};
+
+static int singular_selector_proc(hash_file *hashfile, void *param, const char *name, const hash_collection *hashes)
+{
+ astring tempstr;
+ struct hashlookup_params *hlparams = (struct hashlookup_params *) param;
+ return (*hashes == *hlparams->hashes);
+}
+
+
+
+/*-------------------------------------------------
+ singular_use_proc
+-------------------------------------------------*/
+
+static void singular_use_proc(hash_file *hashfile, void *param, hash_info *hi)
+{
+ struct hashlookup_params *hlparams = (struct hashlookup_params *) param;
+ hlparams->hi = hi;
+}
+
+
+
+/*-------------------------------------------------
+ hashfile_lookup
+-------------------------------------------------*/
+
+const hash_info *hashfile_lookup(hash_file *hashfile, const hash_collection *hashes)
+{
+ struct hashlookup_params param;
+ int i;
+
+ param.hashes = hashes;
+ param.hi = NULL;
+
+ for (i = 0; i < hashfile->preloaded_hash_count; i++)
+ {
+ if (singular_selector_proc(hashfile, &param, NULL, hashfile->preloaded_hashes[i]->hashes))
+ return hashfile->preloaded_hashes[i];
+ }
+
+ hashfile_parse(hashfile, singular_selector_proc, singular_use_proc,
+ hashfile->error_proc, (void *) &param);
+ return param.hi;
+}
+
+const char *extra_info = NULL;
+
+const char *read_hash_config(device_image_interface &image, const char *sysname)
+{
+ hash_file *hashfile = NULL;
+ const hash_info *info = NULL;
+
+ /* open the hash file */
+ hashfile = hashfile_open(image.device().machine().options(), sysname, FALSE, NULL);
+ if (!hashfile)
+ return NULL;
+
+ /* look up this entry in the hash file */
+ info = hashfile_lookup(hashfile, &image.hash());
+
+ if (!info || !info->extrainfo)
+ {
+ hashfile_close(hashfile);
+ return NULL;
+ }
+
+ extra_info = auto_strdup(image.device().machine(),info->extrainfo);
+ if (!extra_info)
+ {
+ hashfile_close(hashfile);
+ return NULL;
+ }
+
+ /* copy the relevant entries */
+ hashfile_close(hashfile);
+
+ return extra_info;
+}
+
+const char *hashfile_extrainfo(device_image_interface &image)
+{
+ const char *rc;
+
+ /* now read the hash file */
+ image.crc();
+ extra_info = NULL;
+ int drv = driver_list::find(image.device().machine().system());
+ do
+ {
+ rc = read_hash_config(image,driver_list::driver(drv).name);
+ drv = driver_list::compatible_with(drv);
+ }
+ while(rc!=NULL && drv != -1);
+ return rc;
+}
+
+/***************************************************************************
+ EXPAT INTERFACES
+***************************************************************************/
+
+/*-------------------------------------------------
+ expat_malloc/expat_realloc/expat_free -
+ wrappers for memory allocation functions so
+ that they pass through out memory tracking
+ systems
+-------------------------------------------------*/
+
+static void *expat_malloc(size_t size)
+{
+ return global_alloc_array_clear(UINT8,size);
+}
+
+static void *expat_realloc(void *ptr, size_t size)
+{
+ if (ptr) global_free(ptr);
+ return global_alloc_array_clear(UINT8,size);
+}
+
+static void expat_free(void *ptr)
+{
+ global_free(ptr);
+}
diff --git a/src/emu/hashfile.h b/src/emu/hashfile.h
new file mode 100644
index 00000000000..c59421487f2
--- /dev/null
+++ b/src/emu/hashfile.h
@@ -0,0 +1,17 @@
+/*********************************************************************
+
+ hashfile.h
+
+ Code for parsing hash info (*.hsi) files
+
+*********************************************************************/
+
+#ifndef __HASHFILE_H__
+#define __HASHFILE_H__
+
+#include "emu.h"
+
+
+const char *hashfile_extrainfo(device_image_interface &image);
+
+#endif /* __HASHFILE_H__ */
diff --git a/src/emu/machine/msm8251.c b/src/emu/machine/msm8251.c
new file mode 100644
index 00000000000..1615f4b5c5f
--- /dev/null
+++ b/src/emu/machine/msm8251.c
@@ -0,0 +1,825 @@
+/*********************************************************************
+
+ msm8251.c
+
+ MSM/Intel 8251 Universal Synchronous/Asynchronous Receiver Transmitter code
+
+*********************************************************************/
+
+#include "emu.h"
+#include "msm8251.h"
+
+
+/***************************************************************************
+ MACROS
+***************************************************************************/
+
+#define VERBOSE 0
+
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _msm8251_t msm8251_t;
+struct _msm8251_t
+{
+ devcb_resolved_read_line in_rxd_func;
+ devcb_resolved_write_line out_txd_func;
+ devcb_resolved_read_line in_dsr_func;
+ devcb_resolved_write_line out_dtr_func;
+ devcb_resolved_write_line out_rts_func;
+ devcb_resolved_write_line out_rxrdy_func;
+ devcb_resolved_write_line out_txrdy_func;
+ devcb_resolved_write_line out_txempty_func;
+ devcb_resolved_write_line out_syndet_func;
+
+ /* flags controlling how msm8251_control_w operates */
+ UINT8 flags;
+ /* offset into sync_bytes used during sync byte transfer */
+ UINT8 sync_byte_offset;
+ /* number of sync bytes written so far */
+ UINT8 sync_byte_count;
+ /* the sync bytes written */
+ UINT8 sync_bytes[2];
+ /* status of msm8251 */
+ UINT8 status;
+ UINT8 command;
+ /* mode byte - bit definitions depend on mode - e.g. synchronous, asynchronous */
+ UINT8 mode_byte;
+
+ /* data being received */
+ UINT8 data;
+
+ /* receive reg */
+ serial_receive_register receive_reg;
+ /* transmit reg */
+ serial_transmit_register transmit_reg;
+
+ data_form form;
+
+ /* the serial connection that data is transfered over */
+ /* this is usually connected to the serial device */
+ serial_connection connection;
+};
+
+
+
+/***************************************************************************
+ PROTOTYPES
+***************************************************************************/
+
+static void msm8251_in_callback(running_machine &machine, int id, unsigned long state);
+static void msm8251_update_tx_empty(device_t *device);
+static void msm8251_update_tx_ready(device_t *device);
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+INLINE msm8251_t *get_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == MSM8251);
+
+ return (msm8251_t *) downcast<legacy_device_base *>(device)->token();
+}
+
+
+INLINE const msm8251_interface *get_interface(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == MSM8251);
+
+ return (const msm8251_interface *) device->static_config();
+}
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+const msm8251_interface default_msm8251_interface = { DEVCB_NULL, };
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ msm8251_in_callback
+-------------------------------------------------*/
+
+static void msm8251_in_callback(running_machine &machine, int id, unsigned long state)
+{
+ device_t *device;
+ msm8251_t *uart;
+ int changed;
+
+ /* NPW 29-Nov-2008 - These two lines are a hack and indicate why our "serial" infrastructure needs to be updated */
+ device = machine.device("uart");
+ uart = get_token(device);
+
+ changed = uart->connection.input_state^state;
+
+ uart->connection.input_state = state;
+
+ /* did cts change state? */
+ if (changed & SERIAL_STATE_CTS)
+ {
+ /* yes */
+ /* update tx ready */
+ /* msm8251_update_tx_ready(device); */
+ }
+}
+
+
+
+/*-------------------------------------------------
+ DEVICE_START( msm8251 )
+-------------------------------------------------*/
+
+static DEVICE_START( msm8251 )
+{
+ msm8251_t *uart = get_token(device);
+ const msm8251_interface *intf = get_interface(device);
+
+ serial_helper_setup();
+
+ // resolve callbacks
+ uart->out_rxrdy_func.resolve(intf->out_rxrdy_func, *device);
+ uart->out_txrdy_func.resolve(intf->out_txrdy_func, *device);
+ uart->out_txempty_func.resolve(intf->out_txempty_func, *device);
+
+ /* setup this side of the serial connection */
+ serial_connection_init(device->machine(),&uart->connection);
+ serial_connection_set_in_callback(device->machine(),&uart->connection, msm8251_in_callback);
+ uart->connection.input_state = 0;
+}
+
+
+
+/*-------------------------------------------------
+ msm8251_update_rx_ready
+-------------------------------------------------*/
+
+static void msm8251_update_rx_ready(device_t *device)
+{
+ msm8251_t *uart = get_token(device);
+ int state;
+
+ state = uart->status & MSM8251_STATUS_RX_READY;
+
+ /* masked? */
+ if ((uart->command & (1<<2))==0)
+ {
+ state = 0;
+ }
+
+ uart->out_rxrdy_func(state != 0);
+}
+
+
+
+/*-------------------------------------------------
+ msm8251_receive_clock
+-------------------------------------------------*/
+
+void msm8251_receive_clock(device_t *device)
+{
+ msm8251_t *uart = get_token(device);
+
+ /* receive enable? */
+ if (uart->command & (1<<2))
+ {
+ //logerror("MSM8251\n");
+ /* get bit received from other side and update receive register */
+ receive_register_update_bit(&uart->receive_reg, get_in_data_bit(uart->connection.input_state));
+
+ if (uart->receive_reg.flags & RECEIVE_REGISTER_FULL)
+ {
+ receive_register_extract(&uart->receive_reg, &uart->form);
+ msm8251_receive_character(device, uart->receive_reg.byte_received);
+ }
+ }
+}
+
+
+
+/*-------------------------------------------------
+ msm8251_transmit_clock
+-------------------------------------------------*/
+
+void msm8251_transmit_clock(device_t *device)
+{
+ msm8251_t *uart = get_token(device);
+
+ /* transmit enable? */
+ if (uart->command & (1<<0))
+ {
+
+ /* transmit register full? */
+ if ((uart->status & MSM8251_STATUS_TX_READY)==0)
+ {
+ /* if transmit reg is empty */
+ if ((uart->transmit_reg.flags & TRANSMIT_REGISTER_EMPTY)!=0)
+ {
+ /* set it up */
+ transmit_register_setup(&uart->transmit_reg, &uart->form, uart->data);
+ /* msm8251 transmit reg now empty */
+ uart->status |=MSM8251_STATUS_TX_EMPTY;
+ /* ready for next transmit */
+ uart->status |=MSM8251_STATUS_TX_READY;
+
+ msm8251_update_tx_empty(device);
+ msm8251_update_tx_ready(device);
+ }
+ }
+
+ /* if transmit is not empty... transmit data */
+ if ((uart->transmit_reg.flags & TRANSMIT_REGISTER_EMPTY)==0)
+ {
+ // logerror("MSM8251\n");
+ transmit_register_send_bit(device->machine(),&uart->transmit_reg, &uart->connection);
+ }
+ }
+
+#if 0
+ /* hunt mode? */
+ /* after each bit has been shifted in, it is compared against the current sync byte */
+ if (uart->command & (1<<7))
+ {
+ /* data matches sync byte? */
+ if (uart->data == uart->sync_bytes[uart->sync_byte_offset])
+ {
+ /* sync byte matches */
+ /* update for next sync byte? */
+ uart->sync_byte_offset++;
+
+ /* do all sync bytes match? */
+ if (uart->sync_byte_offset == uart->sync_byte_count)
+ {
+ /* ent hunt mode */
+ uart->command &=~(1<<7);
+ }
+ }
+ else
+ {
+ /* if there is no match, reset */
+ uart->sync_byte_offset = 0;
+ }
+ }
+#endif
+}
+
+
+
+/*-------------------------------------------------
+ msm8251_update_tx_ready
+-------------------------------------------------*/
+
+static void msm8251_update_tx_ready(device_t *device)
+{
+ msm8251_t *uart = get_token(device);
+
+ /* clear tx ready state */
+ int tx_ready;
+
+ /* tx ready output is set if:
+ DB Buffer Empty &
+ CTS is set &
+ Transmit enable is 1
+ */
+
+ tx_ready = 0;
+
+ /* transmit enable? */
+ if ((uart->command & (1<<0))!=0)
+ {
+ /* other side has rts set (comes in as CTS at this side) */
+ if (uart->connection.input_state & SERIAL_STATE_CTS)
+ {
+ if (uart->status & MSM8251_STATUS_TX_EMPTY)
+ {
+ /* enable transfer */
+ tx_ready = 1;
+ }
+ }
+ }
+
+ uart->out_txrdy_func(tx_ready);
+}
+
+
+
+/*-------------------------------------------------
+ msm8251_update_tx_empty
+-------------------------------------------------*/
+
+static void msm8251_update_tx_empty(device_t *device)
+{
+ msm8251_t *uart = get_token(device);
+
+ if (uart->status & MSM8251_STATUS_TX_EMPTY)
+ {
+ /* tx is in marking state (high) when tx empty! */
+ set_out_data_bit(uart->connection.State, 1);
+ serial_connection_out(device->machine(),&uart->connection);
+ }
+
+ uart->out_txempty_func((uart->status & MSM8251_STATUS_TX_EMPTY) != 0);
+}
+
+
+
+/*-------------------------------------------------
+ DEVICE_RESET( msm8251 )
+-------------------------------------------------*/
+
+static DEVICE_RESET( msm8251 )
+{
+ msm8251_t *uart = get_token(device);
+
+ LOG(("MSM8251: Reset\n"));
+
+ /* what is the default setup when the 8251 has been reset??? */
+
+ /* msm8251 datasheet explains the state of tx pin at reset */
+ /* tx is set to 1 */
+ set_out_data_bit(uart->connection.State,1);
+
+ /* assumption, rts is set to 1 */
+ uart->connection.State &= ~SERIAL_STATE_RTS;
+ serial_connection_out(device->machine(), &uart->connection);
+
+ transmit_register_reset(&uart->transmit_reg);
+ receive_register_reset(&uart->receive_reg);
+ /* expecting mode byte */
+ uart->flags |= MSM8251_EXPECTING_MODE;
+ /* not expecting a sync byte */
+ uart->flags &= ~MSM8251_EXPECTING_SYNC_BYTE;
+
+ /* no character to read by cpu */
+ /* transmitter is ready and is empty */
+ uart->status = MSM8251_STATUS_TX_EMPTY | MSM8251_STATUS_TX_READY;
+ uart->mode_byte = 0;
+ uart->command = 0;
+
+ /* update tx empty pin output */
+ msm8251_update_tx_empty(device);
+ /* update rx ready pin output */
+ msm8251_update_rx_ready(device);
+ /* update tx ready pin output */
+ msm8251_update_tx_ready(device);
+}
+
+
+
+/*-------------------------------------------------
+ WRITE8_DEVICE_HANDLER(msm8251_control_w)
+-------------------------------------------------*/
+
+WRITE8_DEVICE_HANDLER(msm8251_control_w)
+{
+ msm8251_t *uart = get_token(device);
+
+ if (uart->flags & MSM8251_EXPECTING_MODE)
+ {
+ if (uart->flags & MSM8251_EXPECTING_SYNC_BYTE)
+ {
+ LOG(("MSM8251: Sync byte\n"));
+
+ LOG(("Sync byte: %02x\n", data));
+ /* store sync byte written */
+ uart->sync_bytes[uart->sync_byte_offset] = data;
+ uart->sync_byte_offset++;
+
+ if (uart->sync_byte_offset == uart->sync_byte_count)
+ {
+ /* finished transfering sync bytes, now expecting command */
+ uart->flags &= ~(MSM8251_EXPECTING_MODE | MSM8251_EXPECTING_SYNC_BYTE);
+ uart->sync_byte_offset = 0;
+ // uart->status = MSM8251_STATUS_TX_EMPTY | MSM8251_STATUS_TX_READY;
+ }
+ }
+ else
+ {
+ LOG(("MSM8251: Mode byte\n"));
+
+ uart->mode_byte = data;
+
+ /* Synchronous or Asynchronous? */
+ if ((data & 0x03)!=0)
+ {
+ /* Asynchronous
+
+ bit 7,6: stop bit length
+ 0 = inhibit
+ 1 = 1 bit
+ 2 = 1.5 bits
+ 3 = 2 bits
+ bit 5: parity type
+ 0 = parity odd
+ 1 = parity even
+ bit 4: parity test enable
+ 0 = disable
+ 1 = enable
+ bit 3,2: character length
+ 0 = 5 bits
+ 1 = 6 bits
+ 2 = 7 bits
+ 3 = 8 bits
+ bit 1,0: baud rate factor
+ 0 = defines command byte for synchronous or asynchronous
+ 1 = x1
+ 2 = x16
+ 3 = x64
+ */
+
+ LOG(("MSM8251: Asynchronous operation\n"));
+
+ LOG(("Character length: %d\n", (((data>>2) & 0x03)+5)));
+
+ if (data & (1<<4))
+ {
+ LOG(("enable parity checking\n"));
+ }
+ else
+ {
+ LOG(("parity check disabled\n"));
+ }
+
+ if (data & (1<<5))
+ {
+ LOG(("even parity\n"));
+ }
+ else
+ {
+ LOG(("odd parity\n"));
+ }
+
+ {
+ UINT8 stop_bit_length;
+
+ stop_bit_length = (data>>6) & 0x03;
+
+ switch (stop_bit_length)
+ {
+ case 0:
+ {
+ /* inhibit */
+ LOG(("stop bit: inhibit\n"));
+ }
+ break;
+
+ case 1:
+ {
+ /* 1 */
+ LOG(("stop bit: 1 bit\n"));
+ }
+ break;
+
+ case 2:
+ {
+ /* 1.5 */
+ LOG(("stop bit: 1.5 bits\n"));
+ }
+ break;
+
+ case 3:
+ {
+ /* 2 */
+ LOG(("stop bit: 2 bits\n"));
+ }
+ break;
+ }
+ }
+
+ uart->form.word_length = ((data>>2) & 0x03)+5;
+ uart->form.parity = SERIAL_PARITY_NONE;
+ switch ((data>>6) & 0x03)
+ {
+ case 0:
+ case 1:
+ uart->form.stop_bit_count = 1;
+ break;
+ case 2:
+ case 3:
+ uart->form.stop_bit_count = 2;
+ break;
+ }
+ receive_register_setup(&uart->receive_reg, &uart->form);
+
+
+#if 0
+ /* data bits */
+ uart->receive_char_length = (((data>>2) & 0x03)+5);
+
+ if (data & (1<<4))
+ {
+ /* parity */
+ uart->receive_char_length++;
+ }
+
+ /* stop bits */
+ uart->receive_char_length++;
+
+ uart->receive_flags &=~MSM8251_TRANSFER_RECEIVE_SYNCHRONISED;
+ uart->receive_flags |= MSM8251_TRANSFER_RECEIVE_WAITING_FOR_START_BIT;
+#endif
+ /* not expecting mode byte now */
+ uart->flags &= ~MSM8251_EXPECTING_MODE;
+// uart->status = MSM8251_STATUS_TX_EMPTY | MSM8251_STATUS_TX_READY;
+ }
+ else
+ {
+ /* bit 7: Number of sync characters
+ 0 = 1 character
+ 1 = 2 character
+ bit 6: Synchronous mode
+ 0 = Internal synchronisation
+ 1 = External synchronisation
+ bit 5: parity type
+ 0 = parity odd
+ 1 = parity even
+ bit 4: parity test enable
+ 0 = disable
+ 1 = enable
+ bit 3,2: character length
+ 0 = 5 bits
+ 1 = 6 bits
+ 2 = 7 bits
+ 3 = 8 bits
+ bit 1,0 = 0
+ */
+ LOG(("MSM8251: Synchronous operation\n"));
+
+ /* setup for sync byte(s) */
+ uart->flags |= MSM8251_EXPECTING_SYNC_BYTE;
+ uart->sync_byte_offset = 0;
+ if (data & 0x07)
+ {
+ uart->sync_byte_count = 1;
+ }
+ else
+ {
+ uart->sync_byte_count = 2;
+ }
+
+ }
+ }
+ }
+ else
+ {
+ /* command */
+ LOG(("MSM8251: Command byte\n"));
+
+ uart->command = data;
+
+ LOG(("Command byte: %02x\n", data));
+
+ if (data & (1<<7))
+ {
+ LOG(("hunt mode\n"));
+ }
+
+ if (data & (1<<5))
+ {
+ LOG(("/rts set to 0\n"));
+ }
+ else
+ {
+ LOG(("/rts set to 1\n"));
+ }
+
+ if (data & (1<<2))
+ {
+ LOG(("receive enable\n"));
+ }
+ else
+ {
+ LOG(("receive disable\n"));
+ }
+
+ if (data & (1<<1))
+ {
+ LOG(("/dtr set to 0\n"));
+ }
+ else
+ {
+ LOG(("/dtr set to 1\n"));
+ }
+
+ if (data & (1<<0))
+ {
+ LOG(("transmit enable\n"));
+ }
+ else
+ {
+ LOG(("transmit disable\n"));
+ }
+
+
+ /* bit 7:
+ 0 = normal operation
+ 1 = hunt mode
+ bit 6:
+ 0 = normal operation
+ 1 = internal reset
+ bit 5:
+ 0 = /RTS set to 1
+ 1 = /RTS set to 0
+ bit 4:
+ 0 = normal operation
+ 1 = reset error flag
+ bit 3:
+ 0 = normal operation
+ 1 = send break character
+ bit 2:
+ 0 = receive disable
+ 1 = receive enable
+ bit 1:
+ 0 = /DTR set to 1
+ 1 = /DTR set to 0
+ bit 0:
+ 0 = transmit disable
+ 1 = transmit enable
+ */
+
+ uart->connection.State &=~SERIAL_STATE_RTS;
+ if (data & (1<<5))
+ {
+ /* rts set to 0 */
+ uart->connection.State |= SERIAL_STATE_RTS;
+ }
+
+ uart->connection.State &=~SERIAL_STATE_DTR;
+ if (data & (1<<1))
+ {
+ uart->connection.State |= SERIAL_STATE_DTR;
+ }
+
+ if ((data & (1<<0))==0)
+ {
+ /* held in high state when transmit disable */
+ set_out_data_bit(uart->connection.State,1);
+ }
+
+
+ /* refresh outputs */
+ serial_connection_out(device->machine(), &uart->connection);
+
+ if (data & (1<<4))
+ {
+ uart->status &= ~(MSM8251_STATUS_PARITY_ERROR | MSM8251_STATUS_OVERRUN_ERROR | MSM8251_STATUS_FRAMING_ERROR);
+ }
+
+ if (data & (1<<6))
+ {
+ device->reset();
+ }
+
+ msm8251_update_rx_ready(device);
+ msm8251_update_tx_ready(device);
+
+ }
+}
+
+
+
+/*-------------------------------------------------
+ READ8_DEVICE_HANDLER(msm8251_status_r)
+-------------------------------------------------*/
+
+READ8_DEVICE_HANDLER(msm8251_status_r)
+{
+ msm8251_t *uart = get_token(device);
+
+ LOG(("status: %02x\n", uart->status));
+ return uart->status;
+}
+
+
+
+/*-------------------------------------------------
+ WRITE8_DEVICE_HANDLER(msm8251_data_w)
+-------------------------------------------------*/
+
+WRITE8_DEVICE_HANDLER(msm8251_data_w)
+{
+ msm8251_t *uart = get_token(device);
+
+ uart->data = data;
+
+ logerror("write data: %02x\n",data);
+
+ /* writing clears */
+ uart->status &=~MSM8251_STATUS_TX_READY;
+
+ /* if transmitter is active, then tx empty will be signalled */
+
+ msm8251_update_tx_ready(device);
+}
+
+
+
+/*-------------------------------------------------
+ msm8251_receive_character - called when last
+ bit of data has been received
+-------------------------------------------------*/
+
+void msm8251_receive_character(device_t *device, UINT8 ch)
+{
+ msm8251_t *uart = get_token(device);
+
+ logerror("msm8251 receive char: %02x\n",ch);
+
+ uart->data = ch;
+
+ /* char has not been read and another has arrived! */
+ if (uart->status & MSM8251_STATUS_RX_READY)
+ {
+ uart->status |= MSM8251_STATUS_OVERRUN_ERROR;
+ }
+ uart->status |= MSM8251_STATUS_RX_READY;
+
+ msm8251_update_rx_ready(device);
+}
+
+
+
+/*-------------------------------------------------
+ READ8_DEVICE_HANDLER(msm8251_data_r) - read data
+-------------------------------------------------*/
+
+READ8_DEVICE_HANDLER(msm8251_data_r)
+{
+ msm8251_t *uart = get_token(device);
+
+ logerror("read data: %02x, STATUS=%02x\n",uart->data,uart->status);
+ /* reading clears */
+ uart->status &= ~MSM8251_STATUS_RX_READY;
+
+ msm8251_update_rx_ready(device);
+ return uart->data;
+}
+
+
+
+/*-------------------------------------------------
+ msm8251_connect_to_serial_device - initialise
+ transfer using serial device - set the callback
+ which will be called when serial device has
+ updated it's state
+-------------------------------------------------*/
+
+void msm8251_connect_to_serial_device(device_t *device, device_t *image)
+{
+ msm8251_t *uart = get_token(device);
+ serial_device_connect(image, &uart->connection);
+}
+
+
+
+/*-------------------------------------------------
+ msm8251_connect
+-------------------------------------------------*/
+
+void msm8251_connect(device_t *device, serial_connection *other_connection)
+{
+ msm8251_t *uart = get_token(device);
+ serial_connection_link(device->machine(), &uart->connection, other_connection);
+}
+
+
+/*-------------------------------------------------
+ DEVICE_GET_INFO( msm8251 )
+-------------------------------------------------*/
+
+DEVICE_GET_INFO( msm8251 )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(msm8251_t); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(msm8251); break;
+ case DEVINFO_FCT_STOP: /* Nothing */ break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(msm8251); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Intel 8251 UART"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Intel 8251 UART"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: /* Nothing */ break;
+ }
+}
+
+DEFINE_LEGACY_DEVICE(MSM8251, msm8251);
diff --git a/src/emu/machine/msm8251.h b/src/emu/machine/msm8251.h
new file mode 100644
index 00000000000..e01bcfc4503
--- /dev/null
+++ b/src/emu/machine/msm8251.h
@@ -0,0 +1,88 @@
+/*********************************************************************
+
+ msm8251.h
+
+ MSM/Intel 8251 Universal Synchronous/Asynchronous Receiver Transmitter code
+
+*********************************************************************/
+
+#ifndef __MSM8251_H__
+#define __MSM8251_H__
+
+#include "machine/serial.h"
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+DECLARE_LEGACY_DEVICE(MSM8251, msm8251);
+
+#define MSM8251_EXPECTING_MODE 0x01
+#define MSM8251_EXPECTING_SYNC_BYTE 0x02
+
+#define MSM8251_STATUS_FRAMING_ERROR 0x020
+#define MSM8251_STATUS_OVERRUN_ERROR 0x010
+#define MSM8251_STATUS_PARITY_ERROR 0x08
+#define MSM8251_STATUS_TX_EMPTY 0x04
+#define MSM8251_STATUS_RX_READY 0x02
+#define MSM8251_STATUS_TX_READY 0x01
+
+#define MCFG_MSM8251_ADD(_tag, _intrf) \
+ MCFG_DEVICE_ADD(_tag, MSM8251, 0) \
+ MCFG_DEVICE_CONFIG(_intrf)
+
+#define MCFG_MSM8251_REMOVE(_tag) \
+ MCFG_DEVICE_REMOVE(_tag)
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _msm8251_interface msm8251_interface;
+struct _msm8251_interface
+{
+ devcb_read_line in_rxd_func;
+ devcb_write_line out_txd_func;
+ devcb_read_line in_dsr_func;
+ devcb_write_line out_dtr_func;
+ devcb_write_line out_rts_func;
+ devcb_write_line out_rxrdy_func;
+ devcb_write_line out_txrdy_func;
+ devcb_write_line out_txempty_func;
+ devcb_write_line out_syndet_func;
+};
+
+
+/***************************************************************************
+ PROTOTYPES
+***************************************************************************/
+
+extern const msm8251_interface default_msm8251_interface;
+
+/* read data register */
+READ8_DEVICE_HANDLER(msm8251_data_r);
+
+/* read status register */
+READ8_DEVICE_HANDLER(msm8251_status_r);
+
+/* write data register */
+WRITE8_DEVICE_HANDLER(msm8251_data_w);
+
+/* write control word */
+WRITE8_DEVICE_HANDLER(msm8251_control_w);
+
+/* The 8251 has seperate transmit and receive clocks */
+/* use these two functions to update the msm8251 for each clock */
+/* on NC100 system, the clocks are the same */
+void msm8251_transmit_clock(device_t *device);
+void msm8251_receive_clock(device_t *device);
+
+/* connecting to serial output */
+void msm8251_connect_to_serial_device(device_t *device, device_t *image);
+void msm8251_connect(device_t *device, serial_connection *other_connection);
+
+void msm8251_receive_character(device_t *device, UINT8 ch);
+
+#endif /* __MSM8251_H__ */
diff --git a/src/emu/machine/serial.c b/src/emu/machine/serial.c
new file mode 100644
index 00000000000..7dc5234c8e6
--- /dev/null
+++ b/src/emu/machine/serial.c
@@ -0,0 +1,733 @@
+/* internal serial transmission */
+
+/* select a file on host filesystem to transfer using serial method,
+setup serial interface software in driver and let the transfer begin */
+
+/* this is used in the Amstrad NC Notepad emulation */
+
+
+
+/*
+
+ the output starts at 1 level. It changes to 0 when the start bit has been transmitted.
+ This therefore signals that data is following.
+ When all data bits have been received, stop bits are transmitted with a value of 1.
+
+ msm8251 expects this in asynchronous mode:
+ packet format:
+
+ bit count function value
+ 1 start bit 0
+ note 1 data bits x
+ note 2 parity bit x
+ note 3 stop bits 1
+
+ Note:
+ 1. Data size can be defined (usual value is 8)
+ 2. Parity bit (if parity is set to odd or even). Value of bit
+ is defined by data parity.
+ 3. There should be at least 1 stop bit.
+*/
+
+#include "emu.h"
+#include "serial.h"
+
+
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+/* a read/write bit stream. used to transmit data and to receive data */
+typedef struct _data_stream data_stream;
+struct _data_stream
+{
+ /* pointer to buffer */
+ unsigned char *pData;
+ /* length of buffer */
+ unsigned long DataLength;
+
+ /* bit offset within current byte */
+ unsigned long BitCount;
+ /* byte offset within data */
+ unsigned long ByteCount;
+};
+
+typedef struct _serial_t serial_t;
+struct _serial_t
+{
+ /* transmit data bit-stream */
+ data_stream transmit;
+ /* receive data bit-stream */
+ data_stream receive;
+
+ /* register to receive data */
+ serial_receive_register receive_reg;
+ /* register to transmit data */
+ serial_transmit_register transmit_reg;
+
+ /* connection to transmit/receive data over */
+ serial_connection connection;
+
+ /* data form to transmit/receive */
+ data_form form;
+
+ int transmit_state;
+
+ /* baud rate */
+ unsigned long BaudRate;
+
+ /* baud rate timer */
+ emu_timer *timer;
+};
+
+INLINE serial_t *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ //assert(device->type() == SERIAL);
+
+ return (serial_t *)downcast<legacy_device_base *>(device)->token();
+}
+
+
+/* the serial streams */
+static TIMER_CALLBACK(serial_device_baud_rate_callback);
+
+
+/*********************************************************/
+
+static unsigned char serial_parity_table[256];
+
+void serial_helper_setup(void)
+{
+ int i;
+
+ /* if sum of all bits in the byte is even, then the data
+ has even parity, otherwise it has odd parity */
+ for (i=0; i<256; i++)
+ {
+ int data;
+ int sum;
+ int b;
+
+ sum = 0;
+ data = i;
+
+ for (b=0; b<8; b++)
+ {
+ sum+=data & 0x01;
+
+ data = data>>1;
+ }
+
+ serial_parity_table[i] = sum & 0x01;
+ }
+}
+
+static unsigned char serial_helper_get_parity(unsigned char data)
+{
+ return serial_parity_table[data & 0x0ff];
+}
+
+static void serial_device_in_callback(running_machine &machine, int id, unsigned long status)
+{
+/* serial_t *ser = get_safe_token(device);
+
+ ser->connection.input_state = status;*/
+}
+
+/***** SERIAL DEVICE ******/
+void serial_device_setup(device_t *device, int baud_rate, int num_data_bits, int stop_bit_count, int parity_code)
+{
+ serial_t *ser = get_safe_token(device);
+
+ ser->BaudRate = baud_rate;
+ ser->form.word_length = num_data_bits;
+ ser->form.stop_bit_count = stop_bit_count;
+ ser->form.parity = parity_code;
+ ser->timer = device->machine().scheduler().timer_alloc(FUNC(serial_device_baud_rate_callback), (void *)device);
+
+ serial_connection_init(device->machine(),&ser->connection);
+ serial_connection_set_in_callback(device->machine(),&ser->connection, serial_device_in_callback);
+
+ /* signal to other end it is clear to send! */
+ /* data is initially high state */
+ /* set /rts */
+ ser->connection.State |= SERIAL_STATE_RTS;
+ /* signal to other end data is ready to be accepted */
+ /* set /dtr */
+ ser->connection.State |= SERIAL_STATE_DTR;
+ set_out_data_bit(ser->connection.State, 1);
+ serial_connection_out(device->machine(),&ser->connection);
+ transmit_register_reset(&ser->transmit_reg);
+ receive_register_reset(&ser->receive_reg);
+ receive_register_setup(&ser->receive_reg, &ser->form);
+}
+
+
+unsigned long serial_device_get_state(device_t *device)
+{
+ serial_t *ser = get_safe_token(device);
+
+ return ser->connection.State;
+}
+
+void serial_device_set_transmit_state(device_t *device, int state)
+{
+ int previous_state;
+ serial_t *ser = get_safe_token(device);
+
+ previous_state = ser->transmit_state;
+
+ ser->transmit_state = state;
+
+ if ((state^previous_state)!=0)
+ {
+ if (state)
+ {
+ /* start timer */
+ ser->timer->adjust(attotime::zero, 0, attotime::from_hz(ser->BaudRate));
+ }
+ else
+ {
+ /* remove timer */
+ ser->timer->reset();
+ }
+ }
+
+}
+
+/* get a bit from input stream */
+static int data_stream_get_data_bit_from_data_byte(data_stream *stream)
+{
+ int data_bit;
+ int data_byte;
+
+ if (stream->ByteCount<stream->DataLength)
+ {
+ /* get data from buffer */
+ data_byte= stream->pData[stream->ByteCount];
+ }
+ else
+ {
+ /* over end of buffer, so return 0 */
+ data_byte= 0;
+ }
+
+ /* get bit from data */
+ data_bit = (data_byte>>(7-stream->BitCount)) & 0x01;
+
+ /* update bit count */
+ stream->BitCount++;
+ /* ripple overflow onto byte count */
+ stream->ByteCount+=stream->BitCount>>3;
+ /* lock bit count into range */
+ stream->BitCount &=0x07;
+
+ /* do not let it read over end of data */
+ if (stream->ByteCount>=stream->DataLength)
+ {
+ stream->ByteCount = stream->DataLength-1;
+ }
+
+ return data_bit;
+}
+
+
+void receive_register_setup(serial_receive_register *receive, data_form *data_form)
+{
+ receive->bit_count = data_form->word_length + data_form->stop_bit_count;
+
+ if (data_form->parity != SERIAL_PARITY_NONE)
+ {
+ receive->bit_count++;
+ }
+}
+
+
+/* this is generic code to be used in serial chip implementations */
+/* the majority of serial chips work in the same way and this code will work */
+/* for them */
+
+/* receive a bit */
+void receive_register_update_bit(serial_receive_register *receive, int bit)
+{
+ int previous_bit;
+
+ LOG(("receive register receive bit: %1x\n",bit));
+ previous_bit = receive->register_data & 1;
+
+ /* shift previous bit 7 out */
+ receive->register_data = receive->register_data<<1;
+ /* shift new bit in */
+ receive->register_data = (receive->register_data & 0xfffe) | bit;
+ /* update bit count received */
+ receive->bit_count_received++;
+
+ /* asyncrhonouse mode */
+ if (receive->flags & RECEIVE_REGISTER_WAITING_FOR_START_BIT)
+ {
+ /* the previous bit is stored in uart.receive char bit 0 */
+ /* has the bit state changed? */
+ if (((previous_bit ^ bit) & 0x01)!=0)
+ {
+ /* yes */
+ if (bit==0)
+ {
+ //logerror("receive register saw start bit\n");
+
+ /* seen start bit! */
+ /* not waiting for start bit now! */
+ receive->flags &=~RECEIVE_REGISTER_WAITING_FOR_START_BIT;
+ receive->flags |=RECEIVE_REGISTER_SYNCHRONISED;
+ /* reset bit count received */
+ receive->bit_count_received = 0;
+ }
+ }
+ }
+ else
+ if (receive->flags & RECEIVE_REGISTER_SYNCHRONISED)
+ {
+ /* received all bits? */
+ if (receive->bit_count_received==receive->bit_count)
+ {
+ receive->bit_count_received = 0;
+ receive->flags &=~RECEIVE_REGISTER_SYNCHRONISED;
+ receive->flags |= RECEIVE_REGISTER_WAITING_FOR_START_BIT;
+ //logerror("receive register full\n");
+ receive->flags |= RECEIVE_REGISTER_FULL;
+ }
+ }
+}
+
+void receive_register_reset(serial_receive_register *receive_reg)
+{
+ receive_reg->bit_count_received = 0;
+ receive_reg->flags &=~RECEIVE_REGISTER_FULL;
+ receive_reg->flags &=~RECEIVE_REGISTER_SYNCHRONISED;
+ receive_reg->flags |= RECEIVE_REGISTER_WAITING_FOR_START_BIT;
+}
+
+void receive_register_extract(serial_receive_register *receive_reg, data_form *data_form)
+{
+ unsigned long data_shift;
+ UINT8 data;
+
+ receive_register_reset(receive_reg);
+
+ data_shift = 0;
+
+ /* if parity is even or odd, there should be a parity bit in the stream! */
+ if (data_form->parity!=SERIAL_PARITY_NONE)
+ {
+ data_shift++;
+ }
+
+ data_shift+=data_form->stop_bit_count;
+
+ /* strip off stop bits and parity */
+ data = receive_reg->register_data>>data_shift;
+
+ /* mask off other bits so data byte has 0's in unused bits */
+ data = data & (0x0ff
+ >>
+ (8-(data_form->word_length)));
+
+ receive_reg->byte_received = data;
+
+ /* parity enable? */
+ switch (data_form->parity)
+ {
+ case SERIAL_PARITY_NONE:
+ break;
+
+ /* check parity */
+ case SERIAL_PARITY_ODD:
+ case SERIAL_PARITY_EVEN:
+ {
+ //unsigned char computed_parity;
+ //unsigned char parity_received;
+
+ /* get state of parity bit received */
+ //parity_received = (receive_reg->register_data>>data_form->stop_bit_count) & 0x01;
+
+ /* compute parity for received bits */
+ //computed_parity = serial_helper_get_parity(data);
+
+ if (data_form->parity == SERIAL_PARITY_ODD)
+ {
+ /* odd parity */
+
+
+ }
+ else
+ {
+ /* even parity */
+
+
+ }
+
+ }
+ break;
+ }
+}
+
+/***** TRANSMIT REGISTER *****/
+
+void transmit_register_reset(serial_transmit_register *transmit_reg)
+{
+ transmit_reg->flags |=TRANSMIT_REGISTER_EMPTY;
+}
+
+/* used to construct data in stream format */
+static void transmit_register_add_bit(serial_transmit_register *transmit_reg, int bit)
+{
+ /* combine bit */
+ transmit_reg->register_data = transmit_reg->register_data<<1;
+ transmit_reg->register_data &=~1;
+ transmit_reg->register_data|=(bit & 0x01);
+ transmit_reg->bit_count++;
+}
+
+
+/* generate data in stream format ready for transfer */
+void transmit_register_setup(serial_transmit_register *transmit_reg, data_form *data_form,unsigned char data_byte)
+{
+ int i;
+ unsigned char transmit_data;
+
+
+ transmit_reg->bit_count_transmitted = 0;
+ transmit_reg->bit_count = 0;
+ transmit_reg->flags &=~TRANSMIT_REGISTER_EMPTY;
+
+ /* start bit */
+ transmit_register_add_bit(transmit_reg,0);
+
+ /* data bits */
+ transmit_data = data_byte;
+ for (i=0; i<data_form->word_length; i++)
+ {
+ int databit;
+
+ /* get bit from data */
+ databit = (transmit_data>>(data_form->word_length-1)) & 0x01;
+ /* add bit to formatted byte */
+ transmit_register_add_bit(transmit_reg, databit);
+ transmit_data = transmit_data<<1;
+ }
+
+ /* parity */
+ if (data_form->parity!=SERIAL_PARITY_NONE)
+ {
+ /* odd or even parity */
+ unsigned char parity;
+
+ /* get parity */
+ /* if parity = 0, data has even parity - i.e. there is an even number of one bits in the data */
+ /* if parity = 1, data has odd parity - i.e. there is an odd number of one bits in the data */
+ parity = serial_helper_get_parity(data_byte);
+
+ transmit_register_add_bit(transmit_reg, parity);
+ }
+
+ /* stop bit(s) */
+ for (i=0; i<data_form->stop_bit_count; i++)
+ {
+ transmit_register_add_bit(transmit_reg,1);
+ }
+}
+
+
+/* get a bit from the transmit register */
+static int transmit_register_get_data_bit(serial_transmit_register *transmit_reg)
+{
+ int bit;
+
+ bit = (transmit_reg->register_data>>
+ (transmit_reg->bit_count - 1 -
+ transmit_reg->bit_count_transmitted)) & 0x01;
+
+ transmit_reg->bit_count_transmitted++;
+
+ /* have all bits of this stream formatted byte been sent? */
+ if (transmit_reg->bit_count_transmitted==transmit_reg->bit_count)
+ {
+ /* yes - generate a new byte to send */
+ transmit_reg->flags |= TRANSMIT_REGISTER_EMPTY;
+ }
+
+ return bit;
+}
+
+
+void transmit_register_send_bit(running_machine &machine, serial_transmit_register *transmit_reg, serial_connection *connection)
+{
+ int data;
+
+ data = transmit_register_get_data_bit(transmit_reg);
+
+ /* set tx data bit */
+ set_out_data_bit(connection->State, data);
+
+ /* state out through connection */
+ serial_connection_out(machine, connection);
+}
+
+static void serial_protocol_none_sent_char(device_t *device)
+{
+ int i;
+ int bit;
+ unsigned char data_byte;
+ serial_t *ser = get_safe_token(device);
+
+ /* generate byte to transmit */
+ data_byte = 0;
+ for (i=0; i<ser->form.word_length; i++)
+ {
+ data_byte = data_byte<<1;
+ bit = data_stream_get_data_bit_from_data_byte(&ser->transmit);
+ data_byte = data_byte|bit;
+ }
+ /* setup register */
+ transmit_register_setup(&ser->transmit_reg,&ser->form, data_byte);
+
+ logerror("serial device transmitted char: %02x\n",data_byte);
+}
+
+static TIMER_CALLBACK(serial_device_baud_rate_callback)
+{
+ serial_t *ser = get_safe_token((device_t*)ptr);
+
+ /* receive data into receive register */
+ receive_register_update_bit(&ser->receive_reg, get_in_data_bit(ser->connection.input_state));
+
+ if (ser->receive_reg.flags & RECEIVE_REGISTER_FULL)
+ {
+ //logerror("SERIAL DEVICE\n");
+ receive_register_extract(&ser->receive_reg, &ser->form);
+
+ logerror("serial device receive char: %02x\n",ser->receive_reg.byte_received);
+ }
+
+ /* is transmit empty? */
+ if (ser->transmit_reg.flags & TRANSMIT_REGISTER_EMPTY)
+ {
+ /* char has been sent, execute callback */
+ serial_protocol_none_sent_char((device_t*)ptr);
+ }
+
+ /* other side says it is clear to send? */
+ if (ser->connection.input_state & SERIAL_STATE_CTS)
+ {
+ /* send bit */
+ transmit_register_send_bit(machine, &ser->transmit_reg, &ser->connection);
+ }
+}
+
+/* connect the specified connection to this serial device */
+void serial_device_connect(device_t *device, serial_connection *connection)
+{
+ serial_t *ser = get_safe_token(device);
+ serial_connection_link(device->machine(), connection, &ser->connection);
+}
+
+
+/* load image */
+static int serial_device_load_internal(device_image_interface &image, unsigned char **ptr, int *pDataSize)
+{
+ int datasize;
+ unsigned char *data;
+
+ /* get file size */
+ datasize = image.length();
+
+ if (datasize!=0)
+ {
+ /* malloc memory for this data */
+ data = (unsigned char *)malloc(datasize);
+
+ if (data!=NULL)
+ {
+ /* read whole file */
+ image.fread(data, datasize);
+
+ *ptr = data;
+ *pDataSize = datasize;
+
+ logerror("File loaded!\r\n");
+
+ /* ok! */
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* reset position in stream */
+static void data_stream_reset(data_stream *stream)
+{
+ /* reset byte offset */
+ stream->ByteCount= 0;
+ /* reset bit count */
+ stream->BitCount = 0;
+}
+
+/* free stream */
+static void data_stream_free(data_stream *stream)
+{
+ if (stream->pData!=NULL)
+ {
+ free(stream->pData);
+ stream->pData = NULL;
+ }
+ stream->DataLength = 0;
+}
+
+/* initialise stream */
+static void data_stream_init(data_stream *stream, unsigned char *pData, unsigned long DataLength)
+{
+ stream->pData = pData;
+ stream->DataLength = DataLength;
+ data_stream_reset(stream);
+}
+
+DEVICE_START(serial)
+{
+ //serial_t *ser = get_safe_token(device);
+}
+
+DEVICE_RESET(serial)
+{
+// serial_t *ser = get_safe_token(device);
+}
+
+DEVICE_IMAGE_LOAD(serial)
+{
+ int data_length;
+ unsigned char *data;
+ device_t *device = &image.device();
+
+ serial_t *ser = get_safe_token(device);
+
+ /* load file and setup transmit data */
+ if (serial_device_load_internal(image, &data, &data_length))
+ {
+ data_stream_init(&ser->transmit, data, data_length);
+ return IMAGE_INIT_PASS;
+ }
+
+ return IMAGE_INIT_FAIL;
+}
+
+
+DEVICE_IMAGE_UNLOAD(serial)
+{
+ device_t *device = &image.device();
+ serial_t *ser = get_safe_token(device);
+
+ /* stop transmit */
+ serial_device_set_transmit_state(device, 0);
+ /* free streams */
+ data_stream_free(&ser->transmit);
+ data_stream_free(&ser->receive);
+}
+
+
+DEVICE_GET_INFO( serial )
+{
+ switch ( state )
+ {
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(serial_t); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
+ case DEVINFO_INT_IMAGE_TYPE: info->i = IO_SERIAL; break;
+ case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_CREATABLE: info->i = 1; break;
+
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( serial ); break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( serial ); break;
+ case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME( serial ); break;
+ case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(serial ); break;
+ case DEVINFO_STR_NAME: strcpy( info->s, "Serial port"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Serial port"); break;
+ case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, ""); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright the MESS Team"); break;
+ }
+}
+
+/*******************************************************************************/
+/*******************************************************************************/
+/********* SERIAL CONNECTION ***********/
+
+
+/* this converts state at this end to a state the other end can accept */
+/* e.g. CTS at this end becomes RTS at other end.
+ RTS at this end becomes CTS at other end.
+ TX at this end becomes RX at other end.
+ RX at this end becomes TX at other end.
+ etc
+
+ The same thing is done inside the serial null-terminal lead */
+
+static unsigned long serial_connection_spin_bits(unsigned long input_status)
+{
+
+ return
+ /* cts -> rts */
+ (((input_status & 0x01)<<1) |
+ /* rts -> cts */
+ ((input_status>>1) & 0x01) |
+ /* dsr -> dtr */
+ (((input_status>>2) & 0x01)<<3) |
+ /* dtr -> dsr */
+ (((input_status>>3) & 0x01)<<2) |
+ /* rx -> tx */
+ (((input_status>>4) & 0x01)<<5) |
+ /* tx -> rx */
+ (((input_status>>5) & 0x01)<<4));
+}
+
+
+/* setup callbacks for connection */
+void serial_connection_init(running_machine &machine, serial_connection *connection)
+{
+ connection->out_callback = NULL;
+ connection->in_callback = NULL;
+}
+
+/* set callback which will be executed when in status has changed */
+void serial_connection_set_in_callback(running_machine &machine, serial_connection *connection, void (*in_cb)(running_machine &machine, int id, unsigned long status))
+{
+ connection->in_callback = in_cb;
+}
+
+/* output new state through connection */
+void serial_connection_out(running_machine &machine, serial_connection *connection)
+{
+ if (connection->out_callback!=NULL)
+ {
+ unsigned long state_at_other_end;
+
+ state_at_other_end = serial_connection_spin_bits(connection->State);
+
+ connection->out_callback(machine, connection->id, state_at_other_end);
+ }
+}
+
+/* join two serial connections together */
+void serial_connection_link(running_machine &machine, serial_connection *connection_a, serial_connection *connection_b)
+{
+ /* both connections should have their in connection setup! */
+ /* the in connection is the callback they use to update their state based
+ on the output from the other side */
+ connection_a->out_callback = connection_b->in_callback;
+ connection_b->out_callback = connection_a->in_callback;
+
+ /* let b know the state of a */
+ serial_connection_out(machine,connection_a);
+ /* let a know the state of b */
+ serial_connection_out(machine,connection_b);
+}
+
+DEFINE_LEGACY_IMAGE_DEVICE(SERIAL, serial);
diff --git a/src/emu/machine/serial.h b/src/emu/machine/serial.h
new file mode 100644
index 00000000000..d958f6a9a08
--- /dev/null
+++ b/src/emu/machine/serial.h
@@ -0,0 +1,247 @@
+/*****************************************************************************
+ *
+ * machine/serial.h
+ *
+ * internal serial transmission
+ *
+ * This code is used to transmit a file stored on the host filesystem
+ * (e.g. PC harddrive) to an emulated system.
+ *
+ * The file is converted into a serial bit-stream which can be received
+ * by the emulated serial chip in the emulated system.
+ *
+ * The file can be transmitted using different protocols.
+ *
+ * A and B are two computers linked with a serial connection
+ * A and B can transmit and receive data, through the same connection
+ *
+ * These flags apply to A and B, and give the state of the input & output
+ * signals at each side.
+ *
+ ****************************************************************************/
+
+#ifndef SERIAL_H_
+#define SERIAL_H_
+
+
+/*
+ CTS = Clear to Send. (INPUT)
+ Other end of connection is ready to accept data
+
+
+ NOTE:
+
+ This output is active low on serial chips (e.g. 0 is CTS is set),
+ but here it is active high!
+*/
+#define SERIAL_STATE_CTS 0x0001
+
+/*
+ RTS = Request to Send. (OUTPUT)
+ This end is ready to send data, and requests if the other
+ end is ready to accept it
+
+ NOTE:
+
+ This output is active low on serial chips (e.g. 0 is RTS is set),
+ but here it is active high!
+*/
+#define SERIAL_STATE_RTS 0x0002
+
+/*
+ DSR = Data Set ready. (INPUT)
+ Other end of connection has data
+
+
+ NOTE:
+
+ This output is active low on serial chips (e.g. 0 is DSR is set),
+ but here it is active high!
+*/
+#define SERIAL_STATE_DSR 0x0004
+
+/*
+ DTR = Data terminal Ready. (OUTPUT)
+ TX contains new data.
+
+ NOTE:
+
+ This output is active low on serial chips (e.g. 0 is DTR is set),
+ but here it is active high!
+*/
+#define SERIAL_STATE_DTR 0x0008
+/* RX = Recieve data. (INPUT) */
+#define SERIAL_STATE_RX_DATA 0x00010
+/* TX = Transmit data. (OUTPUT) */
+#define SERIAL_STATE_TX_DATA 0x00020
+
+/* parity selections */
+/* if all the bits are added in a byte, if the result is:
+ even -> parity is even
+ odd -> parity is odd
+*/
+enum
+{
+ SERIAL_PARITY_NONE, /* no parity. a parity bit will not be in the transmitted/received data */
+ SERIAL_PARITY_ODD, /* odd parity */
+ SERIAL_PARITY_EVEN /* even parity */
+};
+
+/* this macro is used to extract the received data from the status */
+#define get_in_data_bit(x) ((x & SERIAL_STATE_RX_DATA)>>4)
+
+/* this macro is used to set the transmitted data in the status */
+#define set_out_data_bit(x, data) \
+ x&=~SERIAL_STATE_TX_DATA; \
+ x|=(data<<5)
+
+
+/*******************************************************************************/
+/**** SERIAL CONNECTION ***/
+
+
+/* this structure represents a serial connection */
+typedef struct _serial_connection serial_connection;
+struct _serial_connection
+{
+ int id;
+ /* state of this side */
+ unsigned long State;
+
+ /* state of other side - store here */
+ unsigned long input_state;
+
+ /* this callback is executed when this side has refreshed it's state,
+ to let the other end know */
+ void (*out_callback)(running_machine &machine, int id, unsigned long state);
+ /* this callback is executed when the other side has refreshed it's state,
+ to let the other end know */
+ void (*in_callback)(running_machine &machine, int id, unsigned long state);
+};
+
+/*----------- defined in machine/serial.c -----------*/
+
+/* setup out and in callbacks */
+void serial_connection_init(running_machine &machine, serial_connection *connection);
+
+/* set callback which will be executed when in status has changed */
+void serial_connection_set_in_callback(running_machine &machine, serial_connection *connection, void (*in_cb)(running_machine &machine, int id, unsigned long status));
+
+/* output status, if callback is setup it will be executed with the new status */
+void serial_connection_out(running_machine &machine, serial_connection *connection);
+
+/* join two serial connections */
+void serial_connection_link(running_machine &machine, serial_connection *connection_a, serial_connection *connection_b);
+
+
+/*******************************************************************************/
+
+/* form of data being transmitted and received */
+typedef struct _data_form data_form;
+struct _data_form
+{
+ /* length of word in bits */
+ unsigned long word_length;
+ /* parity state */
+ unsigned long parity;
+ /* number of stop bits */
+ unsigned long stop_bit_count;
+};
+
+/*******************************************************************************/
+
+/*******************************************************************************/
+/**** RECEIVE AND TRANSMIT GENERIC CODE ****/
+
+/* this can be used by most of the serial chip implementations,
+because they all work in roughly the same way.
+There is generic code to send and receive data in the specified form */
+
+/* receive is waiting for start bit. The transition from high-low indicates
+start of start bit. This is used to synchronise with the data being transfered */
+#define RECEIVE_REGISTER_WAITING_FOR_START_BIT 0x01
+/* receive is synchronised with data, data bits will be clocked in */
+#define RECEIVE_REGISTER_SYNCHRONISED 0x02
+/* set if receive register has been filled */
+#define RECEIVE_REGISTER_FULL 0x04
+
+
+/* the receive register holds data in receive form! */
+/* this must be extracted to get the data byte received */
+typedef struct _serial_receive_register serial_receive_register;
+struct _serial_receive_register
+{
+ /* data */
+ unsigned long register_data;
+ /* flags */
+ unsigned long flags;
+ /* bit count received */
+ unsigned long bit_count_received;
+ /* length of data to receive - includes data bits, parity bit and stop bit */
+ unsigned long bit_count;
+
+ /* the byte of data received */
+ unsigned char byte_received;
+};
+
+void receive_register_setup(serial_receive_register *receive, data_form *data_form);
+void receive_register_update_bit(serial_receive_register *receive, int bit_state);
+void receive_register_extract(serial_receive_register *receive_reg, data_form *data_form);
+void receive_register_reset(serial_receive_register *receive_reg);
+
+/* the transmit register is the final stage
+in the serial transmit procedure */
+/* normally, data is written to the transmit reg,
+then it is assembled into transmit form and transmitted */
+/* the transmit register holds data in transmit form */
+
+/* register is empty and ready to be filled with data */
+#define TRANSMIT_REGISTER_EMPTY 0x0001
+
+typedef struct _serial_transmit_register serial_transmit_register;
+struct _serial_transmit_register
+{
+ /* data */
+ unsigned long register_data;
+ /* flags */
+ unsigned long flags;
+ /* number of bits transmitted */
+ unsigned long bit_count_transmitted;
+ /* length of data to send */
+ unsigned long bit_count;
+};
+
+/* setup transmit reg ready for transmit */
+void transmit_register_setup(serial_transmit_register *transmit_reg, data_form *data_form,unsigned char data_byte);
+void transmit_register_send_bit(running_machine &machine, serial_transmit_register *transmit_reg, serial_connection *connection);
+void transmit_register_reset(serial_transmit_register *transmit_reg);
+
+/*******************************************************************************/
+/**** SERIAL HELPER ****/
+
+void serial_helper_setup(void);
+
+/*******************************************************************************/
+/**** SERIAL DEVICE ****/
+
+unsigned long serial_device_get_state(device_t *device);
+
+/* connect this device to the emulated serial chip */
+/* id is the serial device to connect to */
+/* connection is the serial connection to connect to the serial device */
+void serial_device_connect(device_t *image, serial_connection *connection);
+
+DECLARE_LEGACY_IMAGE_DEVICE(SERIAL, serial);
+
+#define MCFG_SERIAL_ADD(_tag) \
+ MCFG_DEVICE_ADD(_tag, SERIAL, 0)
+
+DEVICE_START(serial);
+DEVICE_IMAGE_LOAD(serial);
+
+void serial_device_setup(device_t *image, int baud_rate, int num_data_bits, int stop_bit_count, int parity_code);
+
+/* set the transmit state of the serial device */
+void serial_device_set_transmit_state(device_t *image, int state);
+
+#endif /* SERIAL_H_ */
diff --git a/src/emu/machine/upd765.c b/src/emu/machine/upd765.c
new file mode 100644
index 00000000000..b6bbaceb5a7
--- /dev/null
+++ b/src/emu/machine/upd765.c
@@ -0,0 +1,2457 @@
+/***************************************************************************
+
+ machine/upd765.c
+
+ Functions to emulate a UPD765/Intel 8272 compatible floppy disk controller
+
+ Code by Kevin Thacker.
+
+ TODO:
+
+ - overrun condition
+ - Scan Commands
+ - crc error in id field and crc error in data field errors
+ - disc not present, and no sectors on track for data, deleted data, write, write deleted,
+ read a track etc
+ - end of cylinder condition - almost working, needs fixing with
+ PCW and PC drivers
+ - resolve "ready" state stuff (ready state when reset for PC, ready state change while processing command AND
+ while idle)
+
+ Changes:
+ 091006 (Mariusz Wojcieszek, changes needed by QX-10):
+ - allowed "Sense Interrupt Status" command when Seek is active
+ - DIO bit in status register (0x40) is cleared when "Read Data" command is executed,
+ it is later set during result phase.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "machine/upd765.h"
+
+
+typedef enum
+{
+ UPD765_COMMAND_PHASE_FIRST_BYTE,
+ UPD765_COMMAND_PHASE_BYTES,
+ UPD765_RESULT_PHASE,
+ UPD765_EXECUTION_PHASE_READ,
+ UPD765_EXECUTION_PHASE_WRITE
+} UPD765_PHASE;
+
+/* supported versions */
+typedef enum
+{
+ TYPE_UPD765A = 0,
+ TYPE_UPD765B = 1,
+ TYPE_SMC37C78 = 2,
+ TYPE_UPD72065 = 3
+} UPD765_VERSION;
+
+
+/* uncomment the following line for verbose information */
+#define LOG_VERBOSE 0
+#define LOG_COMMAND 0
+#define LOG_EXTRA 0
+#define LOG_INTERRUPT 0
+
+/* uncomment this to not allow end of cylinder "error" */
+#define NO_END_OF_CYLINDER
+
+
+
+/* state of upd765 Interrupt (INT) output */
+#define UPD765_INT 0x02
+/* data rate for floppy discs (MFM data) */
+#define UPD765_DATA_RATE 32
+/* state of upd765 terminal count input*/
+#define UPD765_TC 0x04
+
+#define UPD765_DMA_MODE 0x08
+
+#define UPD765_SEEK_OPERATION_IS_RECALIBRATE 0x01
+
+#define UPD765_SEEK_ACTIVE 0x010
+/* state of upd765 DMA DRQ output */
+#define UPD765_DMA_DRQ 0x020
+/* state of upd765 FDD READY input */
+#define UPD765_FDD_READY 0x040
+
+#define UPD765_MF 0x40
+
+#define UPD765_RESET 0x080
+
+typedef struct _upd765_t upd765_t;
+struct _upd765_t
+{
+ devcb_resolved_write_line out_int_func;
+ devcb_resolved_write_line out_drq_func;
+
+ unsigned long sector_counter;
+ /* version of fdc to emulate */
+ UPD765_VERSION version;
+
+ /* main status register */
+ unsigned char FDC_main;
+ /* data register */
+ unsigned char upd765_data_reg;
+
+ unsigned char c,h,r,n;
+
+ int sector_id;
+
+ int data_type;
+
+ char format_data[4];
+
+ UPD765_PHASE upd765_phase;
+ unsigned int upd765_command_bytes[16];
+ unsigned int upd765_result_bytes[16];
+ unsigned int upd765_transfer_bytes_remaining;
+ unsigned int upd765_transfer_bytes_count;
+ unsigned int upd765_status[4];
+ /* present cylinder number per drive */
+ unsigned int pcn[4];
+
+ /* drive being accessed. drive outputs from fdc */
+ unsigned int drive;
+ /* side being accessed: side output from fdc */
+ unsigned int side;
+
+
+ /* step rate time in us */
+ unsigned long srt_in_ms;
+
+ unsigned int ncn;
+
+// unsigned int upd765_id_index;
+ char *execution_phase_data;
+ unsigned int upd765_flags;
+
+// unsigned char specify[2];
+// unsigned char perpendicular_mode[1];
+
+ int command;
+
+ emu_timer *seek_timer;
+ emu_timer *timer;
+ int timer_type;
+
+ emu_timer *command_timer;
+
+ char *data_buffer;
+ const upd765_interface *intf;
+};
+
+//static void upd765_setup_data_request(unsigned char Data);
+static void upd765_setup_command(device_t *device);
+static TIMER_CALLBACK(upd765_continue_command);
+static int upd765_sector_count_complete(device_t *device);
+static void upd765_increment_sector(device_t *device);
+static void upd765_update_state(device_t *device);
+static void upd765_set_dma_drq(device_t *device,int state);
+static void upd765_set_int(device_t *device,int state);
+
+static const INT8 upd765_cmd_size[32] =
+{
+ 1,1,9,3,2,9,9,2,1,9,2,1,9,6,1,3,
+ 1,9,1,1,1,1,9,1,1,9,1,1,1,9,1,1
+};
+
+INLINE upd765_t *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == UPD765A || device->type() == UPD765B ||
+ device->type() == SMC37C78 || device->type() == UPD72065);
+
+ return (upd765_t *)downcast<legacy_device_base *>(device)->token();
+}
+
+static device_t *current_image(device_t *device)
+{
+ device_t *image = NULL;
+ upd765_t *fdc = get_safe_token(device);
+
+ if (!fdc->intf->get_image)
+ {
+ if (fdc->intf->floppy_drive_tags[fdc->drive] != NULL)
+ {
+ if (device->owner() != NULL)
+ image = device->owner()->subdevice(fdc->intf->floppy_drive_tags[fdc->drive]);
+ else
+ image = device->machine().device(fdc->intf->floppy_drive_tags[fdc->drive]);
+ }
+ }
+ else
+ {
+ image = fdc->intf->get_image(device, fdc->drive);
+ }
+ return image;
+}
+
+static void upd765_setup_drive_and_side(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+ /* drive index upd765 sees */
+ fdc->drive = fdc->upd765_command_bytes[1] & 0x03;
+ /* side index upd765 sees */
+ fdc->side = (fdc->upd765_command_bytes[1]>>2) & 0x01;
+}
+
+
+/* setup status register 0 based on data in status register 1 and 2 */
+static void upd765_setup_st0(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+ /* clear completition status bits, drive bits and side bits */
+ fdc->upd765_status[0] &= ~((1<<7) | (1<<6) | (1<<2) | (1<<1) | (1<<0));
+ /* fill in drive */
+ fdc->upd765_status[0] |= fdc->drive | (fdc->side<<2);
+
+ /* fill in completion status bits based on bits in st0, st1, st2 */
+ /* no error bits set */
+ if ((fdc->upd765_status[1] | fdc->upd765_status[2])==0)
+ {
+ return;
+ }
+
+ fdc->upd765_status[0] |= 0x040;
+}
+
+
+static int upd765_n_to_bytes(int n)
+{
+ /* 0-> 128 bytes, 1->256 bytes, 2->512 bytes etc */
+ /* data_size = ((1<<(N+7)) */
+ return 1<<(n+7);
+}
+
+static void upd765_set_data_request(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+ fdc->FDC_main |= 0x080;
+}
+
+static void upd765_clear_data_request(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+ fdc->FDC_main &= ~0x080;
+}
+
+static int upd765_get_rdy(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+
+ if (fdc->intf->rdy_pin == UPD765_RDY_PIN_CONNECTED)
+ {
+ device_t *img = current_image(device);
+ return floppy_drive_get_flag_state(img, FLOPPY_DRIVE_READY);
+ }
+ else
+ return 1;
+}
+
+static void upd765_seek_complete(device_t *device)
+{
+ /* tested on Amstrad CPC */
+
+ /* if a seek is done without drive connected: */
+ /* abnormal termination of command,
+ seek complete,
+ not ready
+ */
+
+ /* if a seek is done with drive connected, but disc missing: */
+ /* abnormal termination of command,
+ seek complete,
+ not ready */
+
+ /* if a seek is done with drive connected and disc in drive */
+ /* seek complete */
+
+
+ /* On the PC however, it appears that recalibrates and seeks can be performed without
+ a disc in the drive. */
+
+ /* Therefore, the above output is dependant on the state of the drive */
+
+ /* In the Amstrad CPC, the drive select is provided by the UPD765. A single port is also
+ assigned for setting the drive motor state. The motor state controls the motor of the selected
+ drive */
+
+ /* On the PC the drive can be selected with the DIGITAL OUTPUT REGISTER, and the motor of each
+ of the 4 possible drives is also settable using the same register */
+
+ /* Assumption for PC: (NOT TESTED - NEEDS VERIFICATION) */
+
+ /* If a seek is done without drive connected: */
+ /* abnormal termination of command,
+ seek complete,
+ fault
+ */
+
+ /* if a seek is done with drive connected, but disc missing: */
+ /* seek complete */
+
+ /* if a seek is done with drive connected and disc in drive: */
+ /* seek complete */
+
+ /* On Amstrad CPC:
+ If drive not connected, or drive connected but disc not in drive, not ready!
+ If drive connected and drive motor on, ready!
+ On PC:
+ Drive is always ready!
+
+ In 37c78 docs, the ready bits of the upd765 are marked as unused.
+ This indicates it is always ready!!!!!
+ */
+
+ device_t *img = current_image(device);
+ upd765_t *fdc = get_safe_token(device);
+
+ fdc->pcn[fdc->drive] = fdc->ncn;
+
+ fdc->upd765_status[0] = 0x20;
+
+ /* drive ready? */
+ if (img != NULL && upd765_get_rdy(device))
+ {
+ /* recalibrate? */
+ if (fdc->upd765_flags & UPD765_SEEK_OPERATION_IS_RECALIBRATE)
+ {
+ /* not at track 0? */
+ if (fdc->pcn[fdc->drive] != 0)
+ /* no, track 0 failed after 77 steps */
+ fdc->upd765_status[0] |= 0x40 | 0x10;
+ }
+ }
+ else
+ {
+ /* abnormal termination, not ready */
+ fdc->upd765_status[0] |= 0x40 | 0x08;
+ }
+
+ /* set drive and side. note: commented out side to avoid problems with the tf20 */
+ fdc->upd765_status[0] |= fdc->drive; //| (fdc->side<<2);
+
+ upd765_set_int(device,0);
+ upd765_set_int(device,1);
+
+ fdc->upd765_flags &= ~UPD765_SEEK_ACTIVE;
+
+ upd765_idle(device);
+}
+
+static TIMER_CALLBACK(upd765_seek_timer_callback)
+{
+ device_t *device = (device_t *)ptr;
+ upd765_t *fdc = get_safe_token(device);
+ /* seek complete */
+ upd765_seek_complete(device);
+
+ fdc->seek_timer->reset();
+}
+
+static void upd765_timer_func(device_t *device, int timer_type)
+{
+ upd765_t *fdc = get_safe_token(device);
+ /* type 0 = data transfer mode in execution phase */
+ if (fdc->timer_type == 0)
+ {
+ /* set data request */
+ upd765_set_data_request(device);
+
+ fdc->timer_type = 4;
+
+ if (!(fdc->upd765_flags & UPD765_DMA_MODE))
+ {
+ if (fdc->upd765_command_bytes[0] & UPD765_MF)
+ {
+ /* MFM */
+ fdc->timer->reset(attotime::from_usec(13));
+ }
+ else
+ {
+ /* FM */
+ fdc->timer->reset(attotime::from_usec(27));
+ }
+ }
+ else
+ {
+ upd765_timer_func(device, fdc->timer_type);
+ }
+ }
+ else if (fdc->timer_type==2)
+ {
+ /* result phase begin */
+
+ /* generate a int for specific commands */
+ switch (fdc->command) {
+ case 2: /* read a track */
+ case 5: /* write data */
+ case 6: /* read data */
+ case 9: /* write deleted data */
+ case 10: /* read id */
+ case 12: /* read deleted data */
+ case 13: /* format at track */
+ case 17: /* scan equal */
+ case 19: /* scan low or equal */
+ case 29: /* scan high or equal */
+ upd765_set_int(device,1);
+ break;
+
+ default:
+ break;
+ }
+
+ upd765_set_data_request(device);
+
+ fdc->timer->reset();
+ }
+ else if (fdc->timer_type == 4)
+ {
+ /* if in dma mode, a int is not generated per byte. If not in DMA mode
+ a int is generated per byte */
+ if (fdc->upd765_flags & UPD765_DMA_MODE)
+ {
+ upd765_set_dma_drq(device,1);
+ }
+ else
+ {
+ if (fdc->FDC_main & (1<<7))
+ {
+ /* set int to indicate data is ready */
+ upd765_set_int(device,1);
+ }
+ }
+
+ fdc->timer->reset();
+ }
+}
+
+static TIMER_CALLBACK(upd765_timer_callback)
+{
+ device_t *device = (device_t *)ptr;
+ upd765_timer_func(device,param);
+}
+
+/* after (32-27) the DRQ is set, then 27 us later, the int is set.
+I don't know if this is correct, but it is required for the PCW driver.
+In this driver, the first NMI calls the handler function, furthur NMI's are
+effectively disabled by reading the data before the NMI int can be set.
+*/
+
+static void upd765_setup_timed_generic(device_t *device, int timer_type, attotime duration)
+{
+ upd765_t *fdc = get_safe_token(device);
+
+ fdc->timer_type = timer_type;
+
+ if (!(fdc->upd765_flags & UPD765_DMA_MODE))
+ {
+ fdc->timer->adjust(duration);
+ }
+ else
+ {
+ upd765_timer_func(device,fdc->timer_type);
+ fdc->timer->reset();
+ }
+}
+
+/* setup data request */
+static void upd765_setup_timed_data_request(device_t *device, int bytes)
+{
+ /* setup timer to trigger in UPD765_DATA_RATE us */
+ upd765_setup_timed_generic(device, 0, attotime::from_usec(32-27) /*UPD765_DATA_RATE)*bytes*/);
+}
+
+/* setup result data request */
+static void upd765_setup_timed_result_data_request(device_t *device)
+{
+ upd765_setup_timed_generic(device, 2, attotime::from_usec(UPD765_DATA_RATE*2));
+}
+
+
+/* sets up a timer to issue a seek complete in signed_tracks time */
+static void upd765_setup_timed_int(device_t *device,int signed_tracks)
+{
+ upd765_t *fdc = get_safe_token(device);
+ /* setup timer to signal after seek time is complete */
+ fdc->seek_timer->adjust(attotime::from_double(fdc->srt_in_ms*abs(signed_tracks)*0.001));
+}
+
+static void upd765_seek_setup(device_t *device, int is_recalibrate)
+{
+ device_t *img;
+ int signed_tracks;
+ upd765_t *fdc = get_safe_token(device);
+
+ fdc->upd765_flags |= UPD765_SEEK_ACTIVE;
+
+ if (is_recalibrate)
+ {
+ /* head cannot be specified with recalibrate */
+ fdc->upd765_command_bytes[1] &=~0x04;
+ }
+
+ upd765_setup_drive_and_side(device);
+
+ img = current_image(device);
+
+ fdc->FDC_main |= (1<<fdc->drive);
+ fdc->FDC_main |= 0x20; // execution phase
+ fdc->FDC_main &= ~0x10; // not busy, can send another seek/recalibrate
+ // for a different drive, or sense int status
+
+ /* recalibrate command? */
+ if (is_recalibrate)
+ {
+ fdc->upd765_flags |= UPD765_SEEK_OPERATION_IS_RECALIBRATE;
+
+ fdc->ncn = 0;
+
+ /* if drive is already at track 0, or drive is not ready */
+ if (img == NULL || floppy_tk00_r(img) == CLEAR_LINE || (!upd765_get_rdy(device)))
+ {
+ /* seek completed */
+// upd765_seek_complete(device);
+ // delay for the time of 1 step, the PCW does not like immediate recalibrates
+ upd765_setup_timed_int(device,1);
+ }
+ else
+ {
+ /* is drive present? */
+ if (1) //image_slotexists(img)) //fix me
+ {
+ /* yes - calculate real number of tracks to seek */
+
+ int current_track;
+
+ /* get current track */
+ current_track = floppy_drive_get_current_track(img);
+
+ /* get number of tracks to seek */
+ signed_tracks = -current_track;
+ }
+ else
+ {
+ /* no, seek 77 tracks and then stop */
+ /* true for UPD765A, but not for other variants */
+ signed_tracks = -77;
+ }
+
+ if (signed_tracks!=0)
+ {
+ /* perform seek - if drive isn't present it will not do anything */
+ floppy_drive_seek(img, signed_tracks);
+
+ upd765_setup_timed_int(device,signed_tracks);
+ }
+ else
+ {
+ upd765_seek_complete(device);
+ }
+ }
+ }
+ else
+ {
+
+ fdc->upd765_flags &= ~UPD765_SEEK_OPERATION_IS_RECALIBRATE;
+
+ fdc->ncn = fdc->upd765_command_bytes[2];
+
+ /* get signed tracks */
+ signed_tracks = fdc->ncn - fdc->pcn[fdc->drive];
+
+ /* if no tracks to seek, or drive is not ready, seek is complete */
+ if (img == NULL || (signed_tracks==0) || (!upd765_get_rdy(device)))
+ {
+ upd765_seek_complete(device);
+ }
+ else
+ {
+ /* perform seek - if drive isn't present it will not do anything */
+ floppy_drive_seek(img, signed_tracks);
+
+ /* seek complete - issue an interrupt */
+ upd765_setup_timed_int(device,signed_tracks);
+ }
+ }
+// upd765_idle(device);
+}
+
+
+
+static void upd765_setup_execution_phase_read(device_t *device, char *ptr, int size)
+{
+ upd765_t *fdc = get_safe_token(device);
+
+ fdc->FDC_main &= ~0x040; /* FDC->CPU */
+
+ fdc->upd765_transfer_bytes_count = 0;
+ fdc->upd765_transfer_bytes_remaining = size;
+ fdc->execution_phase_data = ptr;
+ fdc->upd765_phase = UPD765_EXECUTION_PHASE_READ;
+
+ upd765_setup_timed_data_request(device, 1);
+}
+
+static void upd765_setup_execution_phase_write(device_t *device, char *ptr, int size)
+{
+ upd765_t *fdc = get_safe_token(device);
+
+ fdc->FDC_main &= ~0x040; /* FDC->CPU */
+
+ fdc->upd765_transfer_bytes_count = 0;
+ fdc->upd765_transfer_bytes_remaining = size;
+ fdc->execution_phase_data = ptr;
+ fdc->upd765_phase = UPD765_EXECUTION_PHASE_WRITE;
+
+ /* setup a data request with first byte */
+ upd765_setup_timed_data_request(device,1);
+}
+
+
+static void upd765_setup_result_phase(device_t *device, int byte_count)
+{
+ upd765_t *fdc = get_safe_token(device);
+
+ fdc->FDC_main |= 0x040; /* FDC->CPU */
+ fdc->FDC_main &= ~0x020; /* not execution phase */
+
+ fdc->upd765_transfer_bytes_count = 0;
+ fdc->upd765_transfer_bytes_remaining = byte_count;
+ fdc->upd765_phase = UPD765_RESULT_PHASE;
+
+ upd765_setup_timed_result_data_request(device);
+}
+
+void upd765_idle(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+
+ fdc->FDC_main &= ~0x040; /* CPU->FDC */
+ fdc->FDC_main &= ~0x020; /* not execution phase */
+ fdc->FDC_main &= ~0x010; /* not busy */
+ fdc->upd765_phase = UPD765_COMMAND_PHASE_FIRST_BYTE;
+
+ upd765_set_data_request(device);
+}
+
+
+
+/* change flags */
+static void upd765_change_flags(device_t *device,unsigned int flags, unsigned int mask)
+{
+ unsigned int new_flags;
+ unsigned int changed_flags;
+ upd765_t *fdc = get_safe_token(device);
+
+ assert((flags & ~mask) == 0);
+
+ /* compute the new flags and which ones have changed */
+ new_flags = fdc->upd765_flags & ~mask;
+ new_flags |= flags;
+ changed_flags = fdc->upd765_flags ^ new_flags;
+ fdc->upd765_flags = new_flags;
+
+ /* if interrupt changed, call the handler */
+ if (changed_flags & UPD765_INT)
+ fdc->out_int_func((fdc->upd765_flags & UPD765_INT) ? 1 : 0);
+
+ /* if DRQ changed, call the handler */
+ if (changed_flags & UPD765_DMA_DRQ)
+ fdc->out_drq_func((fdc->upd765_flags & UPD765_DMA_DRQ) ? 1 : 0);
+}
+
+
+
+/* set int output */
+static void upd765_set_int(device_t *device, int state)
+{
+ if (LOG_INTERRUPT)
+ logerror("upd765_set_int(): state=%d\n", state);
+ upd765_change_flags(device, state ? UPD765_INT : 0, UPD765_INT);
+}
+
+
+
+/* set dma request output */
+static void upd765_set_dma_drq(device_t *device, int state)
+{
+ upd765_change_flags(device, state ? UPD765_DMA_DRQ : 0, UPD765_DMA_DRQ);
+}
+
+
+
+/* Drive ready */
+
+/*
+
+A drive will report ready if:
+- drive is selected
+- disc is in the drive
+- disk is rotating at a constant speed (normally 300rpm)
+
+On more modern PCs, a ready signal is not provided by the drive.
+This signal is not used in the PC design and was eliminated to save costs
+If you look at the datasheets for the modern UPD765 variants, you will see the Ready
+signal is not mentioned.
+
+On the original UPD765A, ready signal is required, and some commands will fail if the drive
+is not ready.
+
+
+
+
+*/
+
+
+
+
+/* done when ready state of drive changes */
+/* this ignores if command is active, in which case command should terminate immediatly
+with error */
+static void upd765_set_ready_change_callback(device_t *controller, device_t *img, int state)
+{
+ upd765_t *fdc = get_safe_token(controller);
+ int drive = floppy_get_drive(img);
+
+ if (LOG_EXTRA)
+ logerror("upd765: ready state change\n");
+
+ /* drive that changed state */
+ fdc->upd765_status[0] = 0x0c0 | drive;
+
+ /* not ready */
+ if (state==0 && fdc->intf->rdy_pin == UPD765_RDY_PIN_CONNECTED )
+ fdc->upd765_status[0] |= 8;
+
+ /* trigger an int */
+ upd765_set_int(controller, 1);
+}
+
+
+/* terminal count input */
+WRITE_LINE_DEVICE_HANDLER( upd765_tc_w )
+{
+ int old_state;
+ upd765_t *fdc = get_safe_token(device);
+
+ old_state = fdc->upd765_flags;
+
+ /* clear drq */
+ upd765_set_dma_drq(device, 0);
+
+ fdc->upd765_flags &= ~UPD765_TC;
+ if (state)
+ {
+ fdc->upd765_flags |= UPD765_TC;
+ }
+
+ /* changed state? */
+ if (((fdc->upd765_flags^old_state) & UPD765_TC)!=0)
+ {
+ /* now set? */
+ if ((fdc->upd765_flags & UPD765_TC)!=0)
+ {
+ /* yes */
+ if (fdc->timer)
+ {
+ if (fdc->timer_type==0)
+ {
+ fdc->timer->reset();
+
+
+ }
+ }
+
+#ifdef NO_END_OF_CYLINDER
+ fdc->command_timer->adjust(attotime::zero);
+#else
+ upd765_update_state(device);
+#endif
+ }
+ }
+}
+
+READ8_DEVICE_HANDLER( upd765_status_r )
+{
+ upd765_t *fdc = get_safe_token(device);
+ if (LOG_EXTRA)
+ logerror("%s: upd765_status_r: %02x\n", device->machine().describe_context(), fdc->FDC_main);
+ return fdc->FDC_main;
+}
+
+
+/* control mark handling code */
+
+/* if SK==1, and we are executing a read data command, and a deleted data mark is found,
+skip it.
+if SK==1, and we are executing a read deleted data command, and a data mark is found,
+skip it. */
+
+static int upd765_read_skip_sector(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+ /* skip set? */
+ if ((fdc->upd765_command_bytes[0] & (1<<5))!=0)
+ {
+ /* read data? */
+ if (fdc->command == 0x06)
+ {
+ /* did we just find a sector with deleted data mark? */
+ if (fdc->data_type == UPD765_DAM_DELETED_DATA)
+ {
+ /* skip it */
+ return TRUE;
+ }
+ }
+ /* deleted data? */
+ else
+ if (fdc->command == 0x0c)
+ {
+ /* did we just find a sector with data mark ? */
+ if (fdc->data_type == UPD765_DAM_DATA)
+ {
+ /* skip it */
+ return TRUE;
+ }
+ }
+ }
+
+ /* do not skip */
+ return FALSE;
+}
+
+/* this is much closer to how the upd765 actually gets sectors */
+/* used by read data, read deleted data, write data, write deleted data */
+/* What the upd765 does:
+
+ - get next sector id from disc
+ - if sector id matches id specified in command, it will
+ search for next data block and read data from it.
+
+ - if the index is seen twice while it is searching for a sector, then the sector cannot be found
+*/
+
+static void upd765_get_next_id(device_t *device, chrn_id *id)
+{
+ upd765_t *fdc = get_safe_token(device);
+ device_t *img = current_image(device);
+
+ /* get next id from disc */
+ floppy_drive_get_next_id(img, fdc->side,id);
+
+ fdc->sector_id = id->data_id;
+
+ /* set correct data type */
+ fdc->data_type = UPD765_DAM_DATA;
+ if (id->flags & ID_FLAG_DELETED_DATA)
+ {
+ fdc->data_type = UPD765_DAM_DELETED_DATA;
+ }
+}
+
+static int upd765_get_matching_sector(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+ device_t *img = current_image(device);
+ chrn_id id;
+
+ /* number of times we have seen index hole */
+ int index_count = 0;
+
+ /* get sector id's */
+ do
+ {
+ upd765_get_next_id(device, &id);
+
+ /* tested on Amstrad CPC - All bytes must match, otherwise
+ a NO DATA error is reported */
+ if (id.R == fdc->upd765_command_bytes[4])
+ {
+ if (id.C == fdc->upd765_command_bytes[2])
+ {
+ if (id.H == fdc->upd765_command_bytes[3])
+ {
+ if (id.N == fdc->upd765_command_bytes[5])
+ {
+ /* end of cylinder is set if:
+ 1. sector data is read completely (i.e. no other errors occur like
+ no data.
+ 2. sector being read is same specified by EOT
+ 3. terminal count is not received */
+ if (fdc->upd765_command_bytes[4]==fdc->upd765_command_bytes[6])
+ {
+ /* set end of cylinder */
+ fdc->upd765_status[1] |= UPD765_ST1_END_OF_CYLINDER;
+ }
+
+ return TRUE;
+ }
+ }
+ }
+ else
+ {
+ /* the specified sector ID was found, however, the C value specified
+ in the read/write command did not match the C value read from the disc */
+
+ /* no data - checked on Amstrad CPC */
+ fdc->upd765_status[1] |= UPD765_ST1_NO_DATA;
+ /* bad C value */
+ fdc->upd765_status[2] |= UPD765_ST2_WRONG_CYLINDER;
+
+ if (id.C == 0x0ff)
+ {
+ /* the C value is 0x0ff which indicates a bad track in the IBM soft-sectored
+ format */
+ fdc->upd765_status[2] |= UPD765_ST2_BAD_CYLINDER;
+ }
+
+ return FALSE;
+ }
+ }
+
+ /* index set? */
+ if (floppy_drive_get_flag_state(img, FLOPPY_DRIVE_INDEX))
+ {
+ index_count++;
+ }
+
+ }
+ while (index_count!=2);
+
+ if (fdc->upd765_command_bytes[4] != fdc->upd765_command_bytes[6])
+ {
+ /* no data - specified sector ID was not found */
+ fdc->upd765_status[1] |= UPD765_ST1_NO_DATA;
+ }
+
+ return 0;
+}
+
+static void upd765_read_complete(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+/* causes problems!!! - need to fix */
+#ifdef NO_END_OF_CYLINDER
+ /* set end of cylinder */
+ fdc->upd765_status[1] &= ~UPD765_ST1_END_OF_CYLINDER;
+#else
+ /* completed read command */
+
+ /* end of cylinder is set when:
+ - a whole sector has been read
+ - terminal count input is not set
+ - AND the the sector specified by EOT was read
+ */
+
+ /* if end of cylinder is set, and we did receive a terminal count, then clear it */
+ if ((fdc->upd765_flags & UPD765_TC)!=0)
+ {
+ /* set end of cylinder */
+ fdc->upd765_status[1] &= ~UPD765_ST1_END_OF_CYLINDER;
+ }
+#endif
+
+ upd765_setup_st0(device);
+
+ fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
+ fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
+ fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
+ fdc->upd765_result_bytes[3] = fdc->upd765_command_bytes[2]; /* C */
+ fdc->upd765_result_bytes[4] = fdc->upd765_command_bytes[3]; /* H */
+ fdc->upd765_result_bytes[5] = fdc->upd765_command_bytes[4]; /* R */
+ fdc->upd765_result_bytes[6] = fdc->upd765_command_bytes[5]; /* N */
+
+ upd765_setup_result_phase(device,7);
+}
+
+static void upd765_read_data(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+ device_t *img = current_image(device);
+
+ if (!upd765_get_rdy(device))
+ {
+ fdc->upd765_status[0] = 0x0c0 | (1<<4) | fdc->drive | (fdc->side<<2);
+ fdc->upd765_status[1] = 0x00;
+ fdc->upd765_status[2] = 0x00;
+
+ fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
+ fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
+ fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
+ fdc->upd765_result_bytes[3] = fdc->upd765_command_bytes[2]; /* C */
+ fdc->upd765_result_bytes[4] = fdc->upd765_command_bytes[3]; /* H */
+ fdc->upd765_result_bytes[5] = fdc->upd765_command_bytes[4]; /* R */
+ fdc->upd765_result_bytes[6] = fdc->upd765_command_bytes[5]; /* N */
+ upd765_setup_result_phase(device,7);
+ return;
+ }
+
+ if (LOG_VERBOSE)
+ logerror("sector c: %02x h: %02x r: %02x n: %02x\n",fdc->upd765_command_bytes[2], fdc->upd765_command_bytes[3],fdc->upd765_command_bytes[4], fdc->upd765_command_bytes[5]);
+
+ /* find a sector to read data from */
+ {
+ int found_sector_to_read;
+
+ found_sector_to_read = 0;
+ /* check for finished reading sectors */
+ do
+ {
+ /* get matching sector */
+ if (upd765_get_matching_sector(device))
+ {
+
+ /* skip it? */
+ if (upd765_read_skip_sector(device))
+ {
+ /* yes */
+
+ /* check that we haven't finished reading all sectors */
+ if (upd765_sector_count_complete(device))
+ {
+ /* read complete */
+ upd765_read_complete(device);
+ return;
+ }
+
+ /* read not finished */
+
+ /* increment sector count */
+ upd765_increment_sector(device);
+ }
+ else
+ {
+ /* found a sector to read */
+ found_sector_to_read = 1;
+ }
+ }
+ else
+ {
+ /* error in finding sector */
+ upd765_read_complete(device);
+ return;
+ }
+ }
+ while (found_sector_to_read==0);
+ }
+
+ {
+ int data_size;
+
+ data_size = upd765_n_to_bytes(fdc->upd765_command_bytes[5]);
+
+ floppy_drive_read_sector_data(img, fdc->side, fdc->sector_id,fdc->data_buffer,data_size);
+
+ upd765_setup_execution_phase_read(device,fdc->data_buffer, data_size);
+ }
+}
+
+
+static void upd765_format_track(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+ device_t *img = current_image(device);
+
+ /* write protected? */
+ if (floppy_wpt_r(img) == CLEAR_LINE)
+ {
+ fdc->upd765_status[1] |= UPD765_ST1_NOT_WRITEABLE;
+
+ upd765_setup_st0(device);
+ /* TODO: Check result is correct */
+ fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
+ fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
+ fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
+ fdc->upd765_result_bytes[3] = fdc->format_data[0];
+ fdc->upd765_result_bytes[4] = fdc->format_data[1];
+ fdc->upd765_result_bytes[5] = fdc->format_data[2];
+ fdc->upd765_result_bytes[6] = fdc->format_data[3];
+ upd765_setup_result_phase(device,7);
+
+ return;
+ }
+
+ upd765_setup_execution_phase_write(device, &fdc->format_data[0], 4);
+}
+
+static void upd765_read_a_track(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+ int data_size;
+
+ /* SKIP not allowed with this command! */
+
+ /* get next id */
+ chrn_id id;
+
+ upd765_get_next_id(device, &id);
+
+ /* TO BE CONFIRMED! */
+ /* check id from disc */
+ if (id.C==fdc->upd765_command_bytes[2])
+ {
+ if (id.H==fdc->upd765_command_bytes[3])
+ {
+ if (id.R==fdc->upd765_command_bytes[4])
+ {
+ if (id.N==fdc->upd765_command_bytes[5])
+ {
+ /* if ID found, then no data is not set */
+ /* otherwise no data will remain set */
+ fdc->upd765_status[1] &=~UPD765_ST1_NO_DATA;
+ }
+ }
+ }
+ }
+
+
+ data_size = upd765_n_to_bytes(id.N);
+
+ floppy_drive_read_sector_data(current_image(device), fdc->side, fdc->sector_id,fdc->data_buffer,data_size);
+
+ upd765_setup_execution_phase_read(device,fdc->data_buffer, data_size);
+}
+
+static int upd765_just_read_last_sector_on_track(device_t *device)
+{
+ if (floppy_drive_get_flag_state(current_image(device), FLOPPY_DRIVE_INDEX))
+ return 1;
+ return 0;
+}
+
+static void upd765_write_complete(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+
+/* causes problems!!! - need to fix */
+#ifdef NO_END_OF_CYLINDER
+ /* set end of cylinder */
+ fdc->upd765_status[1] &= ~UPD765_ST1_END_OF_CYLINDER;
+#else
+ /* completed read command */
+
+ /* end of cylinder is set when:
+ - a whole sector has been read
+ - terminal count input is not set
+ - AND the the sector specified by EOT was read
+ */
+
+ /* if end of cylinder is set, and we did receive a terminal count, then clear it */
+ if ((fdc->upd765_flags & UPD765_TC)!=0)
+ {
+ /* set end of cylinder */
+ fdc->upd765_status[1] &= ~UPD765_ST1_END_OF_CYLINDER;
+ }
+#endif
+
+ upd765_setup_st0(device);
+
+ fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
+ fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
+ fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
+ fdc->upd765_result_bytes[3] = fdc->upd765_command_bytes[2]; /* C */
+ fdc->upd765_result_bytes[4] = fdc->upd765_command_bytes[3]; /* H */
+ fdc->upd765_result_bytes[5] = fdc->upd765_command_bytes[4]; /* R */
+ fdc->upd765_result_bytes[6] = fdc->upd765_command_bytes[5]; /* N */
+
+ upd765_setup_result_phase(device,7);
+}
+
+
+static void upd765_write_data(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+ if (!upd765_get_rdy(device))
+ {
+ fdc->upd765_status[0] = 0x0c0 | (1<<4) | fdc->drive | (fdc->side<<2);
+ fdc->upd765_status[1] = 0x00;
+ fdc->upd765_status[2] = 0x00;
+
+ fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
+ fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
+ fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
+ fdc->upd765_result_bytes[3] = fdc->upd765_command_bytes[2]; /* C */
+ fdc->upd765_result_bytes[4] = fdc->upd765_command_bytes[3]; /* H */
+ fdc->upd765_result_bytes[5] = fdc->upd765_command_bytes[4]; /* R */
+ fdc->upd765_result_bytes[6] = fdc->upd765_command_bytes[5]; /* N */
+ upd765_setup_result_phase(device,7);
+ return;
+ }
+
+ /* write protected? */
+ if (floppy_wpt_r(current_image(device)) == CLEAR_LINE)
+ {
+ fdc->upd765_status[1] |= UPD765_ST1_NOT_WRITEABLE;
+
+ upd765_write_complete(device);
+ return;
+ }
+
+ if (upd765_get_matching_sector(device))
+ {
+ int data_size;
+
+ data_size = upd765_n_to_bytes(fdc->upd765_command_bytes[5]);
+
+ upd765_setup_execution_phase_write(device,fdc->data_buffer, data_size);
+ }
+ else
+ {
+ upd765_setup_result_phase(device,7);
+ }
+}
+
+
+/* return true if we have read all sectors, false if not */
+static int upd765_sector_count_complete(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+/* this is not correct?? */
+#if 1
+ /* if terminal count has been set - yes */
+ if (fdc->upd765_flags & UPD765_TC)
+ {
+ /* completed */
+ return 1;
+ }
+
+
+
+ /* multi-track? */
+ if (fdc->upd765_command_bytes[0] & 0x080)
+ {
+ /* it appears that in multi-track mode,
+ the EOT parameter of the command is ignored!? -
+ or is it ignored the first time and not the next, so that
+ if it is started on side 0, it will end at EOT on side 1,
+ but if started on side 1 it will end at end of track????
+
+ PC driver requires this to end at last sector on side 1, and
+ ignore EOT parameter.
+
+ To be checked!!!!
+ */
+
+ /* if just read last sector and on side 1 - finish */
+ if (upd765_just_read_last_sector_on_track(device))
+ {
+ if (floppy_get_heads_per_disk(flopimg_get_image(current_image(device)))==1) {
+ return 1;
+ } else {
+ if (fdc->side==1)
+ return 1;
+ }
+ }
+
+ /* if not on second side then we haven't finished yet */
+ if (fdc->side!=1)
+ {
+ /* haven't finished yet */
+ return 0;
+ }
+ }
+ else
+ {
+ /* sector id == EOT? */
+ if ((fdc->upd765_command_bytes[4]==fdc->upd765_command_bytes[6]))
+ {
+
+ /* completed */
+ return 1;
+ }
+ }
+#else
+
+ /* if terminal count has been set - yes */
+ if (fdc->upd765_flags & UPD765_TC)
+ {
+ /* completed */
+ return 1;
+ }
+
+ /* Multi-Track operation:
+
+ Verified on Amstrad CPC.
+
+ disc format used:
+ 9 sectors per track
+ 2 sides
+ Sector IDs: &01, &02, &03, &04, &05, &06, &07, &08, &09
+
+ Command specified:
+ SIDE = 0,
+ C = 0,H = 0,R = 1, N = 2, EOT = 1
+ Sectors read:
+ Sector 1 side 0
+ Sector 1 side 1
+
+ Command specified:
+ SIDE = 0,
+ C = 0,H = 0,R = 1, N = 2, EOT = 3
+ Sectors read:
+ Sector 1 side 0
+ Sector 2 side 0
+ Sector 3 side 0
+ Sector 1 side 1
+ Sector 2 side 1
+ Sector 3 side 1
+
+
+ Command specified:
+ SIDE = 0,
+ C = 0, H = 0, R = 7, N = 2, EOT = 3
+ Sectors read:
+ Sector 7 side 0
+ Sector 8 side 0
+ Sector 9 side 0
+ Sector 10 not found. Error "No Data"
+
+ Command specified:
+ SIDE = 1,
+ C = 0, H = 1, R = 1, N = 2, EOT = 1
+ Sectors read:
+ Sector 1 side 1
+
+ Command specified:
+ SIDE = 1,
+ C = 0, H = 1, R = 1, N = 2, EOT = 2
+ Sectors read:
+ Sector 1 side 1
+ Sector 1 side 2
+
+ */
+
+ /* sector id == EOT? */
+ if ((fdc->upd765_command_bytes[4]==fdc->upd765_command_bytes[6]))
+ {
+ /* multi-track? */
+ if (fdc->upd765_command_bytes[0] & 0x080)
+ {
+ /* if we have reached EOT (fdc->upd765_command_bytes[6])
+ on side 1, then read is complete */
+ if (fdc->side==1)
+ return 1;
+
+ return 0;
+
+ }
+
+ /* completed */
+ return 1;
+ }
+#endif
+ /* not complete */
+ return 0;
+}
+
+static void upd765_increment_sector(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+ /* multi-track? */
+ if (fdc->upd765_command_bytes[0] & 0x080)
+ {
+ /* reached EOT? */
+ /* if (fdc->upd765_command_bytes[4]==fdc->upd765_command_bytes[6])*/
+ if (upd765_just_read_last_sector_on_track(device))
+ {
+ /* yes */
+
+ /* reached EOT */
+ /* change side to 1 */
+ fdc->side = 1;
+ /* reset sector id to 1 */
+ fdc->upd765_command_bytes[4] = 1;
+ /* set head to 1 for get next sector test */
+ fdc->upd765_command_bytes[3] = 1;
+ }
+ else
+ {
+ /* increment */
+ fdc->upd765_command_bytes[4]++;
+ }
+
+ }
+ else
+ {
+ fdc->upd765_command_bytes[4]++;
+ }
+}
+
+/* control mark handling code */
+
+/* if SK==0, and we are executing a read data command, and a deleted data sector is found,
+the data is not skipped. The data is read, but the control mark is set and the read is stopped */
+/* if SK==0, and we are executing a read deleted data command, and a data sector is found,
+the data is not skipped. The data is read, but the control mark is set and the read is stopped */
+static int upd765_read_data_stop(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+ /* skip not set? */
+ if ((fdc->upd765_command_bytes[0] & (1<<5))==0)
+ {
+ /* read data? */
+ if (fdc->command == 0x06)
+ {
+ /* did we just read a sector with deleted data? */
+ if (fdc->data_type == UPD765_DAM_DELETED_DATA)
+ {
+ /* set control mark */
+ fdc->upd765_status[2] |= UPD765_ST2_CONTROL_MARK;
+
+ /* quit */
+ return TRUE;
+ }
+ }
+ /* deleted data? */
+ else
+ if (fdc->command == 0x0c)
+ {
+ /* did we just read a sector with data? */
+ if (fdc->data_type == UPD765_DAM_DATA)
+ {
+ /* set control mark */
+ fdc->upd765_status[2] |= UPD765_ST2_CONTROL_MARK;
+
+ /* quit */
+ return TRUE;
+ }
+ }
+ }
+
+ /* continue */
+ return FALSE;
+}
+
+static TIMER_CALLBACK(upd765_continue_command)
+{
+ device_t *device = (device_t *)ptr;
+ upd765_t *fdc = get_safe_token(device);
+ if ((fdc->upd765_phase == UPD765_EXECUTION_PHASE_READ) ||
+ (fdc->upd765_phase == UPD765_EXECUTION_PHASE_WRITE))
+ {
+ switch (fdc->command)
+ {
+ /* read a track */
+ case 0x02:
+ {
+ fdc->sector_counter++;
+
+ /* sector counter == EOT */
+ if (fdc->sector_counter==fdc->upd765_command_bytes[6])
+ {
+ /* TODO: Add correct info here */
+
+ fdc->upd765_status[1] |= UPD765_ST1_END_OF_CYLINDER;
+
+ upd765_setup_st0(device);
+
+ fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
+ fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
+ fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
+ fdc->upd765_result_bytes[3] = fdc->upd765_command_bytes[2]; /* C */
+ fdc->upd765_result_bytes[4] = fdc->upd765_command_bytes[3]; /* H */
+ fdc->upd765_result_bytes[5] = fdc->upd765_command_bytes[4]; /* R */
+ fdc->upd765_result_bytes[6] = fdc->upd765_command_bytes[5]; /* N */
+
+ upd765_setup_result_phase(device,7);
+ }
+ else
+ {
+ upd765_read_a_track(device);
+ }
+ }
+ break;
+
+ /* format track */
+ case 0x0d:
+ {
+ floppy_drive_format_sector(current_image(device), fdc->side, fdc->sector_counter,
+ fdc->format_data[0], fdc->format_data[1],
+ fdc->format_data[2], fdc->format_data[3],
+ fdc->upd765_command_bytes[5]);
+
+ fdc->sector_counter++;
+
+ /* sector_counter = SC */
+ if (fdc->sector_counter == fdc->upd765_command_bytes[3])
+ {
+ /* TODO: Check result is correct */
+ fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
+ fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
+ fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
+ fdc->upd765_result_bytes[3] = fdc->format_data[0];
+ fdc->upd765_result_bytes[4] = fdc->format_data[1];
+ fdc->upd765_result_bytes[5] = fdc->format_data[2];
+ fdc->upd765_result_bytes[6] = fdc->format_data[3];
+ upd765_setup_result_phase(device,7);
+ }
+ else
+ {
+ upd765_format_track(device);
+ }
+ }
+ break;
+
+ /* write data, write deleted data */
+ case 0x09:
+ case 0x05:
+ {
+ /* sector id == EOT */
+ UINT8 ddam;
+
+ ddam = 0;
+ if (fdc->command == 0x09)
+ {
+ ddam = 1;
+ }
+
+ /* write data to disc */
+ floppy_drive_write_sector_data(current_image(device), fdc->side, fdc->sector_id,fdc->data_buffer,upd765_n_to_bytes(fdc->upd765_command_bytes[5]),ddam);
+
+ if (upd765_sector_count_complete(device))
+ {
+ upd765_increment_sector(device);
+ upd765_write_complete(device);
+ }
+ else
+ {
+ upd765_increment_sector(device);
+ upd765_write_data(device);
+ }
+ }
+ break;
+
+ /* read data, read deleted data */
+ case 0x0c:
+ case 0x06:
+ {
+
+ /* read all sectors? */
+
+ /* sector id == EOT */
+ if (upd765_sector_count_complete(device) || upd765_read_data_stop(device))
+ {
+ upd765_read_complete(device);
+ }
+ else
+ {
+ upd765_increment_sector(device);
+ upd765_read_data(device);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+
+static int upd765_get_command_byte_count(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+ fdc->command = fdc->upd765_command_bytes[0] & 0x01f;
+
+ if (fdc->version==TYPE_UPD765A)
+ {
+ return upd765_cmd_size[fdc->command];
+ }
+ else
+ {
+ if (fdc->version==TYPE_UPD72065)
+ {
+ switch(fdc->upd765_command_bytes[0])
+ {
+ case 0x34: // Reset Standby
+ case 0x35: // Set Standby
+ case 0x36: // Software Reset
+ return 1;
+ default:
+ return upd765_cmd_size[fdc->command];
+ }
+ }
+ if (fdc->version==TYPE_SMC37C78)
+ {
+ switch (fdc->command)
+ {
+ /* version */
+ case 0x010:
+ return 1;
+
+ /* verify */
+ case 0x016:
+ return 9;
+
+ /* configure */
+ case 0x013:
+ return 4;
+
+ /* dumpreg */
+ case 0x0e:
+ return 1;
+
+ /* perpendicular mode */
+ case 0x012:
+ return 1;
+
+ /* lock */
+ case 0x014:
+ return 1;
+
+ /* seek/relative seek are together! */
+
+ default:
+ return upd765_cmd_size[fdc->command];
+ }
+ }
+ }
+
+ return upd765_cmd_size[fdc->command];
+}
+
+
+
+
+
+void upd765_update_state(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+ switch (fdc->upd765_phase) {
+ case UPD765_RESULT_PHASE:
+ /* set data reg */
+ fdc->upd765_data_reg = fdc->upd765_result_bytes[fdc->upd765_transfer_bytes_count];
+
+ if (fdc->upd765_transfer_bytes_count == 0)
+ {
+ /* clear int for specific commands */
+ switch (fdc->command) {
+ case 2: /* read a track */
+ case 5: /* write data */
+ case 6: /* read data */
+ case 9: /* write deleted data */
+ case 10: /* read id */
+ case 12: /* read deleted data */
+ case 13: /* format at track */
+ case 17: /* scan equal */
+ case 19: /* scan low or equal */
+ case 29: /* scan high or equal */
+ upd765_set_int(device, 0);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (LOG_VERBOSE)
+ logerror("UPD765: RESULT: %02x\n", fdc->upd765_data_reg);
+
+ fdc->upd765_transfer_bytes_count++;
+ fdc->upd765_transfer_bytes_remaining--;
+
+ if (fdc->upd765_transfer_bytes_remaining==0)
+ {
+ upd765_idle(device);
+ }
+ else
+ {
+ upd765_set_data_request(device);
+ }
+ break;
+
+ case UPD765_EXECUTION_PHASE_READ:
+ /* setup data register */
+ fdc->upd765_data_reg = fdc->execution_phase_data[fdc->upd765_transfer_bytes_count];
+ fdc->upd765_transfer_bytes_count++;
+ fdc->upd765_transfer_bytes_remaining--;
+
+ if (LOG_EXTRA)
+ logerror("EXECUTION PHASE READ: %02x\n", fdc->upd765_data_reg);
+
+ if ((fdc->upd765_transfer_bytes_remaining==0) || (fdc->upd765_flags & UPD765_TC))
+ {
+ fdc->command_timer->adjust(attotime::zero);
+ }
+ else
+ {
+ /* trigger int */
+ upd765_setup_timed_data_request(device,1);
+ }
+ break;
+
+ case UPD765_COMMAND_PHASE_FIRST_BYTE:
+ fdc->FDC_main |= 0x10; /* set BUSY */
+
+ if (LOG_VERBOSE)
+ logerror("%s: upd765(): command=0x%02x\n", device->machine().describe_context(), fdc->upd765_data_reg);
+
+ /* seek in progress? */
+ if (fdc->upd765_flags & UPD765_SEEK_ACTIVE)
+ {
+ /* any command results in a invalid - I think that seek, recalibrate and
+ sense interrupt status may work*/
+ if (fdc->upd765_data_reg != 8) /* Sense Interrupt Status */
+ fdc->upd765_data_reg = 0;
+ }
+
+ fdc->upd765_command_bytes[0] = fdc->upd765_data_reg;
+
+ fdc->upd765_transfer_bytes_remaining = upd765_get_command_byte_count(device);
+
+ fdc->upd765_transfer_bytes_count = 1;
+ fdc->upd765_transfer_bytes_remaining--;
+
+ if (fdc->upd765_transfer_bytes_remaining==0)
+ {
+ upd765_setup_command(device);
+ }
+ else
+ {
+ /* request more data */
+ upd765_set_data_request(device);
+ fdc->upd765_phase = UPD765_COMMAND_PHASE_BYTES;
+ }
+ break;
+
+ case UPD765_COMMAND_PHASE_BYTES:
+ if (LOG_VERBOSE)
+ logerror("%s: upd765(): command=0x%02x\n", device->machine().describe_context(), fdc->upd765_data_reg);
+
+ fdc->upd765_command_bytes[fdc->upd765_transfer_bytes_count] = fdc->upd765_data_reg;
+ fdc->upd765_transfer_bytes_count++;
+ fdc->upd765_transfer_bytes_remaining--;
+
+ if (fdc->upd765_transfer_bytes_remaining==0)
+ {
+ upd765_setup_command(device);
+ }
+ else
+ {
+ /* request more data */
+ upd765_set_data_request(device);
+ }
+ break;
+
+ case UPD765_EXECUTION_PHASE_WRITE:
+ fdc->execution_phase_data[fdc->upd765_transfer_bytes_count]=fdc->upd765_data_reg;
+ fdc->upd765_transfer_bytes_count++;
+ fdc->upd765_transfer_bytes_remaining--;
+
+ if ((fdc->upd765_transfer_bytes_remaining == 0) || (fdc->upd765_flags & UPD765_TC))
+ {
+ fdc->command_timer->adjust(attotime::zero);
+ }
+ else
+ {
+ upd765_setup_timed_data_request(device,1);
+ }
+ break;
+ }
+}
+
+
+READ8_DEVICE_HANDLER(upd765_data_r)
+{
+ upd765_t *fdc = get_safe_token(device);
+
+// if ((fdc->FDC_main & 0x0c0) == 0x0c0)
+ if ((fdc->FDC_main & 0x080) == 0x080)
+ {
+ if (
+ (fdc->upd765_phase == UPD765_EXECUTION_PHASE_READ) ||
+ (fdc->upd765_phase == UPD765_EXECUTION_PHASE_WRITE))
+ {
+
+ /* reading the data byte clears the interrupt */
+ upd765_set_int(device,CLEAR_LINE);
+ }
+
+ /* reset data request */
+ upd765_clear_data_request(device);
+
+ /* update state */
+ upd765_update_state(device);
+ }
+
+ if (LOG_EXTRA)
+ logerror("DATA R: %02x\n", fdc->upd765_data_reg);
+
+ return fdc->upd765_data_reg;
+}
+
+WRITE8_DEVICE_HANDLER(upd765_data_w)
+{
+ upd765_t *fdc = get_safe_token(device);
+
+ if (LOG_EXTRA)
+ logerror("DATA W: %02x\n", data);
+
+ /* write data to data reg */
+ fdc->upd765_data_reg = data;
+
+ if ((fdc->FDC_main & 0x0c0)==0x080)
+ {
+ if (
+ (fdc->upd765_phase == UPD765_EXECUTION_PHASE_READ) ||
+ (fdc->upd765_phase == UPD765_EXECUTION_PHASE_WRITE))
+ {
+
+ /* reading the data byte clears the interrupt */
+ upd765_set_int(device, CLEAR_LINE);
+ }
+
+ /* reset data request */
+ upd765_clear_data_request(device);
+
+ /* update state */
+ upd765_update_state(device);
+ }
+}
+
+static void upd765_setup_invalid(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+
+ fdc->command = 0;
+ fdc->upd765_result_bytes[0] = 0x080;
+ upd765_setup_result_phase(device,1);
+}
+
+static void upd765_setup_command(device_t *device)
+{
+ upd765_t *fdc = get_safe_token(device);
+
+ static const char *const commands[] =
+ {
+ NULL, /* [00] */
+ NULL, /* [01] */
+ "Read Track", /* [02] */
+ "Specify", /* [03] */
+ "Sense Drive Status", /* [04] */
+ "Write Data", /* [05] */
+ "Read Data", /* [06] */
+ "Recalibrate", /* [07] */
+ "Sense Interrupt Status", /* [08] */
+ "Write Deleted Data", /* [09] */
+ "Read ID", /* [0A] */
+ NULL, /* [0B] */
+ "Read Deleted Data", /* [0C] */
+ "Format Track", /* [0D] */
+ "Dump Registers", /* [0E] */
+ "Seek", /* [0F] */
+ "Version", /* [10] */
+ NULL, /* [11] */
+ "Perpendicular Mode", /* [12] */
+ "Configure", /* [13] */
+ "Lock" /* [14] */
+ };
+
+ device_t *img;
+ const char *cmd = NULL;
+ chrn_id id;
+
+ /* if not in dma mode set execution phase bit */
+ if (!(fdc->upd765_flags & UPD765_DMA_MODE))
+ {
+ fdc->FDC_main |= 0x020; /* execution phase */
+ }
+
+ if (LOG_COMMAND)
+ {
+ if ((fdc->upd765_command_bytes[0] & 0x1f) < ARRAY_LENGTH(commands))
+ cmd = commands[fdc->upd765_command_bytes[0] & 0x1f];
+ logerror("upd765_setup_command(): Setting up command 0x%02X (%s)\n",
+ fdc->upd765_command_bytes[0] & 0x1f, cmd ? cmd : "???");
+ }
+
+ switch (fdc->upd765_command_bytes[0] & 0x1f)
+ {
+ case 0x03: /* specify */
+ /* setup step rate */
+ fdc->srt_in_ms = 16-((fdc->upd765_command_bytes[1]>>4) & 0x0f);
+
+ fdc->upd765_flags &= ~UPD765_DMA_MODE;
+
+ if ((fdc->upd765_command_bytes[2] & 0x01)==0)
+ {
+ fdc->upd765_flags |= UPD765_DMA_MODE;
+ }
+
+ upd765_idle(device);
+ break;
+
+ case 0x04: /* sense drive status */
+ upd765_setup_drive_and_side(device);
+ img = current_image(device);
+
+ fdc->upd765_status[3] = fdc->drive | (fdc->side<<2);
+
+ if (img)
+ {
+ fdc->upd765_status[3] |= !floppy_tk00_r(img) << 4;
+ fdc->upd765_status[3] |= !floppy_wpt_r(img) << 6;
+
+ if (upd765_get_rdy(device))
+ {
+ fdc->upd765_status[3] |= 0x20;
+ }
+ }
+
+ fdc->upd765_status[3] |= 0x08;
+
+ /* two side and fault not set but should be? */
+ fdc->upd765_result_bytes[0] = fdc->upd765_status[3];
+ upd765_setup_result_phase(device,1);
+ break;
+
+ case 0x07: /* recalibrate */
+ upd765_seek_setup(device, 1);
+ break;
+
+ case 0x0f: /* seek */
+ upd765_seek_setup(device, 0);
+ break;
+
+ case 0x0a: /* read id */
+ upd765_setup_drive_and_side(device);
+ img = current_image(device);
+
+ fdc->upd765_status[0] = fdc->drive | (fdc->side<<2);
+ fdc->upd765_status[1] = 0;
+ fdc->upd765_status[2] = 0;
+
+ /* drive ready? */
+ if (upd765_get_rdy(device))
+ {
+ /* is disk inserted? */
+ device_image_interface *image = dynamic_cast<device_image_interface *>( img);
+ if (image->exists())
+ {
+ int index_count = 0;
+
+ /* floppy drive is ready and disc is inserted */
+
+ /* this is the id that appears when a disc is not formatted */
+ /* to be checked on Amstrad */
+ id.C = 0;
+ id.H = 0;
+ id.R = 0x01;
+ id.N = 0x02;
+
+ /* repeat for two index counts before quitting */
+ do
+ {
+ /* get next id from disc */
+ if (floppy_drive_get_next_id(img, fdc->side, &id))
+ {
+ /* got an id - quit */
+ break;
+ }
+
+ if (floppy_drive_get_flag_state(img, FLOPPY_DRIVE_INDEX))
+ {
+ /* update index count */
+ index_count++;
+ }
+ }
+ while (index_count!=2);
+
+ /* at this point, we have seen a id or two index pulses have occured! */
+ fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
+ fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
+ fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
+ fdc->upd765_result_bytes[3] = id.C; /* C */
+ fdc->upd765_result_bytes[4] = id.H; /* H */
+ fdc->upd765_result_bytes[5] = id.R; /* R */
+ fdc->upd765_result_bytes[6] = id.N; /* N */
+
+ upd765_setup_result_phase(device,7);
+ }
+ else
+ {
+ /* floppy drive is ready, but no disc is inserted */
+ /* this occurs on the PC */
+ /* in this case, the command never quits! */
+ /* there are no index pulses to stop the command! */
+ }
+ }
+ else
+ {
+ /* what are id values when drive not ready? */
+
+ /* not ready, abnormal termination */
+ fdc->upd765_status[0] |= (1<<3) | (1<<6);
+ fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
+ fdc->upd765_result_bytes[1] = fdc->upd765_status[1];
+ fdc->upd765_result_bytes[2] = fdc->upd765_status[2];
+ fdc->upd765_result_bytes[3] = 0; /* C */
+ fdc->upd765_result_bytes[4] = 0; /* H */
+ fdc->upd765_result_bytes[5] = 0; /* R */
+ fdc->upd765_result_bytes[6] = 0; /* N */
+ }
+ break;
+
+
+ case 0x08: /* sense interrupt status */
+ /* interrupt pending? */
+ if (fdc->upd765_flags & UPD765_INT)
+ {
+ /* yes. Clear int */
+ upd765_set_int(device, CLEAR_LINE);
+
+ /* clear drive seek bits */
+ fdc->FDC_main &= ~(1 | 2 | 4 | 8);
+
+ /* return status */
+ fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
+ /* return pcn */
+ fdc->upd765_result_bytes[1] = fdc->pcn[fdc->drive];
+
+ /* return result */
+ upd765_setup_result_phase(device,2);
+ }
+ else
+ {
+ if(fdc->version == TYPE_UPD72065 && (fdc->FDC_main & 0x0f) == 0x00)
+ { // based on XM6
+ upd765_setup_invalid(device);
+ }
+ else
+ {
+ /* no int */
+ fdc->upd765_result_bytes[0] = 0x80;
+ /* return pcn */
+ fdc->upd765_result_bytes[1] = fdc->pcn[fdc->drive];
+
+ /* return result */
+ upd765_setup_result_phase(device,2);
+ }
+ }
+ break;
+
+ case 0x06: /* read data */
+ upd765_setup_drive_and_side(device);
+
+ fdc->upd765_status[0] = fdc->drive | (fdc->side<<2);
+ fdc->upd765_status[1] = 0;
+ fdc->upd765_status[2] = 0;
+
+ upd765_read_data(device);
+ break;
+
+ case 0x0c:
+ /* read deleted data */
+ upd765_setup_drive_and_side(device);
+
+ fdc->upd765_status[0] = fdc->drive | (fdc->side<<2);
+ fdc->upd765_status[1] = 0;
+ fdc->upd765_status[2] = 0;
+
+ /* .. for now */
+ upd765_read_data(device);
+ break;
+
+ case 0x09:
+ /* write deleted data */
+ upd765_setup_drive_and_side(device);
+
+ fdc->upd765_status[0] = fdc->drive | (fdc->side<<2);
+ fdc->upd765_status[1] = 0;
+ fdc->upd765_status[2] = 0;
+
+ /* ... for now */
+ upd765_write_data(device);
+ break;
+
+ case 0x02:
+ /* read a track */
+ upd765_setup_drive_and_side(device);
+ img = current_image(device);
+ fdc->upd765_status[0] = fdc->drive | (fdc->side<<2);
+ fdc->upd765_status[1] = 0;
+ fdc->upd765_status[2] = 0;
+
+ fdc->upd765_status[0] |= UPD765_ST1_NO_DATA;
+
+ /* wait for index */
+ do
+ {
+ /* get next id from disc */
+ floppy_drive_get_next_id(img, fdc->side,&id);
+ }
+ while ((floppy_drive_get_flag_state(img, FLOPPY_DRIVE_INDEX))==0);
+
+ fdc->sector_counter = 0;
+
+ upd765_read_a_track(device);
+ break;
+
+ case 0x05: /* write data */
+ upd765_setup_drive_and_side(device);
+
+ fdc->upd765_status[0] = fdc->drive | (fdc->side<<2);
+ fdc->upd765_status[1] = 0;
+ fdc->upd765_status[2] = 0;
+
+ upd765_write_data(device);
+ break;
+
+ case 0x0d: /* format a track */
+ upd765_setup_drive_and_side(device);
+
+ fdc->upd765_status[0] = fdc->drive | (fdc->side<<2);
+ fdc->upd765_status[1] = 0;
+ fdc->upd765_status[2] = 0;
+
+ fdc->sector_counter = 0;
+
+ upd765_format_track(device);
+ break;
+
+ default: /* invalid */
+ switch (fdc->version)
+ {
+ case TYPE_UPD765A:
+ upd765_setup_invalid(device);
+ break;
+
+ case TYPE_UPD765B:
+ /* from upd765b data sheet */
+ if ((fdc->upd765_command_bytes[0] & 0x01f)==0x010)
+ {
+ /* version */
+ fdc->upd765_status[0] = 0x090;
+ fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
+ upd765_setup_result_phase(device,1);
+ }
+ break;
+
+ case TYPE_UPD72065:
+ switch(fdc->upd765_command_bytes[0] & 0x3f)
+ {
+ case 0x36: // Software Reset
+ upd765_reset(device,0);
+ upd765_idle(device);
+ if(LOG_COMMAND)
+ logerror("upd72065: command - Software Reset\n");
+ break;
+ default:
+ upd765_setup_invalid(device);
+ break;
+ }
+ break;
+
+ case TYPE_SMC37C78:
+ /* TO BE COMPLETED!!! !*/
+ switch (fdc->upd765_command_bytes[0] & 0x1f)
+ {
+ case 0x10: /* version */
+ fdc->upd765_status[0] = 0x90;
+ fdc->upd765_result_bytes[0] = fdc->upd765_status[0];
+ upd765_setup_result_phase(device,1);
+ break;
+
+ case 0x13: /* configure */
+ upd765_idle(device);
+ break;
+
+ case 0x0e: /* dump reg */
+ fdc->upd765_result_bytes[0] = fdc->pcn[0];
+ fdc->upd765_result_bytes[1] = fdc->pcn[1];
+ fdc->upd765_result_bytes[2] = fdc->pcn[2];
+ fdc->upd765_result_bytes[3] = fdc->pcn[3];
+
+ upd765_setup_result_phase(device,10);
+ break;
+
+ case 0x12: /* perpendicular mode */
+ upd765_idle(device);
+ break;
+
+ case 0x14: /* lock */
+ upd765_setup_result_phase(device,1);
+ break;
+ }
+ break;
+ }
+ }
+}
+
+
+/* dma acknowledge write */
+WRITE8_DEVICE_HANDLER(upd765_dack_w)
+{
+ /* clear request */
+ upd765_set_dma_drq(device, CLEAR_LINE);
+
+ /* write data */
+ upd765_data_w(device, offset, data);
+}
+
+READ8_DEVICE_HANDLER(upd765_dack_r)
+{
+ /* clear data request */
+ upd765_set_dma_drq(device,CLEAR_LINE);
+
+ /* read data */
+ return upd765_data_r(device, offset);
+}
+
+static TIMER_CALLBACK( interrupt_callback )
+{
+ device_t* device = (device_t*)ptr;
+ upd765_set_int(device, 1);
+}
+
+void upd765_reset(device_t *device, int offset)
+{
+ upd765_t *fdc = get_safe_token(device);
+
+ /* upd765 in idle state - ready to accept commands */
+ upd765_idle(device);
+
+ /* set int low */
+ upd765_set_int(device, 0);
+ /* set dma drq output */
+ upd765_set_dma_drq(device,0);
+
+ /* tandy 100hx assumes that after NEC is reset, it is in DMA mode */
+ fdc->upd765_flags |= UPD765_DMA_MODE;
+
+ /* if ready input is set during reset generate an int */
+ if (fdc->upd765_flags & UPD765_FDD_READY)
+ {
+ int i;
+ int a_drive_is_ready;
+
+ fdc->upd765_status[0] = 0x080 | 0x040;
+
+ /* for the purpose of pc-xt. If any of the drives have a disk inserted,
+ do not set not-ready - need to check with pc_fdc->c whether all drives
+ are checked or only the drive selected with the drive select bits?? */
+
+ a_drive_is_ready = 0;
+ for (i = 0; i < 4; i++)
+ {
+ if (fdc->intf->floppy_drive_tags[i]!=NULL)
+ {
+ device_t *img;
+
+ if (device->owner() != NULL)
+ img = device->owner()->subdevice(fdc->intf->floppy_drive_tags[i]);
+ else
+ img = device->machine().device(fdc->intf->floppy_drive_tags[i]);
+
+ device_image_interface *image = dynamic_cast<device_image_interface *>( img);
+ if (image->exists())
+ {
+ a_drive_is_ready = 1;
+ break;
+ }
+ }
+
+ }
+
+ if (!a_drive_is_ready && fdc->intf->rdy_pin == UPD765_RDY_PIN_CONNECTED )
+ {
+ fdc->upd765_status[0] |= 0x08;
+ }
+
+ device->machine().scheduler().timer_set(attotime::from_usec(5), FUNC(interrupt_callback),0,device);
+ }
+}
+
+WRITE_LINE_DEVICE_HANDLER( upd765_reset_w )
+{
+ upd765_t *fdc = get_safe_token(device);
+
+ int flags;
+
+ /* get previous reset state */
+ flags = fdc->upd765_flags;
+
+ /* set new reset state */
+ /* clear reset */
+ fdc->upd765_flags &= ~UPD765_RESET;
+
+ /* reset */
+ if (state)
+ {
+ fdc->upd765_flags |= UPD765_RESET;
+
+ upd765_set_int(device, 0);
+ }
+
+ /* reset changed state? */
+ if (((flags^fdc->upd765_flags) & UPD765_RESET)!=0)
+ {
+ /* yes */
+
+ /* no longer reset */
+ if ((fdc->upd765_flags & UPD765_RESET)==0)
+ {
+ /* reset nec */
+ upd765_reset(device, 0);
+ }
+ }
+}
+
+
+WRITE_LINE_DEVICE_HANDLER( upd765_ready_w )
+{
+ upd765_t *fdc = get_safe_token(device);
+
+ /* clear ready state */
+ fdc->upd765_flags &= ~UPD765_FDD_READY;
+
+ if (state)
+ {
+ fdc->upd765_flags |= UPD765_FDD_READY;
+ }
+}
+
+
+/* Device Interface */
+
+static void common_start(device_t *device, int device_type)
+{
+ upd765_t *fdc = get_safe_token(device);
+ // validate arguments
+
+ assert(device != NULL);
+ assert(device->tag() != NULL);
+ assert(device->static_config() != NULL);
+
+ fdc->intf = (const upd765_interface*)device->static_config();
+
+ fdc->version = (UPD765_VERSION)device_type;
+ fdc->timer = device->machine().scheduler().timer_alloc(FUNC(upd765_timer_callback), (void*)device);
+ fdc->seek_timer = device->machine().scheduler().timer_alloc(FUNC(upd765_seek_timer_callback), (void*)device);
+ fdc->command_timer = device->machine().scheduler().timer_alloc(FUNC(upd765_continue_command), (void*)device);
+
+ fdc->upd765_flags &= UPD765_FDD_READY;
+ fdc->data_buffer = auto_alloc_array(device->machine(), char, 32*1024);
+
+ fdc->out_int_func.resolve(fdc->intf->out_int_func, *device);
+ fdc->out_drq_func.resolve(fdc->intf->out_drq_func, *device);
+
+ // register for state saving
+ //state_save_register_item(device->machine(), "upd765", device->tag(), 0, upd765->number);
+}
+
+static DEVICE_START( upd765a )
+{
+ common_start(device, TYPE_UPD765A);
+}
+
+static DEVICE_START( upd765b )
+{
+ common_start(device, TYPE_UPD765B);
+}
+
+static DEVICE_START( smc37c78 )
+{
+ common_start(device, TYPE_SMC37C78);
+}
+
+static DEVICE_START( upd72065 )
+{
+ common_start(device, TYPE_UPD72065);
+}
+
+static DEVICE_RESET( upd765 )
+{
+ int i;
+ upd765_t *fdc = get_safe_token(device);
+ for (i = 0; i < 4; i++) {
+ if (fdc->intf->floppy_drive_tags[i]!=NULL)
+ {
+ device_t *img;
+
+ if (device->owner() != NULL)
+ img = device->owner()->subdevice(fdc->intf->floppy_drive_tags[i]);
+ else
+ img = device->machine().device(fdc->intf->floppy_drive_tags[i]);
+
+ floppy_drive_set_controller(img, device);
+ floppy_drive_set_ready_state_change_callback(img, upd765_set_ready_change_callback);
+ }
+ }
+
+ upd765_reset(device,0);
+}
+
+DEVICE_GET_INFO( upd765a )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(upd765_t); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(upd765a); break;
+ case DEVINFO_FCT_STOP: /* Nothing */ break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(upd765); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "UPD765A"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "UPD765A"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright MESS Team"); break;
+ }
+}
+
+
+DEVICE_GET_INFO( upd765b )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "UPD765B"); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(upd765b); break;
+
+ default: DEVICE_GET_INFO_CALL(upd765a); break;
+ }
+}
+
+DEVICE_GET_INFO( smc37c78 )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "SMC37C78"); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(smc37c78); break;
+
+ default: DEVICE_GET_INFO_CALL(upd765a); break;
+ }
+}
+
+DEVICE_GET_INFO( upd72065 )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "UPD72065"); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(upd72065); break;
+
+ default: DEVICE_GET_INFO_CALL(upd765a); break;
+ }
+}
+
+DEFINE_LEGACY_DEVICE(UPD765A, upd765a);
+DEFINE_LEGACY_DEVICE(UPD765B, upd765b);
+DEFINE_LEGACY_DEVICE(SMC37C78, smc37c78);
+DEFINE_LEGACY_DEVICE(UPD72065, upd72065);
diff --git a/src/emu/machine/upd765.h b/src/emu/machine/upd765.h
new file mode 100644
index 00000000000..17e86526dd4
--- /dev/null
+++ b/src/emu/machine/upd765.h
@@ -0,0 +1,161 @@
+/***************************************************************************
+
+ machine/upd765.h
+
+ Functions to emulate a NEC upd765/Intel 8272 compatible
+ floppy disk controller
+
+***************************************************************************/
+
+#ifndef __UPD765_H__
+#define __UPD765_H__
+
+#include "devcb.h"
+#include "imagedev/flopdrv.h"
+
+
+/***************************************************************************
+ MACROS
+***************************************************************************/
+
+DECLARE_LEGACY_DEVICE(UPD765A, upd765a);
+DECLARE_LEGACY_DEVICE(UPD765B, upd765b);
+DECLARE_LEGACY_DEVICE(SMC37C78, smc37c78);
+DECLARE_LEGACY_DEVICE(UPD72065, upd72065);
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+/* RDY pin connected state */
+typedef enum
+{
+ UPD765_RDY_PIN_NOT_CONNECTED = 0,
+ UPD765_RDY_PIN_CONNECTED = 1
+} UPD765_RDY_PIN;
+
+#define UPD765_DAM_DELETED_DATA 0x0f8
+#define UPD765_DAM_DATA 0x0fb
+
+typedef device_t *(*upd765_get_image_func)(device_t *device, int floppy_index);
+#define UPD765_GET_IMAGE(name) device_t *name(device_t *device, int floppy_index )
+
+
+typedef struct upd765_interface
+{
+ /* interrupt issued */
+ devcb_write_line out_int_func;
+
+ /* dma data request */
+ devcb_write_line out_drq_func;
+
+ /* image lookup */
+ upd765_get_image_func get_image;
+
+ UPD765_RDY_PIN rdy_pin;
+
+ const char *floppy_drive_tags[4];
+} upd765_interface;
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+/* read of data register */
+READ8_DEVICE_HANDLER(upd765_data_r);
+/* write to data register */
+WRITE8_DEVICE_HANDLER(upd765_data_w);
+/* read of main status register */
+READ8_DEVICE_HANDLER(upd765_status_r);
+
+/* dma acknowledge with write */
+WRITE8_DEVICE_HANDLER(upd765_dack_w);
+/* dma acknowledge with read */
+READ8_DEVICE_HANDLER(upd765_dack_r);
+
+/* reset upd765 */
+void upd765_reset(device_t *device, int);
+
+/* reset pin of upd765 */
+WRITE_LINE_DEVICE_HANDLER(upd765_reset_w);
+
+/* set upd765 terminal count input state */
+WRITE_LINE_DEVICE_HANDLER(upd765_tc_w);
+
+/* set upd765 ready input*/
+WRITE_LINE_DEVICE_HANDLER(upd765_ready_w);
+
+void upd765_idle(device_t *device);
+
+/*********************/
+/* STATUS REGISTER 1 */
+
+/* this is set if a TC signal was not received after the sector data was read */
+#define UPD765_ST1_END_OF_CYLINDER (1<<7)
+/* this is set if the sector ID being searched for is not found */
+#define UPD765_ST1_NO_DATA (1<<2)
+/* set if disc is write protected and a write/format operation was performed */
+#define UPD765_ST1_NOT_WRITEABLE (1<<1)
+
+/*********************/
+/* STATUS REGISTER 2 */
+
+/* C parameter specified did not match C value read from disc */
+#define UPD765_ST2_WRONG_CYLINDER (1<<4)
+/* C parameter specified did not match C value read from disc, and C read from disc was 0x0ff */
+#define UPD765_ST2_BAD_CYLINDER (1<<1)
+/* this is set if the FDC encounters a Deleted Data Mark when executing a read data
+command, or FDC encounters a Data Mark when executing a read deleted data command */
+#define UPD765_ST2_CONTROL_MARK (1<<6)
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+#define MCFG_UPD765A_ADD(_tag, _intrf) \
+ MCFG_DEVICE_ADD(_tag, UPD765A, 0) \
+ MCFG_DEVICE_CONFIG(_intrf)
+
+#define MCFG_UPD765A_MODIFY(_tag, _intrf) \
+ MCFG_DEVICE_MODIFY(_tag) \
+ MCFG_DEVICE_CONFIG(_intrf)
+
+#define MCFG_UPD765A_REMOVE(_tag) \
+ MCFG_DEVICE_REMOVE(_tag)
+
+#define MCFG_UPD765B_ADD(_tag, _intrf) \
+ MCFG_DEVICE_ADD(_tag, UPD765B, 0) \
+ MCFG_DEVICE_CONFIG(_intrf)
+
+#define MCFG_UPD765B_MODIFY(_tag, _intrf) \
+ MCFG_DEVICE_MODIFY(_tag) \
+ MCFG_DEVICE_CONFIG(_intrf)
+
+#define MCFG_UPD765B_REMOVE(_tag) \
+ MCFG_DEVICE_REMOVE(_tag)
+
+#define MCFG_SMC37C78_ADD(_tag, _intrf) \
+ MCFG_DEVICE_ADD(_tag, SMC37C78, 0) \
+ MCFG_DEVICE_CONFIG(_intrf)
+
+#define MCFG_SMC37C78_MODIFY(_tag, _intrf) \
+ MCFG_DEVICE_MODIFY(_tag) \
+ MCFG_DEVICE_CONFIG(_intrf)
+
+#define MCFG_SMC37C78_REMOVE(_tag) \
+ MCFG_DEVICE_REMOVE(_tag)
+
+#define MCFG_UPD72065_ADD(_tag, _intrf) \
+ MCFG_DEVICE_ADD(_tag, UPD72065, 0) \
+ MCFG_DEVICE_CONFIG(_intrf)
+
+#define MCFG_UPD72065_MODIFY(_tag, _intrf) \
+ MCFG_DEVICE_MODIFY(_tag) \
+ MCFG_DEVICE_CONFIG(_intrf)
+
+#define MCFG_UPD72065_REMOVE(_tag) \
+ MCFG_DEVICE_REMOVE(_tag)
+
+
+#endif /* __UPD765_H__ */
diff --git a/src/mame/drivers/dc.c b/src/mame/drivers/dc.c
new file mode 100644
index 00000000000..7145e3dd97b
--- /dev/null
+++ b/src/mame/drivers/dc.c
@@ -0,0 +1,379 @@
+/*
+
+ dc.c - Sega Dreamcast driver
+ by R. Belmont
+
+ SH-4 @ 200 MHz
+ ARM7DI @ 2.8223 MHz (no T or M extensions)
+ PowerVR 3D video
+ AICA audio
+ GD-ROM drive (modified ATAPI interface)
+
+ NTSC/N NTSC/I PAL/N PAL/I VGA
+ (x/240) (x/480) (x/240) (x/480) (640x480)
+ VTOTAL 262 524 312 624 524
+ HTOTAL 857 857 863 863 857
+
+ PCLKs = 26917135 (NTSC 480 @ 59.94), 26944080 (VGA 480 @ 60.0), 13458568 (NTSC 240 @ 59.94),
+ 25925600 (PAL 480 @ 50.00), 13462800 (PAL 240 @ 50.00)
+
+*/
+
+#include "emu.h"
+#include "cpu/arm7/arm7.h"
+#include "cpu/sh4/sh4.h"
+#include "cpu/arm7/arm7core.h"
+#include "sound/aica.h"
+#include "includes/dc.h"
+#include "imagedev/chd_cd.h"
+#include "machine/maple-dc.h"
+#define CPU_CLOCK (200000000)
+
+#ifdef MESS
+UINT16 actel_id;
+int jvsboard_type;
+#else
+extern UINT16 actel_id;
+extern int jvsboard_type;
+#endif
+
+// things from mess/machine/dc.c
+void dreamcast_atapi_init(running_machine &machine);
+void dreamcast_atapi_reset(running_machine &machine);
+extern READ64_HANDLER( dc_mess_gdrom_r );
+extern WRITE64_HANDLER( dc_mess_gdrom_w );
+extern READ64_HANDLER( dc_mess_g1_ctrl_r );
+extern WRITE64_HANDLER( dc_mess_g1_ctrl_w );
+
+static UINT32 *dc_sound_ram;
+static UINT64 *dc_ram;
+
+static READ64_HANDLER( dcus_idle_skip_r )
+{
+ if (cpu_get_pc(&space->device())==0xc0ba52a)
+ device_spin_until_time(&space->device(), attotime::from_usec(2500));
+ // device_spinuntil_int(&space->device());
+
+ return dc_ram[0x2303b0/8];
+}
+
+static READ64_HANDLER( dcjp_idle_skip_r )
+{
+ if (cpu_get_pc(&space->device())==0xc0bac62)
+ device_spin_until_time(&space->device(), attotime::from_usec(2500));
+ // device_spinuntil_int(&space->device());
+
+ return dc_ram[0x2302f8/8];
+}
+
+static DRIVER_INIT(dc)
+{
+ dreamcast_atapi_init(machine);
+}
+
+static DRIVER_INIT(dcus)
+{
+ machine.device("maincpu")->memory().space(AS_PROGRAM)->install_legacy_read_handler(0xc2303b0, 0xc2303b7, FUNC(dcus_idle_skip_r));
+
+ DRIVER_INIT_CALL(dc);
+}
+
+static DRIVER_INIT(dcjp)
+{
+ machine.device("maincpu")->memory().space(AS_PROGRAM)->install_legacy_read_handler(0xc2302f8, 0xc2302ff, FUNC(dcjp_idle_skip_r));
+
+ DRIVER_INIT_CALL(dc);
+}
+
+static UINT64 PDTRA, PCTRA;
+static READ64_HANDLER( dc_pdtra_r )
+{
+ UINT64 out = PCTRA<<32;
+
+ out |= PDTRA & ~3;
+
+ // if both bits are inputs
+ if (!(PCTRA & 0x5))
+ {
+ out |= 3;
+ }
+
+ // one's input one's output, always pull up both bits
+ if (((PCTRA & 5) == 1) || ((PCTRA & 5) == 4))
+ {
+ if (PDTRA & 3)
+ {
+ out |= 3;
+ }
+ }
+
+ return out;
+}
+
+static WRITE64_HANDLER( dc_pdtra_w )
+{
+ PCTRA = (data>>16) & 0xffff;
+ PDTRA = (data & 0xffff);
+}
+
+static READ64_HANDLER( dc_arm_r )
+{
+ return *((UINT64 *)dc_sound_ram+offset);
+}
+
+static WRITE64_HANDLER( dc_arm_w )
+{
+ COMBINE_DATA((UINT64 *)dc_sound_ram + offset);
+}
+
+ // SB_LMMODE0
+ static WRITE64_HANDLER( ta_texture_directpath0_w )
+ {
+ int mode = pvrctrl_regs[SB_LMMODE0]&1;
+ if (mode&1)
+ {
+ printf("ta_texture_directpath0_w 32-bit access!\n");
+ COMBINE_DATA(&dc_framebuffer_ram[offset]);
+ }
+ else
+ {
+ COMBINE_DATA(&dc_texture_ram[offset]);
+ }
+ }
+
+ // SB_LMMODE1
+ static WRITE64_HANDLER( ta_texture_directpath1_w )
+ {
+ int mode = pvrctrl_regs[SB_LMMODE1]&1;
+ if (mode&1)
+ {
+ printf("ta_texture_directpath0_w 32-bit access!\n");
+ COMBINE_DATA(&dc_framebuffer_ram[offset]);
+ }
+ else
+ {
+ COMBINE_DATA(&dc_texture_ram[offset]);
+ }
+ }
+
+static ADDRESS_MAP_START( dc_map, AS_PROGRAM, 64 )
+ AM_RANGE(0x00000000, 0x001fffff) AM_ROM AM_WRITENOP // BIOS
+ AM_RANGE(0x00200000, 0x0021ffff) AM_ROM AM_REGION("maincpu", 0x200000) // flash
+ AM_RANGE(0x005f6800, 0x005f69ff) AM_READWRITE( dc_sysctrl_r, dc_sysctrl_w )
+ AM_RANGE(0x005f6c00, 0x005f6cff) AM_DEVREADWRITE32_MODERN( "maple_dc", maple_dc_device, sb_mdstar_r, sb_mdstar_w, U64(0xffffffff00000000) )
+ AM_RANGE(0x005f7000, 0x005f70ff) AM_READWRITE( dc_mess_gdrom_r, dc_mess_gdrom_w )
+ AM_RANGE(0x005f7400, 0x005f74ff) AM_READWRITE( dc_mess_g1_ctrl_r, dc_mess_g1_ctrl_w )
+ AM_RANGE(0x005f7800, 0x005f78ff) AM_READWRITE( dc_g2_ctrl_r, dc_g2_ctrl_w )
+ AM_RANGE(0x005f7c00, 0x005f7cff) AM_READWRITE( pvr_ctrl_r, pvr_ctrl_w )
+ AM_RANGE(0x005f8000, 0x005f9fff) AM_READWRITE( pvr_ta_r, pvr_ta_w )
+ AM_RANGE(0x00600000, 0x006007ff) AM_READWRITE( dc_modem_r, dc_modem_w )
+ AM_RANGE(0x00700000, 0x00707fff) AM_DEVREADWRITE("aica", dc_aica_reg_r, dc_aica_reg_w )
+ AM_RANGE(0x00710000, 0x0071000f) AM_READWRITE( dc_rtc_r, dc_rtc_w )
+ AM_RANGE(0x00800000, 0x009fffff) AM_READWRITE( dc_arm_r, dc_arm_w )
+
+ /* Area 1 */
+ AM_RANGE(0x04000000, 0x04ffffff) AM_RAM AM_BASE( &dc_texture_ram ) // texture memory 64 bit access
+ AM_RANGE(0x05000000, 0x05ffffff) AM_RAM AM_BASE( &dc_framebuffer_ram ) // apparently this actually accesses the same memory as the 64-bit texture memory access, but in a different format, keep it apart for now
+
+ /* Area 3 */
+ AM_RANGE(0x0c000000, 0x0cffffff) AM_RAM AM_SHARE("share4") AM_BASE(&dc_ram)
+ AM_RANGE(0x0d000000, 0x0dffffff) AM_RAM AM_SHARE("share4")// extra ram on Naomi (mirror on DC)
+ AM_RANGE(0x0e000000, 0x0effffff) AM_RAM AM_SHARE("share4")// mirror
+ AM_RANGE(0x0f000000, 0x0fffffff) AM_RAM AM_SHARE("share4")// mirror
+
+ /* Area 4 */
+ AM_RANGE(0x10000000, 0x107fffff) AM_WRITE( ta_fifo_poly_w )
+ AM_RANGE(0x10800000, 0x10ffffff) AM_WRITE( ta_fifo_yuv_w )
+ AM_RANGE(0x11000000, 0x117fffff) AM_WRITE( ta_texture_directpath0_w ) AM_MIRROR(0x00800000) // access to texture / fraembfufer memory (either 32-bit or 64-bit area depending on SB_LMMODE0 register - cannot be written directly, only through dma / store queue
+
+ AM_RANGE(0x12000000, 0x127fffff) AM_WRITE( ta_fifo_poly_w )
+ AM_RANGE(0x12800000, 0x12ffffff) AM_WRITE( ta_fifo_yuv_w )
+ AM_RANGE(0x13000000, 0x137fffff) AM_WRITE( ta_texture_directpath1_w ) AM_MIRROR(0x00800000) // access to texture / fraembfufer memory (either 32-bit or 64-bit area depending on SB_LMMODE1 register - cannot be written directly, only through dma / store queue
+
+ AM_RANGE(0x8c000000, 0x8cffffff) AM_RAM AM_SHARE("share4") // another RAM mirror
+
+ AM_RANGE(0xa0000000, 0xa01fffff) AM_ROM AM_REGION("maincpu", 0)
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( dc_port, AS_IO, 64 )
+ AM_RANGE(0x00000000, 0x00000007) AM_READWRITE( dc_pdtra_r, dc_pdtra_w )
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( dc_audio_map, AS_PROGRAM, 32 )
+ AM_RANGE(0x00000000, 0x001fffff) AM_RAM AM_BASE(&dc_sound_ram) /* shared with SH-4 */
+ AM_RANGE(0x00800000, 0x00807fff) AM_DEVREADWRITE("aica", dc_arm_aica_r, dc_arm_aica_w)
+ADDRESS_MAP_END
+
+static MACHINE_RESET( dc_console )
+{
+ device_t *aica = machine.device("aica");
+ MACHINE_RESET_CALL(dc);
+ aica_set_ram_base(aica, dc_sound_ram, 2*1024*1024);
+ dreamcast_atapi_reset(machine);
+}
+
+static void aica_irq(device_t *device, int irq)
+{
+ cputag_set_input_line(device->machine(), "soundcpu", ARM7_FIRQ_LINE, irq ? ASSERT_LINE : CLEAR_LINE);
+}
+
+static const aica_interface dc_aica_interface =
+{
+ 0,
+ 0,
+ aica_irq
+};
+
+static const struct sh4_config sh4cpu_config = { 1, 0, 1, 0, 0, 0, 1, 1, 0, CPU_CLOCK };
+
+static MACHINE_CONFIG_START( dc, driver_device )
+ /* basic machine hardware */
+ MCFG_CPU_ADD("maincpu", SH4, CPU_CLOCK)
+ MCFG_CPU_CONFIG(sh4cpu_config)
+ MCFG_CPU_PROGRAM_MAP(dc_map)
+ MCFG_CPU_IO_MAP(dc_port)
+
+ MCFG_CPU_ADD("soundcpu", ARM7, ((XTAL_33_8688MHz*2)/3)/8) // AICA bus clock is 2/3rds * 33.8688. ARM7 gets 1 bus cycle out of each 8.
+ MCFG_CPU_PROGRAM_MAP(dc_audio_map)
+
+ MCFG_MACHINE_START( dc )
+ MCFG_MACHINE_RESET( dc_console )
+
+ MCFG_MAPLE_DC_ADD( "maple_dc", "maincpu" )
+
+ /* video hardware */
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_REFRESH_RATE(60)
+ MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500) /* not accurate */)
+ MCFG_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
+ MCFG_SCREEN_SIZE(640, 480)
+ MCFG_SCREEN_VISIBLE_AREA(0, 640-1, 0, 480-1)
+ MCFG_SCREEN_UPDATE(dc)
+
+ MCFG_PALETTE_LENGTH(0x1000)
+
+ MCFG_VIDEO_START(dc)
+
+ MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
+ MCFG_SOUND_ADD("aica", AICA, 0)
+ MCFG_SOUND_CONFIG(dc_aica_interface)
+ MCFG_SOUND_ROUTE(0, "lspeaker", 2.0)
+ MCFG_SOUND_ROUTE(0, "rspeaker", 2.0)
+
+ MCFG_CDROM_ADD( "cdrom" )
+MACHINE_CONFIG_END
+
+ROM_START(dc)
+ ROM_REGION(0x220000, "maincpu", 0)
+ ROM_LOAD( "dc101d_us.bin", 0x000000, 0x200000, CRC(89f2b1a1) SHA1(8951d1bb219ab2ff8583033d2119c899cc81f18c) ) // BIOS
+ ROM_LOAD( "dcus_ntsc.bin", 0x200000, 0x020000, CRC(c611b498) SHA1(94d44d7f9529ec1642ba3771ed3c5f756d5bc872) ) // Flash
+ROM_END
+
+ROM_START( dceu )
+ ROM_REGION(0x220000, "maincpu", 0)
+ ROM_LOAD( "dc101d_eu.bin", 0x000000, 0x200000, CRC(a2564fad) SHA1(edc5d3d70a93c935703d26119b37731fd317d2bf) ) // BIOS
+ ROM_LOAD( "dceu_pal.bin", 0x200000, 0x020000, CRC(b7e5aeeb) SHA1(11e02433e13b793ec7ffe0ae2356750bb8a575b4) ) // Flash
+ROM_END
+
+ROM_START( dcjp )
+ ROM_REGION(0x220000, "maincpu", 0)
+ ROM_LOAD( "dc1004jp.bin", 0x000000, 0x200000, CRC(5454841f) SHA1(1ea132c0fbbf07ef76789eadc07908045c089bd6) ) // BIOS
+ /* ROM_LOAD( "dcjp_ntsc.bad", 0x200000, 0x020000, BAD_DUMP CRC(307a7035) SHA1(1411423a9d071340ea52c56e19c1aafc4e1309ee) ) // Hacked Flash */
+ ROM_LOAD( "dcjp_ntsc.bin", 0x200000, 0x020000, CRC(5F92BF76) SHA1(BE78B834F512AB2CF3D67B96E377C9F3093FF82A) ) // Flash
+ROM_END
+
+ROM_START( dcdev )
+ ROM_REGION(0x220000, "maincpu", 0)
+ ROM_LOAD( "hkt-0120.bin", 0x000000, 0x200000, CRC(2186E0E5) SHA1(6BD18FB83F8FDB56F1941E079580E5DD672A6DAD) ) // BIOS
+ ROM_LOAD( "hkt-0120-flash.bin", 0x200000, 0x020000, CRC(7784C304) SHA1(31EF57F550D8CD13E40263CBC657253089E53034) ) // Flash
+ROM_END
+
+static INPUT_PORTS_START( dc )
+ PORT_START("P1L")
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1)
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1)
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1)
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1)
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START1 )
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
+ PORT_START("P1H")
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_COIN1 )
+ PORT_SERVICE_NO_TOGGLE( 0x40, IP_ACTIVE_LOW )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_SERVICE1 )
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(1)
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(1)
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(1)
+
+ PORT_START("P2L")
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2)
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2)
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(2)
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2)
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START2 )
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
+ PORT_START("P2H")
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_COIN2 )
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_SERVICE2 )
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(2)
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(2)
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(2)
+
+ PORT_START("P3L")
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(3)
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(3)
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(3)
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(3)
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START3 )
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(3)
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(3)
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(3)
+ PORT_START("P3H")
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_COIN3 )
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_SERVICE3 )
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(3)
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(3)
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(3)
+
+ PORT_START("P4L")
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(4)
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(4)
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(4)
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(4)
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START4 )
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(4)
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(4)
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(4)
+ PORT_START("P4H")
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_COIN4 )
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_SERVICE4 )
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_PLAYER(4)
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_PLAYER(4)
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_PLAYER(4)
+
+ PORT_START("MAMEDEBUG") \
+ PORT_DIPNAME( 0x01, 0x00, "Bilinear Filtering" )
+ PORT_DIPSETTING( 0x00, DEF_STR( Off ) )
+ PORT_DIPSETTING( 0x01, DEF_STR( On ) )
+INPUT_PORTS_END
+
+
+/* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME */
+CONS( 1999, dc, dcjp, 0, dc, dc, dcus, "Sega", "Dreamcast (USA, NTSC)", GAME_NOT_WORKING )
+CONS( 1998, dcjp, 0, 0, dc, dc, dcjp, "Sega", "Dreamcast (Japan, NTSC)", GAME_NOT_WORKING )
+CONS( 1999, dceu, dcjp, 0, dc, dc, dcus, "Sega", "Dreamcast (Europe, PAL)", GAME_NOT_WORKING )
+CONS( 1998, dcdev, dcjp, 0, dc, dc, dc, "Sega", "HKT-0120 Sega Dreamcast Development Box", GAME_NOT_WORKING )
diff --git a/src/mame/drivers/segasms.c b/src/mame/drivers/segasms.c
new file mode 100644
index 00000000000..83487493836
--- /dev/null
+++ b/src/mame/drivers/segasms.c
@@ -0,0 +1,783 @@
+/******************************************************************************
+ Contributors:
+
+ Marat Fayzullin (MG source)
+ Charles Mac Donald
+ Mathis Rosenhauer
+ Brad Oliver
+ Michael Luong
+
+ To do:
+
+ - PSG control for Game Gear (needs custom SN76489 with stereo output for each channel)
+ - SIO interface for Game Gear (needs netplay, I guess)
+ - SMS lightgun support
+ - LCD persistence emulation for GG
+ - SMS 3D glass support
+
+ The Game Gear SIO and PSG hardware are not emulated but have some
+ placeholders in 'machine/sms.c'
+
+ Changes:
+ Apr 02 - Added raster palette effects for SMS & GG (ML)
+ - Added sprite collision (ML)
+ - Added zoomed sprites (ML)
+ May 02 - Fixed paging bug (ML)
+ - Fixed sprite and tile priority bug (ML)
+ - Fixed bug #66 (ML)
+ - Fixed bug #78 (ML)
+ - try to implement LCD persistence emulation for GG (ML)
+ Jun 10, 02 - Added bios emulation (ML)
+ Jun 12, 02 - Added PAL & NTSC systems (ML)
+ Jun 25, 02 - Added border emulation (ML)
+ Jun 27, 02 - Version bits for Game Gear (bits 6 of port 00) (ML)
+ Nov-Dec, 05 - Numerous cleanups, fixes, updates (WP)
+ Mar, 07 - More cleanups, fixes, mapper additions, etc (WP)
+
+SMS Store Unit memory map for the second CPU:
+
+0000-3FFF - BIOS
+4000-47FF - RAM
+8000 - System Control Register (R/W)
+ Reading:
+ bit7 - ready (0 = ready, 1 = not ready)
+ bit6-bit5 - unknown
+ bit4-bit3 - timer selection bit switches
+ bit2-bit0 - unknown
+ Writing:
+ bit7-bit4 - unknown, maybe led of selected game to set?
+ bit3 - unknown, 1 seems to be written all the time
+ bit2 - unknown, 1 seems to be written all the time
+ bit1 - reset signal for sms cpu, 0 = reset low, 1 = reset high
+ bit0 - which cpu receives interrupt signals, 0 = sms cpu, 1 = controlling cpu
+C000 - Card/Cartridge selction register (W)
+ bit7-bit4 - slot to select
+ bit3 - slot type (0 = cartridge, 1 = card ?)
+ bit2-bit0 - unknown
+C400 - ???? (used once)
+D800 - Selection buttons #1, 1-8 (R)
+DC00 - Selection buttons #2, 9-16 (R)
+
+ ******************************************************************************/
+
+#include "emu.h"
+#include "cpu/z80/z80.h"
+#include "sound/sn76496.h"
+#include "sound/2413intf.h"
+#include "includes/segasms.h"
+#include "video/smsvdp.h"
+#include "imagedev/cartslot.h"
+
+#include "sms1.lh"
+
+#define MASTER_CLOCK_PAL 53203400 /* This might be a tiny bit too low */
+
+
+static ADDRESS_MAP_START( sms1_mem, AS_PROGRAM, 8 )
+ AM_RANGE(0x0000, 0x03ff) AM_ROMBANK("bank1") /* First 0x0400 part always points to first page */
+ AM_RANGE(0x0400, 0x3fff) AM_ROMBANK("bank2") /* switchable rom bank */
+ AM_RANGE(0x4000, 0x5fff) AM_ROMBANK("bank3") /* switchable rom bank */
+ AM_RANGE(0x6000, 0x7fff) AM_ROMBANK("bank4") /* switchable rom bank */
+ AM_RANGE(0x8000, 0x9fff) AM_READ_BANK("bank5") AM_WRITE(sms_cartram_w) /* ROM bank / on-cart RAM */
+ AM_RANGE(0xa000, 0xbfff) AM_READ_BANK("bank6") AM_WRITE(sms_cartram2_w) /* ROM bank / on-cart RAM */
+ AM_RANGE(0xc000, 0xdff7) AM_MIRROR(0x2000) AM_RAM /* RAM (mirror at 0xE000) */
+ AM_RANGE(0xdff8, 0xdfff) AM_RAM /* RAM "underneath" frame registers */
+ AM_RANGE(0xfff8, 0xfffb) AM_READWRITE(sms_sscope_r, sms_sscope_w) /* 3-D glasses */
+ AM_RANGE(0xfffc, 0xffff) AM_READWRITE(sms_mapper_r, sms_mapper_w) /* Bankswitch control */
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( sms_mem, AS_PROGRAM, 8 )
+ AM_RANGE(0x0000, 0x03ff) AM_ROMBANK("bank1") /* First 0x0400 part always points to first page */
+ AM_RANGE(0x0400, 0x3fff) AM_ROMBANK("bank2") /* switchable rom bank */
+ AM_RANGE(0x4000, 0x5fff) AM_ROMBANK("bank3") /* switchable rom bank */
+ AM_RANGE(0x6000, 0x7fff) AM_ROMBANK("bank4") /* switchable rom bank */
+ AM_RANGE(0x8000, 0x9fff) AM_READ_BANK("bank5") AM_WRITE(sms_cartram_w) /* ROM bank / on-cart RAM */
+ AM_RANGE(0xa000, 0xbfff) AM_READ_BANK("bank6") AM_WRITE(sms_cartram2_w) /* ROM bank / on-cart RAM */
+ AM_RANGE(0xc000, 0xdffb) AM_MIRROR(0x2000) AM_RAM /* RAM (mirror at 0xE000) */
+ AM_RANGE(0xdffc, 0xdfff) AM_RAM /* RAM "underneath" frame registers */
+ AM_RANGE(0xfffc, 0xffff) AM_READWRITE(sms_mapper_r, sms_mapper_w) /* Bankswitch control */
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( sms_store_mem, AS_PROGRAM, 8 )
+ AM_RANGE(0x0000, 0x3fff) AM_ROM /* BIOS */
+ AM_RANGE(0x4000, 0x47ff) AM_RAM /* RAM */
+ AM_RANGE(0x6000, 0x7fff) AM_ROMBANK("bank10") /* Cartridge/card peek area */
+ AM_RANGE(0x8000, 0x8000) AM_READWRITE(sms_store_control_r, sms_store_control_w) /* Control */
+ AM_RANGE(0xc000, 0xc000) AM_READWRITE(sms_store_cart_select_r, sms_store_cart_select_w) /* cartridge/card slot selector */
+ AM_RANGE(0xd800, 0xd800) AM_READ(sms_store_select1) /* Game selector port #1 */
+ AM_RANGE(0xdc00, 0xdc00) AM_READ(sms_store_select2) /* Game selector port #2 */
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( sms_io, AS_IO, 8 )
+ ADDRESS_MAP_GLOBAL_MASK(0xff)
+ ADDRESS_MAP_UNMAP_HIGH
+ AM_RANGE(0x00, 0x00) AM_MIRROR(0x3e) AM_WRITE(sms_bios_w)
+ AM_RANGE(0x01, 0x01) AM_MIRROR(0x3e) AM_WRITE(sms_io_control_w)
+ AM_RANGE(0x40, 0x7f) AM_READ(sms_count_r)
+ AM_RANGE(0x40, 0x7f) AM_DEVWRITE("smsiii", sn76496_w)
+ AM_RANGE(0x80, 0x80) AM_MIRROR(0x3e) AM_DEVREADWRITE("sms_vdp", sms_vdp_data_r, sms_vdp_data_w)
+ AM_RANGE(0x81, 0x81) AM_MIRROR(0x3e) AM_DEVREADWRITE("sms_vdp", sms_vdp_ctrl_r, sms_vdp_ctrl_w)
+ AM_RANGE(0xc0, 0xc0) AM_MIRROR(0x1e) AM_READ(sms_input_port_0_r)
+ AM_RANGE(0xc1, 0xc1) AM_MIRROR(0x1e) AM_READ(sms_input_port_1_r)
+ AM_RANGE(0xe0, 0xe0) AM_MIRROR(0x0e) AM_READ(sms_input_port_0_r)
+ AM_RANGE(0xe1, 0xe1) AM_MIRROR(0x0e) AM_READ(sms_input_port_1_r)
+ AM_RANGE(0xf0, 0xf0) AM_READWRITE(sms_input_port_0_r, sms_ym2413_register_port_0_w)
+ AM_RANGE(0xf1, 0xf1) AM_READWRITE(sms_input_port_1_r, sms_ym2413_data_port_0_w)
+ AM_RANGE(0xf2, 0xf2) AM_READWRITE(sms_fm_detect_r, sms_fm_detect_w)
+ AM_RANGE(0xf3, 0xf3) AM_READ(sms_input_port_1_r)
+ AM_RANGE(0xf4, 0xf4) AM_MIRROR(0x02) AM_READ(sms_input_port_0_r)
+ AM_RANGE(0xf5, 0xf5) AM_MIRROR(0x02) AM_READ(sms_input_port_1_r)
+ AM_RANGE(0xf8, 0xf8) AM_MIRROR(0x06) AM_READ(sms_input_port_0_r)
+ AM_RANGE(0xf9, 0xf9) AM_MIRROR(0x06) AM_READ(sms_input_port_1_r)
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( gg_io, AS_IO, 8 )
+ ADDRESS_MAP_GLOBAL_MASK(0xff)
+ ADDRESS_MAP_UNMAP_HIGH
+ AM_RANGE(0x00, 0x00) AM_READ(gg_input_port_2_r)
+ AM_RANGE(0x01, 0x05) AM_READWRITE(gg_sio_r, gg_sio_w)
+ AM_RANGE(0x06, 0x06) AM_DEVWRITE("gamegear", sn76496_stereo_w)
+ AM_RANGE(0x07, 0x07) AM_WRITE(sms_io_control_w)
+ AM_RANGE(0x08, 0x08) AM_MIRROR(0x06) AM_WRITE(sms_bios_w)
+ AM_RANGE(0x09, 0x09) AM_MIRROR(0x06) AM_WRITE(sms_io_control_w)
+ AM_RANGE(0x10, 0x10) AM_MIRROR(0x0e) AM_WRITE(sms_bios_w)
+ AM_RANGE(0x11, 0x11) AM_MIRROR(0x0e) AM_WRITE(sms_io_control_w)
+ AM_RANGE(0x20, 0x20) AM_MIRROR(0x1e) AM_WRITE(sms_bios_w)
+ AM_RANGE(0x21, 0x21) AM_MIRROR(0x1e) AM_WRITE(sms_io_control_w)
+ AM_RANGE(0x40, 0x7f) AM_READ(sms_count_r)
+ AM_RANGE(0x40, 0x7f) AM_DEVWRITE("gamegear", sn76496_w)
+ AM_RANGE(0x80, 0x80) AM_MIRROR(0x3e) AM_DEVREADWRITE("sms_vdp", sms_vdp_data_r, sms_vdp_data_w)
+ AM_RANGE(0x81, 0x81) AM_MIRROR(0x3e) AM_DEVREADWRITE("sms_vdp", sms_vdp_ctrl_r, sms_vdp_ctrl_w)
+ AM_RANGE(0xc0, 0xc0) AM_READ_PORT("PORT_DC")
+ AM_RANGE(0xc1, 0xc1) AM_READ_PORT("PORT_DD")
+ AM_RANGE(0xdc, 0xdc) AM_READ_PORT("PORT_DC")
+ AM_RANGE(0xdd, 0xdd) AM_READ_PORT("PORT_DD")
+ADDRESS_MAP_END
+
+
+static INPUT_PORTS_START( sms )
+ PORT_START("PORT_DC")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_CATEGORY(10) PORT_PLAYER(1) PORT_8WAY
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_CATEGORY(10) PORT_PLAYER(1) PORT_8WAY
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_CATEGORY(10) PORT_PLAYER(1) PORT_8WAY
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_CATEGORY(10) PORT_PLAYER(1) PORT_8WAY
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CATEGORY(10) PORT_PLAYER(1)
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CATEGORY(10) PORT_PLAYER(1)
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_CATEGORY(20) PORT_PLAYER(2) PORT_8WAY
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_CATEGORY(20) PORT_PLAYER(2) PORT_8WAY
+
+ PORT_START("PORT_DD")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_CATEGORY(20) PORT_PLAYER(2) PORT_8WAY
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_CATEGORY(20) PORT_PLAYER(2) PORT_8WAY
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CATEGORY(20) PORT_PLAYER(2)
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CATEGORY(20) PORT_PLAYER(2)
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) /* Software Reset bit */
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) /* Port A TH */
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) /* Port B TH */
+
+ PORT_START("PAUSE")
+ PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START ) PORT_NAME(DEF_STR(Pause))
+
+ PORT_START("LPHASER0") /* Light phaser X - player 1 */
+ PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR( X, 1.0, 0.0, 0 ) PORT_SENSITIVITY(50) PORT_KEYDELTA(15) PORT_CATEGORY(11) PORT_PLAYER(1) PORT_CHANGED(lgun1_changed, NULL)
+
+ PORT_START("LPHASER1") /* Light phaser Y - player 1 */
+ PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR( Y, 1.0, 0.0, 0 ) PORT_SENSITIVITY(50) PORT_KEYDELTA(15) PORT_CATEGORY(11) PORT_PLAYER(1) PORT_CHANGED(lgun1_changed, NULL)
+
+ PORT_START("LPHASER2") /* Light phaser X - player 2 */
+ PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR( X, 1.0, 0.0, 0 ) PORT_SENSITIVITY(50) PORT_KEYDELTA(15) PORT_CATEGORY(21) PORT_PLAYER(2) PORT_CHANGED(lgun2_changed, NULL)
+
+ PORT_START("LPHASER3") /* Light phaser Y - player 2 */
+ PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR( Y, 1.0, 0.0, 0 ) PORT_SENSITIVITY(50) PORT_KEYDELTA(25) PORT_CATEGORY(21) PORT_PLAYER(2) PORT_CHANGED(lgun2_changed, NULL)
+
+ PORT_START("RFU") /* Rapid Fire Unit */
+ PORT_CONFNAME( 0x03, 0x00, "Rapid Fire Unit - Player 1" )
+ PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
+ PORT_CONFSETTING( 0x01, "Button A" )
+ PORT_CONFSETTING( 0x02, "Button B" )
+ PORT_CONFSETTING( 0x03, "Button A + B" )
+ PORT_CONFNAME( 0x0c, 0x00, "Rapid Fire Unit - Player 2" )
+ PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
+ PORT_CONFSETTING( 0x04, "Button A" )
+ PORT_CONFSETTING( 0x08, "Button B" )
+ PORT_CONFSETTING( 0x0c, "Button A + B" )
+
+ PORT_START("PADDLE0") /* Paddle player 1 */
+ PORT_BIT( 0xff, 0x80, IPT_PADDLE) PORT_SENSITIVITY(40) PORT_KEYDELTA(20) PORT_CENTERDELTA(0) PORT_MINMAX(0,255) PORT_CATEGORY(12) PORT_PLAYER(1)
+
+ PORT_START("PADDLE1") /* Paddle player 2 */
+ PORT_BIT( 0xff, 0x80, IPT_PADDLE) PORT_SENSITIVITY(40) PORT_KEYDELTA(20) PORT_CENTERDELTA(0) PORT_MINMAX(0,255) PORT_CATEGORY(22) PORT_PLAYER(2)
+
+ PORT_START("CTRLIPT") /* Light Phaser and Paddle Control buttons */
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CATEGORY(11) PORT_PLAYER(1)
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CATEGORY(12) PORT_PLAYER(1)
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CATEGORY(13) PORT_PLAYER(1)
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CATEGORY(13) PORT_PLAYER(1)
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CATEGORY(21) PORT_PLAYER(2)
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CATEGORY(22) PORT_PLAYER(2)
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CATEGORY(23) PORT_PLAYER(2)
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CATEGORY(23) PORT_PLAYER(2)
+
+ PORT_START("CTRLSEL") /* Controller selection */
+ PORT_CATEGORY_CLASS( 0x0f, 0x00, "Player 1 Controller" )
+ PORT_CATEGORY_ITEM( 0x00, DEF_STR( Joystick ), 10 )
+ PORT_CATEGORY_ITEM( 0x01, "Light Phaser", 11 )
+ PORT_CATEGORY_ITEM( 0x02, "Sega Paddle Control", 12 )
+ PORT_CATEGORY_ITEM( 0x03, "Sega Sports Pad", 13 )
+ PORT_CATEGORY_CLASS( 0xf0, 0x00, "Player 2 Controller" )
+ PORT_CATEGORY_ITEM( 0x00, DEF_STR( Joystick ), 20 )
+ PORT_CATEGORY_ITEM( 0x10, "Light Phaser", 21 )
+ PORT_CATEGORY_ITEM( 0x20, "Sega Paddle Control", 22 )
+ PORT_CATEGORY_ITEM( 0x30, "Sega Sports Pad", 23 )
+
+ PORT_START("SPORT0") /* Player 1 Sports Pad X axis */
+ PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40) PORT_RESET PORT_REVERSE PORT_CATEGORY(13) PORT_PLAYER(1)
+
+ PORT_START("SPORT1") /* Player 1 Sports Pad Y axis */
+ PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40) PORT_RESET PORT_REVERSE PORT_CATEGORY(13) PORT_PLAYER(1)
+
+ PORT_START("SPORT2") /* Player 2 Sports Pad X axis */
+ PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40) PORT_RESET PORT_REVERSE PORT_CATEGORY(23) PORT_PLAYER(2)
+
+ PORT_START("SPORT3") /* Player 2 Sports Pad Y axis */
+ PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40) PORT_RESET PORT_REVERSE PORT_CATEGORY(23) PORT_PLAYER(2)
+INPUT_PORTS_END
+
+static INPUT_PORTS_START( sms1 )
+ PORT_INCLUDE( sms )
+
+ PORT_START("RESET")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("Reset Button")
+
+ PORT_START("SEGASCOPE")
+ PORT_CONFNAME( 0x01, 0x00, "SegaScope (3-D Glasses)" )
+ PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
+ PORT_CONFSETTING( 0x01, DEF_STR( On ) )
+
+ PORT_START("SSCOPE_BINOCULAR")
+ PORT_CONFNAME( 0x03, 0x00, "SegaScope - Binocular Hack" ) PORT_CONDITION("SEGASCOPE", 0x01, PORTCOND_EQUALS, 0x01)
+ PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
+ PORT_CONFSETTING( 0x01, "Left Lens" )
+ PORT_CONFSETTING( 0x02, "Right Lens" )
+ PORT_CONFSETTING( 0x03, "Both Lens" )
+ PORT_BIT( 0x03, 0x00, IPT_UNUSED ) PORT_CONDITION("SEGASCOPE", 0x01, PORTCOND_EQUALS, 0x00)
+INPUT_PORTS_END
+
+static INPUT_PORTS_START( smsj )
+ PORT_INCLUDE( sms1 )
+
+ PORT_START("TVDRAW")
+ PORT_CONFNAME( 0x01, 0x00, "Terebi Oekaki Graphics Tablet" )
+ PORT_CONFSETTING( 0x00, DEF_STR( Off ) )
+ PORT_CONFSETTING( 0x01, DEF_STR( On ) )
+
+ PORT_START("TVDRAW_X")
+ PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_NAME("Tablet - X Axis") PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(10) PORT_PLAYER(1)
+ PORT_CONDITION("TVDRAW", 0x01, PORTCOND_EQUALS, 0x01)
+
+ PORT_START("TVDRAW_Y")
+ PORT_BIT( 0xff, 0x60, IPT_LIGHTGUN_Y ) PORT_NAME("Tablet - Y Axis") PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_MINMAX(0, 191) PORT_SENSITIVITY(50) PORT_KEYDELTA(10) PORT_PLAYER(1)
+ PORT_CONDITION("TVDRAW", 0x01, PORTCOND_EQUALS, 0x01)
+
+ PORT_START("TVDRAW_PEN")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Tablet - Pen") PORT_CONDITION("TVDRAW", 0x01, PORTCOND_EQUALS, 0x01)
+INPUT_PORTS_END
+
+static INPUT_PORTS_START( gg )
+ PORT_START("PORT_DC")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
+ PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("PORT_DD")
+ PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("START")
+ PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_START ) PORT_NAME("Start") /* Game Gear START */
+INPUT_PORTS_END
+
+static PALETTE_INIT( sms )
+{
+ int i;
+ for (i = 0; i < 64; i++)
+ {
+ int r = i & 0x03;
+ int g = (i & 0x0c) >> 2;
+ int b = (i & 0x30) >> 4;
+ palette_set_color_rgb(machine, i, r << 6, g << 6, b << 6);
+ }
+ /* TMS9918 palette */
+ palette_set_color_rgb(machine, 64+ 0, 0, 0, 0);
+ palette_set_color_rgb(machine, 64+ 1, 0, 0, 0);
+ palette_set_color_rgb(machine, 64+ 2, 33, 200, 66);
+ palette_set_color_rgb(machine, 64+ 3, 94, 220, 120);
+ palette_set_color_rgb(machine, 64+ 4, 84, 85, 237);
+ palette_set_color_rgb(machine, 64+ 5, 125, 118, 252);
+ palette_set_color_rgb(machine, 64+ 6, 212, 82, 77);
+ palette_set_color_rgb(machine, 64+ 7, 66, 235, 245);
+ palette_set_color_rgb(machine, 64+ 8, 252, 85, 84);
+ palette_set_color_rgb(machine, 64+ 9, 255, 121, 120);
+ palette_set_color_rgb(machine, 64+10, 212, 193, 84);
+ palette_set_color_rgb(machine, 64+11, 230, 206, 128);
+ palette_set_color_rgb(machine, 64+12, 33, 176, 59);
+ palette_set_color_rgb(machine, 64+13, 201, 91, 186);
+ palette_set_color_rgb(machine, 64+14, 204, 204, 204);
+ palette_set_color_rgb(machine, 64+15, 255, 255, 255);
+}
+
+static PALETTE_INIT( gamegear )
+{
+ int i;
+ for (i = 0; i < 4096; i++)
+ {
+ int r = i & 0x000f;
+ int g = (i & 0x00f0) >> 4;
+ int b = (i & 0x0f00) >> 8;
+ palette_set_color_rgb(machine, i, r << 4, g << 4, b << 4);
+ }
+}
+
+
+
+static void sms_int_callback( running_machine &machine, int state )
+{
+ cputag_set_input_line(machine, "maincpu", 0, state);
+}
+
+static const smsvdp_interface _315_5124_intf =
+{
+ MODEL_315_5124,
+ sms_int_callback,
+ sms_pause_callback
+};
+
+static const smsvdp_interface _315_5246_intf =
+{
+ MODEL_315_5246,
+ sms_int_callback,
+ sms_pause_callback
+};
+
+static const smsvdp_interface _315_5378_intf =
+{
+ MODEL_315_5378,
+ sms_int_callback,
+ sms_pause_callback
+};
+
+static const smsvdp_interface sms_store_intf =
+{
+ MODEL_315_5124,
+ sms_store_int_callback,
+ sms_pause_callback
+};
+
+static MACHINE_CONFIG_FRAGMENT( sms_cartslot )
+ MCFG_CARTSLOT_ADD("cart1")
+ MCFG_CARTSLOT_EXTENSION_LIST("sms,bin")
+ MCFG_CARTSLOT_NOT_MANDATORY
+ MCFG_CARTSLOT_INTERFACE("sms_cart")
+ MCFG_CARTSLOT_START(sms_cart)
+ MCFG_CARTSLOT_LOAD(sms_cart)
+
+ MCFG_SOFTWARE_LIST_ADD("cart_list","sms")
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_FRAGMENT( gg_cartslot )
+ MCFG_CARTSLOT_ADD("cart1")
+ MCFG_CARTSLOT_EXTENSION_LIST("gg,bin")
+ MCFG_CARTSLOT_MANDATORY
+ MCFG_CARTSLOT_INTERFACE("gamegear_cart")
+ MCFG_CARTSLOT_START(sms_cart)
+ MCFG_CARTSLOT_LOAD(sms_cart)
+
+ MCFG_SOFTWARE_LIST_ADD("cart_list","gamegear")
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_START( sms_ntsc_base, sms_state )
+ /* basic machine hardware */
+ MCFG_CPU_ADD("maincpu", Z80, XTAL_53_693175MHz/15)
+ MCFG_CPU_PROGRAM_MAP(sms_mem)
+ MCFG_CPU_IO_MAP(sms_io)
+
+ MCFG_QUANTUM_TIME(attotime::from_hz(60))
+
+ MCFG_MACHINE_START(sms)
+ MCFG_MACHINE_RESET(sms)
+
+ /* sound hardware */
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+ MCFG_SOUND_ADD("smsiii", SMSIII, XTAL_53_693175MHz/15)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.00)
+
+ MCFG_FRAGMENT_ADD( sms_cartslot )
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_DERIVED( sms2_ntsc, sms_ntsc_base )
+ /* video hardware */
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
+ MCFG_SCREEN_RAW_PARAMS(XTAL_53_693175MHz/10, SMS_X_PIXELS, LBORDER_START + LBORDER_X_PIXELS - 2, LBORDER_START + LBORDER_X_PIXELS + 256 + 10, NTSC_Y_PIXELS, TBORDER_START + NTSC_224_TBORDER_Y_PIXELS, TBORDER_START + NTSC_224_TBORDER_Y_PIXELS + 224)
+ MCFG_SCREEN_UPDATE(sms)
+
+ MCFG_PALETTE_LENGTH(64+16)
+ MCFG_PALETTE_INIT(sms)
+
+ MCFG_SMSVDP_ADD("sms_vdp", _315_5246_intf)
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_DERIVED( sms1_ntsc, sms_ntsc_base )
+
+ MCFG_CPU_MODIFY("maincpu")
+ MCFG_CPU_PROGRAM_MAP(sms1_mem) // This adds the SegaScope handlers for 3-D glasses
+ MCFG_CPU_IO_MAP(sms_io)
+
+ /* video hardware */
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
+ MCFG_SCREEN_RAW_PARAMS(XTAL_53_693175MHz/10, SMS_X_PIXELS, LBORDER_START + LBORDER_X_PIXELS - 2, LBORDER_START + LBORDER_X_PIXELS + 256 + 10, NTSC_Y_PIXELS, TBORDER_START + NTSC_224_TBORDER_Y_PIXELS, TBORDER_START + NTSC_224_TBORDER_Y_PIXELS + 224)
+ MCFG_SCREEN_UPDATE(sms1)
+
+ MCFG_SCREEN_ADD("left_lcd", LCD) // This is needed for SegaScope Left LCD
+ MCFG_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
+ MCFG_SCREEN_RAW_PARAMS(XTAL_53_693175MHz/10, SMS_X_PIXELS, LBORDER_START + LBORDER_X_PIXELS - 2, LBORDER_START + LBORDER_X_PIXELS + 256 + 10, NTSC_Y_PIXELS, TBORDER_START + NTSC_224_TBORDER_Y_PIXELS, TBORDER_START + NTSC_224_TBORDER_Y_PIXELS + 224)
+ MCFG_SCREEN_UPDATE(sms1)
+
+ MCFG_SCREEN_ADD("right_lcd", LCD) // This is needed for SegaScope Right LCD
+ MCFG_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
+ MCFG_SCREEN_RAW_PARAMS(XTAL_53_693175MHz/10, SMS_X_PIXELS, LBORDER_START + LBORDER_X_PIXELS - 2, LBORDER_START + LBORDER_X_PIXELS + 256 + 10, NTSC_Y_PIXELS, TBORDER_START + NTSC_224_TBORDER_Y_PIXELS, TBORDER_START + NTSC_224_TBORDER_Y_PIXELS + 224)
+ MCFG_SCREEN_UPDATE(sms1)
+
+ MCFG_DEFAULT_LAYOUT(layout_sms1)
+
+ MCFG_PALETTE_LENGTH(64+16)
+ MCFG_PALETTE_INIT(sms)
+
+ MCFG_VIDEO_START(sms1)
+
+ MCFG_SMSVDP_ADD("sms_vdp", _315_5124_intf)
+MACHINE_CONFIG_END
+
+#define MCFG_SMSSDISP_CARTSLOT_ADD(_tag) \
+ MCFG_CARTSLOT_ADD(_tag) \
+ MCFG_CARTSLOT_EXTENSION_LIST("sms,bin") \
+ MCFG_CARTSLOT_NOT_MANDATORY \
+ MCFG_CARTSLOT_INTERFACE("sms_cart") \
+ MCFG_CARTSLOT_START(sms_cart) \
+ MCFG_CARTSLOT_LOAD(sms_cart)
+
+static MACHINE_CONFIG_DERIVED( sms_sdisp, sms2_ntsc )
+
+ MCFG_DEVICE_REMOVE("sms_vdp")
+ MCFG_SMSVDP_ADD("sms_vdp", sms_store_intf)
+
+ MCFG_CPU_ADD("control", Z80, XTAL_53_693175MHz/15)
+ MCFG_CPU_PROGRAM_MAP(sms_store_mem)
+ /* Both CPUs seem to communicate with the VDP etc? */
+ MCFG_CPU_IO_MAP(sms_io)
+
+ MCFG_CARTSLOT_MODIFY("cart1")
+ MCFG_CARTSLOT_EXTENSION_LIST("sms,bin")
+ MCFG_CARTSLOT_MANDATORY
+ MCFG_CARTSLOT_INTERFACE("sms_cart")
+ MCFG_CARTSLOT_START(sms_cart)
+ MCFG_CARTSLOT_LOAD(sms_cart)
+
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart2")
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart3")
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart4")
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart5")
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart6")
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart7")
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart8")
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart9")
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart10")
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart11")
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart12")
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart13")
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart14")
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart15")
+ MCFG_SMSSDISP_CARTSLOT_ADD("cart16")
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_START( sms_pal_base, sms_state )
+ /* basic machine hardware */
+ MCFG_CPU_ADD("maincpu", Z80, MASTER_CLOCK_PAL/15)
+ MCFG_CPU_PROGRAM_MAP(sms_mem)
+ MCFG_CPU_IO_MAP(sms_io)
+
+ MCFG_QUANTUM_TIME(attotime::from_hz(60))
+
+ MCFG_MACHINE_START(sms)
+ MCFG_MACHINE_RESET(sms)
+
+ /* sound hardware */
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+ MCFG_SOUND_ADD("smsiii", SMSIII, MASTER_CLOCK_PAL/15)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.00)
+
+ MCFG_FRAGMENT_ADD( sms_cartslot )
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_DERIVED( sms2_pal, sms_pal_base )
+
+ /* video hardware */
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
+ MCFG_SCREEN_RAW_PARAMS(MASTER_CLOCK_PAL/10, SMS_X_PIXELS, LBORDER_START + LBORDER_X_PIXELS - 2, LBORDER_START + LBORDER_X_PIXELS + 256 + 10, PAL_Y_PIXELS, TBORDER_START + PAL_240_TBORDER_Y_PIXELS, TBORDER_START + PAL_240_TBORDER_Y_PIXELS + 240)
+ MCFG_SCREEN_UPDATE(sms)
+
+ MCFG_PALETTE_LENGTH(64+16)
+ MCFG_PALETTE_INIT(sms)
+
+ MCFG_SMSVDP_ADD("sms_vdp", _315_5246_intf)
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_DERIVED( sms1_pal, sms_pal_base )
+
+ MCFG_CPU_MODIFY("maincpu")
+ MCFG_CPU_PROGRAM_MAP(sms1_mem) // This adds the SegaScope handlers for 3-D glasses
+ MCFG_CPU_IO_MAP(sms_io)
+
+ /* video hardware */
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
+ MCFG_SCREEN_RAW_PARAMS(MASTER_CLOCK_PAL/10, SMS_X_PIXELS, LBORDER_START + LBORDER_X_PIXELS - 2, LBORDER_START + LBORDER_X_PIXELS + 256 + 10, PAL_Y_PIXELS, TBORDER_START + PAL_240_TBORDER_Y_PIXELS, TBORDER_START + PAL_240_TBORDER_Y_PIXELS + 240)
+ MCFG_SCREEN_UPDATE(sms1)
+
+ MCFG_SCREEN_ADD("left_lcd", LCD) // This is needed for SegaScope Left LCD
+ MCFG_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
+ MCFG_SCREEN_RAW_PARAMS(MASTER_CLOCK_PAL/10, SMS_X_PIXELS, LBORDER_START + LBORDER_X_PIXELS - 2, LBORDER_START + LBORDER_X_PIXELS + 256 + 10, PAL_Y_PIXELS, TBORDER_START + PAL_240_TBORDER_Y_PIXELS, TBORDER_START + PAL_240_TBORDER_Y_PIXELS + 240)
+ MCFG_SCREEN_UPDATE(sms1)
+
+ MCFG_SCREEN_ADD("right_lcd", LCD) // This is needed for SegaScope Right LCD
+ MCFG_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
+ MCFG_SCREEN_RAW_PARAMS(MASTER_CLOCK_PAL/10, SMS_X_PIXELS, LBORDER_START + LBORDER_X_PIXELS - 2, LBORDER_START + LBORDER_X_PIXELS + 256 + 10, PAL_Y_PIXELS, TBORDER_START + PAL_240_TBORDER_Y_PIXELS, TBORDER_START + PAL_240_TBORDER_Y_PIXELS + 240)
+ MCFG_SCREEN_UPDATE(sms1)
+
+ MCFG_PALETTE_LENGTH(64+16)
+ MCFG_PALETTE_INIT(sms)
+
+ MCFG_DEFAULT_LAYOUT(layout_sms1)
+
+ MCFG_SMSVDP_ADD("sms_vdp", _315_5124_intf)
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_DERIVED( sms_fm, sms1_ntsc )
+
+ MCFG_SOUND_ADD("ym2413", YM2413, XTAL_53_693175MHz/15)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.00)
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_DERIVED( sg1000m3, sms_fm )
+
+ MCFG_CARTSLOT_MODIFY("cart1")
+ MCFG_CARTSLOT_EXTENSION_LIST("sms,bin,sg")
+ MCFG_CARTSLOT_MANDATORY
+ MCFG_CARTSLOT_START(sms_cart)
+ MCFG_CARTSLOT_LOAD(sms_cart)
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_DERIVED( sms2_fm, sms2_ntsc )
+
+ MCFG_SOUND_ADD("ym2413", YM2413, XTAL_53_693175MHz/15)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.00)
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_START( gamegear, sms_state )
+ /* basic machine hardware */
+ MCFG_CPU_ADD("maincpu", Z80, XTAL_53_693175MHz/15)
+ MCFG_CPU_PROGRAM_MAP(sms_mem)
+ MCFG_CPU_IO_MAP(gg_io)
+
+ MCFG_QUANTUM_TIME(attotime::from_hz(60))
+
+ MCFG_MACHINE_START(sms)
+ MCFG_MACHINE_RESET(sms)
+
+ /* video hardware */
+ MCFG_SCREEN_ADD("screen", LCD)
+ MCFG_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
+ MCFG_SCREEN_RAW_PARAMS(XTAL_53_693175MHz/10, SMS_X_PIXELS, LBORDER_START + LBORDER_X_PIXELS + 6*8, LBORDER_START + LBORDER_X_PIXELS + 26*8, NTSC_Y_PIXELS, TBORDER_START + NTSC_192_TBORDER_Y_PIXELS + 3*8, TBORDER_START + NTSC_192_TBORDER_Y_PIXELS + 21*8 )
+ MCFG_SCREEN_UPDATE(gamegear)
+
+ MCFG_PALETTE_LENGTH(4096)
+ MCFG_PALETTE_INIT(gamegear)
+
+ MCFG_VIDEO_START(gamegear)
+
+ MCFG_SMSVDP_ADD("sms_vdp", _315_5378_intf)
+
+ /* sound hardware */
+ MCFG_SPEAKER_STANDARD_STEREO("lspeaker","rspeaker")
+ MCFG_SOUND_ADD("gamegear", GAMEGEAR, XTAL_53_693175MHz/15)
+ MCFG_SOUND_ROUTE(0, "lspeaker", 1.00)
+ MCFG_SOUND_ROUTE(1, "rspeaker", 1.00)
+
+ /* cartridge */
+ MCFG_FRAGMENT_ADD( gg_cartslot )
+MACHINE_CONFIG_END
+
+
+ROM_START(sms1)
+ ROM_REGION(0x4000, "maincpu", 0)
+ ROM_FILL(0x0000, 0x4000, 0xff)
+
+ ROM_REGION(0x20000, "user1", 0)
+ ROM_SYSTEM_BIOS( 0, "bios13", "US/European BIOS v1.3 (1986)" )
+ ROMX_LOAD("bios13fx.rom", 0x0000, 0x2000, CRC(0072ED54) SHA1(c315672807d8ddb8d91443729405c766dd95cae7), ROM_BIOS(1))
+ ROM_SYSTEM_BIOS( 1, "hangonsh", "US/European BIOS v2.4 with Hang On and Safari Hunt (1988)" )
+ ROMX_LOAD("hshbios.rom", 0x0000, 0x20000, CRC(91E93385) SHA1(9e179392cd416af14024d8f31c981d9ee9a64517), ROM_BIOS(2))
+ ROM_SYSTEM_BIOS( 2, "hangon", "US/European BIOS v3.4 with Hang On (1988)" )
+ ROMX_LOAD("hangbios.rom", 0x0000, 0x20000, CRC(8EDF7AC6) SHA1(51fd6d7990f62cd9d18c9ecfc62ed7936169107e), ROM_BIOS(3))
+ ROM_SYSTEM_BIOS( 3, "missiled", "US/European BIOS v4.4 with Missile Defense 3D (1988)" )
+ ROMX_LOAD("missiled.rom", 0x0000, 0x20000, CRC(E79BB689) SHA1(aa92ae576ca670b00855e278378d89e9f85e0351), ROM_BIOS(4))
+ ROM_SYSTEM_BIOS( 4, "proto", "US Master System Prototype BIOS" )
+ ROMX_LOAD("m404prot.rom", 0x0000, 0x2000, CRC(1a15dfcc) SHA1(4a06c8e66261611dce0305217c42138b71331701), ROM_BIOS(5))
+ROM_END
+
+ROM_START(sms)
+ ROM_REGION(0x4000, "maincpu", 0)
+ ROM_FILL(0x0000, 0x4000, 0xff)
+
+ ROM_REGION(0x20000, "user1", 0)
+ ROM_SYSTEM_BIOS( 0, "alexkidd", "US/European BIOS with Alex Kidd in Miracle World (1990)" )
+ ROMX_LOAD("akbios.rom", 0x0000, 0x20000, CRC(CF4A09EA) SHA1(3af7b66248d34eb26da40c92bf2fa4c73a46a051), ROM_BIOS(1))
+ROM_END
+
+ROM_START(smssdisp)
+ ROM_REGION(0x4000, "maincpu", 0)
+ ROM_FILL(0x0000, 0x4000, 0x00)
+
+ ROM_REGION(0x4000, "user1", 0)
+ ROM_FILL(0x0000, 0x4000, 0xff)
+
+ ROM_REGION(0x4000, "control", 0)
+ ROM_LOAD("smssdisp.rom", 0x0000, 0x4000, CRC(ee2c29ba) SHA1(fc465122134d95363112eb51b9ab71db3576cefd))
+ROM_END
+
+ROM_START(sms1pal)
+ ROM_REGION(0x4000, "maincpu", 0)
+ ROM_FILL(0x0000, 0x4000, 0xff)
+
+ ROM_REGION(0x20000, "user1", 0)
+ ROM_SYSTEM_BIOS( 0, "bios13", "US/European BIOS v1.3 (1986)" )
+ ROMX_LOAD("bios13fx.rom", 0x0000, 0x2000, CRC(0072ED54) SHA1(c315672807d8ddb8d91443729405c766dd95cae7), ROM_BIOS(1))
+ ROM_SYSTEM_BIOS( 1, "hangonsh", "US/European BIOS v2.4 with Hang On and Safari Hunt (1988)" )
+ ROMX_LOAD("hshbios.rom", 0x0000, 0x20000, CRC(91E93385) SHA1(9e179392cd416af14024d8f31c981d9ee9a64517), ROM_BIOS(2))
+ ROM_SYSTEM_BIOS( 2, "hangon", "Sega Master System - US/European BIOS v3.4 with Hang On (1988)" )
+ ROMX_LOAD("hangbios.rom", 0x0000, 0x20000, CRC(8EDF7AC6) SHA1(51fd6d7990f62cd9d18c9ecfc62ed7936169107e), ROM_BIOS(3))
+ ROM_SYSTEM_BIOS( 3, "missiled", "US/European BIOS v4.4 with Missile Defense 3D (1988)" )
+ ROMX_LOAD("missiled.rom", 0x0000, 0x20000, CRC(E79BB689) SHA1(aa92ae576ca670b00855e278378d89e9f85e0351), ROM_BIOS(4))
+ROM_END
+
+ROM_START(smspal)
+ ROM_REGION(0x4000, "maincpu", 0)
+ ROM_FILL(0x0000, 0x4000, 0xff)
+
+ ROM_REGION(0x40000, "user1", 0)
+ ROM_SYSTEM_BIOS( 0, "alexkidd", "US/European BIOS with Alex Kidd in Miracle World (1990)" )
+ ROMX_LOAD("akbios.rom", 0x0000, 0x20000, CRC(CF4A09EA) SHA1(3af7b66248d34eb26da40c92bf2fa4c73a46a051), ROM_BIOS(1))
+ ROM_SYSTEM_BIOS( 1, "sonic", "European/Brazilian BIOS with Sonic the Hedgehog (1991)" )
+ ROMX_LOAD("sonbios.rom", 0x0000, 0x40000, CRC(81C3476B) SHA1(6aca0e3dffe461ba1cb11a86cd4caf5b97e1b8df), ROM_BIOS(2))
+ROM_END
+
+ROM_START(sg1000m3)
+ ROM_REGION(0x4000, "maincpu", 0)
+ ROM_FILL(0x0000, 0x4000, 0x00)
+ROM_END
+
+ROM_START(smsj)
+ ROM_REGION(0x4000, "maincpu", 0)
+ ROM_FILL(0x0000, 0x4000, 0xff)
+
+ ROM_REGION(0x4000, "user1", 0)
+ ROM_SYSTEM_BIOS( 0, "jbios21", "Japanese BIOS v2.1 (1987)" )
+ ROMX_LOAD("jbios21.rom", 0x0000, 0x2000, CRC(48D44A13) SHA1(a8c1b39a2e41137835eda6a5de6d46dd9fadbaf2), ROM_BIOS(1))
+ROM_END
+
+ROM_START(sms2kr)
+ ROM_REGION(0x4000, "maincpu", 0)
+ ROM_FILL(0x0000, 0x4000, 0xff)
+
+ ROM_REGION(0x20000, "user1", 0)
+ ROM_SYSTEM_BIOS( 0, "akbioskr", "Samsung Gam*Boy II with Alex Kidd in Miracle World (1990)" )
+ ROMX_LOAD("akbioskr.rom", 0x000, 0x20000, CRC(9c5bad91) SHA1(2feafd8f1c40fdf1bd5668f8c5c02e5560945b17), ROM_BIOS(1))
+ROM_END
+
+ROM_START(gamegear)
+ ROM_REGION(0x4000, "maincpu", 0)
+ ROM_FILL(0x0000, 0x4000, 0x00)
+
+ ROM_REGION(0x0400, "user1", 0)
+ ROM_SYSTEM_BIOS( 0, "none", "No BIOS" ) /* gamegear */
+ ROM_SYSTEM_BIOS( 1, "majesco", "Majesco BIOS" ) /* gamg */
+ ROMX_LOAD("majbios.rom", 0x0000, 0x0400, CRC(0EBEA9D4) SHA1(914aa165e3d879f060be77870d345b60cfeb4ede), ROM_BIOS(2))
+ROM_END
+
+#define rom_gamegeaj rom_gamegear
+
+/***************************************************************************
+
+ Game driver(s)
+
+ US
+ - Sega Master System I (sms1)
+ - prototype bios - 1986
+ - without built-in game v1.3 - 1986
+ - built-in Hang On/Safari Hunt v2.4 - 1988
+ - built-in Hang On v3.4 - 1988
+ - built-in Missile Defense 3-D v4.4 - 1988
+ - built-in Hang On/Astro Warrior ????
+ - Sega Master System II (sms/sms2)
+ - built-in Alex Kidd in Miracle World - 1990
+
+ JP
+ - Sega SG-1000 Mark III (smsm3)
+ - no bios
+ - Sega Master System (I) (smsj)
+ - without built-in game v2.1 - 1987
+
+ KR
+ - Sega Master System II (sms2kr)
+ - built-in Alex Kidd in Miracle World (Korean)
+
+ EU
+ - Sega Master System I (sms1pal)
+ - without built-in game v1.3 - 1986
+ - built-in Hang On/Safari Hunt v2.4 - 1988
+ - built-in Hang On v3.4 - 1988
+ - built-in Missile Defense 3-D v4.4 - 1988
+ - built-in Hang On/Astro Warrior ????
+ - Sega Master System II (sms2pal)
+ - built-in Alex Kidd in Miracle World - 1990
+ - built-in Sonic the Hedgehog - 1991
+
+ BR
+ - Sega Master System I - 1987
+ - Sega Master System II???
+ - Sega Master System III - Tec Toy, 1987
+ - Sega Master System Compact - Tec Toy, 1992
+ - Sega Master System Girl - Tec Toy, 1992
+
+***************************************************************************/
+
+/* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME FLAGS */
+CONS( 1984, sg1000m3, sms, 0, sg1000m3, smsj, sg1000m3, "Sega", "SG-1000 Mark III", 0 )
+CONS( 1986, sms1, sms, 0, sms1_ntsc, sms1, sms1, "Sega", "Master System I", 0 )
+CONS( 1986, sms1pal, sms, 0, sms1_pal, sms1, sms1, "Sega", "Master System I (PAL)" , 0 )
+CONS( 1986, smssdisp, sms, 0, sms_sdisp, sms, smssdisp, "Sega", "Master System Store Display Unit", GAME_NOT_WORKING )
+CONS( 1987, smsj, sms, 0, sms_fm, smsj, smsj, "Sega", "Master System (Japan)", 0 )
+CONS( 1990, sms, 0, 0, sms2_ntsc, sms, sms1, "Sega", "Master System II", 0 )
+CONS( 1990, smspal, sms, 0, sms2_pal, sms, sms1, "Sega", "Master System II (PAL)", 0 )
+CONS( 1990, sms2kr, sms, 0, sms2_fm, sms, sms2kr, "Samsung", "Gam*Boy II (Korea)", 0 )
+
+CONS( 1990, gamegear, 0, sms, gamegear, gg, gamegear, "Sega", "Game Gear (Europe/America)", 0 )
+CONS( 1990, gamegeaj, gamegear, 0, gamegear, gg, gamegeaj, "Sega", "Game Gear (Japan)", 0 )
diff --git a/src/mame/drivers/sg1000.c b/src/mame/drivers/sg1000.c
new file mode 100644
index 00000000000..dec385ef3a4
--- /dev/null
+++ b/src/mame/drivers/sg1000.c
@@ -0,0 +1,1221 @@
+/*
+
+Sega SG-1000
+
+PCB Layout
+----------
+
+171-5078 (C) SEGA 1983
+171-5046 REV. A (C) SEGA 1983
+
+|---------------------------| |----------------------------|
+| SW1 CN2 | |------|---------------| | SW2 CN4 |
+| |---| CN3 |---| |
+| CN1 CN5 |
+| |
+| 10.738635MHz |------------------------------| 7805 |
+| |---| |------------------------------| |
+| | | CN6 |
+| | 9 | |
+| | 9 | LS32 |
+| | 1 | |---------| |
+| | 8 | | TMM2009 | LS139 |
+| | A | |---------| |------------------| |
+| | | | Z80 | |
+| |---| |------------------| |
+| |
+| |
+| MB8118 MB8118 MB8118 MB8118 SN76489A SW3 |
+| MB8118 MB8118 MB8118 MB8118 LS257 LS257 |
+|---------------------------------------------------------------------------------------|
+
+Notes:
+ All IC's shown.
+
+ Z80 - NEC D780C-1 / Zilog Z8400A (REV.A) Z80A CPU @ 3.579545
+ TMS9918A- Texas Instruments TMS9918ANL Video Display Processor @ 10.738635MHz
+ MB8118 - Fujitsu MB8118-12 16K x 1 Dynamic RAM
+ TMM2009 - Toshiba TMM2009P-A / TMM2009P-B (REV.A)
+ SN76489A- Texas Instruments SN76489AN Digital Complex Sound Generator @ 3.579545
+ CN1 - player 1 joystick connector
+ CN2 - RF video connector
+ CN3 - keyboard connector
+ CN4 - power connector (+9VDC)
+ CN5 - player 2 joystick connector
+ CN6 - cartridge connector
+ SW1 - TV channel select switch
+ SW2 - power switch
+ SW3 - hold switch
+
+*/
+
+/*
+
+ TODO:
+
+ - SC-3000 return instruction referenced by R when reading ports 60-7f,e0-ff
+ - connect PSG /READY signal to Z80 WAIT
+ - accurate video timing
+ - SP-400 serial printer
+ - SH-400 racing controller
+ - SF-7000 serial comms
+
+*/
+
+#define ADDRESS_MAP_MODERN
+
+#include "emu.h"
+#include "cpu/z80/z80.h"
+#include "imagedev/flopdrv.h"
+#include "imagedev/cartslot.h"
+#include "imagedev/cassette.h"
+#include "machine/ram.h"
+#include "imagedev/printer.h"
+#include "formats/basicdsk.h"
+#include "machine/ctronics.h"
+#include "machine/i8255.h"
+#include "machine/msm8251.h"
+#include "machine/upd765.h"
+#include "sound/sn76496.h"
+#include "video/tms9928a.h"
+#include "crsshair.h"
+#include "includes/sg1000.h"
+
+/***************************************************************************
+ READ/WRITE HANDLERS
+***************************************************************************/
+
+/*
+
+ Terebi Oekaki (TV Draw)
+
+ Address Access Bits
+ 7 6 5 4 3 2 1 0
+ $6000 W - - - - - - - AXIS
+ $8000 R BUSY - - - - - - PRESS
+ $A000 R/W DATA
+
+ AXIS: write 0 to select X axis, 1 to select Y axis.
+ BUSY: reads 1 when graphic board is busy sampling position, else 0.
+ PRESS: reads 0 when pen is touching graphic board, else 1.
+ DATA: when pen is touching graphic board, return 8-bit sample position for currently selected axis (X is in the 0-255 range, Y in the 0-191 range). Else, return 0.
+
+*/
+
+/*-------------------------------------------------
+ tvdraw_axis_w - TV Draw axis select
+-------------------------------------------------*/
+
+WRITE8_MEMBER( sg1000_state::tvdraw_axis_w )
+{
+ if (data & 0x01)
+ {
+ m_tvdraw_data = input_port_read(machine(), "TVDRAW_X");
+
+ if (m_tvdraw_data < 4) m_tvdraw_data = 4;
+ if (m_tvdraw_data > 251) m_tvdraw_data = 251;
+ }
+ else
+ {
+ m_tvdraw_data = input_port_read(machine(), "TVDRAW_Y") + 32;
+ }
+}
+
+/*-------------------------------------------------
+ tvdraw_status_r - TV Draw status read
+-------------------------------------------------*/
+
+READ8_MEMBER( sg1000_state::tvdraw_status_r )
+{
+ return input_port_read(machine(), "TVDRAW_PEN");
+}
+
+/*-------------------------------------------------
+ tvdraw_data_r - TV Draw data read
+-------------------------------------------------*/
+
+READ8_MEMBER( sg1000_state::tvdraw_data_r )
+{
+ return m_tvdraw_data;
+}
+
+/*-------------------------------------------------
+ joysel_r -
+-------------------------------------------------*/
+
+READ8_MEMBER( sg1000_state::joysel_r )
+{
+ return 0x80;
+}
+
+/***************************************************************************
+ MEMORY MAPS
+***************************************************************************/
+
+/*-------------------------------------------------
+ ADDRESS_MAP( sg1000_map )
+-------------------------------------------------*/
+
+static ADDRESS_MAP_START( sg1000_map, AS_PROGRAM, 8, sg1000_state )
+ AM_RANGE(0x0000, 0x7fff) AM_ROM
+ AM_RANGE(0x8000, 0xbfff) AM_RAMBANK("bank1")
+ AM_RANGE(0xc000, 0xffff) AM_RAMBANK("bank2")
+ADDRESS_MAP_END
+
+/*-------------------------------------------------
+ ADDRESS_MAP( sg1000_io_map )
+-------------------------------------------------*/
+
+static ADDRESS_MAP_START( sg1000_io_map, AS_IO, 8, sg1000_state )
+ ADDRESS_MAP_GLOBAL_MASK(0xff)
+ AM_RANGE(0x40, 0x40) AM_MIRROR(0x3f) AM_DEVWRITE_LEGACY(SN76489A_TAG, sn76496_w)
+ AM_RANGE(0x80, 0x80) AM_MIRROR(0x3e) AM_READWRITE_LEGACY(TMS9928A_vram_r, TMS9928A_vram_w)
+ AM_RANGE(0x81, 0x81) AM_MIRROR(0x3e) AM_READWRITE_LEGACY(TMS9928A_register_r, TMS9928A_register_w)
+ AM_RANGE(0xdc, 0xdc) AM_READ_PORT("PA7")
+ AM_RANGE(0xdd, 0xdd) AM_READ_PORT("PB7")
+ AM_RANGE(0xde, 0xde) AM_READ(joysel_r) AM_WRITENOP
+ AM_RANGE(0xdf, 0xdf) AM_NOP
+ADDRESS_MAP_END
+
+/*-------------------------------------------------
+ ADDRESS_MAP( omv_map )
+-------------------------------------------------*/
+
+static ADDRESS_MAP_START( omv_map, AS_PROGRAM, 8, sg1000_state )
+ AM_RANGE(0x0000, 0x7fff) AM_ROM
+ AM_RANGE(0x8000, 0xbfff) AM_RAMBANK("bank1")
+ AM_RANGE(0xc000, 0xc7ff) AM_MIRROR(0x3800) AM_RAM
+ADDRESS_MAP_END
+
+/*-------------------------------------------------
+ ADDRESS_MAP( omv_io_map )
+-------------------------------------------------*/
+
+static ADDRESS_MAP_START( omv_io_map, AS_IO, 8, sg1000_state )
+ ADDRESS_MAP_GLOBAL_MASK(0xff)
+ AM_RANGE(0x40, 0x40) AM_MIRROR(0x3f) AM_DEVWRITE_LEGACY(SN76489A_TAG, sn76496_w)
+ AM_RANGE(0x80, 0x80) AM_MIRROR(0x3e) AM_READWRITE_LEGACY(TMS9928A_vram_r, TMS9928A_vram_w)
+ AM_RANGE(0x81, 0x81) AM_MIRROR(0x3e) AM_READWRITE_LEGACY(TMS9928A_register_r, TMS9928A_register_w)
+ AM_RANGE(0xc0, 0xc0) AM_MIRROR(0x38) AM_READ_PORT("C0")
+ AM_RANGE(0xc1, 0xc1) AM_MIRROR(0x38) AM_READ_PORT("C1")
+ AM_RANGE(0xc2, 0xc2) AM_MIRROR(0x38) AM_READ_PORT("C2")
+ AM_RANGE(0xc3, 0xc3) AM_MIRROR(0x38) AM_READ_PORT("C3")
+ AM_RANGE(0xc4, 0xc4) AM_MIRROR(0x3a) AM_READ_PORT("C4")
+ AM_RANGE(0xc5, 0xc5) AM_MIRROR(0x3a) AM_READ_PORT("C5")
+ADDRESS_MAP_END
+
+/*-------------------------------------------------
+ ADDRESS_MAP( sc3000_map )
+-------------------------------------------------*/
+
+static ADDRESS_MAP_START( sc3000_map, AS_PROGRAM, 8, sg1000_state )
+ AM_RANGE(0x0000, 0x7fff) AM_ROM
+ AM_RANGE(0x8000, 0xbfff) AM_RAMBANK("bank1")
+ AM_RANGE(0xc000, 0xffff) AM_RAMBANK("bank2")
+ADDRESS_MAP_END
+
+/*-------------------------------------------------
+ ADDRESS_MAP( sc3000_io_map )
+-------------------------------------------------*/
+
+static ADDRESS_MAP_START( sc3000_io_map, AS_IO, 8, sg1000_state )
+ ADDRESS_MAP_GLOBAL_MASK(0xff)
+ AM_RANGE(0x7f, 0x7f) AM_DEVWRITE_LEGACY(SN76489A_TAG, sn76496_w)
+ AM_RANGE(0xbe, 0xbe) AM_READWRITE_LEGACY(TMS9928A_vram_r, TMS9928A_vram_w)
+ AM_RANGE(0xbf, 0xbf) AM_READWRITE_LEGACY(TMS9928A_register_r, TMS9928A_register_w)
+ AM_RANGE(0xdc, 0xdf) AM_DEVREADWRITE(UPD9255_TAG, i8255_device, read, write)
+ADDRESS_MAP_END
+
+/* This is how the I/O ports are really mapped, but MAME does not support overlapping ranges
+static ADDRESS_MAP_START( sc3000_io_map, AS_IO, 8, sg1000_state )
+ ADDRESS_MAP_GLOBAL_MASK(0xff)
+ AM_RANGE(0x00, 0x00) AM_MIRROR(0xdf) AM_DEVREADWRITE(UPD9255_TAG, i8255_device, read, write)
+ AM_RANGE(0x00, 0x00) AM_MIRROR(0x7f) AM_DEVWRITE_LEGACY(SN76489A_TAG, sn76496_w)
+ AM_RANGE(0x00, 0x00) AM_MIRROR(0xae) AM_READWRITE_LEGACY(TMS9928A_vram_r, TMS9928A_vram_w)
+ AM_RANGE(0x01, 0x01) AM_MIRROR(0xae) AM_READWRITE_LEGACY(TMS9928A_register_r, TMS9928A_register_w)
+ AM_RANGE(0x60, 0x60) AM_MIRROR(0x9f) AM_READ(sc3000_r_r)
+ADDRESS_MAP_END
+*/
+
+/*-------------------------------------------------
+ ADDRESS_MAP( sf7000_map )
+-------------------------------------------------*/
+
+static ADDRESS_MAP_START( sf7000_map, AS_PROGRAM, 8, sf7000_state )
+ AM_RANGE(0x0000, 0x3fff) AM_READ_BANK("bank1") AM_WRITE_BANK("bank2")
+ AM_RANGE(0x4000, 0xffff) AM_RAM
+ADDRESS_MAP_END
+
+/*-------------------------------------------------
+ ADDRESS_MAP( sf7000_io_map )
+-------------------------------------------------*/
+
+static ADDRESS_MAP_START( sf7000_io_map, AS_IO, 8, sf7000_state )
+ ADDRESS_MAP_GLOBAL_MASK(0xff)
+ AM_RANGE(0x7f, 0x7f) AM_DEVWRITE_LEGACY(SN76489A_TAG, sn76496_w)
+ AM_RANGE(0xbe, 0xbe) AM_READWRITE_LEGACY(TMS9928A_vram_r, TMS9928A_vram_w)
+ AM_RANGE(0xbf, 0xbf) AM_READWRITE_LEGACY(TMS9928A_register_r, TMS9928A_register_w)
+ AM_RANGE(0xdc, 0xdf) AM_DEVREADWRITE(UPD9255_0_TAG, i8255_device, read, write)
+ AM_RANGE(0xe0, 0xe0) AM_DEVREAD_LEGACY(UPD765_TAG, upd765_status_r)
+ AM_RANGE(0xe1, 0xe1) AM_DEVREADWRITE_LEGACY(UPD765_TAG, upd765_data_r, upd765_data_w)
+ AM_RANGE(0xe4, 0xe7) AM_DEVREADWRITE(UPD9255_1_TAG, i8255_device, read, write)
+ AM_RANGE(0xe8, 0xe8) AM_DEVREADWRITE_LEGACY(UPD8251_TAG, msm8251_data_r, msm8251_data_w)
+ AM_RANGE(0xe9, 0xe9) AM_DEVREADWRITE_LEGACY(UPD8251_TAG, msm8251_status_r, msm8251_control_w)
+ADDRESS_MAP_END
+
+/***************************************************************************
+ INPUT PORTS
+***************************************************************************/
+
+/*-------------------------------------------------
+ INPUT_CHANGED( trigger_nmi )
+-------------------------------------------------*/
+
+static INPUT_CHANGED( trigger_nmi )
+{
+ cputag_set_input_line(field->port->machine(), Z80_TAG, INPUT_LINE_NMI, (input_port_read(field->port->machine(), "NMI") ? CLEAR_LINE : ASSERT_LINE));
+}
+
+/*-------------------------------------------------
+ INPUT_PORTS( tvdraw )
+-------------------------------------------------*/
+
+static INPUT_PORTS_START( tvdraw )
+ PORT_START("TVDRAW_X")
+ PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(10) PORT_PLAYER(1)
+
+ PORT_START("TVDRAW_Y")
+ PORT_BIT( 0xff, 0x60, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_MINMAX(0, 191) PORT_SENSITIVITY(50) PORT_KEYDELTA(10) PORT_PLAYER(1)
+
+ PORT_START("TVDRAW_PEN")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Pen")
+INPUT_PORTS_END
+
+/*-------------------------------------------------
+ INPUT_PORTS( sg1000 )
+-------------------------------------------------*/
+
+static INPUT_PORTS_START( sg1000 )
+ PORT_START("PA7")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 )
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
+
+ PORT_START("PB7")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
+ PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("NMI")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("PAUSE") PORT_CODE(KEYCODE_P) PORT_CHANGED(trigger_nmi, 0)
+
+ PORT_INCLUDE( tvdraw )
+INPUT_PORTS_END
+
+/*-------------------------------------------------
+ INPUT_PORTS( omv )
+-------------------------------------------------*/
+
+static INPUT_PORTS_START( omv )
+ PORT_START("C0")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1')
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2')
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3')
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4')
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5')
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6')
+ PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("C1")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7')
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8')
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("9 #") PORT_CODE(KEYCODE_9) PORT_CHAR('9')
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("0 *") PORT_CODE(KEYCODE_0) PORT_CHAR('0')
+ PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("C2")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A')
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B')
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C')
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D')
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E')
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F')
+ PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("C3")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G')
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H')
+ PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("C4")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("S-1")
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("S-2")
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
+
+ PORT_START("C5")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
+ PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_INCLUDE( tvdraw )
+INPUT_PORTS_END
+
+/*-------------------------------------------------
+ INPUT_PORTS( sk1100 )
+-------------------------------------------------*/
+
+static INPUT_PORTS_START( sk1100 )
+ PORT_START("PA0")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_CHAR('!')
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_CHAR('Q') PORT_CHAR('q')
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_CHAR('a')
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Z) PORT_CHAR('Z') PORT_CHAR('z')
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("ENG DIER'S") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(RALT))
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') PORT_CHAR('<')
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_K) PORT_CHAR('K') PORT_CHAR('k')
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_I) PORT_CHAR('I') PORT_CHAR('i')
+
+ PORT_START("PA1")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_CHAR('"')
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_W) PORT_CHAR('W') PORT_CHAR('w')
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_CHAR('S') PORT_CHAR('s')
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_X) PORT_CHAR('X') PORT_CHAR('x')
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SPC") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_STOP) PORT_CHAR('.') PORT_CHAR('>')
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_CHAR('L') PORT_CHAR('l')
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_O) PORT_CHAR('O') PORT_CHAR('o')
+
+ PORT_START("PA2")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_CHAR('#')
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_CHAR('e')
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_CHAR('d')
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_CHAR('c')
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HOME CLR") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME))
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SLASH) PORT_CHAR('/') PORT_CHAR('?')
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_COLON) PORT_CHAR(';') PORT_CHAR('+')
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_CHAR('P') PORT_CHAR('p')
+
+ PORT_START("PA3")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_CHAR('$')
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_CHAR('R') PORT_CHAR('r')
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_CHAR('f')
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_V) PORT_CHAR('V') PORT_CHAR('v')
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("INS DEL") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8)
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xcf\x80") PORT_CODE(KEYCODE_EQUALS) PORT_CHAR(0x03c0)
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_QUOTE) PORT_CHAR(':') PORT_CHAR('*')
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_OPENBRACE) PORT_CHAR('@') PORT_CHAR('`')
+
+ PORT_START("PA4")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_CHAR('%')
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_T) PORT_CHAR('T') PORT_CHAR('t')
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_CHAR('G') PORT_CHAR('g')
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_CHAR('b')
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_DOWN) PORT_CODE(KEYCODE_DOWN) PORT_CHAR(UCHAR_MAMEKEY(DOWN))
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH) PORT_CHAR(']') PORT_CHAR('}')
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('[') PORT_CHAR('{')
+
+ PORT_START("PA5")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_CHAR('&')
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_CHAR('y')
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_H) PORT_CHAR('H') PORT_CHAR('h')
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_CHAR('N') PORT_CHAR('n')
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_LEFT) PORT_CODE(KEYCODE_LEFT) PORT_CHAR(UCHAR_MAMEKEY(LEFT))
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CR") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13)
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("PA6")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_CHAR('\'')
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_U) PORT_CHAR('U') PORT_CHAR('u')
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_J) PORT_CHAR('J') PORT_CHAR('j')
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_CHAR('M') PORT_CHAR('m')
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_RIGHT) PORT_CODE(KEYCODE_RIGHT) PORT_CHAR(UCHAR_MAMEKEY(RIGHT))
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("PA7")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP )
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN )
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT )
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT )
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 )
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
+
+ PORT_START("PB0")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_CHAR('(')
+ PORT_BIT( 0x06, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("PB1")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_CHAR(')')
+ PORT_BIT( 0x06, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("PB2")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0')
+ PORT_BIT( 0x06, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("PB3")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_MINUS) PORT_CHAR('-') PORT_CHAR('=')
+ PORT_BIT( 0x06, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("PB4")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_BACKSLASH2) PORT_CHAR('^')
+ PORT_BIT( 0x06, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("PB5")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("\xc2\xa5") PORT_CODE(KEYCODE_TILDE) PORT_CHAR(0x00a5)
+ PORT_BIT( 0x06, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("FUNC") PORT_CODE(KEYCODE_TAB)
+
+ PORT_START("PB6")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("BREAK") PORT_CODE(KEYCODE_ESC) PORT_CHAR(UCHAR_MAMEKEY(ESC))
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GRAPH") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT))
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL))
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1)
+
+ PORT_START("PB7")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
+
+ PORT_START("NMI")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("RESET") PORT_CODE(KEYCODE_F10) PORT_CHANGED(trigger_nmi, 0)
+INPUT_PORTS_END
+
+/*-------------------------------------------------
+ INPUT_PORTS( sc3000 )
+-------------------------------------------------*/
+
+static INPUT_PORTS_START( sc3000 )
+ PORT_INCLUDE( sk1100 )
+ PORT_INCLUDE( tvdraw )
+INPUT_PORTS_END
+
+/*-------------------------------------------------
+ INPUT_PORTS( sf7000 )
+-------------------------------------------------*/
+
+static INPUT_PORTS_START( sf7000 )
+ PORT_INCLUDE( sk1100 )
+
+ PORT_START("BAUD")
+ PORT_CONFNAME( 0x05, 0x05, "Baud rate")
+ PORT_CONFSETTING( 0x00, "9600 baud" )
+ PORT_CONFSETTING( 0x01, "4800 baud" )
+ PORT_CONFSETTING( 0x02, "2400 baud" )
+ PORT_CONFSETTING( 0x03, "1200 baud" )
+ PORT_CONFSETTING( 0x04, "600 baud" )
+ PORT_CONFSETTING( 0x05, "300 baud" )
+INPUT_PORTS_END
+
+/***************************************************************************
+ DEVICE CONFIGURATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ TMS9928a_interface tms9928a_interface
+-------------------------------------------------*/
+
+static INTERRUPT_GEN( sg1000_int )
+{
+ TMS9928A_interrupt(device->machine());
+}
+
+static void sg1000_vdp_interrupt(running_machine &machine, int state)
+{
+ cputag_set_input_line(machine, Z80_TAG, INPUT_LINE_IRQ0, state);
+}
+
+static const TMS9928a_interface tms9928a_interface =
+{
+ TMS99x8A,
+ 0x4000,
+ 0, 0,
+ sg1000_vdp_interrupt
+};
+
+/*-------------------------------------------------
+ I8255_INTERFACE( sc3000_ppi_intf )
+-------------------------------------------------*/
+
+READ8_MEMBER( sc3000_state::ppi_pa_r )
+{
+ /*
+ Signal Description
+
+ PA0 Keyboard input
+ PA1 Keyboard input
+ PA2 Keyboard input
+ PA3 Keyboard input
+ PA4 Keyboard input
+ PA5 Keyboard input
+ PA6 Keyboard input
+ PA7 Keyboard input
+ */
+
+ static const char *const keynames[] = { "PA0", "PA1", "PA2", "PA3", "PA4", "PA5", "PA6", "PA7" };
+
+ return input_port_read(machine(), keynames[m_keylatch]);
+}
+
+READ8_MEMBER( sc3000_state::ppi_pb_r )
+{
+ /*
+ Signal Description
+
+ PB0 Keyboard input
+ PB1 Keyboard input
+ PB2 Keyboard input
+ PB3 Keyboard input
+ PB4 /CONT input from cartridge terminal B-11
+ PB5 FAULT input from printer
+ PB6 BUSY input from printer
+ PB7 Cassette tape input
+ */
+
+ static const char *const keynames[] = { "PB0", "PB1", "PB2", "PB3", "PB4", "PB5", "PB6", "PB7" };
+
+ /* keyboard */
+ UINT8 data = input_port_read(machine(), keynames[m_keylatch]);
+
+ /* cartridge contact */
+ data |= 0x10;
+
+ /* printer */
+ data |= 0x60;
+
+ /* tape input */
+ if (cassette_input(m_cassette) > +0.0) data |= 0x80;
+
+ return data;
+}
+
+WRITE8_MEMBER( sc3000_state::ppi_pc_w )
+{
+ /*
+ Signal Description
+
+ PC0 Keyboard raster output
+ PC1 Keyboard raster output
+ PC2 Keyboard raster output
+ PC3 not connected
+ PC4 Cassette tape output
+ PC5 DATA to printer
+ PC6 /RESET to printer
+ PC7 /FEED to printer
+ */
+
+ /* keyboard */
+ m_keylatch = data & 0x07;
+
+ /* cassette */
+ cassette_output(m_cassette, BIT(data, 4) ? +1.0 : -1.0);
+
+ /* TODO printer */
+}
+
+static I8255_INTERFACE( sc3000_ppi_intf )
+{
+ DEVCB_DRIVER_MEMBER(sc3000_state, ppi_pa_r), // Port A read
+ DEVCB_NULL, // Port A write
+ DEVCB_DRIVER_MEMBER(sc3000_state, ppi_pb_r), // Port B read
+ DEVCB_NULL, // Port B write
+ DEVCB_NULL, // Port C read
+ DEVCB_DRIVER_MEMBER(sc3000_state, ppi_pc_w), // Port C write
+};
+
+/*-------------------------------------------------
+ cassette_config sc3000_cassette_config
+-------------------------------------------------*/
+
+static const cassette_config sc3000_cassette_config =
+{
+ cassette_default_formats,
+ NULL,
+ (cassette_state)(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED),
+ NULL
+};
+
+/*-------------------------------------------------
+ I8255_INTERFACE( sf7000_ppi_intf )
+-------------------------------------------------*/
+
+READ8_MEMBER( sf7000_state::ppi_pa_r )
+{
+ /*
+ Signal Description
+
+ PA0 INT from FDC
+ PA1 BUSY from Centronics printer
+ PA2 INDEX from FDD
+ PA3
+ PA4
+ PA5
+ PA6
+ PA7
+ */
+
+ UINT8 data = 0;
+
+ data |= m_fdc_irq;
+ data |= centronics_busy_r(m_centronics) << 1;
+ data |= m_fdc_index << 2;
+
+ return data;
+}
+
+WRITE8_MEMBER( sf7000_state::ppi_pc_w )
+{
+ /*
+ Signal Description
+
+ PC0 /INUSE signal to FDD
+ PC1 /MOTOR ON signal to FDD
+ PC2 TC signal to FDC
+ PC3 RESET signal to FDC
+ PC4 not connected
+ PC5 not connected
+ PC6 /ROM SEL (switch between IPL ROM and RAM)
+ PC7 /STROBE to Centronics printer
+ */
+
+ /* floppy motor */
+ floppy_mon_w(m_floppy0, BIT(data, 1));
+ floppy_drive_set_ready_state(m_floppy0, 1, 1);
+
+ /* FDC terminal count */
+ upd765_tc_w(m_fdc, BIT(data, 2));
+
+ /* FDC reset */
+ if (BIT(data, 3))
+ {
+ upd765_reset(m_fdc, 0);
+ }
+
+ /* ROM selection */
+ memory_set_bank(machine(), "bank1", BIT(data, 6));
+
+ /* printer strobe */
+ centronics_strobe_w(m_centronics, BIT(data, 7));
+}
+
+static I8255_INTERFACE( sf7000_ppi_intf )
+{
+ DEVCB_DRIVER_MEMBER(sf7000_state, ppi_pa_r), // Port A read
+ DEVCB_NULL, // Port A write
+ DEVCB_NULL, // Port B read
+ DEVCB_DEVICE_HANDLER(CENTRONICS_TAG, centronics_data_w), // Port B write
+ DEVCB_NULL, // Port C read
+ DEVCB_DRIVER_MEMBER(sf7000_state, ppi_pc_w) // Port C write
+};
+
+/*-------------------------------------------------
+ upd765_interface sf7000_upd765_interface
+-------------------------------------------------*/
+
+WRITE_LINE_MEMBER( sf7000_state::fdc_intrq_w )
+{
+ m_fdc_irq = state;
+}
+
+static const struct upd765_interface sf7000_upd765_interface =
+{
+ DEVCB_DRIVER_LINE_MEMBER(sf7000_state, fdc_intrq_w),
+ DEVCB_NULL,
+ NULL,
+ UPD765_RDY_PIN_CONNECTED,
+ { FLOPPY_0, NULL, NULL, NULL }
+};
+
+/*-------------------------------------------------
+ FLOPPY_OPTIONS( sf7000 )
+-------------------------------------------------*/
+
+static FLOPPY_OPTIONS_START( sf7000 )
+ FLOPPY_OPTION(sf7000, "sf7", "SF7 disk image", basicdsk_identify_default, basicdsk_construct_default, NULL,
+ HEADS([1])
+ TRACKS([40])
+ SECTORS([16])
+ SECTOR_LENGTH([256])
+ FIRST_SECTOR_ID([1]))
+FLOPPY_OPTIONS_END
+
+/*-------------------------------------------------
+ floppy_config sf7000_floppy_config
+-------------------------------------------------*/
+
+static const floppy_config sf7000_floppy_config =
+{
+ DEVCB_NULL,
+ DEVCB_NULL,
+ DEVCB_NULL,
+ DEVCB_NULL,
+ DEVCB_NULL,
+ FLOPPY_STANDARD_5_25_DSHD,
+ FLOPPY_OPTIONS_NAME(sf7000),
+ NULL
+};
+
+/***************************************************************************
+ CARTRIDGE LOADING
+***************************************************************************/
+
+/*-------------------------------------------------
+ sg1000_install_cartridge -
+-------------------------------------------------*/
+
+void sg1000_state::install_cartridge(UINT8 *ptr, int size)
+{
+ address_space *program = m_maincpu->memory().space(AS_PROGRAM);
+
+ switch (size)
+ {
+ case 40 * 1024:
+ program->install_read_bank(0x8000, 0x9fff, "bank1");
+ program->unmap_write(0x8000, 0x9fff);
+ memory_configure_bank(machine(), "bank1", 0, 1, machine().region(Z80_TAG)->base() + 0x8000, 0);
+ memory_set_bank(machine(), "bank1", 0);
+ break;
+
+ case 48 * 1024:
+ program->install_read_bank(0x8000, 0xbfff, "bank1");
+ program->unmap_write(0x8000, 0xbfff);
+ memory_configure_bank(machine(), "bank1", 0, 1, machine().region(Z80_TAG)->base() + 0x8000, 0);
+ memory_set_bank(machine(), "bank1", 0);
+ break;
+
+ default:
+ if (IS_CARTRIDGE_TV_DRAW(ptr))
+ {
+ program->install_write_handler(0x6000, 0x6000, 0, 0, write8_delegate(FUNC(sg1000_state::tvdraw_axis_w), this), 0);
+ program->install_read_handler(0x8000, 0x8000, 0, 0, read8_delegate(FUNC(sg1000_state::tvdraw_status_r), this), 0);
+ program->install_read_handler(0xa000, 0xa000, 0, 0, read8_delegate(FUNC(sg1000_state::tvdraw_data_r), this), 0);
+ program->nop_write(0xa000, 0xa000);
+ }
+ else if (IS_CARTRIDGE_THE_CASTLE(ptr))
+ {
+ program->install_readwrite_bank(0x8000, 0x9fff, "bank1");
+ }
+ break;
+ }
+}
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_LOAD( sg1000_cart )
+-------------------------------------------------*/
+
+static DEVICE_IMAGE_LOAD( sg1000_cart )
+{
+ running_machine &machine = image.device().machine();
+ sg1000_state *state = machine.driver_data<sg1000_state>();
+ address_space *program = machine.device(Z80_TAG)->memory().space(AS_PROGRAM);
+ UINT8 *ptr = machine.region(Z80_TAG)->base();
+ UINT32 size;
+
+ if (image.software_entry() == NULL)
+ {
+ size = image.length();
+ if (image.fread( ptr, size) != size)
+ return IMAGE_INIT_FAIL;
+ }
+ else
+ {
+ size = image.get_software_region_length("rom");
+ memcpy(ptr, image.get_software_region("rom"), size);
+ }
+
+ /* cartridge ROM banking */
+ state->install_cartridge(ptr, size);
+
+ /* work RAM banking */
+ program->install_readwrite_bank(0xc000, 0xc3ff, 0, 0x3c00, "bank2");
+
+ return IMAGE_INIT_PASS;
+}
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_LOAD( omv_cart )
+-------------------------------------------------*/
+
+static DEVICE_IMAGE_LOAD( omv_cart )
+{
+ running_machine &machine = image.device().machine();
+ sg1000_state *state = machine.driver_data<sg1000_state>();
+ UINT32 size;
+ UINT8 *ptr = machine.region(Z80_TAG)->base();
+
+ if (image.software_entry() == NULL)
+ {
+ size = image.length();
+ if (image.fread( ptr, size) != size)
+ return IMAGE_INIT_FAIL;
+ }
+ else
+ {
+ size = image.get_software_region_length("rom");
+ memcpy(ptr, image.get_software_region("rom"), size);
+ }
+
+ /* cartridge ROM banking */
+ state->install_cartridge(ptr, size);
+
+ return IMAGE_INIT_PASS;
+}
+
+/*-------------------------------------------------
+ sc3000_install_cartridge -
+-------------------------------------------------*/
+
+void sc3000_state::install_cartridge(UINT8 *ptr, int size)
+{
+ address_space *program = m_maincpu->memory().space(AS_PROGRAM);
+
+ /* include SG-1000 mapping */
+ sg1000_state::install_cartridge(ptr, size);
+
+ if (IS_CARTRIDGE_BASIC_LEVEL_III(ptr))
+ {
+ program->install_readwrite_bank(0x8000, 0xbfff, "bank1");
+ program->install_readwrite_bank(0xc000, 0xffff, "bank2");
+ }
+ else if (IS_CARTRIDGE_MUSIC_EDITOR(ptr))
+ {
+ program->install_readwrite_bank(0x8000, 0x9fff, "bank1");
+ program->install_readwrite_bank(0xc000, 0xc7ff, 0, 0x3800, "bank2");
+ }
+ else
+ {
+ /* regular cartridges */
+ program->install_readwrite_bank(0xc000, 0xc7ff, 0, 0x3800, "bank2");
+ }
+}
+
+/*-------------------------------------------------
+ DEVICE_IMAGE_LOAD( sc3000_cart )
+-------------------------------------------------*/
+
+static DEVICE_IMAGE_LOAD( sc3000_cart )
+{
+ running_machine &machine = image.device().machine();
+ sc3000_state *state = machine.driver_data<sc3000_state>();
+ UINT8 *ptr = machine.region(Z80_TAG)->base();
+ UINT32 size;
+
+ if (image.software_entry() == NULL)
+ {
+ size = image.length();
+ if (image.fread( ptr, size) != size)
+ return IMAGE_INIT_FAIL;
+ }
+ else
+ {
+ size = image.get_software_region_length("rom");
+ memcpy(ptr, image.get_software_region("rom"), size);
+ }
+
+ /* cartridge ROM and work RAM banking */
+ state->install_cartridge(ptr, size);
+
+ return IMAGE_INIT_PASS;
+}
+
+/***************************************************************************
+ MACHINE INITIALIZATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ TIMER_CALLBACK( lightgun_tick )
+-------------------------------------------------*/
+
+static TIMER_CALLBACK( lightgun_tick )
+{
+ UINT8 *rom = machine.region(Z80_TAG)->base();
+
+ if (IS_CARTRIDGE_TV_DRAW(rom))
+ {
+ /* enable crosshair for TV Draw */
+ crosshair_set_screen(machine, 0, CROSSHAIR_SCREEN_ALL);
+ }
+ else
+ {
+ /* disable crosshair for other cartridges */
+ crosshair_set_screen(machine, 0, CROSSHAIR_SCREEN_NONE);
+ }
+}
+
+/*-------------------------------------------------
+ MACHINE_START( sg1000 )
+-------------------------------------------------*/
+
+void sg1000_state::machine_start()
+{
+ /* configure VDP */
+ TMS9928A_configure(&tms9928a_interface);
+
+ /* toggle light gun crosshair */
+ machine().scheduler().timer_set(attotime::zero, FUNC(lightgun_tick));
+
+ /* register for state saving */
+ state_save_register_global(machine(), m_tvdraw_data);
+}
+
+/*-------------------------------------------------
+ MACHINE_START( sc3000 )
+-------------------------------------------------*/
+
+void sc3000_state::machine_start()
+{
+ /* configure VDP */
+ TMS9928A_configure(&tms9928a_interface);
+
+ /* toggle light gun crosshair */
+ machine().scheduler().timer_set(attotime::zero, FUNC(lightgun_tick));
+
+ /* register for state saving */
+ state_save_register_global(machine(), m_tvdraw_data);
+ state_save_register_global(machine(), m_keylatch);
+}
+
+/*-------------------------------------------------
+ sf7000_fdc_index_callback -
+-------------------------------------------------*/
+
+static void sf7000_fdc_index_callback(device_t *controller, device_t *img, int state)
+{
+ sf7000_state *driver_state = img->machine().driver_data<sf7000_state>();
+
+ driver_state->m_fdc_index = state;
+}
+
+/*-------------------------------------------------
+ MACHINE_START( sf7000 )
+-------------------------------------------------*/
+
+void sf7000_state::machine_start()
+{
+ /* configure VDP */
+ TMS9928A_configure(&tms9928a_interface);
+
+ /* configure FDC */
+ floppy_drive_set_index_pulse_callback(m_floppy0, sf7000_fdc_index_callback);
+
+ /* configure memory banking */
+ memory_configure_bank(machine(), "bank1", 0, 1, machine().region(Z80_TAG)->base(), 0);
+ memory_configure_bank(machine(), "bank1", 1, 1, ram_get_ptr(m_ram), 0);
+ memory_configure_bank(machine(), "bank2", 0, 1, ram_get_ptr(m_ram), 0);
+
+ /* register for state saving */
+ state_save_register_global(machine(), m_keylatch);
+ state_save_register_global(machine(), m_fdc_irq);
+ state_save_register_global(machine(), m_fdc_index);
+}
+
+/*-------------------------------------------------
+ MACHINE_RESET( sf7000 )
+-------------------------------------------------*/
+
+void sf7000_state::machine_reset()
+{
+ memory_set_bank(machine(), "bank1", 0);
+ memory_set_bank(machine(), "bank2", 0);
+}
+
+/***************************************************************************
+ MACHINE DRIVERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ MACHINE_CONFIG_START( sg1000, sg1000_state )
+-------------------------------------------------*/
+
+static MACHINE_CONFIG_START( sg1000, sg1000_state )
+ /* basic machine hardware */
+ MCFG_CPU_ADD(Z80_TAG, Z80, XTAL_10_738635MHz/3)
+ MCFG_CPU_PROGRAM_MAP(sg1000_map)
+ MCFG_CPU_IO_MAP(sg1000_io_map)
+ MCFG_CPU_VBLANK_INT(SCREEN_TAG, sg1000_int)
+
+ /* video hardware */
+ MCFG_FRAGMENT_ADD(tms9928a)
+ MCFG_SCREEN_MODIFY(SCREEN_TAG)
+ MCFG_SCREEN_REFRESH_RATE((float)XTAL_10_738635MHz/2/342/262)
+ MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) /* not accurate */
+
+ /* sound hardware */
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+ MCFG_SOUND_ADD(SN76489A_TAG, SN76489A, XTAL_10_738635MHz/3)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.00)
+
+ /* cartridge */
+ MCFG_CARTSLOT_ADD("cart")
+ MCFG_CARTSLOT_EXTENSION_LIST("sg,bin")
+ MCFG_CARTSLOT_MANDATORY
+ MCFG_CARTSLOT_INTERFACE("sg1000_cart")
+ MCFG_CARTSLOT_LOAD(sg1000_cart)
+
+ /* software lists */
+ MCFG_SOFTWARE_LIST_ADD("cart_list","sg1000")
+
+ /* internal ram */
+ MCFG_RAM_ADD(RAM_TAG)
+ MCFG_RAM_DEFAULT_SIZE("1K")
+MACHINE_CONFIG_END
+
+/*-------------------------------------------------
+ MACHINE_CONFIG_DERIVED( omv, sg1000 )
+-------------------------------------------------*/
+
+static MACHINE_CONFIG_DERIVED( omv, sg1000 )
+ MCFG_CPU_MODIFY(Z80_TAG)
+ MCFG_CPU_PROGRAM_MAP(omv_map)
+ MCFG_CPU_IO_MAP(omv_io_map)
+
+ MCFG_CARTSLOT_MODIFY("cart")
+ MCFG_CARTSLOT_EXTENSION_LIST("sg,bin")
+ MCFG_CARTSLOT_NOT_MANDATORY
+ MCFG_CARTSLOT_LOAD(omv_cart)
+
+ MCFG_RAM_MODIFY(RAM_TAG)
+ MCFG_RAM_DEFAULT_SIZE("2K")
+MACHINE_CONFIG_END
+
+/*-------------------------------------------------
+ MACHINE_CONFIG_START( sc3000, sc3000_state )
+-------------------------------------------------*/
+
+static MACHINE_CONFIG_START( sc3000, sc3000_state )
+ /* basic machine hardware */
+ MCFG_CPU_ADD(Z80_TAG, Z80, XTAL_10_738635MHz/3) // LH0080A
+ MCFG_CPU_PROGRAM_MAP(sc3000_map)
+ MCFG_CPU_IO_MAP(sc3000_io_map)
+ MCFG_CPU_VBLANK_INT(SCREEN_TAG, sg1000_int)
+
+ /* video hardware */
+ MCFG_FRAGMENT_ADD(tms9928a)
+ MCFG_SCREEN_MODIFY(SCREEN_TAG)
+ MCFG_SCREEN_REFRESH_RATE((float)XTAL_10_738635MHz/2/342/262)
+ MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) /* not accurate */
+
+ /* sound hardware */
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+ MCFG_SOUND_ADD(SN76489A_TAG, SN76489A, XTAL_10_738635MHz/3)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.00)
+
+ /* devices */
+ MCFG_I8255_ADD(UPD9255_TAG, sc3000_ppi_intf)
+// MCFG_PRINTER_ADD("sp400") /* serial printer */
+ MCFG_CASSETTE_ADD(CASSETTE_TAG, sc3000_cassette_config)
+
+ /* cartridge */
+ MCFG_CARTSLOT_ADD("cart")
+ MCFG_CARTSLOT_EXTENSION_LIST("sg,sc,bin")
+ MCFG_CARTSLOT_INTERFACE("sg1000_cart")
+ MCFG_CARTSLOT_LOAD(sc3000_cart)
+
+ /* software lists */
+ MCFG_SOFTWARE_LIST_ADD("cart_list","sg1000")
+
+ /* internal ram */
+ MCFG_RAM_ADD(RAM_TAG)
+ MCFG_RAM_DEFAULT_SIZE("2K")
+MACHINE_CONFIG_END
+
+/*-------------------------------------------------
+ MACHINE_CONFIG_START( sf7000, sf7000_state )
+-------------------------------------------------*/
+
+static MACHINE_CONFIG_START( sf7000, sf7000_state )
+ /* basic machine hardware */
+ MCFG_CPU_ADD(Z80_TAG, Z80, XTAL_10_738635MHz/3)
+ MCFG_CPU_PROGRAM_MAP(sf7000_map)
+ MCFG_CPU_IO_MAP(sf7000_io_map)
+ MCFG_CPU_VBLANK_INT(SCREEN_TAG, sg1000_int)
+
+ /* video hardware */
+ MCFG_FRAGMENT_ADD(tms9928a)
+ MCFG_SCREEN_MODIFY(SCREEN_TAG)
+ MCFG_SCREEN_REFRESH_RATE((float)XTAL_10_738635MHz/2/342/262)
+ MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) /* not accurate */
+
+ /* sound hardware */
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+ MCFG_SOUND_ADD(SN76489A_TAG, SN76489A, XTAL_10_738635MHz/3)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.00)
+
+ /* devices */
+ MCFG_I8255_ADD(UPD9255_0_TAG, sc3000_ppi_intf)
+ MCFG_I8255_ADD(UPD9255_1_TAG, sf7000_ppi_intf)
+ MCFG_MSM8251_ADD(UPD8251_TAG, default_msm8251_interface)
+ MCFG_UPD765A_ADD(UPD765_TAG, sf7000_upd765_interface)
+ MCFG_FLOPPY_DRIVE_ADD(FLOPPY_0, sf7000_floppy_config)
+// MCFG_PRINTER_ADD("sp400") /* serial printer */
+ MCFG_CENTRONICS_ADD(CENTRONICS_TAG, standard_centronics)
+ MCFG_CASSETTE_ADD(CASSETTE_TAG, sc3000_cassette_config)
+
+ /* internal ram */
+ MCFG_RAM_ADD(RAM_TAG)
+ MCFG_RAM_DEFAULT_SIZE("64K")
+MACHINE_CONFIG_END
+
+/***************************************************************************
+ ROMS
+***************************************************************************/
+
+ROM_START( sg1000 )
+ ROM_REGION( 0x10000, Z80_TAG, ROMREGION_ERASE00 )
+ROM_END
+
+#define rom_sg1000m2 rom_sg1000
+
+ROM_START( sc3000 )
+ ROM_REGION( 0x10000, Z80_TAG, ROMREGION_ERASE00 )
+ ROM_LOAD( "sc3000.rom", 0x0000, 0x8000, CRC(a46e3c73) SHA1(b1a8585a9afff962e8fd0e79b3305199da4e4562))
+ROM_END
+
+#define rom_sc3000h rom_sg1000
+
+ROM_START( omv1000 )
+ ROM_REGION( 0x10000, Z80_TAG, ROMREGION_ERASE00 )
+ ROM_LOAD( "omvbios.bin", 0x0000, 0x4000, BAD_DUMP CRC(c5a67b95) SHA1(6d7c64dd60dee4a33061d3d3a7c2ed190d895cdb) ) // The BIOS comes from a Multivision FG-2000. It is still unknown if the FG-1000 BIOS differs
+ROM_END
+
+ROM_START( omv2000 )
+ ROM_REGION( 0x10000, Z80_TAG, ROMREGION_ERASE00 )
+ ROM_LOAD( "omvbios.bin", 0x0000, 0x4000, CRC(c5a67b95) SHA1(6d7c64dd60dee4a33061d3d3a7c2ed190d895cdb) )
+ROM_END
+
+ROM_START( sf7000 )
+ ROM_REGION( 0x10000, Z80_TAG, 0 )
+ ROM_LOAD( "ipl.rom", 0x0000, 0x2000, CRC(d76810b8) SHA1(77339a6db2593aadc638bed77b8e9bed5d9d87e3) )
+ROM_END
+
+/***************************************************************************
+ SYSTEM DRIVERS
+***************************************************************************/
+
+/* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME FLAGS */
+CONS( 1983, sg1000, 0, 0, sg1000, sg1000, 0, "Sega", "SG-1000", GAME_SUPPORTS_SAVE )
+CONS( 1984, sg1000m2, sg1000, 0, sc3000, sc3000, 0, "Sega", "SG-1000 II", GAME_SUPPORTS_SAVE )
+COMP( 1983, sc3000, 0, sg1000, sc3000, sc3000, 0, "Sega", "SC-3000", GAME_SUPPORTS_SAVE )
+COMP( 1983, sc3000h, sc3000, 0, sc3000, sc3000, 0, "Sega", "SC-3000H", GAME_SUPPORTS_SAVE )
+COMP( 1983, sf7000, sc3000, 0, sf7000, sf7000, 0, "Sega", "SC-3000/Super Control Station SF-7000", GAME_SUPPORTS_SAVE )
+CONS( 1984, omv1000, sg1000, 0, omv, omv, 0, "Tsukuda Original", "Othello Multivision FG-1000", GAME_SUPPORTS_SAVE )
+CONS( 1984, omv2000, sg1000, 0, omv, omv, 0, "Tsukuda Original", "Othello Multivision FG-2000", GAME_SUPPORTS_SAVE )
diff --git a/src/mame/formats/basicdsk.c b/src/mame/formats/basicdsk.c
new file mode 100644
index 00000000000..10e07a6ccd9
--- /dev/null
+++ b/src/mame/formats/basicdsk.c
@@ -0,0 +1,352 @@
+/*********************************************************************
+
+ formats/basicdsk.c
+
+ Floppy format code for basic disks
+
+*********************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "basicdsk.h"
+
+static floperr_t basicdsk_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen);
+static floperr_t basicdsk_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam);
+static floperr_t basicdsk_read_indexed_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen);
+static floperr_t basicdsk_write_indexed_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam);
+static floperr_t basicdsk_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length);
+static floperr_t basicdsk_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags);
+static int basicdsk_get_heads_per_disk(floppy_image *floppy);
+static int basicdsk_get_tracks_per_disk(floppy_image *floppy);
+static floperr_t basicdsk_format_track(floppy_image *floppy, int head, int track, option_resolution *params);
+
+
+
+#define BASICDSK_TAG "basicdsktag"
+
+struct basicdsk_tag
+{
+ struct basicdsk_geometry geometry;
+};
+
+
+
+/********************************************************************/
+
+static const struct basicdsk_geometry *get_geometry(floppy_image *floppy)
+{
+ const struct basicdsk_tag *tag;
+ tag = (const basicdsk_tag *)floppy_tag(floppy);
+ return &tag->geometry;
+}
+
+
+
+floperr_t basicdsk_construct(floppy_image *floppy, const struct basicdsk_geometry *geometry)
+{
+ struct basicdsk_tag *tag;
+ struct FloppyCallbacks *format;
+
+ assert(geometry->heads);
+ assert(geometry->tracks);
+ assert(geometry->sectors);
+
+ tag = (struct basicdsk_tag *) floppy_create_tag(floppy, sizeof(struct basicdsk_tag));
+ if (!tag)
+ return FLOPPY_ERROR_OUTOFMEMORY;
+ tag->geometry = *geometry;
+
+ /* set up format callbacks */
+ format = floppy_callbacks(floppy);
+ format->read_sector = basicdsk_read_sector;
+ format->write_sector = basicdsk_write_sector;
+ format->read_indexed_sector = basicdsk_read_indexed_sector;
+ format->write_indexed_sector = basicdsk_write_indexed_sector;
+ format->get_sector_length = basicdsk_get_sector_length;
+ format->get_heads_per_disk = basicdsk_get_heads_per_disk;
+ format->get_tracks_per_disk = basicdsk_get_tracks_per_disk;
+ format->get_indexed_sector_info = basicdsk_get_indexed_sector_info;
+ format->format_track = basicdsk_format_track;
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+static floperr_t get_offset(floppy_image *floppy, int head, int track, int sector, int sector_is_index, UINT64 *offset)
+{
+ const struct basicdsk_geometry *geom;
+ UINT64 offs;
+
+ geom = get_geometry(floppy);
+
+ /* translate the sector to a raw sector */
+ if (!sector_is_index)
+ {
+ sector -= geom->first_sector_id;
+ }
+
+ if (geom->translate_sector)
+ sector = geom->translate_sector(floppy, sector);
+
+ /* check to see if we are out of range */
+ if ((head < 0) || (head >= geom->heads) || (track < 0) || (track >= geom->tracks)
+ || (sector < 0) || (sector >= geom->sectors))
+ return FLOPPY_ERROR_SEEKERROR;
+
+ if (geom->translate_offset)
+ offs = geom->translate_offset(floppy, geom, track, head, sector);
+ else
+ {
+ offs = 0;
+ offs += track;
+ offs *= geom->heads;
+ offs += head;
+ offs *= geom->sectors;
+ offs += sector;
+ }
+ offs *= geom->sector_length;
+ offs += geom->offset;
+
+ if (offset)
+ *offset = offs;
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+static int internal_basicdsk_translate_sector_interleave(floppy_image *floppy, int sector)
+{
+ const struct basicdsk_geometry *geom = get_geometry(floppy);
+ if (sector >= geom->sectors)
+ return sector;
+ return geom->sector_map[sector];
+}
+
+
+
+static floperr_t internal_basicdsk_read_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, void *buffer, size_t buflen)
+{
+ UINT64 offset;
+ floperr_t err;
+
+ err = get_offset(floppy, head, track, sector, sector_is_index, &offset);
+ if (err)
+ return err;
+ floppy_image_read(floppy, buffer, offset, buflen);
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+static floperr_t internal_basicdsk_write_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, const void *buffer, size_t buflen, int ddam)
+{
+ UINT64 offset;
+ floperr_t err;
+
+ err = get_offset(floppy, head, track, sector, sector_is_index, &offset);
+ if (err)
+ return err;
+
+ floppy_image_write(floppy, buffer, offset, buflen);
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+static floperr_t basicdsk_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen)
+{
+ return internal_basicdsk_read_sector(floppy, head, track, sector, FALSE, buffer, buflen);
+}
+
+static floperr_t basicdsk_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
+{
+ return internal_basicdsk_write_sector(floppy, head, track, sector, FALSE, buffer, buflen, ddam);
+}
+
+static floperr_t basicdsk_read_indexed_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen)
+{
+ return internal_basicdsk_read_sector(floppy, head, track, sector, TRUE, buffer, buflen);
+}
+
+static floperr_t basicdsk_write_indexed_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
+{
+ return internal_basicdsk_write_sector(floppy, head, track, sector, TRUE, buffer, buflen, ddam);
+}
+
+
+
+static floperr_t basicdsk_format_track(floppy_image *floppy, int head, int track, option_resolution *params)
+{
+ floperr_t err = FLOPPY_ERROR_SUCCESS;
+ UINT8 local_buffer[512];
+ void *alloc_buffer = NULL;
+ void *buffer;
+ UINT32 sector_length;
+ int sector;
+ const struct basicdsk_geometry *geometry;
+
+ geometry = get_geometry(floppy);
+
+ sector_length = geometry->sector_length;
+
+ if (sector_length > sizeof(local_buffer))
+ {
+ alloc_buffer = malloc(sector_length);
+ if (!alloc_buffer)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+ buffer = alloc_buffer;
+ }
+ else
+ {
+ alloc_buffer = NULL;
+ buffer = local_buffer;
+ }
+
+ memset(buffer, floppy_get_filler(floppy), sector_length);
+
+ for (sector = 0; sector < geometry->sectors; sector++)
+ {
+ err = basicdsk_write_sector(floppy, head, track, sector + geometry->first_sector_id, buffer, sector_length, 0);
+ if (err)
+ goto done;
+ }
+
+done:
+ if (alloc_buffer)
+ free(alloc_buffer);
+ return err;
+}
+
+
+
+static int basicdsk_get_heads_per_disk(floppy_image *floppy)
+{
+ return get_geometry(floppy)->heads;
+}
+
+
+
+static int basicdsk_get_tracks_per_disk(floppy_image *floppy)
+{
+ return get_geometry(floppy)->tracks;
+}
+
+
+
+static floperr_t basicdsk_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length)
+{
+ floperr_t err;
+
+ err = get_offset(floppy, head, track, sector, FALSE, NULL);
+ if (err)
+ return err;
+
+ if (sector_length)
+ *sector_length = get_geometry(floppy)->sector_length;
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+static floperr_t basicdsk_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags)
+{
+ const struct basicdsk_geometry *geom = get_geometry(floppy);
+
+ if (geom->translate_sector)
+ sector_index = geom->translate_sector(floppy, sector_index);
+
+ sector_index += geom->first_sector_id;
+
+ if (cylinder)
+ *cylinder = track;
+ if (side)
+ *side = head;
+ if (sector)
+ *sector = sector_index;
+ if (flags) {
+ /* TODO: read DAM or DDAM and determine flags */
+ *flags = 0;
+ if (geom->get_ddam)
+ *flags = geom->get_ddam(floppy, geom, track, head, sector_index);
+ }
+ return basicdsk_get_sector_length(floppy, head, track, sector_index, sector_length);
+}
+
+
+
+/********************************************************************
+ * Generic Basicdsk Constructors
+ ********************************************************************/
+
+static void basicdsk_default_geometry(const struct FloppyFormat *format, struct basicdsk_geometry *geometry)
+{
+ optreserr_t err;
+ int sector_length;
+ memset(geometry, 0, sizeof(*geometry));
+
+ err = option_resolution_getdefault(format->param_guidelines, PARAM_HEADS, &geometry->heads);
+ assert(!err);
+ err = option_resolution_getdefault(format->param_guidelines, PARAM_TRACKS, &geometry->tracks);
+ assert(!err);
+ err = option_resolution_getdefault(format->param_guidelines, PARAM_SECTORS, &geometry->sectors);
+ assert(!err);
+ err = option_resolution_getdefault(format->param_guidelines, PARAM_FIRST_SECTOR_ID, &geometry->first_sector_id);
+ assert(!err);
+ err = option_resolution_getdefault(format->param_guidelines, PARAM_INTERLEAVE, &geometry->interleave);
+ if (err!=0) {
+ geometry->interleave = 1;
+ }
+ err = option_resolution_getdefault(format->param_guidelines, PARAM_SECTOR_LENGTH, &sector_length);
+ assert(!err);
+ geometry->sector_length = sector_length;
+
+ if (geometry->interleave > 1)
+ {
+ int sector = 0;
+
+ for (int i = 0; i < geometry->sectors; i++)
+ {
+ geometry->sector_map[i] = sector;
+
+ sector += geometry->interleave;
+
+ if (sector >= geometry->sectors)
+ sector -= geometry->sectors;
+ }
+
+ geometry->translate_sector = internal_basicdsk_translate_sector_interleave;
+ }
+}
+
+
+
+FLOPPY_CONSTRUCT(basicdsk_construct_default)
+{
+ struct basicdsk_geometry geometry;
+ basicdsk_default_geometry(format, &geometry);
+ return basicdsk_construct(floppy, &geometry);
+}
+
+
+
+FLOPPY_IDENTIFY(basicdsk_identify_default)
+{
+ UINT64 expected_size;
+ struct basicdsk_geometry geometry;
+
+ basicdsk_default_geometry(format, &geometry);
+
+ expected_size = geometry.sector_length;
+ expected_size *= geometry.heads;
+ expected_size *= geometry.tracks;
+ expected_size *= geometry.sectors;
+ *vote = (floppy_image_size(floppy) == expected_size) ? 100 : 0;
+ return FLOPPY_ERROR_SUCCESS;
+}
+
diff --git a/src/mame/formats/basicdsk.h b/src/mame/formats/basicdsk.h
new file mode 100644
index 00000000000..22af0499014
--- /dev/null
+++ b/src/mame/formats/basicdsk.h
@@ -0,0 +1,35 @@
+/*********************************************************************
+
+ formats/basicdsk.h
+
+ Floppy format code for basic disks
+
+*********************************************************************/
+
+#ifndef BASICDSK_H
+#define BASICDSK_H
+
+#include "imagedev/flopimg.h"
+
+struct basicdsk_geometry
+{
+ int heads;
+ int tracks;
+ int sectors;
+ int first_sector_id;
+ int interleave;
+ int sector_map[256];
+ UINT32 sector_length;
+ UINT64 offset;
+
+ int (*translate_sector)(floppy_image *floppy, int sector);
+ UINT64 (*translate_offset)(floppy_image *floppy, const struct basicdsk_geometry *geom, int track, int head, int sector);
+ UINT64 (*get_ddam)(floppy_image *floppy, const struct basicdsk_geometry *geom, int track, int head, int sector);
+};
+
+floperr_t basicdsk_construct(floppy_image *floppy, const struct basicdsk_geometry *geometry);
+
+FLOPPY_IDENTIFY(basicdsk_identify_default);
+FLOPPY_CONSTRUCT(basicdsk_construct_default);
+
+#endif /* BASICDSK_H */
diff --git a/src/mame/includes/segasms.h b/src/mame/includes/segasms.h
new file mode 100644
index 00000000000..8ec361e0b37
--- /dev/null
+++ b/src/mame/includes/segasms.h
@@ -0,0 +1,183 @@
+/*****************************************************************************
+ *
+ * includes/sms.h
+ *
+ ****************************************************************************/
+
+#ifndef SMS_H_
+#define SMS_H_
+
+#define LOG_REG
+#define LOG_PAGING
+#define LOG_COLOR
+
+#define NVRAM_SIZE (0x08000)
+#define CPU_ADDRESSABLE_SIZE (0x10000)
+
+#define MAX_CARTRIDGES 16
+
+class sms_state : public driver_device
+{
+public:
+ sms_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag) { }
+
+ // device_ts
+ device_t *m_main_cpu;
+ device_t *m_control_cpu;
+ device_t *m_vdp;
+ device_t *m_ym;
+ device_t *m_main_scr;
+ device_t *m_left_lcd;
+ device_t *m_right_lcd;
+
+ UINT8 m_bios_page_count;
+ UINT8 m_fm_detect;
+ UINT8 m_ctrl_reg;
+ int m_paused;
+ UINT8 m_bios_port;
+ UINT8 *m_BIOS;
+ UINT8 *m_mapper_ram;
+ UINT8 m_mapper[4];
+ // we are going to use 1-6, same as bank numbers. Notice, though, that most mappers
+ // only work on 16K banks and, hence, banks 4-6 are not always directly set
+ // (they often use bank3 + 0x2000 and bank5 + 0x2000)
+ UINT8 *m_banking_bios[7];
+ UINT8 *m_banking_cart[7];
+ UINT8 *m_banking_none[7];
+ UINT8 m_gg_sio[5];
+ UINT8 m_store_control;
+ UINT8 m_input_port0;
+ UINT8 m_input_port1;
+
+ // for gamegear LCD persistence hack
+ bitmap_t *m_tmp_bitmap;
+ bitmap_t *m_prev_bitmap;
+
+ // for 3D glass binocular hack
+ bitmap_t *m_prevleft_bitmap;
+ bitmap_t *m_prevright_bitmap;
+
+ /* Model identifiers */
+ UINT8 m_is_gamegear;
+ UINT8 m_is_region_japan;
+ UINT8 m_has_bios_0400;
+ UINT8 m_has_bios_2000;
+ UINT8 m_has_bios_full;
+ UINT8 m_has_bios;
+ UINT8 m_has_fm;
+
+ /* Data needed for Rapid Fire Unit support */
+ emu_timer *m_rapid_fire_timer;
+ UINT8 m_rapid_fire_state_1;
+ UINT8 m_rapid_fire_state_2;
+
+ /* Data needed for Paddle Control controller */
+ UINT32 m_last_paddle_read_time;
+ UINT8 m_paddle_read_state;
+
+ /* Data needed for Sports Pad controller */
+ UINT32 m_last_sports_pad_time_1;
+ UINT32 m_last_sports_pad_time_2;
+ UINT8 m_sports_pad_state_1;
+ UINT8 m_sports_pad_state_2;
+ UINT8 m_sports_pad_last_data_1;
+ UINT8 m_sports_pad_last_data_2;
+ UINT8 m_sports_pad_1_x;
+ UINT8 m_sports_pad_1_y;
+ UINT8 m_sports_pad_2_x;
+ UINT8 m_sports_pad_2_y;
+
+ /* Data needed for Light Phaser */
+ emu_timer *m_lphaser_1_timer;
+ emu_timer *m_lphaser_2_timer;
+ UINT8 m_lphaser_1_latch;
+ UINT8 m_lphaser_2_latch;
+ int m_lphaser_x_offs; /* Needed to 'calibrate' lphaser; set at cart loading */
+
+ /* Data needed for SegaScope (3D glasses) */
+ UINT8 m_sscope_state;
+
+ /* Data needed for Terebi Oekaki (TV Draw) */
+ UINT8 m_tvdraw_data;
+
+ /* Cartridge slot info */
+ UINT8 m_current_cartridge;
+ struct
+ {
+ UINT8 *ROM; /* Pointer to ROM image data */
+ UINT32 size; /* Size of the ROM image */
+ UINT8 features; /* on-cartridge special hardware */
+ UINT8 *cartSRAM; /* on-cartridge SRAM */
+ UINT8 sram_save; /* should be the contents of the on-cartridge SRAM be saved */
+ UINT8 *cartRAM; /* additional on-cartridge RAM (64KB for Ernie Els Golf) */
+ UINT32 ram_size; /* size of the on-cartridge RAM */
+ UINT8 ram_page; /* currently swapped in cartridge RAM */
+ } m_cartridge[MAX_CARTRIDGES];
+};
+
+
+/*----------- defined in machine/sms.c -----------*/
+
+/* Function prototypes */
+WRITE8_HANDLER( sms_cartram_w );
+WRITE8_HANDLER( sms_cartram2_w );
+WRITE8_HANDLER( sms_fm_detect_w );
+READ8_HANDLER( sms_fm_detect_r );
+READ8_HANDLER( sms_input_port_0_r );
+READ8_HANDLER( sms_input_port_1_r );
+WRITE8_HANDLER( sms_ym2413_register_port_0_w );
+WRITE8_HANDLER( sms_ym2413_data_port_0_w );
+WRITE8_HANDLER( sms_io_control_w );
+READ8_HANDLER( sms_count_r );
+WRITE8_HANDLER( sms_sscope_w );
+READ8_HANDLER( sms_sscope_r );
+WRITE8_HANDLER( sms_mapper_w );
+READ8_HANDLER( sms_mapper_r );
+WRITE8_HANDLER( sms_bios_w );
+WRITE8_HANDLER( gg_sio_w );
+READ8_HANDLER( gg_sio_r );
+READ8_HANDLER( gg_input_port_2_r );
+
+INPUT_CHANGED( lgun1_changed );
+INPUT_CHANGED( lgun2_changed );
+
+void sms_pause_callback( running_machine &machine );
+void sms_store_int_callback( running_machine &machine, int state );
+
+DEVICE_START( sms_cart );
+DEVICE_IMAGE_LOAD( sms_cart );
+
+MACHINE_START( sms );
+MACHINE_RESET( sms );
+
+READ8_HANDLER( sms_store_cart_select_r );
+WRITE8_HANDLER( sms_store_cart_select_w );
+READ8_HANDLER( sms_store_select1 );
+READ8_HANDLER( sms_store_select2 );
+READ8_HANDLER( sms_store_control_r );
+WRITE8_HANDLER( sms_store_control_w );
+
+#define IO_EXPANSION (0x80) /* Expansion slot enable (1= disabled, 0= enabled) */
+#define IO_CARTRIDGE (0x40) /* Cartridge slot enable (1= disabled, 0= enabled) */
+#define IO_CARD (0x20) /* Card slot disabled (1= disabled, 0= enabled) */
+#define IO_WORK_RAM (0x10) /* Work RAM disabled (1= disabled, 0= enabled) */
+#define IO_BIOS_ROM (0x08) /* BIOS ROM disabled (1= disabled, 0= enabled) */
+#define IO_CHIP (0x04) /* I/O chip disabled (1= disabled, 0= enabled) */
+
+
+DRIVER_INIT( sg1000m3 );
+DRIVER_INIT( sms1 );
+DRIVER_INIT( smsj );
+DRIVER_INIT( sms2kr );
+DRIVER_INIT( smssdisp );
+DRIVER_INIT( gamegear );
+DRIVER_INIT( gamegeaj );
+
+VIDEO_START( sms1 );
+VIDEO_START( gamegear );
+SCREEN_UPDATE( sms1 );
+SCREEN_UPDATE( sms );
+SCREEN_UPDATE( gamegear );
+
+#endif /* SMS_H_ */
diff --git a/src/mame/includes/sg1000.h b/src/mame/includes/sg1000.h
new file mode 100644
index 00000000000..20b1b792af5
--- /dev/null
+++ b/src/mame/includes/sg1000.h
@@ -0,0 +1,102 @@
+#ifndef __SG1000__
+#define __SG1000__
+
+#include "machine/ram.h"
+
+#define SCREEN_TAG "screen"
+#define Z80_TAG "z80"
+#define SN76489A_TAG "sn76489a"
+#define UPD765_TAG "upd765"
+#define CASSETTE_TAG "cassette"
+#define UPD8251_TAG "upd8251"
+#define UPD9255_TAG "upd9255"
+#define UPD9255_0_TAG "upd9255_0"
+#define UPD9255_1_TAG "upd9255_1"
+#define CENTRONICS_TAG "centronics"
+
+#define IS_CARTRIDGE_TV_DRAW(ptr) \
+ (!strncmp("annakmn", (const char *)&ptr[0x13b3], 7))
+
+#define IS_CARTRIDGE_THE_CASTLE(ptr) \
+ (!strncmp("ASCII 1986", (const char *)&ptr[0x1cc3], 10))
+
+#define IS_CARTRIDGE_BASIC_LEVEL_III(ptr) \
+ (!strncmp("SC-3000 BASIC Level 3 ver 1.0", (const char *)&ptr[0x6a20], 29))
+
+#define IS_CARTRIDGE_MUSIC_EDITOR(ptr) \
+ (!strncmp("PIANO", (const char *)&ptr[0x0841], 5))
+
+class sg1000_state : public driver_device
+{
+public:
+ sg1000_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag),
+ m_maincpu(*this, Z80_TAG),
+ m_ram(*this, RAM_TAG)
+ { }
+
+ required_device<cpu_device> m_maincpu;
+ required_device<device_t> m_ram;
+
+ virtual void machine_start();
+
+ void install_cartridge(UINT8 *ptr, int size);
+
+ DECLARE_WRITE8_MEMBER( tvdraw_axis_w );
+ DECLARE_READ8_MEMBER( tvdraw_status_r );
+ DECLARE_READ8_MEMBER( tvdraw_data_r );
+ DECLARE_READ8_MEMBER( joysel_r );
+
+ /* keyboard state */
+ UINT8 m_keylatch;
+
+ /* TV Draw state */
+ UINT8 m_tvdraw_data;
+};
+
+class sc3000_state : public sg1000_state
+{
+public:
+ sc3000_state(const machine_config &mconfig, device_type type, const char *tag)
+ : sg1000_state(mconfig, type, tag),
+ m_cassette(*this, CASSETTE_TAG)
+ { }
+
+ required_device<device_t> m_cassette;
+
+ virtual void machine_start();
+
+ void install_cartridge(UINT8 *ptr, int size);
+
+ DECLARE_READ8_MEMBER( ppi_pa_r );
+ DECLARE_READ8_MEMBER( ppi_pb_r );
+ DECLARE_WRITE8_MEMBER( ppi_pc_w );
+};
+
+class sf7000_state : public sc3000_state
+{
+public:
+ sf7000_state(const machine_config &mconfig, device_type type, const char *tag)
+ : sc3000_state(mconfig, type, tag),
+ m_fdc(*this, UPD765_TAG),
+ m_centronics(*this, CENTRONICS_TAG),
+ m_floppy0(*this, FLOPPY_0)
+ { }
+
+ required_device<device_t> m_fdc;
+ required_device<device_t> m_centronics;
+ required_device<device_t> m_floppy0;
+
+ virtual void machine_start();
+ virtual void machine_reset();
+
+ DECLARE_READ8_MEMBER( ppi_pa_r );
+ DECLARE_WRITE8_MEMBER( ppi_pc_w );
+ DECLARE_WRITE_LINE_MEMBER( fdc_intrq_w );
+
+ /* floppy state */
+ int m_fdc_irq;
+ int m_fdc_index;
+};
+
+#endif
diff --git a/src/mame/layout/sms1.lay b/src/mame/layout/sms1.lay
new file mode 100644
index 00000000000..3275da610d6
--- /dev/null
+++ b/src/mame/layout/sms1.lay
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<mamelayout version="2">
+ <view name="Main Screen Standard (4:3)">
+ <screen index="0">
+ <bounds left="0" top="0" right="4" bottom="3" />
+ </screen>
+ </view>
+
+ <view name="SegaScope Left LCD Standard (4:3)">
+ <screen index="1">
+ <bounds left="0" top="0" right="4" bottom="3" />
+ </screen>
+ </view>
+
+ <view name="SegaScope Right LCD Standard (4:3)">
+ <screen index="2">
+ <bounds left="0" top="0" right="4" bottom="3" />
+ </screen>
+ </view>
+
+ <view name="Main Screen Pixel Aspect (~scr0nativexaspect~:~scr0nativeyaspect~)">
+ <screen index="0">
+ <bounds left="0" top="0" right="~scr0width~" bottom="~scr0height~" />
+ </screen>
+ </view>
+
+ <view name="SegaScope Left LCD Aspect (~scr1nativexaspect~:~scr1nativeyaspect~)">
+ <screen index="1">
+ <bounds left="0" top="0" right="~scr1width~" bottom="~scr1height~" />
+ </screen>
+ </view>
+
+ <view name="SegaScope Right LCD Pixel Aspect (~scr1nativexaspect~:~scr1nativeyaspect~)">
+ <screen index="2">
+ <bounds left="0" top="0" right="~scr1width~" bottom="~scr1height~" />
+ </screen>
+ </view>
+
+ <view name="Main Screen">
+ <screen index="0">
+ <bounds x="0" y="0" width="4" height="3" />
+ </screen>
+ </view>
+
+ <view name="SegaScope 3-D Glasses">
+ <screen index="1">
+ <bounds x="0" y="0" width="4" height="3" />
+ </screen>
+ <screen index="2">
+ <bounds x="4.03" y="0" width="4" height="3" />
+ </screen>
+ </view>
+
+ <view name="SegaScope 3-D Glasses (Gapless)">
+ <screen index="1">
+ <bounds x="0" y="0" width="4" height="3" />
+ </screen>
+ <screen index="2">
+ <bounds x="4" y="0" width="4" height="3" />
+ </screen>
+ </view>
+</mamelayout>
diff --git a/src/mame/machine/ctronics.c b/src/mame/machine/ctronics.c
new file mode 100644
index 00000000000..8d6acbe7b64
--- /dev/null
+++ b/src/mame/machine/ctronics.c
@@ -0,0 +1,391 @@
+/***************************************************************************
+
+ Centronics printer interface
+
+***************************************************************************/
+
+#include "emu.h"
+#include "ctronics.h"
+#include "imagedev/printer.h"
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+static WRITE_LINE_DEVICE_HANDLER( centronics_printer_online );
+static TIMER_CALLBACK( ack_callback );
+static TIMER_CALLBACK( busy_callback );
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _centronics_state centronics_state;
+struct _centronics_state
+{
+ device_t *printer;
+
+ devcb_resolved_write_line out_ack_func;
+ devcb_resolved_write_line out_busy_func;
+ devcb_resolved_write_line out_not_busy_func;
+
+ int strobe;
+ int busy;
+ int ack;
+ int auto_fd;
+ int pe;
+ int fault;
+
+ UINT8 data;
+};
+
+
+/*****************************************************************************
+ INLINE FUNCTIONS
+*****************************************************************************/
+
+INLINE centronics_state *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == CENTRONICS);
+
+ return (centronics_state *)downcast<legacy_device_base *>(device)->token();
+}
+
+
+/*****************************************************************************
+ GLOBAL VARIABLES
+*****************************************************************************/
+
+const centronics_interface standard_centronics =
+{
+ FALSE,
+ DEVCB_NULL,
+ DEVCB_NULL,
+ DEVCB_NULL
+};
+
+
+/*****************************************************************************
+ PRINTER INTERFACE
+*****************************************************************************/
+
+static MACHINE_CONFIG_FRAGMENT( centronics )
+ MCFG_PRINTER_ADD("printer")
+ MCFG_PRINTER_ONLINE(centronics_printer_online)
+MACHINE_CONFIG_END
+
+
+/*****************************************************************************
+ DEVICE INTERFACE
+*****************************************************************************/
+
+static DEVICE_START( centronics )
+{
+ centronics_state *centronics = get_safe_token(device);
+ const centronics_interface *intf = (const centronics_interface *)device->static_config();
+
+ /* validate some basic stuff */
+ assert(device->static_config() != NULL);
+
+ /* set some initial values */
+ centronics->pe = FALSE;
+ centronics->fault = FALSE;
+ centronics->busy = TRUE;
+ centronics->strobe = TRUE;
+
+ /* get printer device */
+ centronics->printer = device->subdevice("printer");
+
+ /* resolve callbacks */
+ centronics->out_ack_func.resolve(intf->out_ack_func, *device);
+ centronics->out_busy_func.resolve(intf->out_busy_func, *device);
+ centronics->out_not_busy_func.resolve(intf->out_not_busy_func, *device);
+
+ /* register for state saving */
+ device->save_item(NAME(centronics->auto_fd));
+ device->save_item(NAME(centronics->strobe));
+ device->save_item(NAME(centronics->busy));
+ device->save_item(NAME(centronics->ack));
+ device->save_item(NAME(centronics->data));
+}
+
+static DEVICE_RESET( centronics )
+{
+}
+
+DEVICE_GET_INFO( centronics )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(centronics_state); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
+
+ /* --- the following bits of info are returned as pointers --- */
+ case DEVINFO_PTR_MACHINE_CONFIG: info->machine_config = MACHINE_CONFIG_NAME(centronics); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(centronics); break;
+ case DEVINFO_FCT_STOP: /* Nothing */ break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(centronics); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Centronics"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Centronics"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright MESS Team"); break;
+ }
+}
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ centronics_printer_online - callback that
+ sets us busy when the printer goes offline
+-------------------------------------------------*/
+
+void centronics_printer_online(device_t *device, int state)
+{
+ centronics_state *centronics = get_safe_token(device->owner());
+
+ /* when going online, set PE and FAULT high and BUSY low */
+ centronics->pe = state;
+ centronics->fault = state;
+ centronics->busy = !state;
+}
+
+
+static TIMER_CALLBACK( ack_callback )
+{
+ centronics_state *centronics = (centronics_state *)ptr;
+
+ /* signal change */
+ centronics->out_ack_func(param);
+ centronics->ack = param;
+
+ if (param == FALSE)
+ {
+ /* data is now ready, output it */
+ printer_output(centronics->printer, centronics->data);
+
+ /* ready to receive more data, return BUSY to low */
+ machine.scheduler().timer_set(attotime::from_usec(7), FUNC(busy_callback), FALSE, ptr);
+ }
+}
+
+
+static TIMER_CALLBACK( busy_callback )
+{
+ centronics_state *centronics = (centronics_state *)ptr;
+
+ /* signal change */
+ centronics->out_busy_func(param);
+ centronics->out_not_busy_func(!param);
+ centronics->busy = param;
+
+ if (param == TRUE)
+ {
+ /* timer to turn ACK low to receive data */
+ machine.scheduler().timer_set(attotime::from_usec(10), FUNC(ack_callback), FALSE, ptr);
+ }
+ else
+ {
+ /* timer to return ACK to high state */
+ machine.scheduler().timer_set(attotime::from_usec(5), FUNC(ack_callback), TRUE, ptr);
+ }
+}
+
+
+/*-------------------------------------------------
+ centronics_data_w - write print data
+-------------------------------------------------*/
+
+WRITE8_DEVICE_HANDLER( centronics_data_w )
+{
+ centronics_state *centronics = get_safe_token(device);
+ centronics->data = data;
+}
+
+
+/*-------------------------------------------------
+ centronics_data_r - return current data
+-------------------------------------------------*/
+
+READ8_DEVICE_HANDLER( centronics_data_r )
+{
+ centronics_state *centronics = get_safe_token(device);
+ return centronics->data;
+}
+
+
+/*-------------------------------------------------
+ set_line - helper to set individual bits
+-------------------------------------------------*/
+
+static void set_line(device_t *device, int line, int state)
+{
+ centronics_state *centronics = get_safe_token(device);
+
+ if (state)
+ centronics->data |= 1 << line;
+ else
+ centronics->data &= ~(1 << line);
+}
+
+
+/*-------------------------------------------------
+ centronics_dx_w - write line dx print data
+-------------------------------------------------*/
+
+WRITE_LINE_DEVICE_HANDLER( centronics_d0_w ) { set_line(device, 0, state); }
+WRITE_LINE_DEVICE_HANDLER( centronics_d1_w ) { set_line(device, 1, state); }
+WRITE_LINE_DEVICE_HANDLER( centronics_d2_w ) { set_line(device, 2, state); }
+WRITE_LINE_DEVICE_HANDLER( centronics_d3_w ) { set_line(device, 3, state); }
+WRITE_LINE_DEVICE_HANDLER( centronics_d4_w ) { set_line(device, 4, state); }
+WRITE_LINE_DEVICE_HANDLER( centronics_d5_w ) { set_line(device, 5, state); }
+WRITE_LINE_DEVICE_HANDLER( centronics_d6_w ) { set_line(device, 6, state); }
+WRITE_LINE_DEVICE_HANDLER( centronics_d7_w ) { set_line(device, 7, state); }
+
+
+/*-------------------------------------------------
+ centronics_strobe_w - signal that data is
+ ready
+-------------------------------------------------*/
+
+WRITE_LINE_DEVICE_HANDLER( centronics_strobe_w )
+{
+ centronics_state *centronics = get_safe_token(device);
+
+ /* look for a high -> low transition */
+ if (centronics->strobe == TRUE && state == FALSE && centronics->busy == FALSE)
+ {
+ /* STROBE has gone low, data is ready */
+ device->machine().scheduler().timer_set(attotime::zero, FUNC(busy_callback), TRUE, centronics);
+ }
+
+ centronics->strobe = state;
+}
+
+
+/*-------------------------------------------------
+ centronics_prime_w - initialize and reset
+ printer (centronics mode)
+-------------------------------------------------*/
+
+WRITE_LINE_DEVICE_HANDLER( centronics_prime_w )
+{
+ assert(((const centronics_interface *)device->static_config())->is_ibmpc == FALSE);
+
+ /* reset printer if line is low */
+ if (state == FALSE)
+ DEVICE_RESET_CALL( centronics );
+}
+
+
+/*-------------------------------------------------
+ centronics_init_w - initialize and reset
+ printer (ibm mode)
+-------------------------------------------------*/
+
+WRITE_LINE_DEVICE_HANDLER( centronics_init_w )
+{
+ assert(((const centronics_interface *)device->static_config())->is_ibmpc == TRUE);
+
+ /* reset printer if line is low */
+ if (state == FALSE)
+ DEVICE_RESET_CALL( centronics );
+}
+
+
+/*-------------------------------------------------
+ centronics_autofeed_w - auto line feed
+-------------------------------------------------*/
+
+WRITE_LINE_DEVICE_HANDLER( centronics_autofeed_w )
+{
+ centronics_state *centronics = get_safe_token(device);
+ assert(((const centronics_interface *)device->static_config())->is_ibmpc == TRUE);
+
+ centronics->auto_fd = state;
+}
+
+
+/*-------------------------------------------------
+ centronics_ack_r - return the state of the
+ ack line
+-------------------------------------------------*/
+
+READ_LINE_DEVICE_HANDLER( centronics_ack_r )
+{
+ centronics_state *centronics = get_safe_token(device);
+ return centronics->ack;
+}
+
+
+/*-------------------------------------------------
+ centronics_busy_r - return the state of the
+ busy line
+-------------------------------------------------*/
+
+READ_LINE_DEVICE_HANDLER( centronics_busy_r )
+{
+ centronics_state *centronics = get_safe_token(device);
+ return centronics->busy;
+}
+
+
+/*-------------------------------------------------
+ centronics_pe_r - return the state of the
+ pe line
+-------------------------------------------------*/
+
+READ_LINE_DEVICE_HANDLER( centronics_pe_r )
+{
+ centronics_state *centronics = get_safe_token(device);
+ return centronics->pe;
+}
+
+
+/*-------------------------------------------------
+ centronics_not_busy_r - return the state of the
+ not busy line
+-------------------------------------------------*/
+
+READ_LINE_DEVICE_HANDLER( centronics_not_busy_r )
+{
+ centronics_state *centronics = get_safe_token(device);
+ return !centronics->busy;
+}
+
+
+/*-------------------------------------------------
+ centronics_vcc_r - return the state of the
+ vcc line
+-------------------------------------------------*/
+
+READ_LINE_DEVICE_HANDLER( centronics_vcc_r )
+{
+ /* always return high */
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ centronics_fault_r - return the state of the
+ fault line
+-------------------------------------------------*/
+
+READ_LINE_DEVICE_HANDLER( centronics_fault_r )
+{
+ centronics_state *centronics = get_safe_token(device);
+ return centronics->fault;
+}
+
+DEFINE_LEGACY_DEVICE(CENTRONICS, centronics);
diff --git a/src/mame/machine/ctronics.h b/src/mame/machine/ctronics.h
new file mode 100644
index 00000000000..a5d3bdf8e1a
--- /dev/null
+++ b/src/mame/machine/ctronics.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+
+ Centronics printer interface
+
+***************************************************************************/
+
+#ifndef __CTRONICS_H__
+#define __CTRONICS_H__
+
+#include "devcb.h"
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _centronics_interface centronics_interface;
+struct _centronics_interface
+{
+ int is_ibmpc;
+
+ devcb_write_line out_ack_func;
+ devcb_write_line out_busy_func;
+ devcb_write_line out_not_busy_func;
+};
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+WRITE8_DEVICE_HANDLER( centronics_data_w );
+READ8_DEVICE_HANDLER( centronics_data_r );
+
+/* access to the individual bits */
+WRITE_LINE_DEVICE_HANDLER( centronics_d0_w );
+WRITE_LINE_DEVICE_HANDLER( centronics_d1_w );
+WRITE_LINE_DEVICE_HANDLER( centronics_d2_w );
+WRITE_LINE_DEVICE_HANDLER( centronics_d3_w );
+WRITE_LINE_DEVICE_HANDLER( centronics_d4_w );
+WRITE_LINE_DEVICE_HANDLER( centronics_d5_w );
+WRITE_LINE_DEVICE_HANDLER( centronics_d6_w );
+WRITE_LINE_DEVICE_HANDLER( centronics_d7_w );
+
+WRITE_LINE_DEVICE_HANDLER( centronics_strobe_w );
+WRITE_LINE_DEVICE_HANDLER( centronics_prime_w );
+WRITE_LINE_DEVICE_HANDLER( centronics_init_w );
+WRITE_LINE_DEVICE_HANDLER( centronics_autofeed_w );
+
+READ_LINE_DEVICE_HANDLER( centronics_ack_r );
+READ_LINE_DEVICE_HANDLER( centronics_busy_r );
+READ_LINE_DEVICE_HANDLER( centronics_pe_r );
+READ_LINE_DEVICE_HANDLER( centronics_not_busy_r );
+READ_LINE_DEVICE_HANDLER( centronics_vcc_r );
+READ_LINE_DEVICE_HANDLER( centronics_fault_r );
+
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+DECLARE_LEGACY_DEVICE(CENTRONICS, centronics);
+
+#define MCFG_CENTRONICS_ADD(_tag, _intf) \
+ MCFG_DEVICE_ADD(_tag, CENTRONICS, 0) \
+ MCFG_DEVICE_CONFIG(_intf)
+
+
+/***************************************************************************
+ DEFAULT INTERFACES
+***************************************************************************/
+
+extern const centronics_interface standard_centronics;
+
+
+#endif /* __CTRONICS_H__ */
diff --git a/src/mame/machine/dccons.c b/src/mame/machine/dccons.c
new file mode 100644
index 00000000000..60d27eef99c
--- /dev/null
+++ b/src/mame/machine/dccons.c
@@ -0,0 +1,725 @@
+/*
+
+ dc.c - Sega Dreamcast hardware
+
+ MESS (DC home console) hardware overrides (GD-ROM drive etc)
+
+ c230048 - 5 is written, want 6
+ c0d9d9e - where bad happens, from routine @ c0da260
+
+ c0d9d8e - R0 on return is the value to put in
+
+ cfffee0 - stack location when bad happens
+
+*/
+
+#include "emu.h"
+#include "cdrom.h"
+#include "debugger.h"
+#include "includes/dc.h"
+#include "cpu/sh4/sh4.h"
+#include "sound/aica.h"
+#include "includes/naomi.h"
+#include "machine/gdrom.h"
+#include "imagedev/chd_cd.h"
+
+#define ATAPI_CYCLES_PER_SECTOR (5000) // TBD for Dreamcast
+
+#define ATAPI_STAT_BSY 0x80
+#define ATAPI_STAT_DRDY 0x40
+#define ATAPI_STAT_DMARDDF 0x20
+#define ATAPI_STAT_SERVDSC 0x10
+#define ATAPI_STAT_DRQ 0x08
+#define ATAPI_STAT_CORR 0x04
+#define ATAPI_STAT_CHECK 0x01
+
+#define ATAPI_INTREASON_COMMAND 0x01
+#define ATAPI_INTREASON_IO 0x02
+#define ATAPI_INTREASON_RELEASE 0x04
+
+#define ATAPI_REG_DATA 0
+#define ATAPI_REG_FEATURES 1
+#define ATAPI_REG_INTREASON 2
+#define ATAPI_REG_SAMTAG 3
+#define ATAPI_REG_COUNTLOW 4
+#define ATAPI_REG_COUNTHIGH 5
+#define ATAPI_REG_DRIVESEL 6
+#define ATAPI_REG_CMDSTATUS 7
+#define ATAPI_REG_ERROR 16 // read-only ERROR (write is FEATURES)
+
+#define ATAPI_REG_MAX 24
+
+#define ATAPI_XFER_PIO 0x00
+#define ATAPI_XFER_PIO_FLOW 0x08
+#define ATAPI_XFER_MULTI_DMA 0x20
+#define ATAPI_XFER_ULTRA_DMA 0x40
+
+#define ATAPI_DATA_SIZE ( 64 * 1024 )
+
+static UINT8 *atapi_regs;
+static emu_timer *atapi_timer;
+static SCSIInstance *gdrom_device;
+static UINT8 *atapi_data;
+static int atapi_data_ptr, atapi_data_len, atapi_xferlen, atapi_xferbase, atapi_cdata_wait, atapi_xfermod;
+static UINT32 gdrom_alt_status;
+static UINT8 xfer_mode = ATAPI_XFER_PIO;
+
+#define MAX_TRANSFER_SIZE ( 63488 )
+
+extern UINT32 dc_sysctrl_regs[0x200/4];
+
+extern UINT32 g1bus_regs[0x100/4];
+
+static void gdrom_raise_irq(running_machine &machine)
+{
+ dc_sysctrl_regs[SB_ISTEXT] |= IST_EXT_GDROM;
+ dc_update_interrupt_status(machine);
+}
+
+static TIMER_CALLBACK( atapi_xfer_end )
+{
+ UINT8 sector_buffer[ 4096 ];
+
+ atapi_timer->adjust(attotime::never);
+
+ printf("atapi_xfer_end atapi_xferlen = %d, atapi_xfermod=%d\n", atapi_xfermod, atapi_xferlen );
+
+ mame_printf_debug("ATAPI: xfer_end. xferlen = %d, atapi_xfermod = %d\n", atapi_xferlen, atapi_xfermod);
+
+ while (atapi_xferlen > 0 )
+ {
+ struct sh4_ddt_dma ddtdata;
+
+ // get a sector from the SCSI device
+ SCSIReadData( gdrom_device, sector_buffer, 2048 );
+
+ atapi_xferlen -= 2048;
+
+ // perform the DMA
+ ddtdata.destination = atapi_xferbase; // destination address
+ ddtdata.length = 2048/4;
+ ddtdata.size = 4;
+ ddtdata.buffer = sector_buffer;
+ ddtdata.direction=1; // 0 source to buffer, 1 buffer to destination
+ ddtdata.channel= -1; // not used
+ ddtdata.mode= -1; // copy from/to buffer
+ printf("ATAPI: DMA one sector to %x, %x remaining\n", atapi_xferbase, atapi_xferlen);
+ sh4_dma_ddt(machine.device("maincpu"), &ddtdata);
+
+ atapi_xferbase += 2048;
+ }
+
+ if (atapi_xfermod > MAX_TRANSFER_SIZE)
+ {
+ atapi_xferlen = MAX_TRANSFER_SIZE;
+ atapi_xfermod = atapi_xfermod - MAX_TRANSFER_SIZE;
+ }
+ else
+ {
+ atapi_xferlen = atapi_xfermod;
+ atapi_xfermod = 0;
+ }
+
+ if (atapi_xferlen > 0)
+ {
+ printf("ATAPI: starting next piece of multi-part transfer\n");
+ atapi_regs[ATAPI_REG_COUNTLOW] = atapi_xferlen & 0xff;
+ atapi_regs[ATAPI_REG_COUNTHIGH] = (atapi_xferlen>>8)&0xff;
+
+ atapi_timer->adjust(machine.device<cpu_device>("maincpu")->cycles_to_attotime((ATAPI_CYCLES_PER_SECTOR * (atapi_xferlen/2048))));
+ }
+ else
+ {
+ printf("ATAPI: Transfer completed, dropping DRQ\n");
+ atapi_regs[ATAPI_REG_CMDSTATUS] = ATAPI_STAT_DRDY;
+ gdrom_alt_status = ATAPI_STAT_DRDY;
+ atapi_regs[ATAPI_REG_INTREASON] = ATAPI_INTREASON_IO | ATAPI_INTREASON_COMMAND;
+
+ g1bus_regs[SB_GDST]=0;
+ dc_sysctrl_regs[SB_ISTNRM] |= IST_DMA_GDROM;
+ dc_update_interrupt_status(machine);
+ }
+
+ gdrom_raise_irq(machine);
+
+ printf( "atapi_xfer_end: %d %d\n", atapi_xferlen, atapi_xfermod );
+}
+
+static READ32_HANDLER( atapi_r )
+{
+ running_machine &machine = space->machine();
+ int reg, data;
+
+ if (mem_mask == 0x0000ffff) // word-wide command read
+ {
+// mame_printf_debug("ATAPI: packet read = %04x\n", atapi_data[atapi_data_ptr]);
+
+ // assert IRQ and drop DRQ
+ if (atapi_data_ptr == 0 && atapi_data_len == 0)
+ {
+ // get the data from the device
+ if( atapi_xferlen > 0 )
+ {
+ SCSIReadData( gdrom_device, atapi_data, atapi_xferlen );
+ atapi_data_len = atapi_xferlen;
+ }
+
+ if (atapi_xfermod > MAX_TRANSFER_SIZE)
+ {
+ atapi_xferlen = MAX_TRANSFER_SIZE;
+ atapi_xfermod = atapi_xfermod - MAX_TRANSFER_SIZE;
+ }
+ else
+ {
+ atapi_xferlen = atapi_xfermod;
+ atapi_xfermod = 0;
+ }
+
+// printf( "atapi_r: atapi_xferlen=%d\n", atapi_xferlen );
+ if( atapi_xferlen != 0 )
+ {
+ atapi_regs[ATAPI_REG_CMDSTATUS] = ATAPI_STAT_DRQ | ATAPI_STAT_SERVDSC;
+ gdrom_alt_status = ATAPI_STAT_DRQ | ATAPI_STAT_SERVDSC;
+ atapi_regs[ATAPI_REG_INTREASON] = ATAPI_INTREASON_IO;
+ }
+ else
+ {
+ //mame_printf_debug("ATAPI: dropping DRQ\n");
+ atapi_regs[ATAPI_REG_CMDSTATUS] = 0;
+ gdrom_alt_status = 0;
+ atapi_regs[ATAPI_REG_INTREASON] = ATAPI_INTREASON_IO;
+ }
+
+ atapi_regs[ATAPI_REG_COUNTLOW] = atapi_xferlen & 0xff;
+ atapi_regs[ATAPI_REG_COUNTHIGH] = (atapi_xferlen>>8)&0xff;
+
+ gdrom_raise_irq(machine);
+ }
+
+ if( atapi_data_ptr < atapi_data_len )
+ {
+ data = atapi_data[atapi_data_ptr++];
+ data |= ( atapi_data[atapi_data_ptr++] << 8 );
+ if( atapi_data_ptr >= atapi_data_len )
+ {
+// printf( "atapi_r: read all bytes\n" );
+ atapi_data_ptr = 0;
+ atapi_data_len = 0;
+
+ if( atapi_xferlen == 0 )
+ {
+ atapi_regs[ATAPI_REG_CMDSTATUS] = 0;
+ gdrom_alt_status = 0;
+ atapi_regs[ATAPI_REG_INTREASON] = ATAPI_INTREASON_IO;
+ gdrom_raise_irq(machine);
+ }
+ }
+ }
+ else
+ {
+ data = 0;
+ }
+ }
+ else
+ {
+ reg = offset;
+
+ // get read-only side of read-only/write-only registers from elsewhere
+ if (reg == ATAPI_REG_FEATURES)
+ {
+ reg = ATAPI_REG_ERROR;
+ }
+ data = atapi_regs[reg];
+
+ #if 0
+ switch( reg )
+ {
+ case ATAPI_REG_DATA:
+ printf( "atapi_r: data=%02x\n", data );
+ break;
+ case ATAPI_REG_ERROR:
+ printf( "atapi_r: error=%02x\n", data );
+ break;
+ case ATAPI_REG_INTREASON:
+ printf( "atapi_r: intreason=%02x\n", data );
+ break;
+ case ATAPI_REG_SAMTAG:
+ printf( "atapi_r: samtag=%02x\n", data );
+ break;
+ case ATAPI_REG_COUNTLOW:
+ printf( "atapi_r: countlow=%02x\n", data );
+ break;
+ case ATAPI_REG_COUNTHIGH:
+ printf( "atapi_r: counthigh=%02x\n", data );
+ break;
+ case ATAPI_REG_DRIVESEL:
+ printf( "atapi_r: drivesel=%02x\n", data );
+ break;
+ case ATAPI_REG_CMDSTATUS:
+ printf( "atapi_r: cmdstatus=%02x\n", data );
+ break;
+ }
+ #endif
+
+ mame_printf_debug("ATAPI: read reg %d = %x (PC=%x)\n", reg, data, cpu_get_pc(&space->device()));
+ }
+
+// printf( "atapi_r( %08x, %08x ) %08x\n", offset, mem_mask, data );
+ return data;
+}
+
+static WRITE32_HANDLER( atapi_w )
+{
+ running_machine &machine = space->machine();
+ int reg;
+
+// printf( "atapi_w( %08x, %08x, %08x )\n", offset, mem_mask, data );
+
+ if (mem_mask == 0x0000ffff) // word-wide command write
+ {
+// printf("atapi_w: data=%04x\n", data );
+
+// printf("ATAPI: packet write %04x\n", data);
+ atapi_data[atapi_data_ptr++] = data & 0xff;
+ atapi_data[atapi_data_ptr++] = data >> 8;
+
+ if (atapi_cdata_wait)
+ {
+// printf("ATAPI: waiting, ptr %d wait %d\n", atapi_data_ptr, atapi_cdata_wait);
+ if (atapi_data_ptr == atapi_cdata_wait)
+ {
+ // send it to the device
+ SCSIWriteData( gdrom_device, atapi_data, atapi_cdata_wait );
+
+ // assert IRQ
+ gdrom_raise_irq(machine);
+
+ // not sure here, but clear DRQ at least?
+ atapi_regs[ATAPI_REG_CMDSTATUS] = 0;
+ }
+ }
+
+ else if ( atapi_data_ptr == 12 )
+ {
+ int phase;
+
+// printf("atapi_w: command %02x\n", atapi_data[0]&0xff );
+
+ // reset data pointer for reading SCSI results
+ atapi_data_ptr = 0;
+ atapi_data_len = 0;
+
+ // send it to the SCSI device
+ SCSISetCommand( gdrom_device, atapi_data, 12 );
+ SCSIExecCommand( gdrom_device, &atapi_xferlen );
+ SCSIGetPhase( gdrom_device, &phase );
+
+ if (atapi_xferlen != -1)
+ {
+ printf("ATAPI: SCSI command %02x returned %d bytes from the device\n", atapi_data[0]&0xff, atapi_xferlen);
+
+ // store the returned command length in the ATAPI regs, splitting into
+ // multiple transfers if necessary
+ atapi_xfermod = 0;
+ if (atapi_xferlen > MAX_TRANSFER_SIZE)
+ {
+ atapi_xfermod = atapi_xferlen - MAX_TRANSFER_SIZE;
+ atapi_xferlen = MAX_TRANSFER_SIZE;
+ }
+
+ atapi_regs[ATAPI_REG_COUNTLOW] = atapi_xferlen & 0xff;
+ atapi_regs[ATAPI_REG_COUNTHIGH] = (atapi_xferlen>>8)&0xff;
+
+ gdrom_alt_status = 0; // (I guess?)
+
+ if (atapi_xferlen == 0)
+ {
+ // if no data to return, set the registers properly
+ atapi_regs[ATAPI_REG_CMDSTATUS] = ATAPI_STAT_DRDY;
+ atapi_regs[ATAPI_REG_INTREASON] = ATAPI_INTREASON_IO|ATAPI_INTREASON_COMMAND;
+ }
+ else
+ {
+ // indicate data ready: set DRQ and DMA ready, and IO in INTREASON
+ if (atapi_regs[ATAPI_REG_FEATURES] & 0x01) // DMA feature
+ {
+ atapi_regs[ATAPI_REG_CMDSTATUS] = ATAPI_STAT_BSY | ATAPI_STAT_DRDY | ATAPI_STAT_SERVDSC;
+ }
+ else
+ {
+ atapi_regs[ATAPI_REG_CMDSTATUS] = ATAPI_STAT_DRQ | ATAPI_STAT_SERVDSC | ATAPI_STAT_DRQ;
+ }
+ atapi_regs[ATAPI_REG_INTREASON] = ATAPI_INTREASON_IO;
+ }
+
+ switch( phase )
+ {
+ case SCSI_PHASE_DATAOUT:
+ atapi_cdata_wait = atapi_xferlen;
+ break;
+ }
+
+ // perform special ATAPI processing of certain commands
+ switch (atapi_data[0]&0xff)
+ {
+ case 0x00: // BUS RESET / TEST UNIT READY
+ case 0xbb: // SET CDROM SPEED
+ atapi_regs[ATAPI_REG_CMDSTATUS] = 0;
+ break;
+
+ case 0x45: // PLAY
+ atapi_regs[ATAPI_REG_CMDSTATUS] = ATAPI_STAT_BSY;
+ atapi_timer->adjust( downcast<cpu_device *>(&space->device())->cycles_to_attotime(ATAPI_CYCLES_PER_SECTOR ) );
+ break;
+ }
+
+ // assert IRQ
+ gdrom_raise_irq(machine);
+ }
+ else
+ {
+ printf("ATAPI: SCSI device returned error!\n");
+
+ atapi_regs[ATAPI_REG_CMDSTATUS] = ATAPI_STAT_DRQ | ATAPI_STAT_CHECK;
+ atapi_regs[ATAPI_REG_ERROR] = 0x50; // sense key = ILLEGAL REQUEST
+ atapi_regs[ATAPI_REG_COUNTLOW] = 0;
+ atapi_regs[ATAPI_REG_COUNTHIGH] = 0;
+ }
+ }
+ }
+ else
+ {
+ reg = offset;
+#if 0
+ switch( reg )
+ {
+ case ATAPI_REG_DATA:
+ printf( "atapi_w: data=%02x\n", data );
+ break;
+ case ATAPI_REG_FEATURES:
+ printf( "atapi_w: features=%02x\n", data );
+ break;
+ case ATAPI_REG_INTREASON:
+ printf( "atapi_w: intreason=%02x\n", data );
+ break;
+ case ATAPI_REG_SAMTAG:
+ printf( "atapi_w: samtag=%02x\n", data );
+ break;
+ case ATAPI_REG_COUNTLOW:
+ printf( "atapi_w: countlow=%02x\n", data );
+ break;
+ case ATAPI_REG_COUNTHIGH:
+ printf( "atapi_w: counthigh=%02x\n", data );
+ break;
+ case ATAPI_REG_DRIVESEL:
+ printf( "atapi_w: drivesel=%02x\n", data );
+ break;
+ case ATAPI_REG_CMDSTATUS:
+ printf( "atapi_w: cmdstatus=%02x\n", data );
+ break;
+ }
+#endif
+ atapi_regs[reg] = data;
+// mame_printf_debug("ATAPI: reg %d = %x (offset %x mask %x PC=%x)\n", reg, data, offset, mem_mask, cpu_get_pc(&space->device()));
+
+ if (reg == ATAPI_REG_CMDSTATUS)
+ {
+ printf("ATAPI command %x issued! (PC=%x)\n", data, cpu_get_pc(&space->device()));
+
+ switch (data)
+ {
+ case 0xa0: // PACKET
+ atapi_regs[ATAPI_REG_CMDSTATUS] = ATAPI_STAT_DRQ;
+ gdrom_alt_status = ATAPI_STAT_DRQ;
+ atapi_regs[ATAPI_REG_INTREASON] = ATAPI_INTREASON_COMMAND;
+
+ atapi_data_ptr = 0;
+ atapi_data_len = 0;
+
+ /* we have no data */
+ atapi_xferlen = 0;
+ atapi_xfermod = 0;
+
+ atapi_cdata_wait = 0;
+ break;
+
+ case 0xa1: // IDENTIFY PACKET DEVICE
+ atapi_regs[ATAPI_REG_CMDSTATUS] = ATAPI_STAT_DRQ;
+ gdrom_alt_status = ATAPI_STAT_DRQ;
+
+ atapi_data_ptr = 0;
+ atapi_data_len = 512;
+
+ /* we have no data */
+ atapi_xferlen = 0;
+ atapi_xfermod = 0;
+
+ memset( atapi_data, 0, atapi_data_len );
+
+ atapi_data[ 0 ^ 1 ] = 0x86; // ATAPI device, cmd set 6 compliant, DRQ within 3 ms of PACKET command
+ atapi_data[ 1 ^ 1 ] = 0x00;
+
+ memset( &atapi_data[ 46 ], ' ', 8 );
+ atapi_data[ 46 ^ 1 ] = 'S';
+ atapi_data[ 47 ^ 1 ] = 'E';
+
+ memset( &atapi_data[ 54 ], ' ', 40 );
+ atapi_data[ 54 ^ 1 ] = 'C';
+ atapi_data[ 55 ^ 1 ] = 'D';
+ atapi_data[ 56 ^ 1 ] = '-';
+ atapi_data[ 57 ^ 1 ] = 'R';
+ atapi_data[ 58 ^ 1 ] = 'O';
+ atapi_data[ 59 ^ 1 ] = 'M';
+ atapi_data[ 60 ^ 1 ] = ' ';
+ atapi_data[ 61 ^ 1 ] = 'D';
+ atapi_data[ 62 ^ 1 ] = 'R';
+ atapi_data[ 63 ^ 1 ] = 'I';
+ atapi_data[ 64 ^ 1 ] = 'V';
+ atapi_data[ 65 ^ 1 ] = 'E';
+ atapi_data[ 66 ^ 1 ] = ' ';
+ atapi_data[ 67 ^ 1 ] = ' ';
+ atapi_data[ 68 ^ 1 ] = ' ';
+ atapi_data[ 69 ^ 1 ] = ' ';
+ atapi_data[ 70 ^ 1 ] = '6';
+ atapi_data[ 71 ^ 1 ] = '.';
+ atapi_data[ 72 ^ 1 ] = '4';
+ atapi_data[ 73 ^ 1 ] = '2';
+
+ atapi_data[ 98 ^ 1 ] = 0x04; // IORDY may be disabled
+ atapi_data[ 99 ^ 1 ] = 0x00;
+
+ atapi_regs[ATAPI_REG_COUNTLOW] = 0;
+ atapi_regs[ATAPI_REG_COUNTHIGH] = 2;
+
+ gdrom_raise_irq(space->machine());
+ break;
+
+ case 0xef: // SET FEATURES
+ // set xfer mode?
+ if (atapi_regs[ATAPI_REG_FEATURES] == 0x03)
+ {
+ printf("Set transfer mode to %x\n", atapi_regs[ATAPI_REG_COUNTLOW] & 0xf8);
+ xfer_mode = atapi_regs[ATAPI_REG_COUNTLOW] & 0xf8;
+ }
+ else
+ {
+ printf("ATAPI: Unknown set features %x\n", atapi_regs[ATAPI_REG_FEATURES]);
+ }
+
+ atapi_regs[ATAPI_REG_CMDSTATUS] = 0;
+ gdrom_alt_status = 0; // is this correct?
+
+ atapi_data_ptr = 0;
+ atapi_data_len = 0;
+
+ gdrom_raise_irq(space->machine());
+ break;
+
+ default:
+ mame_printf_debug("ATAPI: Unknown IDE command %x\n", data);
+ break;
+ }
+ }
+ }
+}
+
+static void dreamcast_atapi_exit(running_machine& machine)
+{
+ if (gdrom_device != NULL)
+ {
+ SCSIDeleteInstance( gdrom_device );
+ }
+}
+
+void dreamcast_atapi_init(running_machine &machine)
+{
+ atapi_regs = auto_alloc_array(machine, UINT8, ATAPI_REG_MAX );
+ memset(atapi_regs, 0, sizeof(atapi_regs));
+
+ atapi_regs[ATAPI_REG_CMDSTATUS] = 0;
+ atapi_regs[ATAPI_REG_ERROR] = 1;
+ atapi_regs[ATAPI_REG_COUNTLOW] = 0x14;
+ atapi_regs[ATAPI_REG_COUNTHIGH] = 0xeb;
+
+ atapi_data_ptr = 0;
+ atapi_data_len = 0;
+ atapi_cdata_wait = 0;
+
+ atapi_timer = machine.scheduler().timer_alloc(FUNC(atapi_xfer_end));
+ atapi_timer->adjust(attotime::never);
+
+ gdrom_device = NULL;
+
+ machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(dreamcast_atapi_exit),&machine));
+
+ atapi_data = auto_alloc_array(machine, UINT8, ATAPI_DATA_SIZE );
+
+ state_save_register_global_pointer(machine, atapi_regs, ATAPI_REG_MAX );
+ state_save_register_global_pointer(machine, atapi_data, ATAPI_DATA_SIZE / 2 );
+ state_save_register_global(machine, atapi_data_ptr );
+ state_save_register_global(machine, atapi_data_len );
+ state_save_register_global(machine, atapi_xferlen );
+ state_save_register_global(machine, atapi_xferbase );
+ state_save_register_global(machine, atapi_cdata_wait );
+ state_save_register_global(machine, atapi_xfermod );
+}
+
+void dreamcast_atapi_reset(running_machine &machine)
+{
+ atapi_regs[ATAPI_REG_CMDSTATUS] = 0;
+ atapi_regs[ATAPI_REG_ERROR] = 1;
+ atapi_regs[ATAPI_REG_COUNTLOW] = 0x14;
+ atapi_regs[ATAPI_REG_COUNTHIGH] = 0xeb;
+
+ atapi_data_ptr = 0;
+ atapi_data_len = 0;
+ atapi_cdata_wait = 0;
+
+ atapi_xferlen = 0;
+ atapi_xfermod = 0;
+
+ if ( cd_get_cdrom_file(machine.device( "cdrom" )) != NULL )
+ {
+ SCSIAllocInstance( machine, &SCSIClassGDROM, &gdrom_device, "cdrom" );
+ }
+ else
+ {
+ gdrom_device = NULL;
+ }
+}
+
+/*
+
+ GDROM regsters:
+
+ 5f7018: alternate status/device control
+ 5f7080: data
+ 5f7084: error/features
+ 5f7088: interrupt reason/sector count
+ 5f708c: sector number
+ 5f7090: byte control low
+ 5f7094: byte control high
+ 5f7098: drive select
+ 5f709c: status/command
+
+c002910 - ATAPI packet writes
+c002796 - aux status read after that
+c000776 - DMA triggered to c008000
+
+*/
+
+READ64_HANDLER( dc_mess_gdrom_r )
+{
+ UINT32 off;
+
+ if ((int)~mem_mask & 1)
+ {
+ off=(offset << 1) | 1;
+ }
+ else
+ {
+ off=offset << 1;
+ }
+
+// printf("gdrom_r: @ %x (off %x), mask %llx (PC %x)\n", offset, off, mem_mask, cpu_get_pc(&space->device()));
+
+ if (offset == 3)
+ {
+ return gdrom_alt_status;
+ }
+ else if (off >= 0x20)
+ {
+ return atapi_r(space, off-0x20, 0xff);
+ }
+
+ return 0;
+}
+
+WRITE64_HANDLER( dc_mess_gdrom_w )
+{
+ UINT32 dat,off;
+
+ if ((int)~mem_mask & 1)
+ {
+ dat=(UINT32)(data >> 32);
+ off=(offset << 1) | 1;
+ }
+ else
+ {
+ dat=(UINT32)data;
+ off=offset << 1;
+ }
+
+// printf("GDROM: [%08x=%x]write %llx to %x, mask %llx (PC %x)\n", 0x5f7000+off*4, dat, data, offset, mem_mask, cpu_get_pc(&space->device()));
+
+ if (off >= 0x20)
+ {
+ atapi_w(space, off-0x20, dat, (UINT32)mem_mask);
+ }
+}
+
+// register decode helpers
+
+// this accepts only 32-bit accesses
+INLINE int decode_reg32_64(running_machine &machine, UINT32 offset, UINT64 mem_mask, UINT64 *shift)
+{
+ int reg = offset * 2;
+
+ *shift = 0;
+
+ // non 32-bit accesses have not yet been seen here, we need to know when they are
+ if ((mem_mask != U64(0xffffffff00000000)) && (mem_mask != U64(0x00000000ffffffff)))
+ {
+ mame_printf_verbose("%s:Wrong mask!\n", machine.describe_context());
+// debugger_break(machine);
+ }
+
+ if (mem_mask == U64(0xffffffff00000000))
+ {
+ reg++;
+ *shift = 32;
+ }
+
+ return reg;
+}
+
+READ64_HANDLER( dc_mess_g1_ctrl_r )
+{
+ int reg;
+ UINT64 shift;
+
+ reg = decode_reg32_64(space->machine(), offset, mem_mask, &shift);
+ mame_printf_verbose("G1CTRL: Unmapped read %08x\n", 0x5f7400+reg*4);
+ return (UINT64)g1bus_regs[reg] << shift;
+}
+
+WRITE64_HANDLER( dc_mess_g1_ctrl_w )
+{
+ int reg;
+ UINT64 shift;
+ UINT32 old,dat;
+
+ reg = decode_reg32_64(space->machine(), offset, mem_mask, &shift);
+ dat = (UINT32)(data >> shift);
+ old = g1bus_regs[reg];
+
+ g1bus_regs[reg] = dat; // 5f7400+reg*4=dat
+ mame_printf_verbose("G1CTRL: [%08x=%x] write %" I64FMT "x to %x, mask %" I64FMT "x\n", 0x5f7400+reg*4, dat, data, offset, mem_mask);
+ switch (reg)
+ {
+ case SB_GDST:
+ if (dat & 1 && g1bus_regs[SB_GDEN] == 1) // 0 -> 1
+ {
+ if (g1bus_regs[SB_GDDIR] == 0)
+ {
+ printf("G1CTRL: unsupported transfer\n");
+ return;
+ }
+
+ atapi_xferbase = g1bus_regs[SB_GDSTAR];
+ atapi_timer->adjust(space->machine().device<cpu_device>("maincpu")->cycles_to_attotime((ATAPI_CYCLES_PER_SECTOR * (atapi_xferlen/2048))));
+ }
+ break;
+ }
+}
+
diff --git a/src/mame/machine/gdrom.c b/src/mame/machine/gdrom.c
new file mode 100644
index 00000000000..31935daa5d1
--- /dev/null
+++ b/src/mame/machine/gdrom.c
@@ -0,0 +1,874 @@
+/***************************************************************************
+
+ gdrom.c - Implementation of the Sega GD-ROM device
+
+***************************************************************************/
+
+#include "emu.h"
+#include "machine/scsidev.h"
+#include "cdrom.h"
+#include "sound/cdda.h"
+#include "imagedev/chd_cd.h"
+#include "gdrom.h"
+
+typedef struct
+{
+ UINT32 lba;
+ UINT32 blocks;
+ UINT32 last_lba;
+ UINT32 bytes_per_sector;
+ UINT32 num_subblocks;
+ UINT32 cur_subblock;
+ UINT32 play_err_flag;
+ UINT32 read_type; // for command 0x30 only
+ UINT32 data_select; // for command 0x30 only
+ cdrom_file *cdrom;
+ bool is_file;
+} SCSIGd;
+
+static const UINT8 GDROM_Cmd11_Reply[32] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xB4, 0x19, 0x00, 0x00, 0x08, 0x53, 0x45, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x52, 0x65, 0x76, 0x20, 0x36, 0x2E, 0x34, 0x32, 0x39, 0x39, 0x30, 0x33, 0x31, 0x36
+};
+
+static void phys_frame_to_msf(int phys_frame, int *m, int *s, int *f)
+{
+ *m = phys_frame / (60*75);
+ phys_frame -= (*m * 60 * 75);
+ *s = phys_frame / 75;
+ *f = phys_frame % 75;
+}
+
+
+// scsicd_exec_command
+//
+// Execute a SCSI command.
+
+static int scsicd_exec_command( SCSIInstance *scsiInstance, UINT8 *statusCode )
+{
+ UINT8 *command;
+ int commandLength;
+ SCSIGd *our_this = (SCSIGd *)SCSIThis( &SCSIClassGDROM, scsiInstance );
+
+ cdrom_file *cdrom = our_this->cdrom;
+ device_t *cdda;
+ int trk;
+
+ SCSIGetCommand( scsiInstance, &command, &commandLength );
+
+ switch ( command[0] )
+ {
+
+
+ case 0x03: // REQUEST SENSE
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return SCSILengthFromUINT8( &command[ 4 ] );
+
+ case 0x11: // REQ_MODE
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ printf("REQ_MODE %02x %02x %02x %02x %02x %02x\n",
+ command[0], command[1],
+ command[2], command[3],
+ command[4], command[5]);
+// if (SCSILengthFromUINT8( &command[ 4 ] ) < 32) return -1;
+ return 32; //SCSILengthFromUINT8( &command[ 4 ] );
+
+ case 0x12: // INQUIRY
+ logerror("GDROM: REQUEST SENSE\n");
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return SCSILengthFromUINT8( &command[ 4 ] );
+
+ case 0x15: // MODE SELECT(6)
+ logerror("GDROM: MODE SELECT(6) length %x control %x\n", command[4], command[5]);
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAOUT );
+ return SCSILengthFromUINT8( &command[ 4 ] );
+
+ case 0x1a: // MODE SENSE(6)
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return SCSILengthFromUINT8( &command[ 4 ] );
+
+ case 0x1b: // START STOP UNIT
+ cdda = cdda_from_cdrom(scsiInstance->machine(), cdrom);
+ if (cdda != NULL)
+ {
+ cdda_stop_audio(cdda);
+ }
+ SCSISetPhase( scsiInstance, SCSI_PHASE_STATUS );
+ return 0;
+
+ case 0x1e: // PREVENT ALLOW MEDIUM REMOVAL
+ SCSISetPhase( scsiInstance, SCSI_PHASE_STATUS );
+ return 0;
+
+ case 0x25: // READ CAPACITY
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return 8;
+
+ case 0x28: // READ(10)
+
+ our_this->lba = command[2]<<24 | command[3]<<16 | command[4]<<8 | command[5];
+ our_this->blocks = SCSILengthFromUINT16( &command[7] );
+
+ logerror("GDROM: READ(10) at LBA %x for %d blocks (%d bytes)\n", our_this->lba, our_this->blocks, our_this->blocks * our_this->bytes_per_sector);
+
+ if (our_this->num_subblocks > 1)
+ {
+ our_this->cur_subblock = our_this->lba % our_this->num_subblocks;
+ our_this->lba /= our_this->num_subblocks;
+ }
+ else
+ {
+ our_this->cur_subblock = 0;
+ }
+
+ cdda = cdda_from_cdrom(scsiInstance->machine(), cdrom);
+ if (cdda != NULL)
+ {
+ cdda_stop_audio(cdda);
+ }
+
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return our_this->blocks * our_this->bytes_per_sector;
+
+ case 0x30: // CD_READ
+ if (command[1] & 1)
+ {
+ fatalerror("GDROM: MSF mode used for CD_READ, unsupported");
+ return 0;
+ }
+ else
+ {
+ our_this->lba = (command[2]<<16 | command[3]<<8 | command[4]) - 150;
+ our_this->blocks = command[8]<<16 | command[9]<<8 | command[10];
+
+ our_this->read_type = (command[1] >> 1) & 7;
+ our_this->data_select = (command[1]>>4) & 0xf;
+
+ if (our_this->read_type != 2) // mode 1
+ {
+ fatalerror("GDROM: Unhandled read_type %d", our_this->read_type);
+ }
+
+ if (our_this->data_select != 2) // just sector data
+ {
+ fatalerror("GDROM: Unhandled data_select %d", our_this->data_select);
+ }
+
+ printf("GDROM: CD_READ at LBA %x for %d blocks (%d bytes, read type %d, data select %d)\n", our_this->lba, our_this->blocks, our_this->blocks * our_this->bytes_per_sector, our_this->read_type, our_this->data_select);
+
+ if (our_this->num_subblocks > 1)
+ {
+ our_this->cur_subblock = our_this->lba % our_this->num_subblocks;
+ our_this->lba /= our_this->num_subblocks;
+ }
+ else
+ {
+ our_this->cur_subblock = 0;
+ }
+
+ cdda = cdda_from_cdrom(scsiInstance->machine(), cdrom);
+ if (cdda != NULL)
+ {
+ cdda_stop_audio(cdda);
+ }
+
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return our_this->blocks * our_this->bytes_per_sector;
+ }
+
+ case 0x42: // READ SUB-CHANNEL
+// logerror("GDROM: READ SUB-CHANNEL type %d\n", command[3]);
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return SCSILengthFromUINT16( &command[ 7 ] );
+
+ case 0x43: // READ TOC
+ {
+ int start_trk = command[6];
+ int end_trk = cdrom_get_last_track(cdrom);
+ int length;
+ int allocation_length = SCSILengthFromUINT16( &command[ 7 ] );
+
+ if( start_trk == 0 )
+ {
+ start_trk = 1;
+ }
+ if( start_trk == 0xaa )
+ {
+ end_trk = start_trk;
+ }
+
+ length = 4 + ( 8 * ( ( end_trk - start_trk ) + 1 ) );
+ if( length > allocation_length )
+ {
+ length = allocation_length;
+ }
+ else if( length < 4 )
+ {
+ length = 4;
+ }
+
+ cdda = cdda_from_cdrom(scsiInstance->machine(), cdrom);
+ if (cdda != NULL)
+ {
+ cdda_stop_audio(cdda);
+ }
+
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return length;
+ }
+ case 0x45: // PLAY AUDIO(10)
+ our_this->lba = command[2]<<24 | command[3]<<16 | command[4]<<8 | command[5];
+ our_this->blocks = SCSILengthFromUINT16( &command[7] );
+
+ // special cases: lba of 0 means MSF of 00:02:00
+ if (our_this->lba == 0)
+ {
+ our_this->lba = 150;
+ }
+ else if (our_this->lba == 0xffffffff)
+ {
+ logerror("GDROM: play audio from current not implemented!\n");
+ }
+
+ logerror("GDROM: PLAY AUDIO(10) at LBA %x for %x blocks\n", our_this->lba, our_this->blocks);
+
+ trk = cdrom_get_track(cdrom, our_this->lba);
+
+ if (cdrom_get_track_type(cdrom, trk) == CD_TRACK_AUDIO)
+ {
+ our_this->play_err_flag = 0;
+ cdda = cdda_from_cdrom(scsiInstance->machine(), cdrom);
+ if (cdda != NULL)
+ cdda_start_audio(cdda, our_this->lba, our_this->blocks);
+ }
+ else
+ {
+ logerror("GDROM: track is NOT audio!\n");
+ our_this->play_err_flag = 1;
+ }
+
+ SCSISetPhase( scsiInstance, SCSI_PHASE_STATUS );
+ return 0;
+
+ case 0x48: // PLAY AUDIO TRACK/INDEX
+ // be careful: tracks here are zero-based, but the SCSI command
+ // uses the real CD track number which is 1-based!
+ our_this->lba = cdrom_get_track_start(cdrom, command[4]-1);
+ our_this->blocks = cdrom_get_track_start(cdrom, command[7]-1) - our_this->lba;
+ if (command[4] > command[7])
+ {
+ our_this->blocks = 0;
+ }
+
+ if (command[4] == command[7])
+ {
+ our_this->blocks = cdrom_get_track_start(cdrom, command[4]) - our_this->lba;
+ }
+
+ if (our_this->blocks && cdrom)
+ {
+ cdda = cdda_from_cdrom(scsiInstance->machine(), cdrom);
+ if (cdda != NULL)
+ cdda_start_audio(cdda, our_this->lba, our_this->blocks);
+ }
+
+ logerror("GDROM: PLAY AUDIO T/I: strk %d idx %d etrk %d idx %d frames %d\n", command[4], command[5], command[7], command[8], our_this->blocks);
+ SCSISetPhase( scsiInstance, SCSI_PHASE_STATUS );
+ return 0;
+
+ case 0x4b: // PAUSE/RESUME
+ if (cdrom)
+ {
+ cdda = cdda_from_cdrom(scsiInstance->machine(), cdrom);
+ if (cdda != NULL)
+ cdda_pause_audio(cdda, (command[8] & 0x01) ^ 0x01);
+ }
+
+ logerror("GDROM: PAUSE/RESUME: %s\n", command[8]&1 ? "RESUME" : "PAUSE");
+ SCSISetPhase( scsiInstance, SCSI_PHASE_STATUS );
+ return 0;
+
+ case 0x55: // MODE SELECT(10)
+ logerror("GDROM: MODE SELECT length %x control %x\n", command[7]<<8 | command[8], command[1]);
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAOUT );
+ return SCSILengthFromUINT16( &command[ 7 ] );
+
+ case 0x5a: // MODE SENSE(10)
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return SCSILengthFromUINT16( &command[ 7 ] );
+
+ case 0xa5: // PLAY AUDIO(12)
+ our_this->lba = command[2]<<24 | command[3]<<16 | command[4]<<8 | command[5];
+ our_this->blocks = command[6]<<24 | command[7]<<16 | command[8]<<8 | command[9];
+
+ // special cases: lba of 0 means MSF of 00:02:00
+ if (our_this->lba == 0)
+ {
+ our_this->lba = 150;
+ }
+ else if (our_this->lba == 0xffffffff)
+ {
+ logerror("GDROM: play audio from current not implemented!\n");
+ }
+
+ logerror("GDROM: PLAY AUDIO(12) at LBA %x for %x blocks\n", our_this->lba, our_this->blocks);
+
+ trk = cdrom_get_track(cdrom, our_this->lba);
+
+ if (cdrom_get_track_type(cdrom, trk) == CD_TRACK_AUDIO)
+ {
+ our_this->play_err_flag = 0;
+ cdda = cdda_from_cdrom(scsiInstance->machine(), cdrom);
+ if (cdda != NULL)
+ cdda_start_audio(cdda, our_this->lba, our_this->blocks);
+ }
+ else
+ {
+ logerror("GDROM: track is NOT audio!\n");
+ our_this->play_err_flag = 1;
+ }
+ SCSISetPhase( scsiInstance, SCSI_PHASE_STATUS );
+ return 0;
+
+ case 0xa8: // READ(12)
+ our_this->lba = command[2]<<24 | command[3]<<16 | command[4]<<8 | command[5];
+ our_this->blocks = command[7]<<16 | command[8]<<8 | command[9];
+
+ logerror("GDROM: READ(12) at LBA %x for %x blocks (%x bytes)\n", our_this->lba, our_this->blocks, our_this->blocks * our_this->bytes_per_sector);
+
+ if (our_this->num_subblocks > 1)
+ {
+ our_this->cur_subblock = our_this->lba % our_this->num_subblocks;
+ our_this->lba /= our_this->num_subblocks;
+ }
+ else
+ {
+ our_this->cur_subblock = 0;
+ }
+
+ cdda = cdda_from_cdrom(scsiInstance->machine(), cdrom);
+ if (cdda != NULL)
+ {
+ cdda_stop_audio(cdda);
+ }
+
+ SCSISetPhase( scsiInstance, SCSI_PHASE_DATAIN );
+ return our_this->blocks * our_this->bytes_per_sector;
+
+ case 0xbb: // SET CD SPEED
+ logerror("GDROM: SET CD SPEED to %d kbytes/sec.\n", command[2]<<8 | command[3]);
+ SCSISetPhase( scsiInstance, SCSI_PHASE_STATUS );
+ return 0;
+
+ default:
+ return SCSIBase( &SCSIClassGDROM, SCSIOP_EXEC_COMMAND, scsiInstance, 0, NULL );
+ }
+}
+
+// scsicd_read_data
+//
+// Read data from the device resulting from the execution of a command
+
+static void scsicd_read_data( SCSIInstance *scsiInstance, UINT8 *data, int dataLength )
+{
+ UINT8 *command;
+ int commandLength;
+ SCSIGd *our_this = (SCSIGd *)SCSIThis( &SCSIClassGDROM, scsiInstance );
+
+ int i;
+ UINT32 last_phys_frame;
+ cdrom_file *cdrom = our_this->cdrom;
+ UINT32 temp;
+ UINT8 tmp_buffer[2048];
+ device_t *cdda;
+
+ SCSIGetCommand( scsiInstance, &command, &commandLength );
+
+ switch ( command[0] )
+ {
+ case 0x03: // REQUEST SENSE
+ logerror("GDROM: Reading REQUEST SENSE data\n");
+
+ memset( data, 0, dataLength );
+
+ data[0] = 0x71; // deferred error
+
+ cdda = cdda_from_cdrom(scsiInstance->machine(), cdrom);
+ if (cdda != NULL && cdda_audio_active(cdda))
+ {
+ data[12] = 0x00;
+ data[13] = 0x11; // AUDIO PLAY OPERATION IN PROGRESS
+ }
+ else if (our_this->play_err_flag)
+ {
+ our_this->play_err_flag = 0;
+ data[12] = 0x64; // ILLEGAL MODE FOR THIS TRACK
+ data[13] = 0x00;
+ }
+ // (else 00/00 means no error to report)
+ break;
+
+ case 0x11: // REQ_MODE
+ printf("REQ_MODE: dataLength %d\n", dataLength);
+ memcpy(data, &GDROM_Cmd11_Reply[0], (dataLength >= 32) ? 32 : dataLength);
+ break;
+
+ case 0x12: // INQUIRY
+ memset( data, 0, dataLength );
+ data[0] = 0x05; // device is present, device is CD/DVD (MMC-3)
+ data[1] = 0x80; // media is removable
+ data[2] = 0x05; // device complies with SPC-3 standard
+ data[3] = 0x02; // response data format = SPC-3 standard
+ // some Konami games freak out if this isn't "Sony", so we'll lie
+ // this is the actual drive on my Nagano '98 board
+ strcpy((char *)&data[8], "Sony");
+ strcpy((char *)&data[16], "CDU-76S");
+ strcpy((char *)&data[32], "1.0");
+ break;
+
+ case 0x25: // READ CAPACITY
+ logerror("GDROM: READ CAPACITY\n");
+
+ temp = cdrom_get_track_start(cdrom, 0xaa);
+ temp--; // return the last used block on the disc
+
+ data[0] = (temp>>24) & 0xff;
+ data[1] = (temp>>16) & 0xff;
+ data[2] = (temp>>8) & 0xff;
+ data[3] = (temp & 0xff);
+ data[4] = 0;
+ data[5] = 0;
+ data[6] = (our_this->bytes_per_sector>>8)&0xff;
+ data[7] = (our_this->bytes_per_sector & 0xff);
+ break;
+
+ case 0x28: // READ(10)
+ case 0xa8: // READ(12)
+ logerror("GDROM: read %x dataLength, \n", dataLength);
+ if ((our_this->cdrom) && (our_this->blocks))
+ {
+ while (dataLength > 0)
+ {
+ if (!cdrom_read_data(our_this->cdrom, our_this->lba, tmp_buffer, CD_TRACK_MODE1))
+ {
+ logerror("GDROM: CD read error!\n");
+ }
+
+ logerror("True LBA: %d, buffer half: %d\n", our_this->lba, our_this->cur_subblock * our_this->bytes_per_sector);
+
+ memcpy(data, &tmp_buffer[our_this->cur_subblock * our_this->bytes_per_sector], our_this->bytes_per_sector);
+
+ our_this->cur_subblock++;
+ if (our_this->cur_subblock >= our_this->num_subblocks)
+ {
+ our_this->cur_subblock = 0;
+
+ our_this->lba++;
+ our_this->blocks--;
+ }
+
+ our_this->last_lba = our_this->lba;
+ dataLength -= our_this->bytes_per_sector;
+ data += our_this->bytes_per_sector;
+ }
+ }
+ break;
+
+ case 0x30: // CD_READ
+ logerror("GDROM: read %x dataLength, \n", dataLength);
+ if ((our_this->cdrom) && (our_this->blocks))
+ {
+ while (dataLength > 0)
+ {
+ if (!cdrom_read_data(our_this->cdrom, our_this->lba, tmp_buffer, CD_TRACK_MODE1))
+ {
+ logerror("GDROM: CD read error!\n");
+ }
+
+ logerror("True LBA: %d, buffer half: %d\n", our_this->lba, our_this->cur_subblock * our_this->bytes_per_sector);
+
+ memcpy(data, &tmp_buffer[our_this->cur_subblock * our_this->bytes_per_sector], our_this->bytes_per_sector);
+
+ our_this->cur_subblock++;
+ if (our_this->cur_subblock >= our_this->num_subblocks)
+ {
+ our_this->cur_subblock = 0;
+
+ our_this->lba++;
+ our_this->blocks--;
+ }
+
+ our_this->last_lba = our_this->lba;
+ dataLength -= our_this->bytes_per_sector;
+ data += our_this->bytes_per_sector;
+ }
+ }
+
+ case 0x42: // READ SUB-CHANNEL
+ switch (command[3])
+ {
+ case 1: // return current position
+ {
+ int audio_active;
+ int msf;
+
+ if (!cdrom)
+ {
+ return;
+ }
+
+ logerror("GDROM: READ SUB-CHANNEL Time = %x, SUBQ = %x\n", command[1], command[2]);
+
+ msf = command[1] & 0x2;
+
+ cdda = cdda_from_cdrom(scsiInstance->machine(), cdrom);
+ audio_active = cdda != NULL && cdda_audio_active(cdda);
+ if (audio_active)
+ {
+ if (cdda_audio_paused(cdda))
+ {
+ data[1] = 0x12; // audio is paused
+ }
+ else
+ {
+ data[1] = 0x11; // audio in progress
+ }
+ }
+ else
+ {
+ if (cdda != NULL && cdda_audio_ended(cdda))
+ {
+ data[1] = 0x13; // ended successfully
+ }
+ else
+ {
+// data[1] = 0x14; // stopped due to error
+ data[1] = 0x15; // No current audio status to return
+ }
+ }
+
+ // if audio is playing, get the latest LBA from the CDROM layer
+ if (audio_active)
+ {
+ our_this->last_lba = cdda_get_audio_lba(cdda);
+ }
+ else
+ {
+ our_this->last_lba = 0;
+ }
+
+ data[2] = 0;
+ data[3] = 12; // data length
+ data[4] = 0x01; // sub-channel format code
+ data[5] = 0x10 | (audio_active ? 0 : 4);
+ data[6] = cdrom_get_track(cdrom, our_this->last_lba) + 1; // track
+ data[7] = 0; // index
+
+ last_phys_frame = our_this->last_lba;
+
+ if (msf)
+ {
+ int m,s,f;
+ phys_frame_to_msf(last_phys_frame, &m, &s, &f);
+ data[8] = 0;
+ data[9] = m;
+ data[10] = s;
+ data[11] = f;
+ }
+ else
+ {
+ data[8] = last_phys_frame>>24;
+ data[9] = (last_phys_frame>>16)&0xff;
+ data[10] = (last_phys_frame>>8)&0xff;
+ data[11] = last_phys_frame&0xff;
+ }
+
+ last_phys_frame -= cdrom_get_track_start(cdrom, data[6] - 1);
+
+ if (msf)
+ {
+ int m,s,f;
+ phys_frame_to_msf(last_phys_frame, &m, &s, &f);
+ data[12] = 0;
+ data[13] = m;
+ data[14] = s;
+ data[15] = f;
+ }
+ else
+ {
+ data[12] = last_phys_frame>>24;
+ data[13] = (last_phys_frame>>16)&0xff;
+ data[14] = (last_phys_frame>>8)&0xff;
+ data[15] = last_phys_frame&0xff;
+ }
+ break;
+ }
+ default:
+ logerror("GDROM: Unknown subchannel type %d requested\n", command[3]);
+ }
+ break;
+
+ case 0x43: // READ TOC
+ /*
+ Track numbers are problematic here: 0 = lead-in, 0xaa = lead-out.
+ That makes sense in terms of how real-world CDs are referred to, but
+ our internal routines for tracks use "0" as track 1. That probably
+ should be fixed...
+ */
+ logerror("GDROM: READ TOC, format = %d time=%d\n", command[2]&0xf,(command[1]>>1)&1);
+ switch (command[2] & 0x0f)
+ {
+ case 0: // normal
+ {
+ int start_trk;
+ int end_trk;
+ int len;
+ int in_len;
+ int dptr;
+ UINT32 tstart;
+
+ start_trk = command[6];
+ if( start_trk == 0 )
+ {
+ start_trk = 1;
+ }
+
+ end_trk = cdrom_get_last_track(cdrom);
+ len = (end_trk * 8) + 2;
+
+ // the returned TOC DATA LENGTH must be the full amount,
+ // regardless of how much we're able to pass back due to in_len
+ dptr = 0;
+ data[dptr++] = (len>>8) & 0xff;
+ data[dptr++] = (len & 0xff);
+ data[dptr++] = 1;
+ data[dptr++] = end_trk;
+
+ if( start_trk == 0xaa )
+ {
+ end_trk = 0xaa;
+ }
+
+ in_len = command[7]<<8 | command[8];
+
+ for (i = start_trk; i <= end_trk; i++)
+ {
+ int cdrom_track = i;
+ if( cdrom_track != 0xaa )
+ {
+ cdrom_track--;
+ }
+
+ if( dptr >= in_len )
+ {
+ break;
+ }
+
+ data[dptr++] = 0;
+ data[dptr++] = cdrom_get_adr_control(cdrom, cdrom_track);
+ data[dptr++] = i;
+ data[dptr++] = 0;
+
+ tstart = cdrom_get_track_start(cdrom, cdrom_track);
+ if ((command[1]&2)>>1)
+ tstart = lba_to_msf(tstart);
+ data[dptr++] = (tstart>>24) & 0xff;
+ data[dptr++] = (tstart>>16) & 0xff;
+ data[dptr++] = (tstart>>8) & 0xff;
+ data[dptr++] = (tstart & 0xff);
+ }
+ }
+ break;
+ default:
+ logerror("GDROM: Unhandled READ TOC format %d\n", command[2]&0xf);
+ break;
+ }
+ break;
+
+ case 0x1a: // MODE SENSE(6)
+ case 0x5a: // MODE SENSE(10)
+ logerror("GDROM: MODE SENSE page code = %x, PC = %x\n", command[2] & 0x3f, (command[2]&0xc0)>>6);
+
+ switch (command[2] & 0x3f)
+ {
+ case 0xe: // CD Audio control page
+ data[0] = 0x8e; // page E, parameter is savable
+ data[1] = 0x0e; // page length
+ data[2] = 0x04; // IMMED = 1, SOTC = 0
+ data[3] = data[4] = data[5] = data[6] = data[7] = 0; // reserved
+
+ // connect each audio channel to 1 output port
+ data[8] = 1;
+ data[10] = 2;
+ data[12] = 4;
+ data[14] = 8;
+
+ // indicate max volume
+ data[9] = data[11] = data[13] = data[15] = 0xff;
+ break;
+
+ default:
+ logerror("GDROM: MODE SENSE unknown page %x\n", command[2] & 0x3f);
+ break;
+ }
+ break;
+
+ default:
+ SCSIBase( &SCSIClassGDROM, SCSIOP_READ_DATA, scsiInstance, dataLength, data );
+ break;
+ }
+}
+
+// scsicd_write_data
+//
+// Write data to the CD-ROM device as part of the execution of a command
+
+static void scsicd_write_data( SCSIInstance *scsiInstance, UINT8 *data, int dataLength )
+{
+ UINT8 *command;
+ int commandLength;
+ SCSIGd *our_this = (SCSIGd *)SCSIThis( &SCSIClassGDROM, scsiInstance );
+ SCSIGetCommand( scsiInstance, &command, &commandLength );
+
+ switch (command[ 0 ])
+ {
+ case 0x15: // MODE SELECT(6)
+ case 0x55: // MODE SELECT(10)
+ logerror("GDROM: MODE SELECT page %x\n", data[0] & 0x3f);
+
+ switch (data[0] & 0x3f)
+ {
+ case 0x0: // vendor-specific
+ // check for SGI extension to force 512-byte blocks
+ if ((data[3] == 8) && (data[10] == 2))
+ {
+ logerror("GDROM: Experimental SGI 512-byte block extension enabled\n");
+
+ our_this->bytes_per_sector = 512;
+ our_this->num_subblocks = 4;
+ }
+ else
+ {
+ logerror("GDROM: Unknown vendor-specific page!\n");
+ }
+ break;
+
+ case 0xe: // audio page
+ logerror("Ch 0 route: %x vol: %x\n", data[8], data[9]);
+ logerror("Ch 1 route: %x vol: %x\n", data[10], data[11]);
+ logerror("Ch 2 route: %x vol: %x\n", data[12], data[13]);
+ logerror("Ch 3 route: %x vol: %x\n", data[14], data[15]);
+ break;
+ }
+ break;
+
+ default:
+ SCSIBase( &SCSIClassGDROM, SCSIOP_WRITE_DATA, scsiInstance, dataLength, data );
+ break;
+ }
+}
+
+static void scsicd_alloc_instance( SCSIInstance *scsiInstance, const char *diskregion )
+{
+ running_machine &machine = scsiInstance->machine();
+ SCSIGd *our_this = (SCSIGd *)SCSIThis( &SCSIClassGDROM, scsiInstance );
+
+ our_this->lba = 0;
+ our_this->blocks = 0;
+ our_this->last_lba = 0;
+ our_this->bytes_per_sector = 2048;
+ our_this->num_subblocks = 1;
+ our_this->cur_subblock = 0;
+ our_this->play_err_flag = 0;
+
+ state_save_register_item( machine, "scsicd", diskregion, 0, our_this->lba );
+ state_save_register_item( machine, "scsicd", diskregion, 0, our_this->blocks );
+ state_save_register_item( machine, "scsicd", diskregion, 0, our_this->last_lba );
+ state_save_register_item( machine, "scsicd", diskregion, 0, our_this->bytes_per_sector );
+ state_save_register_item( machine, "scsicd", diskregion, 0, our_this->num_subblocks );
+ state_save_register_item( machine, "scsicd", diskregion, 0, our_this->cur_subblock );
+ state_save_register_item( machine, "scsicd", diskregion, 0, our_this->play_err_flag );
+
+ if (machine.device( diskregion )) {
+ our_this->is_file = TRUE;
+ our_this->cdrom = cd_get_cdrom_file( machine.device( diskregion ) );
+ } else {
+ our_this->is_file = FALSE;
+ our_this->cdrom = cdrom_open(get_disk_handle( machine, diskregion ));
+ }
+
+ if (!our_this->cdrom)
+ {
+ logerror("GDROM: no CD found!\n");
+ }
+}
+
+static void scsicd_delete_instance( SCSIInstance *scsiInstance )
+{
+ SCSIGd *our_this = (SCSIGd *)SCSIThis( &SCSIClassGDROM, scsiInstance );
+ if (!our_this->is_file) {
+ if( our_this->cdrom )
+ {
+ cdrom_close( our_this->cdrom );
+ }
+ }
+}
+
+static void scsicd_get_device( SCSIInstance *scsiInstance, cdrom_file **cdrom )
+{
+ SCSIGd *our_this = (SCSIGd *)SCSIThis( &SCSIClassGDROM, scsiInstance );
+ *cdrom = our_this->cdrom;
+}
+
+static void scsicd_set_device( SCSIInstance *scsiInstance, cdrom_file *cdrom )
+{
+ SCSIGd *our_this = (SCSIGd *)SCSIThis( &SCSIClassGDROM, scsiInstance );
+ our_this->cdrom = cdrom;
+}
+
+static int scsigd_dispatch(int operation, void *file, INT64 intparm, void *ptrparm)
+{
+ SCSIAllocInstanceParams *params;
+
+ switch (operation)
+ {
+ case SCSIOP_EXEC_COMMAND:
+ return scsicd_exec_command( (SCSIInstance *)file, (UINT8 *)ptrparm );
+
+ case SCSIOP_READ_DATA:
+ scsicd_read_data( (SCSIInstance *)file, (UINT8 *)ptrparm, intparm );
+ return 0;
+
+ case SCSIOP_WRITE_DATA:
+ scsicd_write_data( (SCSIInstance *)file, (UINT8 *)ptrparm, intparm );
+ return 0;
+
+ case SCSIOP_ALLOC_INSTANCE:
+ params = (SCSIAllocInstanceParams *)ptrparm;
+ SCSIBase( &SCSIClassGDROM, operation, (SCSIInstance *)file, intparm, (UINT8 *)ptrparm );
+ scsicd_alloc_instance( params->instance, params->diskregion );
+ return 0;
+
+ case SCSIOP_DELETE_INSTANCE:
+ scsicd_delete_instance( (SCSIInstance *)file );
+ break;
+
+ case SCSIOP_GET_DEVICE:
+ scsicd_get_device( (SCSIInstance *)file, (cdrom_file **)ptrparm );
+ return 0;
+
+ case SCSIOP_SET_DEVICE:
+ scsicd_set_device( (SCSIInstance *)file, (cdrom_file *)ptrparm );
+ return 0;
+ }
+
+ return SCSIBase( &SCSIClassGDROM, operation, (SCSIInstance *)file, intparm, (UINT8 *)ptrparm );
+}
+
+const SCSIClass SCSIClassGDROM =
+{
+ &SCSIClassDevice,
+ scsigd_dispatch,
+ sizeof( SCSIGd )
+};
diff --git a/src/mame/machine/gdrom.h b/src/mame/machine/gdrom.h
new file mode 100644
index 00000000000..ff4944ac545
--- /dev/null
+++ b/src/mame/machine/gdrom.h
@@ -0,0 +1,17 @@
+/***************************************************************************
+
+ gdrom.h
+
+***************************************************************************/
+
+#ifndef _GDROM_H_
+#define _GDROM_H_
+
+#include "machine/scsi.h"
+
+// Sega GD-ROM handler
+extern const SCSIClass SCSIClassGDROM;
+#define SCSI_DEVICE_GDROM &SCSIClassGDROM
+
+#endif
+
diff --git a/src/mame/machine/segasms.c b/src/mame/machine/segasms.c
new file mode 100644
index 00000000000..be79559a118
--- /dev/null
+++ b/src/mame/machine/segasms.c
@@ -0,0 +1,2053 @@
+#include "emu.h"
+#include "crsshair.h"
+#include "image.h"
+#include "includes/segasms.h"
+#include "video/smsvdp.h"
+#include "sound/2413intf.h"
+#include "imagedev/cartslot.h"
+#include "hashfile.h"
+
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+#define CF_CODEMASTERS_MAPPER 0x01
+#define CF_KOREAN_MAPPER 0x02
+#define CF_KOREAN_ZEMINA_MAPPER 0x04
+#define CF_KOREAN_NOBANK_MAPPER 0x08
+#define CF_93C46_EEPROM 0x10
+#define CF_ONCART_RAM 0x20
+#define CF_GG_SMS_MODE 0x40
+
+#define LGUN_RADIUS 6
+#define LGUN_X_INTERVAL 4
+
+
+static void setup_rom(address_space *space);
+
+
+static TIMER_CALLBACK( rapid_fire_callback )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ state->m_rapid_fire_state_1 ^= 0xff;
+ state->m_rapid_fire_state_2 ^= 0xff;
+}
+
+
+static WRITE8_HANDLER( sms_input_write )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+
+ switch (offset)
+ {
+ case 0:
+ switch (input_port_read_safe(space->machine(), "CTRLSEL", 0x00) & 0x0f)
+ {
+ case 0x03: /* Sports Pad */
+ if (data != state->m_sports_pad_last_data_1)
+ {
+ UINT32 cpu_cycles = downcast<cpu_device *>(&space->device())->total_cycles();
+
+ state->m_sports_pad_last_data_1 = data;
+ if (cpu_cycles - state->m_last_sports_pad_time_1 > 512)
+ {
+ state->m_sports_pad_state_1 = 3;
+ state->m_sports_pad_1_x = input_port_read(space->machine(), "SPORT0");
+ state->m_sports_pad_1_y = input_port_read(space->machine(), "SPORT1");
+ }
+ state->m_last_sports_pad_time_1 = cpu_cycles;
+ state->m_sports_pad_state_1 = (state->m_sports_pad_state_1 + 1) & 3;
+ }
+ break;
+ }
+ break;
+
+ case 1:
+ switch (input_port_read_safe(space->machine(), "CTRLSEL", 0x00) >> 4)
+ {
+ case 0x03: /* Sports Pad */
+ if (data != state->m_sports_pad_last_data_2)
+ {
+ UINT32 cpu_cycles = downcast<cpu_device *>(&space->device())->total_cycles();
+
+ state->m_sports_pad_last_data_2 = data;
+ if (cpu_cycles - state->m_last_sports_pad_time_2 > 2048)
+ {
+ state->m_sports_pad_state_2 = 3;
+ state->m_sports_pad_2_x = input_port_read(space->machine(), "SPORT2");
+ state->m_sports_pad_2_y = input_port_read(space->machine(), "SPORT3");
+ }
+ state->m_last_sports_pad_time_2 = cpu_cycles;
+ state->m_sports_pad_state_2 = (state->m_sports_pad_state_2 + 1) & 3;
+ }
+ break;
+ }
+ break;
+ }
+}
+
+/* FIXME: this function is a hack for Light Phaser emulation. Theoretically
+ sms_vdp_hcount_latch() should be used instead, but it returns incorrect
+ position for unknown reason (timing?) */
+static void sms_vdp_hcount_lphaser( running_machine &machine, int hpos )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ int hpos_tmp = hpos + state->m_lphaser_x_offs;
+ UINT8 tmp = ((hpos_tmp - 46) >> 1) & 0xff;
+
+ //printf ("sms_vdp_hcount_lphaser: hpos %3d hpos_tmp %3d => hcount %2X\n", hpos, hpos_tmp, tmp);
+ sms_vdp_hcount_latch_w(state->m_vdp, 0, tmp);
+}
+
+
+/*
+ Light Phaser (light gun) emulation notes:
+ - The sensor is activated based on color brightness of some individual
+ pixels being drawn by the beam, at circular area where the gun is aiming.
+ - Currently, brightness is calculated based only on single pixels.
+ - In general, after the trigger is pressed, games draw the next frame using
+ a light color pattern, to make sure sensor will be activated. If emulation
+ skips that frame, sensor may stay deactivated. Frameskip set to 0 (no skip) is
+ recommended to avoid problems.
+ - When sensor switches from off to on, a value is latched for HCount register
+ and a flag is set. The emulation uses the flag to avoid sequential latches and
+ to signal that TH line is activated when the status of the input port is read.
+ When the status is read, the flag is cleared, or else it is cleared later when
+ the Pause status is read (end of a frame). This is necessary because the
+ "Color & Switch Test" ROM only reads the TH bit after the VINT line.
+ - The gun test of "Color & Switch Test" is an example that requires checks
+ of sensor status independent of other events, like trigger press or TH bit
+ reads. Another example is the title screen of "Hang-On & Safari Hunt", where
+ the game only reads HCount register in a loop, expecting a latch by the gun.
+ - The whole procedure is managed by a timer callback, that always reschedule
+ itself to run in some intervals when the beam is at the circular area.
+*/
+static int lgun_bright_aim_area( running_machine &machine, emu_timer *timer, int lgun_x, int lgun_y )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ const int r_x_r = LGUN_RADIUS * LGUN_RADIUS;
+ screen_device *screen = machine.first_screen();
+ const rectangle &visarea = screen->visible_area();
+ int beam_x = screen->hpos();
+ int beam_y = screen->vpos();
+ int dx, dy;
+ int result = 0;
+ int pos_changed = 0;
+ double dx_circ;
+
+ while (1)
+ {
+ dy = abs(beam_y - lgun_y);
+
+ if (dy > LGUN_RADIUS || beam_y < visarea.min_y || beam_y > visarea.max_y)
+ {
+ beam_y = lgun_y - LGUN_RADIUS;
+ if (beam_y < visarea.min_y)
+ beam_y = visarea.min_y;
+ dy = abs(beam_y - lgun_y);
+ pos_changed = 1;
+ }
+ /* step 1: r^2 = dx^2 + dy^2 */
+ /* step 2: dx^2 = r^2 - dy^2 */
+ /* step 3: dx = sqrt(r^2 - dy^2) */
+ dx_circ = ceil((float) sqrt((float) (r_x_r - (dy * dy))));
+ dx = abs(beam_x - lgun_x);
+
+ if (dx > dx_circ || beam_x < visarea.min_x || beam_x > visarea.max_x)
+ {
+ if (beam_x > lgun_x)
+ {
+ beam_x = 0;
+ beam_y++;
+ continue;
+ }
+ beam_x = lgun_x - dx_circ;
+ if (beam_x < visarea.min_x)
+ beam_x = visarea.min_x;
+ pos_changed = 1;
+ }
+
+ if (!pos_changed)
+ {
+ result = sms_vdp_check_brightness(state->m_vdp, beam_x, beam_y);
+ /* next check at same line */
+ beam_x += LGUN_X_INTERVAL;
+ pos_changed = 1;
+ }
+ else
+ break;
+ }
+ timer->adjust(machine.first_screen()->time_until_pos(beam_y, beam_x));
+
+ return result;
+}
+
+static UINT8 sms_vdp_hcount( running_machine &machine )
+{
+ UINT8 tmp;
+ screen_device *screen = machine.first_screen();
+ int hpos = screen->hpos();
+
+ /* alternative method: pass HCounter test, but some others fail */
+ //int hpos_tmp = hpos;
+ //if ((hpos + 2) % 6 == 0) hpos_tmp--;
+ //tmp = ((hpos_tmp - 46) >> 1) & 0xff;
+
+ UINT64 calc_cycles;
+ attotime time_end;
+ int vpos = screen->vpos();
+ int max_hpos = screen->width() - 1;
+
+ if (hpos == max_hpos)
+ time_end = attotime::zero;
+ else
+ time_end = screen->time_until_pos(vpos, max_hpos);
+ calc_cycles = machine.device<cpu_device>("maincpu")->attotime_to_clocks(time_end);
+
+ /* equation got from SMSPower forum, posted by Flubba. */
+ tmp = ((590 - (calc_cycles * 3)) / 4) & 0xff;
+
+ //printf ("sms_vdp_hcount: hpos %3d => hcount %2X\n", hpos, tmp);
+ return tmp;
+}
+
+
+static void sms_vdp_hcount_latch( running_machine &machine )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ UINT8 value = sms_vdp_hcount(machine);
+
+ sms_vdp_hcount_latch_w(state->m_vdp, 0, value);
+}
+
+
+static UINT16 screen_hpos_nonscaled( screen_device *screen, int scaled_hpos )
+{
+ const rectangle &visarea = screen->visible_area();
+ int offset_x = (scaled_hpos * (visarea.max_x - visarea.min_x)) / 255;
+ return visarea.min_x + offset_x;
+}
+
+
+static UINT16 screen_vpos_nonscaled( screen_device *screen, int scaled_vpos )
+{
+ const rectangle &visarea = screen->visible_area();
+ int offset_y = (scaled_vpos * (visarea.max_y - visarea.min_y)) / 255;
+ return visarea.min_y + offset_y;
+}
+
+
+static void lphaser1_sensor_check( running_machine &machine )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ const int x = screen_hpos_nonscaled(machine.first_screen(), input_port_read(machine, "LPHASER0"));
+ const int y = screen_vpos_nonscaled(machine.first_screen(), input_port_read(machine, "LPHASER1"));
+
+ if (lgun_bright_aim_area(machine, state->m_lphaser_1_timer, x, y))
+ {
+ if (state->m_lphaser_1_latch == 0)
+ {
+ state->m_lphaser_1_latch = 1;
+ sms_vdp_hcount_lphaser(machine, x);
+ }
+ }
+}
+
+static void lphaser2_sensor_check( running_machine &machine )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ const int x = screen_hpos_nonscaled(machine.first_screen(), input_port_read(machine, "LPHASER2"));
+ const int y = screen_vpos_nonscaled(machine.first_screen(), input_port_read(machine, "LPHASER3"));
+
+ if (lgun_bright_aim_area(machine, state->m_lphaser_2_timer, x, y))
+ {
+ if (state->m_lphaser_2_latch == 0)
+ {
+ state->m_lphaser_2_latch = 1;
+ sms_vdp_hcount_lphaser(machine, x);
+ }
+ }
+}
+
+
+// at each input port read we check if lightguns are enabled in one of the ports:
+// if so, we turn on crosshair and the lightgun timer
+static TIMER_CALLBACK( lightgun_tick )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+
+ if ((input_port_read_safe(machine, "CTRLSEL", 0x00) & 0x0f) == 0x01)
+ {
+ /* enable crosshair */
+ crosshair_set_screen(machine, 0, CROSSHAIR_SCREEN_ALL);
+ if (!state->m_lphaser_1_timer->enabled())
+ lphaser1_sensor_check(machine);
+ }
+ else
+ {
+ /* disable crosshair */
+ crosshair_set_screen(machine, 0, CROSSHAIR_SCREEN_NONE);
+ state->m_lphaser_1_timer->enable(0);
+ }
+
+ if ((input_port_read_safe(machine, "CTRLSEL", 0x00) & 0xf0) == 0x10)
+ {
+ /* enable crosshair */
+ crosshair_set_screen(machine, 1, CROSSHAIR_SCREEN_ALL);
+ if (!state->m_lphaser_2_timer->enabled())
+ lphaser2_sensor_check(machine);
+ }
+ else
+ {
+ /* disable crosshair */
+ crosshair_set_screen(machine, 1, CROSSHAIR_SCREEN_NONE);
+ state->m_lphaser_2_timer->enable(0);
+ }
+}
+
+
+static TIMER_CALLBACK( lphaser_1_callback )
+{
+ lphaser1_sensor_check(machine);
+}
+
+
+static TIMER_CALLBACK( lphaser_2_callback )
+{
+ lphaser2_sensor_check(machine);
+}
+
+
+INPUT_CHANGED( lgun1_changed )
+{
+ sms_state *state = field->port->machine().driver_data<sms_state>();
+ if (!state->m_lphaser_1_timer ||
+ (input_port_read_safe(field->port->machine(), "CTRLSEL", 0x00) & 0x0f) != 0x01)
+ return;
+
+ if (newval != oldval)
+ lphaser1_sensor_check(field->port->machine());
+}
+
+INPUT_CHANGED( lgun2_changed )
+{
+ sms_state *state = field->port->machine().driver_data<sms_state>();
+ if (!state->m_lphaser_2_timer ||
+ (input_port_read_safe(field->port->machine(), "CTRLSEL", 0x00) & 0xf0) != 0x10)
+ return;
+
+ if (newval != oldval)
+ lphaser2_sensor_check(field->port->machine());
+}
+
+
+static void sms_get_inputs( address_space *space )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ UINT8 data = 0x00;
+ UINT32 cpu_cycles = downcast<cpu_device *>(&space->device())->total_cycles();
+ running_machine &machine = space->machine();
+
+ state->m_input_port0 = 0xff;
+ state->m_input_port1 = 0xff;
+
+ if (cpu_cycles - state->m_last_paddle_read_time > 256)
+ {
+ state->m_paddle_read_state ^= 0xff;
+ state->m_last_paddle_read_time = cpu_cycles;
+ }
+
+ /* Check if lightgun has been chosen as input: if so, enable crosshair */
+ machine.scheduler().timer_set(attotime::zero, FUNC(lightgun_tick));
+
+ /* Player 1 */
+ switch (input_port_read_safe(machine, "CTRLSEL", 0x00) & 0x0f)
+ {
+ case 0x00: /* Joystick */
+ data = input_port_read(machine, "PORT_DC");
+ /* Check Rapid Fire setting for Button A */
+ if (!(data & 0x10) && (input_port_read(machine, "RFU") & 0x01))
+ data |= state->m_rapid_fire_state_1 & 0x10;
+
+ /* Check Rapid Fire setting for Button B */
+ if (!(data & 0x20) && (input_port_read(machine, "RFU") & 0x02))
+ data |= state->m_rapid_fire_state_1 & 0x20;
+
+ state->m_input_port0 = (state->m_input_port0 & 0xc0) | (data & 0x3f);
+ break;
+
+ case 0x01: /* Light Phaser */
+ data = (input_port_read(machine, "CTRLIPT") & 0x01) << 4;
+ if (!(data & 0x10))
+ {
+ if (input_port_read(machine, "RFU") & 0x01)
+ data |= state->m_rapid_fire_state_1 & 0x10;
+ }
+ /* just consider the button (trigger) bit */
+ data |= ~0x10;
+ state->m_input_port0 = (state->m_input_port0 & 0xc0) | (data & 0x3f);
+ break;
+
+ case 0x02: /* Paddle Control */
+ /* Get button A state */
+ data = input_port_read(machine, "PADDLE0");
+
+ if (state->m_paddle_read_state)
+ data = data >> 4;
+
+ state->m_input_port0 = (state->m_input_port0 & 0xc0) | (data & 0x0f) | (state->m_paddle_read_state & 0x20)
+ | ((input_port_read(machine, "CTRLIPT") & 0x02) << 3);
+ break;
+
+ case 0x03: /* Sega Sports Pad */
+ switch (state->m_sports_pad_state_1)
+ {
+ case 0:
+ data = (state->m_sports_pad_1_x >> 4) & 0x0f;
+ break;
+ case 1:
+ data = state->m_sports_pad_1_x & 0x0f;
+ break;
+ case 2:
+ data = (state->m_sports_pad_1_y >> 4) & 0x0f;
+ break;
+ case 3:
+ data = state->m_sports_pad_1_y & 0x0f;
+ break;
+ }
+ state->m_input_port0 = (state->m_input_port0 & 0xc0) | data | ((input_port_read(machine, "CTRLIPT") & 0x0c) << 2);
+ break;
+ }
+
+ /* Player 2 */
+ switch (input_port_read_safe(machine, "CTRLSEL", 0x00) & 0xf0)
+ {
+ case 0x00: /* Joystick */
+ data = input_port_read(machine, "PORT_DC");
+ state->m_input_port0 = (state->m_input_port0 & 0x3f) | (data & 0xc0);
+
+ data = input_port_read(machine, "PORT_DD");
+ /* Check Rapid Fire setting for Button A */
+ if (!(data & 0x04) && (input_port_read(machine, "RFU") & 0x04))
+ data |= state->m_rapid_fire_state_2 & 0x04;
+
+ /* Check Rapid Fire setting for Button B */
+ if (!(data & 0x08) && (input_port_read(machine, "RFU") & 0x08))
+ data |= state->m_rapid_fire_state_2 & 0x08;
+
+ state->m_input_port1 = (state->m_input_port1 & 0xf0) | (data & 0x0f);
+ break;
+
+ case 0x10: /* Light Phaser */
+ data = (input_port_read(machine, "CTRLIPT") & 0x10) >> 2;
+ if (!(data & 0x04))
+ {
+ if (input_port_read(machine, "RFU") & 0x04)
+ data |= state->m_rapid_fire_state_2 & 0x04;
+ }
+ /* just consider the button (trigger) bit */
+ data |= ~0x04;
+ state->m_input_port1 = (state->m_input_port1 & 0xf0) | (data & 0x0f);
+ break;
+
+ case 0x20: /* Paddle Control */
+ /* Get button A state */
+ data = input_port_read(machine, "PADDLE1");
+ if (state->m_paddle_read_state)
+ data = data >> 4;
+
+ state->m_input_port0 = (state->m_input_port0 & 0x3f) | ((data & 0x03) << 6);
+ state->m_input_port1 = (state->m_input_port1 & 0xf0) | ((data & 0x0c) >> 2) | (state->m_paddle_read_state & 0x08)
+ | ((input_port_read(machine, "CTRLIPT") & 0x20) >> 3);
+ break;
+
+ case 0x30: /* Sega Sports Pad */
+ switch (state->m_sports_pad_state_2)
+ {
+ case 0:
+ data = state->m_sports_pad_2_x & 0x0f;
+ break;
+ case 1:
+ data = (state->m_sports_pad_2_x >> 4) & 0x0f;
+ break;
+ case 2:
+ data = state->m_sports_pad_2_y & 0x0f;
+ break;
+ case 3:
+ data = (state->m_sports_pad_2_y >> 4) & 0x0f;
+ break;
+ }
+ state->m_input_port0 = (state->m_input_port0 & 0x3f) | ((data & 0x03) << 6);
+ state->m_input_port1 = (state->m_input_port1 & 0xf0) | (data >> 2) | ((input_port_read(machine, "CTRLIPT") & 0xc0) >> 4);
+ break;
+ }
+}
+
+
+WRITE8_HANDLER( sms_fm_detect_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+
+ if (state->m_has_fm)
+ state->m_fm_detect = (data & 0x01);
+}
+
+
+READ8_HANDLER( sms_fm_detect_r )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+
+ if (state->m_has_fm)
+ {
+ return state->m_fm_detect;
+ }
+ else
+ {
+ if (state->m_bios_port & IO_CHIP)
+ {
+ return 0xff;
+ }
+ else
+ {
+ sms_get_inputs(space);
+ return state->m_input_port0;
+ }
+ }
+}
+
+WRITE8_HANDLER( sms_io_control_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+
+ if (data & 0x08)
+ {
+ /* check if TH pin level is high (1) and was low last time */
+ if (data & 0x80 && !(state->m_ctrl_reg & 0x80))
+ {
+ sms_vdp_hcount_latch(space->machine());
+ }
+ sms_input_write(space, 0, (data & 0x20) >> 5);
+ }
+
+ if (data & 0x02)
+ {
+ if (data & 0x20 && !(state->m_ctrl_reg & 0x20))
+ {
+ sms_vdp_hcount_latch(space->machine());
+ }
+ sms_input_write(space, 1, (data & 0x80) >> 7);
+ }
+
+ state->m_ctrl_reg = data;
+}
+
+
+READ8_HANDLER( sms_count_r )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+
+ if (offset & 0x01)
+ return sms_vdp_hcount_latch_r(state->m_vdp, offset);
+ else
+ return sms_vdp_vcount_r(state->m_vdp, offset);
+}
+
+
+/*
+ Check if the pause button is pressed.
+ If the gamegear is in sms mode, check if the start button is pressed.
+ */
+void sms_pause_callback( running_machine &machine )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+
+ if (state->m_is_gamegear && !(state->m_cartridge[state->m_current_cartridge].features & CF_GG_SMS_MODE))
+ return;
+
+ if (!(input_port_read(machine, state->m_is_gamegear ? "START" : "PAUSE") & 0x80))
+ {
+ if (!state->m_paused)
+ {
+ cputag_set_input_line(machine, "maincpu", INPUT_LINE_NMI, PULSE_LINE);
+ }
+ state->m_paused = 1;
+ }
+ else
+ {
+ state->m_paused = 0;
+ }
+
+ /* clear Light Phaser latch flags for next frame */
+ state->m_lphaser_1_latch = 0;
+ state->m_lphaser_2_latch = 0;
+}
+
+READ8_HANDLER( sms_input_port_0_r )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+
+ if (state->m_bios_port & IO_CHIP)
+ {
+ return 0xff;
+ }
+ else
+ {
+ sms_get_inputs(space);
+ return state->m_input_port0;
+ }
+}
+
+
+READ8_HANDLER( sms_input_port_1_r )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+
+ if (state->m_bios_port & IO_CHIP)
+ return 0xff;
+
+ sms_get_inputs(space);
+
+ /* Reset Button */
+ state->m_input_port1 = (state->m_input_port1 & 0xef) | (input_port_read_safe(space->machine(), "RESET", 0x01) & 0x01) << 4;
+
+ /* Do region detection if TH of ports A and B are set to output (0) */
+ if (!(state->m_ctrl_reg & 0x0a))
+ {
+ /* Move bits 7,5 of IO control port into bits 7, 6 */
+ state->m_input_port1 = (state->m_input_port1 & 0x3f) | (state->m_ctrl_reg & 0x80) | (state->m_ctrl_reg & 0x20) << 1;
+
+ /* Inverse region detect value for Japanese machines */
+ if (state->m_is_region_japan)
+ state->m_input_port1 ^= 0xc0;
+ }
+ else
+ {
+ if (state->m_ctrl_reg & 0x02 && state->m_lphaser_1_latch)
+ {
+ state->m_input_port1 &= ~0x40;
+ state->m_lphaser_1_latch = 0;
+ }
+
+ if (state->m_ctrl_reg & 0x08 && state->m_lphaser_2_latch)
+ {
+ state->m_input_port1 &= ~0x80;
+ state->m_lphaser_2_latch = 0;
+ }
+ }
+
+ return state->m_input_port1;
+}
+
+
+
+WRITE8_HANDLER( sms_ym2413_register_port_0_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+
+ if (state->m_has_fm)
+ ym2413_w(state->m_ym, 0, (data & 0x3f));
+}
+
+
+WRITE8_HANDLER( sms_ym2413_data_port_0_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+
+ if (state->m_has_fm)
+ {
+ logerror("data_port_0_w %x %x\n", offset, data);
+ ym2413_w(state->m_ym, 1, data);
+ }
+}
+
+
+READ8_HANDLER( gg_input_port_2_r )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+
+ //logerror("joy 2 read, val: %02x, pc: %04x\n", ((state->m_is_region_japan ? 0x00 : 0x40) | (input_port_read(machine, "START") & 0x80)), activecpu_get_pc());
+ return ((state->m_is_region_japan ? 0x00 : 0x40) | (input_port_read(space->machine(), "START") & 0x80));
+}
+
+
+READ8_HANDLER( sms_sscope_r )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ return state->m_sscope_state;
+}
+
+
+WRITE8_HANDLER( sms_sscope_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ state->m_sscope_state = data;
+}
+
+
+READ8_HANDLER( sms_mapper_r )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ return state->m_mapper[offset];
+}
+
+/* Terebi Oekaki */
+/* The following code comes from sg1000.c. We should eventually merge these TV Draw implementations */
+static WRITE8_HANDLER( sms_tvdraw_axis_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ UINT8 tvboard_on = input_port_read_safe(space->machine(), "TVDRAW", 0x00);
+
+ if (data & 0x01)
+ {
+ state->m_tvdraw_data = tvboard_on ? input_port_read(space->machine(), "TVDRAW_X") : 0x80;
+
+ if (state->m_tvdraw_data < 4) state->m_tvdraw_data = 4;
+ if (state->m_tvdraw_data > 251) state->m_tvdraw_data = 251;
+ }
+ else
+ {
+ state->m_tvdraw_data = tvboard_on ? input_port_read(space->machine(), "TVDRAW_Y") + 0x20 : 0x80;
+ }
+}
+
+static READ8_HANDLER( sms_tvdraw_status_r )
+{
+ UINT8 tvboard_on = input_port_read_safe(space->machine(), "TVDRAW", 0x00);
+ return tvboard_on ? input_port_read(space->machine(), "TVDRAW_PEN") : 0x01;
+}
+
+static READ8_HANDLER( sms_tvdraw_data_r )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ return state->m_tvdraw_data;
+}
+
+
+WRITE8_HANDLER( sms_mapper_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ int page;
+ UINT8 *SOURCE_BIOS;
+ UINT8 *SOURCE_CART;
+ UINT8 *SOURCE;
+ UINT8 rom_page_count = state->m_cartridge[state->m_current_cartridge].size / 0x4000;
+
+ offset &= 3;
+
+ state->m_mapper[offset] = data;
+ state->m_mapper_ram[offset] = data;
+
+ if (state->m_cartridge[state->m_current_cartridge].ROM)
+ {
+ SOURCE_CART = state->m_cartridge[state->m_current_cartridge].ROM + (( rom_page_count > 0) ? data % rom_page_count : 0) * 0x4000;
+ }
+ else
+ {
+ SOURCE_CART = state->m_banking_none[1];
+ }
+
+ if (state->m_BIOS)
+ {
+ SOURCE_BIOS = state->m_BIOS + ((state->m_bios_page_count > 0) ? data % state->m_bios_page_count : 0) * 0x4000;
+ }
+ else
+ {
+ SOURCE_BIOS = state->m_banking_none[1];
+ }
+
+ if (state->m_bios_port & IO_BIOS_ROM || (state->m_is_gamegear && state->m_BIOS == NULL))
+ {
+ if (!(state->m_bios_port & IO_CARTRIDGE) || (state->m_is_gamegear && state->m_BIOS == NULL))
+ {
+ page = (rom_page_count > 0) ? data % rom_page_count : 0;
+ if (!state->m_cartridge[state->m_current_cartridge].ROM)
+ return;
+ SOURCE = SOURCE_CART;
+ }
+ else
+ {
+ /* nothing to page in */
+ return;
+ }
+ }
+ else
+ {
+ page = (state->m_bios_page_count > 0) ? data % state->m_bios_page_count : 0;
+ if (!state->m_BIOS)
+ return;
+ SOURCE = SOURCE_BIOS;
+ }
+
+ switch (offset)
+ {
+ case 0: /* Control */
+ /* Is it ram or rom? */
+ if (data & 0x08) /* it's ram */
+ {
+ state->m_cartridge[state->m_current_cartridge].sram_save = 1; /* SRAM should be saved on exit. */
+ if (data & 0x04)
+ {
+ LOG(("ram 1 paged.\n"));
+ SOURCE = state->m_cartridge[state->m_current_cartridge].cartSRAM + 0x4000;
+ }
+ else
+ {
+ LOG(("ram 0 paged.\n"));
+ SOURCE = state->m_cartridge[state->m_current_cartridge].cartSRAM;
+ }
+ memory_set_bankptr(space->machine(), "bank5", SOURCE);
+ memory_set_bankptr(space->machine(), "bank6", SOURCE + 0x2000);
+ }
+ else /* it's rom */
+ {
+ if (state->m_bios_port & IO_BIOS_ROM || ! state->m_has_bios)
+ {
+ page = (rom_page_count > 0) ? state->m_mapper[3] % rom_page_count : 0;
+ SOURCE = state->m_banking_cart[5];
+ }
+ else
+ {
+ page = (state->m_bios_page_count > 0) ? state->m_mapper[3] % state->m_bios_page_count : 0;
+ SOURCE = state->m_banking_bios[5];
+ }
+ LOG(("rom 2 paged in %x.\n", page));
+ memory_set_bankptr(space->machine(), "bank5", SOURCE);
+ memory_set_bankptr(space->machine(), "bank6", SOURCE + 0x2000);
+ }
+ break;
+
+ case 1: /* Select 16k ROM bank for 0400-3FFF */
+ LOG(("rom 0 paged in %x.\n", page));
+ state->m_banking_bios[2] = SOURCE_BIOS + 0x0400;
+ state->m_banking_cart[2] = SOURCE_CART + 0x0400;
+ if (state->m_is_gamegear)
+ SOURCE = SOURCE_CART;
+
+ memory_set_bankptr(space->machine(), "bank2", SOURCE + 0x0400);
+ break;
+
+ case 2: /* Select 16k ROM bank for 4000-7FFF */
+ LOG(("rom 1 paged in %x.\n", page));
+ state->m_banking_bios[3] = SOURCE_BIOS;
+ state->m_banking_cart[3] = SOURCE_CART;
+ if (state->m_is_gamegear)
+ SOURCE = SOURCE_CART;
+
+ memory_set_bankptr(space->machine(), "bank3", SOURCE);
+ memory_set_bankptr(space->machine(), "bank4", SOURCE + 0x2000);
+ break;
+
+ case 3: /* Select 16k ROM bank for 8000-BFFF */
+ state->m_banking_bios[5] = SOURCE_BIOS;
+ if (state->m_is_gamegear)
+ SOURCE = SOURCE_CART;
+
+ if ( state->m_cartridge[state->m_current_cartridge].features & CF_CODEMASTERS_MAPPER)
+ {
+ if (SOURCE == SOURCE_CART)
+ {
+ SOURCE = state->m_banking_cart[5];
+ }
+ }
+ else
+ {
+ state->m_banking_cart[5] = SOURCE_CART;
+ }
+
+ if (!(state->m_mapper[0] & 0x08)) /* is RAM disabled? */
+ {
+ LOG(("rom 2 paged in %x.\n", page));
+ memory_set_bankptr(space->machine(), "bank5", SOURCE);
+ memory_set_bankptr(space->machine(), "bank6", SOURCE + 0x2000);
+ }
+ break;
+ }
+}
+
+#ifdef MESS
+static WRITE8_HANDLER( sms_korean_zemina_banksw_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+
+ if (state->m_cartridge[state->m_current_cartridge].features & CF_KOREAN_ZEMINA_MAPPER)
+ {
+ UINT8 rom_page_count = state->m_cartridge[state->m_current_cartridge].size / 0x2000;
+ int page = (rom_page_count > 0) ? data % rom_page_count : 0;
+
+ if (!state->m_cartridge[state->m_current_cartridge].ROM)
+ return;
+
+ switch (offset & 3)
+ {
+ case 0:
+ state->m_banking_cart[5] = state->m_cartridge[state->m_current_cartridge].ROM + page * 0x2000;
+ memory_set_bankptr(space->machine(), "bank5", state->m_banking_cart[5]);
+ break;
+ case 1:
+ state->m_banking_cart[6] = state->m_cartridge[state->m_current_cartridge].ROM + page * 0x2000;
+ memory_set_bankptr(space->machine(), "bank6", state->m_banking_cart[6]);
+ break;
+ case 2:
+ state->m_banking_cart[3] = state->m_cartridge[state->m_current_cartridge].ROM + page * 0x2000;
+ memory_set_bankptr(space->machine(), "bank3", state->m_banking_cart[3]);
+ break;
+ case 3:
+ state->m_banking_cart[4] = state->m_cartridge[state->m_current_cartridge].ROM + page * 0x2000;
+ memory_set_bankptr(space->machine(), "bank4", state->m_banking_cart[4]);
+ break;
+ }
+ LOG(("Zemina mapper write: offset %x data %x.\n", offset, page));
+ }
+}
+
+static WRITE8_HANDLER( sms_codemasters_page0_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+
+ if (state->m_cartridge[state->m_current_cartridge].ROM && state->m_cartridge[state->m_current_cartridge].features & CF_CODEMASTERS_MAPPER)
+ {
+ UINT8 rom_page_count = state->m_cartridge[state->m_current_cartridge].size / 0x4000;
+ state->m_banking_cart[1] = state->m_cartridge[state->m_current_cartridge].ROM + ((rom_page_count > 0) ? data % rom_page_count : 0) * 0x4000;
+ state->m_banking_cart[2] = state->m_banking_cart[1] + 0x0400;
+ memory_set_bankptr(space->machine(), "bank1", state->m_banking_cart[1]);
+ memory_set_bankptr(space->machine(), "bank2", state->m_banking_cart[2]);
+ }
+}
+
+
+static WRITE8_HANDLER( sms_codemasters_page1_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+
+ if (state->m_cartridge[state->m_current_cartridge].ROM && state->m_cartridge[state->m_current_cartridge].features & CF_CODEMASTERS_MAPPER)
+ {
+ /* Check if we need to switch in some RAM */
+ if (data & 0x80)
+ {
+ state->m_cartridge[state->m_current_cartridge].ram_page = data & 0x07;
+ memory_set_bankptr(space->machine(), "bank6", state->m_cartridge[state->m_current_cartridge].cartRAM + state->m_cartridge[state->m_current_cartridge].ram_page * 0x2000);
+ }
+ else
+ {
+ UINT8 rom_page_count = state->m_cartridge[state->m_current_cartridge].size / 0x4000;
+ state->m_banking_cart[3] = state->m_cartridge[state->m_current_cartridge].ROM + ((rom_page_count > 0) ? data % rom_page_count : 0) * 0x4000;
+ memory_set_bankptr(space->machine(), "bank3", state->m_banking_cart[3]);
+ memory_set_bankptr(space->machine(), "bank4", state->m_banking_cart[3] + 0x2000);
+ memory_set_bankptr(space->machine(), "bank6", state->m_banking_cart[5] + 0x2000);
+ }
+ }
+}
+#endif
+
+static READ8_HANDLER( sms_kor_nobank_r )
+{
+// printf("read %x\n", offset);
+ return space->read_byte(0xdffc + offset);
+}
+
+static WRITE8_HANDLER( sms_kor_nobank_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ UINT8 *SOURCE_BIOS;
+ UINT8 *SOURCE = NULL;
+
+ space->write_byte(0xdffc + offset, data);
+ state->m_mapper[offset] = data;
+
+ // FIXME: the code below (which only handles the BIOS bankswitch at start) should be cleaned up
+ // currently, it's just a stripped down version of sms_mapper_w
+ if (state->m_BIOS)
+ SOURCE_BIOS = state->m_BIOS + ((state->m_bios_page_count > 0) ? data % state->m_bios_page_count : 0) * 0x4000;
+ else
+ SOURCE_BIOS = state->m_banking_none[1];
+
+ if (state->m_bios_port & IO_BIOS_ROM || (state->m_is_gamegear && state->m_BIOS == NULL))
+ return;
+ else
+ {
+ if (!state->m_BIOS)
+ return;
+ SOURCE = SOURCE_BIOS;
+ }
+
+// printf("write %x, %x\n", offset, data);
+
+ switch (offset)
+ {
+ case 0: /* Control */
+ if (!(data & 0x08)) /* it's not ram */
+ {
+ if (!(state->m_bios_port & IO_BIOS_ROM) && state->m_has_bios)
+ {
+ SOURCE = state->m_banking_bios[5];
+ memory_set_bankptr(space->machine(), "bank5", SOURCE);
+ memory_set_bankptr(space->machine(), "bank6", SOURCE + 0x2000);
+ }
+ }
+ break;
+
+ case 1: /* Select 16k ROM bank for 0400-3FFF */
+ state->m_banking_bios[2] = SOURCE_BIOS + 0x0400;
+ memory_set_bankptr(space->machine(), "bank2", SOURCE + 0x0400);
+ break;
+
+ case 2: /* Select 16k ROM bank for 4000-7FFF */
+ state->m_banking_bios[3] = SOURCE_BIOS;
+ memory_set_bankptr(space->machine(), "bank3", SOURCE);
+ memory_set_bankptr(space->machine(), "bank4", SOURCE + 0x2000);
+ break;
+
+ case 3: /* Select 16k ROM bank for 8000-BFFF */
+ state->m_banking_bios[5] = SOURCE_BIOS;
+ if (!(state->m_mapper[0] & 0x08)) /* is RAM disabled? */
+ {
+ memory_set_bankptr(space->machine(), "bank5", SOURCE);
+ memory_set_bankptr(space->machine(), "bank6", SOURCE + 0x2000);
+ }
+ break;
+ }
+}
+
+WRITE8_HANDLER( sms_bios_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ state->m_bios_port = data;
+
+ logerror("bios write %02x, pc: %04x\n", data, cpu_get_pc(&space->device()));
+
+ setup_rom(space);
+}
+
+
+WRITE8_HANDLER( sms_cartram2_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+
+ if (state->m_mapper[0] & 0x08)
+ {
+ logerror("write %02X to cartram at offset #%04X\n", data, offset + 0x2000);
+ if (state->m_mapper[0] & 0x04)
+ {
+ state->m_cartridge[state->m_current_cartridge].cartSRAM[offset + 0x6000] = data;
+ }
+ else
+ {
+ state->m_cartridge[state->m_current_cartridge].cartSRAM[offset + 0x2000] = data;
+ }
+ }
+
+ if (state->m_cartridge[state->m_current_cartridge].features & CF_CODEMASTERS_MAPPER)
+ {
+ state->m_cartridge[state->m_current_cartridge].cartRAM[state->m_cartridge[state->m_current_cartridge].ram_page * 0x2000 + offset] = data;
+ }
+
+ if (state->m_cartridge[state->m_current_cartridge].features & CF_KOREAN_MAPPER && offset == 0) /* Dodgeball King mapper */
+ {
+ UINT8 rom_page_count = state->m_cartridge[state->m_current_cartridge].size / 0x4000;
+ int page = (rom_page_count > 0) ? data % rom_page_count : 0;
+
+ if (!state->m_cartridge[state->m_current_cartridge].ROM)
+ return;
+
+ state->m_banking_cart[5] = state->m_cartridge[state->m_current_cartridge].ROM + page * 0x4000;
+ memory_set_bankptr(space->machine(), "bank5", state->m_banking_cart[5]);
+ memory_set_bankptr(space->machine(), "bank6", state->m_banking_cart[5] + 0x2000);
+ LOG(("rom 2 paged in %x (Korean mapper).\n", page));
+ }
+}
+
+
+WRITE8_HANDLER( sms_cartram_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ int page;
+
+ if (state->m_mapper[0] & 0x08)
+ {
+ logerror("write %02X to cartram at offset #%04X\n", data, offset);
+ if (state->m_mapper[0] & 0x04)
+ {
+ state->m_cartridge[state->m_current_cartridge].cartSRAM[offset + 0x4000] = data;
+ }
+ else
+ {
+ state->m_cartridge[state->m_current_cartridge].cartSRAM[offset] = data;
+ }
+ }
+ else
+ {
+ if (state->m_cartridge[state->m_current_cartridge].features & CF_CODEMASTERS_MAPPER && offset == 0) /* Codemasters mapper */
+ {
+ UINT8 rom_page_count = state->m_cartridge[state->m_current_cartridge].size / 0x4000;
+ page = (rom_page_count > 0) ? data % rom_page_count : 0;
+ if (!state->m_cartridge[state->m_current_cartridge].ROM)
+ return;
+ state->m_banking_cart[5] = state->m_cartridge[state->m_current_cartridge].ROM + page * 0x4000;
+ memory_set_bankptr(space->machine(), "bank5", state->m_banking_cart[5]);
+ memory_set_bankptr(space->machine(), "bank6", state->m_banking_cart[5] + 0x2000);
+ LOG(("rom 2 paged in %x (Codemasters mapper).\n", page));
+ }
+ else if (state->m_cartridge[state->m_current_cartridge].features & CF_ONCART_RAM)
+ {
+ state->m_cartridge[state->m_current_cartridge].cartRAM[offset & (state->m_cartridge[state->m_current_cartridge].ram_size - 1)] = data;
+ }
+ else
+ {
+ logerror("INVALID write %02X to cartram at offset #%04X\n", data, offset);
+ }
+ }
+}
+
+
+WRITE8_HANDLER( gg_sio_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ logerror("*** write %02X to SIO register #%d\n", data, offset);
+
+ state->m_gg_sio[offset & 0x07] = data;
+ switch (offset & 7)
+ {
+ case 0x00: /* Parallel Data */
+ break;
+
+ case 0x01: /* Data Direction/ NMI Enable */
+ break;
+
+ case 0x02: /* Serial Output */
+ break;
+
+ case 0x03: /* Serial Input */
+ break;
+
+ case 0x04: /* Serial Control / Status */
+ break;
+ }
+}
+
+
+READ8_HANDLER( gg_sio_r )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ logerror("*** read SIO register #%d\n", offset);
+
+ switch (offset & 7)
+ {
+ case 0x00: /* Parallel Data */
+ break;
+
+ case 0x01: /* Data Direction/ NMI Enable */
+ break;
+
+ case 0x02: /* Serial Output */
+ break;
+
+ case 0x03: /* Serial Input */
+ break;
+
+ case 0x04: /* Serial Control / Status */
+ break;
+ }
+
+ return state->m_gg_sio[offset];
+}
+
+static void sms_machine_stop( running_machine &machine )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+
+ /* Does the cartridge have SRAM that should be saved? */
+ if (state->m_cartridge[state->m_current_cartridge].sram_save) {
+ device_image_interface *image = dynamic_cast<device_image_interface *>(machine.device("cart1"));
+ image->battery_save(state->m_cartridge[state->m_current_cartridge].cartSRAM, sizeof(UINT8) * NVRAM_SIZE );
+ }
+}
+
+
+static void setup_rom( address_space *space )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ running_machine &machine = space->machine();
+
+ /* 1. set up bank pointers to point to nothing */
+ memory_set_bankptr(machine, "bank1", state->m_banking_none[1]);
+ memory_set_bankptr(machine, "bank2", state->m_banking_none[2]);
+ memory_set_bankptr(machine, "bank3", state->m_banking_none[3]);
+ memory_set_bankptr(machine, "bank4", state->m_banking_none[3] + 0x2000);
+ memory_set_bankptr(machine, "bank5", state->m_banking_none[5]);
+ memory_set_bankptr(machine, "bank6", state->m_banking_none[5] + 0x2000);
+
+ /* 2. check and set up expansion port */
+ if (!(state->m_bios_port & IO_EXPANSION) && (state->m_bios_port & IO_CARTRIDGE) && (state->m_bios_port & IO_CARD))
+ {
+ /* TODO: Implement me */
+ logerror("Switching to unsupported expansion port.\n");
+ }
+
+ /* 3. check and set up card rom */
+ if (!(state->m_bios_port & IO_CARD) && (state->m_bios_port & IO_CARTRIDGE) && (state->m_bios_port & IO_EXPANSION))
+ {
+ /* TODO: Implement me */
+ logerror("Switching to unsupported card rom port.\n");
+ }
+
+ /* 4. check and set up cartridge rom */
+ /* if ((!(bios_port & IO_CARTRIDGE) && (bios_port & IO_EXPANSION) && (bios_port & IO_CARD)) || state->m_is_gamegear) { */
+ /* Out Run Europa initially writes a value to port 3E where IO_CARTRIDGE, IO_EXPANSION and IO_CARD are reset */
+ if ((!(state->m_bios_port & IO_CARTRIDGE)) || state->m_is_gamegear)
+ {
+ memory_set_bankptr(machine, "bank1", state->m_banking_cart[1]);
+ memory_set_bankptr(machine, "bank2", state->m_banking_cart[2]);
+ memory_set_bankptr(machine, "bank3", state->m_banking_cart[3]);
+ memory_set_bankptr(machine, "bank4", state->m_banking_cart[3] + 0x2000);
+ memory_set_bankptr(machine, "bank5", state->m_banking_cart[5]);
+ memory_set_bankptr(machine, "bank6", state->m_banking_cart[5] + 0x2000);
+ logerror("Switched in cartridge rom.\n");
+ }
+
+ /* 5. check and set up bios rom */
+ if (!(state->m_bios_port & IO_BIOS_ROM))
+ {
+ /* 0x0400 bioses */
+ if (state->m_has_bios_0400)
+ {
+ memory_set_bankptr(machine, "bank1", state->m_banking_bios[1]);
+ logerror("Switched in 0x0400 bios.\n");
+ }
+ /* 0x2000 bioses */
+ if (state->m_has_bios_2000)
+ {
+ memory_set_bankptr(machine, "bank1", state->m_banking_bios[1]);
+ memory_set_bankptr(machine, "bank2", state->m_banking_bios[2]);
+ logerror("Switched in 0x2000 bios.\n");
+ }
+ if (state->m_has_bios_full)
+ {
+ memory_set_bankptr(machine, "bank1", state->m_banking_bios[1]);
+ memory_set_bankptr(machine, "bank2", state->m_banking_bios[2]);
+ memory_set_bankptr(machine, "bank3", state->m_banking_bios[3]);
+ memory_set_bankptr(machine, "bank4", state->m_banking_bios[3] + 0x2000);
+ memory_set_bankptr(machine, "bank5", state->m_banking_bios[5]);
+ memory_set_bankptr(machine, "bank6", state->m_banking_bios[5] + 0x2000);
+ logerror("Switched in full bios.\n");
+ }
+ }
+
+ if (state->m_cartridge[state->m_current_cartridge].features & CF_ONCART_RAM)
+ {
+ memory_set_bankptr(machine, "bank5", state->m_cartridge[state->m_current_cartridge].cartRAM);
+ memory_set_bankptr(machine, "bank6", state->m_cartridge[state->m_current_cartridge].cartRAM);
+ }
+}
+
+
+static int sms_verify_cart( UINT8 *magic, int size )
+{
+ int retval;
+
+ retval = IMAGE_VERIFY_FAIL;
+
+ /* Verify the file is a valid image - check $7ff0 for "TMR SEGA" */
+ if (size >= 0x8000)
+ {
+ if (!strncmp((char*)&magic[0x7ff0], "TMR SEGA", 8))
+ {
+#if 0
+ /* Technically, it should be this, but remove for now until verified: */
+ if (!strcmp(sysname, "gamegear"))
+ {
+ if ((unsigned char)magic[0x7ffd] < 0x50)
+ retval = IMAGE_VERIFY_PASS;
+ }
+ if (!strcmp(sysname, "sms"))
+ {
+ if ((unsigned char)magic[0x7ffd] >= 0x50)
+ retval = IMAGE_VERIFY_PASS;
+ }
+#endif
+ retval = IMAGE_VERIFY_PASS;
+ }
+
+#if 0
+ /* Check at $81f0 also */
+ if (!retval)
+ {
+ if (!strncmp(&magic[0x81f0], "TMR SEGA", 8))
+ {
+#if 0
+ /* Technically, it should be this, but remove for now until verified: */
+ if (!strcmp(sysname, "gamegear"))
+ {
+ if ((unsigned char)magic[0x81fd] < 0x50)
+ retval = IMAGE_VERIFY_PASS;
+ }
+ if (!strcmp(sysname, "sms"))
+ {
+ if ((unsigned char)magic[0x81fd] >= 0x50)
+ retval = IMAGE_VERIFY_PASS;
+ }
+#endif
+ retval = IMAGE_VERIFY_PASS;
+ }
+ }
+#endif
+
+ }
+
+ return retval;
+}
+
+#ifdef UNUSED_FUNCTION
+// For the moment we switch to a different detection routine which allows to detect
+// in a single run Codemasters mapper, Korean mapper (including Jang Pung 3 which
+// uses a diff signature then the one below here) and Zemina mapper (used by Wonsiin, etc.).
+// I leave these here to document alt. detection routines and in the case these functions
+// can be updated
+
+/* Check for Codemasters mapper
+ 0x7FE3 - 93 - sms Cosmis Spacehead
+ - sms Dinobasher
+ - sms The Excellent Dizzy Collection
+ - sms Fantastic Dizzy
+ - sms Micro Machines
+ - gamegear Cosmic Spacehead
+ - gamegear Micro Machines
+ - 94 - gamegear Dropzone
+ - gamegear Ernie Els Golf (also has 64KB additional RAM on the cartridge)
+ - gamegear Pete Sampras Tennis
+ - gamegear S.S. Lucifer
+ - 95 - gamegear Micro Machines 2 - Turbo Tournament
+
+The Korean game Jang Pung II also seems to use a codemasters style mapper.
+ */
+static int detect_codemasters_mapper( UINT8 *rom )
+{
+ static const UINT8 jang_pung2[16] = { 0x00, 0xba, 0x38, 0x0d, 0x00, 0xb8, 0x38, 0x0c, 0x00, 0xb6, 0x38, 0x0b, 0x00, 0xb4, 0x38, 0x0a };
+
+ if (((rom[0x7fe0] & 0x0f ) <= 9) && (rom[0x7fe3] == 0x93 || rom[0x7fe3] == 0x94 || rom[0x7fe3] == 0x95) && rom[0x7fef] == 0x00)
+ return 1;
+
+ if (!memcmp(&rom[0x7ff0], jang_pung2, 16))
+ return 1;
+
+ return 0;
+}
+
+
+static int detect_korean_mapper( UINT8 *rom )
+{
+ static const UINT8 signatures[2][16] =
+ {
+ { 0x3e, 0x11, 0x32, 0x00, 0xa0, 0x78, 0xcd, 0x84, 0x85, 0x3e, 0x02, 0x32, 0x00, 0xa0, 0xc9, 0xff }, /* Dodgeball King */
+ { 0x41, 0x48, 0x37, 0x37, 0x44, 0x37, 0x4e, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20 }, /* Sangokushi 3 */
+ };
+ int i;
+
+ for (i = 0; i < 2; i++)
+ {
+ if (!memcmp(&rom[0x7ff0], signatures[i], 16))
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+#endif
+
+
+static int detect_tvdraw( UINT8 *rom )
+{
+ static const UINT8 terebi_oekaki[7] = { 0x61, 0x6e, 0x6e, 0x61, 0x6b, 0x6d, 0x6e }; // "annakmn"
+
+ if (!memcmp(&rom[0x13b3], terebi_oekaki, 7))
+ return 1;
+
+ return 0;
+}
+
+
+static int detect_lphaser_xoffset( running_machine &machine, UINT8 *rom )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+
+ static const UINT8 signatures[6][16] =
+ {
+ /* Spacegun */
+ { 0x54, 0x4d, 0x52, 0x20, 0x53, 0x45, 0x47, 0x41, 0xff, 0xff, 0x9d, 0x99, 0x10, 0x90, 0x00, 0x40 },
+ /* Gangster Town */
+ { 0x54, 0x4d, 0x52, 0x20, 0x53, 0x45, 0x47, 0x41, 0x19, 0x87, 0x1b, 0xc9, 0x74, 0x50, 0x00, 0x4f },
+ /* Shooting Gallery */
+ { 0x54, 0x4d, 0x52, 0x20, 0x53, 0x45, 0x47, 0x41, 0x20, 0x20, 0x8a, 0x3a, 0x72, 0x50, 0x00, 0x4f },
+ /* Rescue Mission */
+ { 0x54, 0x4d, 0x52, 0x20, 0x53, 0x45, 0x47, 0x41, 0x20, 0x20, 0xfb, 0xd3, 0x06, 0x51, 0x00, 0x4f },
+ /* Laser Ghost */
+ { 0x54, 0x4d, 0x52, 0x20, 0x53, 0x45, 0x47, 0x41, 0x00, 0x00, 0xb7, 0x55, 0x74, 0x70, 0x00, 0x40 },
+ /* Assault City */
+ { 0x54, 0x4d, 0x52, 0x20, 0x53, 0x45, 0x47, 0x41, 0xff, 0xff, 0x9f, 0x74, 0x34, 0x70, 0x00, 0x40 },
+ };
+
+ if (!(state->m_bios_port & IO_CARTRIDGE) && state->m_cartridge[state->m_current_cartridge].size >= 0x8000)
+ {
+ if (!memcmp(&rom[0x7ff0], signatures[0], 16) || !memcmp(&rom[0x7ff0], signatures[1], 16))
+ return 40;
+
+ if (!memcmp(&rom[0x7ff0], signatures[2], 16))
+ return 49;
+
+ if (!memcmp(&rom[0x7ff0], signatures[3], 16))
+ return 47;
+
+ if (!memcmp(&rom[0x7ff0], signatures[4], 16))
+ return 44;
+
+ if (!memcmp(&rom[0x7ff0], signatures[5], 16))
+ return 53;
+
+ }
+ return 50;
+}
+
+
+DEVICE_START( sms_cart )
+{
+ sms_state *state = device->machine().driver_data<sms_state>();
+ int i;
+
+ for (i = 0; i < MAX_CARTRIDGES; i++)
+ {
+ state->m_cartridge[i].ROM = NULL;
+ state->m_cartridge[i].size = 0;
+ state->m_cartridge[i].features = 0;
+ state->m_cartridge[i].cartSRAM = NULL;
+ state->m_cartridge[i].sram_save = 0;
+ state->m_cartridge[i].cartRAM = NULL;
+ state->m_cartridge[i].ram_size = 0;
+ state->m_cartridge[i].ram_page = 0;
+ }
+ state->m_current_cartridge = 0;
+
+ state->m_bios_port = (IO_EXPANSION | IO_CARTRIDGE | IO_CARD);
+ if (!state->m_is_gamegear && !state->m_has_bios)
+ {
+ state->m_bios_port &= ~(IO_CARTRIDGE);
+ state->m_bios_port |= IO_BIOS_ROM;
+ }
+}
+
+
+DEVICE_IMAGE_LOAD( sms_cart )
+{
+ running_machine &machine = image.device().machine();
+ sms_state *state = machine.driver_data<sms_state>();
+ int size, index = 0, offset = 0;
+ const char *extrainfo = NULL;
+
+ if (strcmp(image.device().tag(), "cart1") == 0)
+ index = 0;
+ if (strcmp(image.device().tag(), "cart2") == 0)
+ index = 1;
+ if (strcmp(image.device().tag(), "cart3") == 0)
+ index = 2;
+ if (strcmp(image.device().tag(), "cart4") == 0)
+ index = 3;
+ if (strcmp(image.device().tag(), "cart5") == 0)
+ index = 4;
+ if (strcmp(image.device().tag(), "cart6") == 0)
+ index = 5;
+ if (strcmp(image.device().tag(), "cart7") == 0)
+ index = 6;
+ if (strcmp(image.device().tag(), "cart8") == 0)
+ index = 7;
+ if (strcmp(image.device().tag(), "cart9") == 0)
+ index = 8;
+ if (strcmp(image.device().tag(), "cart10") == 0)
+ index = 9;
+ if (strcmp(image.device().tag(), "cart11") == 0)
+ index = 10;
+ if (strcmp(image.device().tag(), "cart12") == 0)
+ index = 11;
+ if (strcmp(image.device().tag(), "cart13") == 0)
+ index = 12;
+ if (strcmp(image.device().tag(), "cart14") == 0)
+ index = 13;
+ if (strcmp(image.device().tag(), "cart15") == 0)
+ index = 14;
+ if (strcmp(image.device().tag(), "cart16") == 0)
+ index = 15;
+
+ state->m_cartridge[index].features = 0;
+
+ if (image.software_entry() == NULL)
+ {
+ size = image.length();
+ extrainfo = hashfile_extrainfo(image);
+ }
+ else
+ size = image.get_software_region_length("rom");
+
+ /* Check for 512-byte header */
+ if ((size / 512) & 1)
+ {
+ offset = 512;
+ size -= 512;
+ }
+
+ if (!size)
+ {
+ image.seterror(IMAGE_ERROR_UNSPECIFIED, "Invalid ROM image: ROM image is too small");
+ return IMAGE_INIT_FAIL;
+ }
+
+ /* Create a new memory region to hold the ROM. */
+ /* Make sure the region holds only complete (0x4000) rom banks */
+ state->m_cartridge[index].size = (size & 0x3fff) ? (((size >> 14) + 1) << 14) : size;
+ state->m_cartridge[index].ROM = auto_alloc_array(machine, UINT8, state->m_cartridge[index].size);
+ state->m_cartridge[index].cartSRAM = auto_alloc_array(machine, UINT8, NVRAM_SIZE);
+
+ /* Load ROM banks */
+ if (image.software_entry() == NULL)
+ {
+ image.fseek(offset, SEEK_SET);
+
+ if (image.fread( state->m_cartridge[index].ROM, size) != size)
+ return IMAGE_INIT_FAIL;
+ }
+ else
+ {
+ memcpy(state->m_cartridge[index].ROM, image.get_software_region("rom") + offset, size);
+
+ // check for mapper feature (currently only KOREAN_NOBANK, but eventually we will add the other mappers as well)
+ if (image.get_feature("pcb") != NULL)
+ {
+ const char *mapper = image.get_feature("pcb");
+ if (!strcmp(mapper, "korean_nobank"))
+ state->m_cartridge[index].features |= CF_KOREAN_NOBANK_MAPPER;
+ }
+ }
+
+ /* check the image */
+ if (!state->m_has_bios)
+ {
+ if (sms_verify_cart(state->m_cartridge[index].ROM, size) == IMAGE_VERIFY_FAIL)
+ {
+ logerror("Warning loading image: sms_verify_cart failed\n");
+ }
+ }
+
+ /* Detect special features from the extrainfo field */
+ if (extrainfo)
+ {
+ /* Check for codemasters mapper */
+ if (strstr(extrainfo, "CODEMASTERS"))
+ state->m_cartridge[index].features |= CF_CODEMASTERS_MAPPER;
+
+ /* Check for korean mapper */
+ if (strstr(extrainfo, "KOREAN"))
+ state->m_cartridge[index].features |= CF_KOREAN_MAPPER;
+
+ /* Check for special SMS Compatibility mode gamegear cartridges */
+ if (state->m_is_gamegear && strstr(extrainfo, "GGSMS"))
+ state->m_cartridge[index].features |= CF_GG_SMS_MODE;
+
+ /* Check for 93C46 eeprom */
+ if (strstr(extrainfo, "93C46"))
+ state->m_cartridge[index].features |= CF_93C46_EEPROM;
+
+ /* Check for 8KB on-cart RAM */
+ if (strstr(extrainfo, "8KB_CART_RAM"))
+ {
+ state->m_cartridge[index].features |= CF_ONCART_RAM;
+ state->m_cartridge[index].ram_size = 0x2000;
+ state->m_cartridge[index].cartRAM = auto_alloc_array(machine, UINT8, state->m_cartridge[index].ram_size);
+ }
+ }
+ else
+ {
+ /* If no extrainfo information is available try to find special information out on our own */
+ /* Check for special cartridge features (new routine, courtesy of Omar Cornut, from MEKA) */
+ if (size >= 0x8000)
+ {
+ int c0002 = 0, c8000 = 0, cA000 = 0, cFFFF = 0, i;
+ for (i = 0; i < 0x8000; i++)
+ {
+ if (state->m_cartridge[index].ROM[i] == 0x32) // Z80 opcode for: LD (xxxx), A
+ {
+ UINT16 addr = (state->m_cartridge[index].ROM[i + 2] << 8) | state->m_cartridge[index].ROM[i + 1];
+ if (addr == 0xFFFF)
+ { i += 2; cFFFF++; continue; }
+ if (addr == 0x0002 || addr == 0x0003 || addr == 0x0004)
+ { i += 2; c0002++; continue; }
+ if (addr == 0x8000)
+ { i += 2; c8000++; continue; }
+ if (addr == 0xA000)
+ { i += 2; cA000++; continue; }
+ }
+ }
+
+ LOG(("Mapper test: c002 = %d, c8000 = %d, cA000 = %d, cFFFF = %d\n", c0002, c8000, cA000, cFFFF));
+
+ // 2 is a security measure, although tests on existing ROM showed it was not needed
+ if (c0002 > cFFFF + 2 || (c0002 > 0 && cFFFF == 0))
+ state->m_cartridge[index].features |= CF_KOREAN_ZEMINA_MAPPER;
+ else if (c8000 > cFFFF + 2 || (c8000 > 0 && cFFFF == 0))
+ state->m_cartridge[index].features |= CF_CODEMASTERS_MAPPER;
+ else if (cA000 > cFFFF + 2 || (cA000 > 0 && cFFFF == 0))
+ state->m_cartridge[index].features |= CF_KOREAN_MAPPER;
+
+ }
+
+ /* Check for special SMS Compatibility mode gamegear cartridges */
+ if (state->m_is_gamegear && image.software_entry() == NULL) // not sure about how to handle this with softlists
+ {
+ /* Just in case someone passes us an sms file */
+ if (!mame_stricmp (image.filetype(), "sms"))
+ state->m_cartridge[index].features |= CF_GG_SMS_MODE;
+ }
+ }
+
+ if (state->m_cartridge[index].features & CF_CODEMASTERS_MAPPER)
+ {
+ state->m_cartridge[index].ram_size = 0x10000;
+ state->m_cartridge[index].cartRAM = auto_alloc_array(machine, UINT8, state->m_cartridge[index].ram_size);
+ state->m_cartridge[index].ram_page = 0;
+ }
+
+ /* For Light Phaser games, we have to detect the x offset */
+ state->m_lphaser_x_offs = detect_lphaser_xoffset(machine, state->m_cartridge[index].ROM);
+
+ /* Terebi Oekaki (TV Draw) is a SG1000 game with special input device which is compatible with SG1000 Mark III */
+ if ((detect_tvdraw(state->m_cartridge[index].ROM)) && state->m_is_region_japan)
+ {
+ address_space *program = machine.device("maincpu")->memory().space(AS_PROGRAM);
+ program->install_legacy_write_handler(0x6000, 0x6000, FUNC(sms_tvdraw_axis_w));
+ program->install_legacy_read_handler(0x8000, 0x8000, FUNC(sms_tvdraw_status_r));
+ program->install_legacy_read_handler(0xa000, 0xa000, FUNC(sms_tvdraw_data_r));
+ program->nop_write(0xa000, 0xa000);
+ }
+
+ if (state->m_cartridge[index].features & CF_KOREAN_NOBANK_MAPPER)
+ {
+ // FIXME: we should have by default FFFD-FFFF to be only a mirror for DFFD-DFFF (with no bankswitch logic)
+ // and then the handlers should be installed here for all but the KOREAN_NOBANK carts
+ // However, to avoid memory map breakage, we currently go the other way around
+ address_space *program = machine.device("maincpu")->memory().space(AS_PROGRAM);
+ program->install_legacy_readwrite_handler(0xfffc, 0xffff, FUNC(sms_kor_nobank_r), FUNC(sms_kor_nobank_w));
+ }
+
+ LOG(("Cart Features: %x\n", state->m_cartridge[index].features));
+
+ /* Load battery backed RAM, if available */
+ image.battery_load(state->m_cartridge[index].cartSRAM, sizeof(UINT8) * NVRAM_SIZE, 0x00);
+
+ return IMAGE_INIT_PASS;
+}
+
+
+static void setup_cart_banks( running_machine &machine )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ if (state->m_cartridge[state->m_current_cartridge].ROM)
+ {
+ UINT8 rom_page_count = state->m_cartridge[state->m_current_cartridge].size / 0x4000;
+ state->m_banking_cart[1] = state->m_cartridge[state->m_current_cartridge].ROM;
+ state->m_banking_cart[2] = state->m_cartridge[state->m_current_cartridge].ROM + 0x0400;
+ state->m_banking_cart[3] = state->m_cartridge[state->m_current_cartridge].ROM + ((1 < rom_page_count) ? 0x4000 : 0);
+ state->m_banking_cart[5] = state->m_cartridge[state->m_current_cartridge].ROM + ((2 < rom_page_count) ? 0x8000 : 0);
+ /* Codemasters mapper points to bank 0 for page 2 */
+ if (state->m_cartridge[state->m_current_cartridge].features & CF_CODEMASTERS_MAPPER)
+ {
+ state->m_banking_cart[5] = state->m_cartridge[state->m_current_cartridge].ROM;
+ }
+ }
+ else
+ {
+ state->m_banking_cart[1] = state->m_banking_none[1];
+ state->m_banking_cart[2] = state->m_banking_none[2];
+ state->m_banking_cart[3] = state->m_banking_none[3];
+ state->m_banking_cart[5] = state->m_banking_none[5];
+ }
+}
+
+#ifdef MESS
+static void setup_banks( running_machine &machine )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ UINT8 *mem = machine.region("maincpu")->base();
+ state->m_banking_bios[1] = state->m_banking_cart[1] = state->m_banking_none[1] = mem;
+ state->m_banking_bios[2] = state->m_banking_cart[2] = state->m_banking_none[2] = mem;
+ state->m_banking_bios[3] = state->m_banking_cart[3] = state->m_banking_none[3] = mem;
+ state->m_banking_bios[4] = state->m_banking_cart[4] = state->m_banking_none[4] = mem;
+ state->m_banking_bios[5] = state->m_banking_cart[5] = state->m_banking_none[5] = mem;
+ state->m_banking_bios[6] = state->m_banking_cart[6] = state->m_banking_none[6] = mem;
+
+ state->m_BIOS = machine.region("user1")->base();
+
+ state->m_bios_page_count = (state->m_BIOS ? machine.region("user1")->bytes() / 0x4000 : 0);
+
+ setup_cart_banks(machine);
+
+ if (state->m_BIOS == NULL || state->m_BIOS[0] == 0x00)
+ {
+ state->m_BIOS = NULL;
+ state->m_bios_port |= IO_BIOS_ROM;
+ }
+
+ if (state->m_BIOS)
+ {
+ state->m_banking_bios[1] = state->m_BIOS;
+ state->m_banking_bios[2] = state->m_BIOS + 0x0400;
+ state->m_banking_bios[3] = state->m_BIOS + ((1 < state->m_bios_page_count) ? 0x4000 : 0);
+ state->m_banking_bios[5] = state->m_BIOS + ((2 < state->m_bios_page_count) ? 0x8000 : 0);
+ }
+}
+#endif
+
+MACHINE_START( sms )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+
+ machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(sms_machine_stop),&machine));
+ state->m_rapid_fire_timer = machine.scheduler().timer_alloc(FUNC(rapid_fire_callback));
+ state->m_rapid_fire_timer->adjust(attotime::from_hz(10), 0, attotime::from_hz(10));
+
+ state->m_lphaser_1_timer = machine.scheduler().timer_alloc(FUNC(lphaser_1_callback));
+ state->m_lphaser_2_timer = machine.scheduler().timer_alloc(FUNC(lphaser_2_callback));
+
+ state->m_main_cpu = machine.device("maincpu");
+ state->m_control_cpu = machine.device("control");
+ state->m_vdp = machine.device("sms_vdp");
+ state->m_ym = machine.device("ym2413");
+ state->m_main_scr = machine.device("screen");
+ state->m_left_lcd = machine.device("left_lcd");
+ state->m_right_lcd = machine.device("right_lcd");
+
+ /* Check if lightgun has been chosen as input: if so, enable crosshair */
+ machine.scheduler().timer_set(attotime::zero, FUNC(lightgun_tick));
+}
+
+#ifdef MESS
+MACHINE_RESET( sms )
+{
+ address_space *space = machine.device("maincpu")->memory().space(AS_PROGRAM);
+ sms_state *state = machine.driver_data<sms_state>();
+
+ state->m_ctrl_reg = 0xff;
+ if (state->m_has_fm)
+ state->m_fm_detect = 0x01;
+
+ state->m_mapper_ram = (UINT8*)space->get_write_ptr(0xdffc);
+
+ state->m_bios_port = 0;
+
+ if (state->m_cartridge[state->m_current_cartridge].features & CF_CODEMASTERS_MAPPER)
+ {
+ /* Install special memory handlers */
+ space->install_legacy_write_handler(0x0000, 0x0000, FUNC(sms_codemasters_page0_w));
+ space->install_legacy_write_handler(0x4000, 0x4000, FUNC(sms_codemasters_page1_w));
+ }
+
+ if (state->m_cartridge[state->m_current_cartridge].features & CF_KOREAN_ZEMINA_MAPPER)
+ space->install_legacy_write_handler(0x0000, 0x0003, FUNC(sms_korean_zemina_banksw_w));
+
+ if (state->m_cartridge[state->m_current_cartridge].features & CF_GG_SMS_MODE)
+ sms_vdp_set_ggsmsmode(state->m_vdp, 1);
+
+ /* Initialize SIO stuff for GG */
+ state->m_gg_sio[0] = 0x7f;
+ state->m_gg_sio[1] = 0xff;
+ state->m_gg_sio[2] = 0x00;
+ state->m_gg_sio[3] = 0xff;
+ state->m_gg_sio[4] = 0x00;
+
+ state->m_store_control = 0;
+
+ setup_banks(machine);
+
+ setup_rom(space);
+
+ state->m_rapid_fire_state_1 = 0;
+ state->m_rapid_fire_state_2 = 0;
+
+ state->m_last_paddle_read_time = 0;
+ state->m_paddle_read_state = 0;
+
+ state->m_last_sports_pad_time_1 = 0;
+ state->m_last_sports_pad_time_2 = 0;
+ state->m_sports_pad_state_1 = 0;
+ state->m_sports_pad_state_2 = 0;
+ state->m_sports_pad_last_data_1 = 0;
+ state->m_sports_pad_last_data_2 = 0;
+ state->m_sports_pad_1_x = 0;
+ state->m_sports_pad_1_y = 0;
+ state->m_sports_pad_2_x = 0;
+ state->m_sports_pad_2_y = 0;
+
+ state->m_lphaser_1_latch = 0;
+ state->m_lphaser_2_latch = 0;
+
+ state->m_sscope_state = 0;
+
+ state->m_tvdraw_data = 0;
+}
+#endif
+
+READ8_HANDLER( sms_store_cart_select_r )
+{
+ return 0xff;
+}
+
+
+WRITE8_HANDLER( sms_store_cart_select_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ UINT8 slot = data >> 4;
+ UINT8 slottype = data & 0x08;
+
+ logerror("switching in part of %s slot #%d\n", slottype ? "card" : "cartridge", slot );
+ /* cartridge? slot #0 */
+ if (slottype == 0)
+ state->m_current_cartridge = slot;
+
+ setup_cart_banks(space->machine());
+ memory_set_bankptr(space->machine(), "bank10", state->m_banking_cart[3] + 0x2000);
+ setup_rom(space);
+}
+
+
+READ8_HANDLER( sms_store_select1 )
+{
+ return 0xff;
+}
+
+
+READ8_HANDLER( sms_store_select2 )
+{
+ return 0xff;
+}
+
+
+READ8_HANDLER( sms_store_control_r )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ return state->m_store_control;
+}
+
+
+WRITE8_HANDLER( sms_store_control_w )
+{
+ sms_state *state = space->machine().driver_data<sms_state>();
+ logerror("0x%04X: sms_store_control write 0x%02X\n", cpu_get_pc(&space->device()), data);
+ if (data & 0x02)
+ {
+ space->machine().device<cpu_device>("maincpu")->resume(SUSPEND_REASON_HALT);
+ }
+ else
+ {
+ /* Pull reset line of CPU #0 low */
+ space->machine().device("maincpu")->reset();
+ space->machine().device<cpu_device>("maincpu")->suspend(SUSPEND_REASON_HALT, 1);
+ }
+ state->m_store_control = data;
+}
+
+void sms_store_int_callback( running_machine &machine, int state )
+{
+ sms_state *driver_state = machine.driver_data<sms_state>();
+ device_set_input_line(driver_state->m_store_control & 0x01 ? driver_state->m_control_cpu : driver_state->m_main_cpu, 0, state);
+}
+
+
+static void sms_set_zero_flag( running_machine &machine )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ state->m_is_gamegear = 0;
+ state->m_is_region_japan = 0;
+ state->m_has_bios_0400 = 0;
+ state->m_has_bios_2000 = 0;
+ state->m_has_bios_full = 0;
+ state->m_has_bios = 0;
+ state->m_has_fm = 0;
+}
+
+DRIVER_INIT( sg1000m3 )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ sms_set_zero_flag(machine);
+ state->m_is_region_japan = 1;
+ state->m_has_fm = 1;
+}
+
+
+DRIVER_INIT( sms1 )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ sms_set_zero_flag(machine);
+ state->m_has_bios_full = 1;
+}
+
+
+DRIVER_INIT( smsj )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ sms_set_zero_flag(machine);
+ state->m_is_region_japan = 1;
+ state->m_has_bios_2000 = 1;
+ state->m_has_fm = 1;
+}
+
+
+DRIVER_INIT( sms2kr )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ sms_set_zero_flag(machine);
+ state->m_is_region_japan = 1;
+ state->m_has_bios_full = 1;
+ state->m_has_fm = 1;
+}
+
+
+DRIVER_INIT( smssdisp )
+{
+ sms_set_zero_flag(machine);
+}
+
+
+DRIVER_INIT( gamegear )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ sms_set_zero_flag(machine);
+ state->m_is_gamegear = 1;
+}
+
+
+DRIVER_INIT( gamegeaj )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ sms_set_zero_flag(machine);
+ state->m_is_region_japan = 1;
+ state->m_is_gamegear = 1;
+ state->m_has_bios_0400 = 1;
+}
+
+
+static void sms_black_bitmap( const screen_device *screen, bitmap_t *bitmap )
+{
+ const int width = screen->width();
+ const int height = screen->height();
+ int x, y;
+
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ *BITMAP_ADDR32(bitmap, y, x) = MAKE_RGB(0,0,0);
+}
+
+VIDEO_START( sms1 )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ screen_device *screen = machine.first_screen();
+ int width = screen->width();
+ int height = screen->height();
+
+ state->m_prevleft_bitmap = auto_bitmap_alloc(machine, width, height, BITMAP_FORMAT_INDEXED32);
+ state->m_prevright_bitmap = auto_bitmap_alloc(machine, width, height, BITMAP_FORMAT_INDEXED32);
+ state->save_item(NAME(*state->m_prevleft_bitmap));
+ state->save_item(NAME(*state->m_prevright_bitmap));
+}
+
+SCREEN_UPDATE( sms1 )
+{
+ sms_state *state = screen->machine().driver_data<sms_state>();
+ UINT8 sscope = input_port_read_safe(screen->machine(), "SEGASCOPE", 0x00);
+ UINT8 sscope_binocular_hack = input_port_read_safe(screen->machine(), "SSCOPE_BINOCULAR", 0x00);
+ UINT8 occluded_view = 0;
+
+ // without SegaScope, both LCDs for glasses go black
+ if ((screen != state->m_main_scr) && !sscope)
+ occluded_view = 1;
+
+ // with SegaScope, sscope_state 0 = left screen OFF, right screen ON
+ if (!(state->m_sscope_state & 0x01) && (screen == state->m_left_lcd))
+ occluded_view = 1;
+
+ // with SegaScope, sscope_state 1 = left screen ON, right screen OFF
+ if ((state->m_sscope_state & 0x01) && (screen == state->m_right_lcd))
+ occluded_view = 1;
+
+ if (!occluded_view)
+ {
+ sms_vdp_update(state->m_vdp, bitmap, cliprect);
+
+ // HACK: fake 3D->2D handling (if enabled, it repeats each frame twice on the selected lens)
+ // save a copy of current bitmap for the binocular hack
+ if (sscope && (screen == state->m_left_lcd) && (sscope_binocular_hack & 0x01))
+ copybitmap(state->m_prevleft_bitmap, bitmap, 0, 0, 0, 0, cliprect);
+ if (sscope && (screen == state->m_right_lcd) && (sscope_binocular_hack & 0x02))
+ copybitmap(state->m_prevright_bitmap, bitmap, 0, 0, 0, 0, cliprect);
+ }
+ else
+ {
+ sms_black_bitmap(screen, bitmap);
+
+ // HACK: fake 3D->2D handling (if enabled, it repeats each frame twice on the selected lens)
+ // use the copied bitmap for the binocular hack
+ if (sscope && (screen == state->m_left_lcd) && (sscope_binocular_hack & 0x01))
+ copybitmap(bitmap, state->m_prevleft_bitmap, 0, 0, 0, 0, cliprect);
+ if (sscope && (screen == state->m_right_lcd) && (sscope_binocular_hack & 0x02))
+ copybitmap(bitmap, state->m_prevright_bitmap, 0, 0, 0, 0, cliprect);
+ }
+
+ return 0;
+}
+
+SCREEN_UPDATE( sms )
+{
+ sms_state *state = screen->machine().driver_data<sms_state>();
+ sms_vdp_update(state->m_vdp, bitmap, cliprect);
+ return 0;
+}
+
+VIDEO_START( gamegear )
+{
+ sms_state *state = machine.driver_data<sms_state>();
+ screen_device *screen = machine.first_screen();
+ int width = screen->width();
+ int height = screen->height();
+
+ state->m_prev_bitmap = auto_bitmap_alloc(machine, width, height, BITMAP_FORMAT_INDEXED32);
+ state->m_tmp_bitmap = auto_bitmap_alloc(machine, width, height, BITMAP_FORMAT_INDEXED32);
+ state->save_item(NAME(*state->m_prev_bitmap));
+}
+
+SCREEN_UPDATE( gamegear )
+{
+ sms_state *state = screen->machine().driver_data<sms_state>();
+ int width = screen->width();
+ int height = screen->height();
+ int x, y;
+
+ sms_vdp_update(state->m_vdp, state->m_tmp_bitmap, cliprect);
+
+ // HACK: fake LCD persistence effect
+ // (it would be better to generalize this in the core, to be used for all LCD systems)
+ for (y = 0; y < height; y++)
+ {
+ UINT32 *line0 = BITMAP_ADDR32(state->m_tmp_bitmap, y, 0);
+ UINT32 *line1 = BITMAP_ADDR32(state->m_prev_bitmap, y, 0);
+ for (x = 0; x < width; x++)
+ {
+ UINT32 color0 = line0[x];
+ UINT32 color1 = line1[x];
+ UINT16 r0 = (color0 >> 16) & 0x000000ff;
+ UINT16 g0 = (color0 >> 8) & 0x000000ff;
+ UINT16 b0 = (color0 >> 0) & 0x000000ff;
+ UINT16 r1 = (color1 >> 16) & 0x000000ff;
+ UINT16 g1 = (color1 >> 8) & 0x000000ff;
+ UINT16 b1 = (color1 >> 0) & 0x000000ff;
+ UINT8 r = (UINT8)((r0 + r1) >> 1);
+ UINT8 g = (UINT8)((g0 + g1) >> 1);
+ UINT8 b = (UINT8)((b0 + b1) >> 1);
+ *BITMAP_ADDR32(bitmap, y, x) = (r << 16) | (g << 8) | b;
+ }
+ }
+ copybitmap(state->m_prev_bitmap, state->m_tmp_bitmap, 0, 0, 0, 0, cliprect);
+
+ return 0;
+}
diff --git a/src/mame/mame.mak b/src/mame/mame.mak
index 4502ee4d957..3d69df4b3af 100644
--- a/src/mame/mame.mak
+++ b/src/mame/mame.mak
@@ -18,6 +18,7 @@ DRIVERS = $(MAMEOBJ)/drivers
LAYOUT = $(MAMEOBJ)/layout
MACHINE = $(MAMEOBJ)/machine
VIDEO = $(MAMEOBJ)/video
+FORMATS = $(MAMEOBJ)/formats
OBJDIRS += \
$(AUDIO) \
@@ -25,6 +26,7 @@ OBJDIRS += \
$(LAYOUT) \
$(MACHINE) \
$(VIDEO) \
+ $(FORMATS) \
@@ -335,6 +337,7 @@ DRVLIBS += \
$(MAMEOBJ)/misc.a \
$(MAMEOBJ)/pinball.a \
$(MAMEOBJ)/shared.a \
+ $(MAMEOBJ)/formats.a \
@@ -345,6 +348,7 @@ DRVLIBS += \
$(MAMEOBJ)/shared.a: \
$(MACHINE)/nmk112.o \
+ $(MACHINE)/ctronics.o \
$(MACHINE)/pckeybrd.o \
$(MACHINE)/pcshare.o \
$(MACHINE)/segacrpt.o \
@@ -352,7 +356,12 @@ $(MAMEOBJ)/shared.a: \
$(MACHINE)/ticket.o \
$(VIDEO)/avgdvg.o \
+#-------------------------------------------------
+# file formats, mostly used for MESS software
+#-------------------------------------------------
+$(MAMEOBJ)/formats.a: \
+ $(FORMATS)/basicdsk.o \
#-------------------------------------------------
# manufacturer-specific groupings for drivers
@@ -1219,6 +1228,13 @@ $(MAMEOBJ)/sega.a: \
$(VIDEO)/segaic16.o \
$(VIDEO)/sega16sp.o \
$(VIDEO)/segaic24.o \
+ $(VIDEO)/smsvdp.o \
+ $(MACHINE)/segasms.o \
+ $(DRIVERS)/segasms.o \
+ $(DRIVERS)/sg1000.o \
+ $(DRIVERS)/dc.o \
+ $(MACHINE)/dccons.o \
+ $(MACHINE)/gdrom.o \
$(MAMEOBJ)/seibu.a: \
$(DRIVERS)/bloodbro.o $(VIDEO)/bloodbro.o \
@@ -1976,6 +1992,8 @@ $(DRIVERS)/segaorun.o: $(LAYOUT)/outrun.lh
$(DRIVERS)/segas32.o: $(LAYOUT)/radr.lh
+$(DRIVERS)/segasms.o: $(LAYOUT)/sms1.lh
+
$(DRIVERS)/segaybd.o: $(LAYOUT)/pdrift.lh
$(DRIVERS)/snookr10.o: $(LAYOUT)/snookr10.lh
diff --git a/src/mame/video/smsvdp.c b/src/mame/video/smsvdp.c
new file mode 100644
index 00000000000..e3348f86c2c
--- /dev/null
+++ b/src/mame/video/smsvdp.c
@@ -0,0 +1,1530 @@
+/*********************************************************************
+
+ smsvdp.c
+
+ Implementation of video hardware chip used by Sega Master System
+
+**********************************************************************/
+
+/*
+ For more information, please see:
+ - http://cgfm2.emuviews.com/txt/msvdp.txt
+ - http://www.smspower.org/forums/viewtopic.php?p=44198
+
+A scanline contains the following sections:
+ - horizontal sync 1 ED => HSYNC high/increment line counter/generate interrupts/etc
+ - left blanking 2 ED-EE
+ - color burst 14 EE-EF
+ - left blanking 8 F5-F9
+ - left border 13 F9-FF
+ - active display 256 00-7F
+ - right border 15 80-87
+ - right blanking 8 87-8B
+ - horizontal sync 25 8B-97 => HSYNC low
+
+
+NTSC frame timing
+ 256x192 256x224 256x240 (doesn't work on real hardware)
+ - vertical blanking 3 D5-D7 3 E5-E7 3 EE-F0
+ - top blanking 13 D8-E4 13 E8-F4 13 F1-FD
+ - top border 27 E5-FF 11 F5-FF 3 FD-FF
+ - active display 192 00-BF 224 00-DF 240 00-EF
+ - bottom border 24 C0-D7 8 E0-E7 0 F0-F0
+ - bottom blanking 3 D8-DA 3 E8-EA 3 F0-F2
+
+
+PAL frame timing
+ 256x192 256x224 256x240
+ - vertical blanking 3 BA-BC 3 CA-CC 3 D2-D4
+ - top blanking 13 BD-C9 13 CD-D9 13 D5-E1
+ - top border 54 CA-FF 38 DA-FF 30 E2-FF
+ - active display 192 00-BF 224 00-DF 240 00-EF
+ - bottom border 48 C0-EF 32 E0-FF 24 F0-07
+ - bottom blanking 3 F0-F2 3 00-02 3 08-0A
+
+ TODO:
+ - implement differences between SMS1_VDP & SMS_VDP/GG_VDP
+
+*/
+
+#include "emu.h"
+#include "video/smsvdp.h"
+
+#define IS_SMS1_VDP (smsvdp->features & MODEL_315_5124)
+#define IS_SMS2_VDP (smsvdp->features & MODEL_315_5246)
+#define IS_GAMEGEAR_VDP (smsvdp->features & MODEL_315_5378)
+
+#define STATUS_VINT 0x80 /* Pending vertical interrupt flag */
+#define STATUS_SPROVR 0x40 /* Sprite overflow flag */
+#define STATUS_SPRCOL 0x20 /* Object collision flag */
+#define STATUS_HINT 0x02 /* Pending horizontal interrupt flag */
+
+#define VINT_HPOS 23
+#define HINT_HPOS 23
+#define VCOUNT_CHANGE_HPOS 22
+#define VINT_FLAG_HPOS 7
+#define SPROVR_HPOS 6
+#define SPRCOL_BASEHPOS 42
+#define DISPLAY_CB_HPOS 5 /* fix X-Scroll latchtime (Flubba's VDPTest) */
+
+#define GG_CRAM_SIZE 0x40 /* 32 colors x 2 bytes per color = 64 bytes */
+#define SMS_CRAM_SIZE 0x20 /* 32 colors x 1 bytes per color = 32 bytes */
+#define MAX_CRAM_SIZE 0x40
+
+#define VRAM_SIZE 0x4000
+
+#define PRIORITY_BIT 0x1000
+#define BACKDROP_COLOR ((smsvdp->vdp_mode == 4 ? 0x10 : 0x00) + (smsvdp->reg[0x07] & 0x0f))
+
+#define NUM_OF_REGISTER 0x10 /* 16 registers */
+
+#define INIT_VCOUNT 0
+#define VERTICAL_BLANKING 1
+#define TOP_BLANKING 2
+#define TOP_BORDER 3
+#define ACTIVE_DISPLAY_V 4
+#define BOTTOM_BORDER 5
+#define BOTTOM_BLANKING 6
+
+static const UINT8 sms_ntsc_192[7] = { 0xd5, 3, 13, 27, 192, 24, 3 };
+static const UINT8 sms_ntsc_224[7] = { 0xe5, 3, 13, 11, 224, 8, 3 };
+static const UINT8 sms_ntsc_240[7] = { 0xee, 3, 13, 3, 240, 0, 3 };
+static const UINT8 sms_pal_192[7] = { 0xba, 3, 13, 54, 192, 48, 3 };
+static const UINT8 sms_pal_224[7] = { 0xca, 3, 13, 38, 224, 32, 3 };
+static const UINT8 sms_pal_240[7] = { 0xd2, 3, 13, 30, 240, 24, 3 };
+
+
+typedef struct _smsvdp_t smsvdp_t;
+struct _smsvdp_t
+{
+ UINT32 features;
+ UINT8 reg[NUM_OF_REGISTER]; /* All the registers */
+ UINT8 status; /* Status register */
+ UINT8 reg9copy; /* Internal copy of register 9 */
+ UINT8 addrmode; /* Type of VDP action */
+ UINT16 addr; /* Contents of internal VDP address register */
+ UINT8 cram_mask; /* Mask to switch between SMS and GG CRAM sizes */
+ int cram_dirty; /* Have there been any changes to the CRAM area */
+ int pending;
+ UINT8 buffer;
+ int gg_sms_mode; /* Shrunk SMS screen on GG lcd mode flag */
+ int irq_state; /* The status of the IRQ line of the VDP */
+ int vdp_mode; /* Current mode of the VDP: 0,1,2,3,4 */
+ int y_pixels; /* 192, 224, 240 */
+ UINT8 line_counter;
+ UINT8 hcounter;
+ memory_region *VRAM; /* Pointer to VRAM */
+ memory_region *CRAM; /* Pointer to CRAM */
+ const UINT8 *sms_frame_timing;
+ bitmap_t *tmpbitmap;
+ UINT8 *collision_buffer;
+
+ /* line_buffer will be used to hold 5 lines of line data. Line #0 is the regular blitting area.
+ Lines #1-#4 will be used as a kind of cache to be used for vertical scaling in the gamegear
+ sms compatibility mode. */
+ int *line_buffer;
+ int current_palette[32];
+ smsvdp_int_cb int_callback;
+ smsvdp_pause_cb pause_callback;
+ emu_timer *smsvdp_display_timer;
+};
+
+static TIMER_CALLBACK( smsvdp_display_callback );
+static void sms_refresh_line( running_machine &machine, smsvdp_t *smsvdp, bitmap_t *bitmap, int offsetx, int offsety, int line );
+static void sms_update_palette( smsvdp_t *smsvdp );
+
+
+/*****************************************************************************
+ INLINE FUNCTIONS
+*****************************************************************************/
+
+INLINE smsvdp_t *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == SMSVDP);
+
+ return (smsvdp_t *)downcast<legacy_device_base *>(device)->token();
+}
+
+INLINE const smsvdp_interface *get_interface(device_t *device)
+{
+ assert(device != NULL);
+ assert((device->type() == SMSVDP));
+ return (const smsvdp_interface *) device->static_config();
+}
+
+/*************************************
+ *
+ * Utilities
+ *
+ *************************************/
+
+static void set_display_settings( device_t *device )
+{
+ smsvdp_t *smsvdp = get_safe_token(device);
+
+ screen_device *screen = device->machine().first_screen();
+ int height = screen->height();
+ int M1, M2, M3, M4;
+ M1 = smsvdp->reg[0x01] & 0x10;
+ M2 = smsvdp->reg[0x00] & 0x02;
+ M3 = smsvdp->reg[0x01] & 0x08;
+ M4 = smsvdp->reg[0x00] & 0x04;
+ smsvdp->y_pixels = 192;
+ if (M4)
+ {
+ /* mode 4 */
+ smsvdp->vdp_mode = 4;
+ if (M2 && (IS_SMS2_VDP || IS_GAMEGEAR_VDP))
+ {
+ if (M1 && !M3)
+ smsvdp->y_pixels = 224; /* 224-line display */
+ else if (!M1 && M3)
+ smsvdp->y_pixels = 240; /* 240-line display */
+ }
+ }
+ else
+ {
+ /* original TMS9918 mode */
+ if (!M1 && !M2 && !M3)
+ {
+ smsvdp->vdp_mode = 0;
+ }
+ else
+// if (M1 && !M2 && !M3)
+// {
+// smsvdp->vdp_mode = 1;
+// }
+// else
+ if (!M1 && M2 && !M3)
+ {
+ smsvdp->vdp_mode = 2;
+// }
+// else
+// if (!M1 && !M2 && M3)
+// {
+// smsvdp->vdp_mode = 3;
+ }
+ else
+ {
+ logerror("Unknown video mode detected (M1 = %c, M2 = %c, M3 = %c, M4 = %c)\n", M1 ? '1' : '0', M2 ? '1' : '0', M3 ? '1' : '0', M4 ? '1' : '0');
+ }
+ }
+
+ switch (smsvdp->y_pixels)
+ {
+ case 192:
+ smsvdp->sms_frame_timing = (height == PAL_Y_PIXELS) ? sms_pal_192 : sms_ntsc_192;
+ break;
+
+ case 224:
+ smsvdp->sms_frame_timing = (height == PAL_Y_PIXELS) ? sms_pal_224 : sms_ntsc_224;
+ break;
+
+ case 240:
+ smsvdp->sms_frame_timing = (height == PAL_Y_PIXELS) ? sms_pal_240 : sms_ntsc_240;
+ break;
+ }
+ smsvdp->cram_dirty = 1;
+}
+
+
+READ8_DEVICE_HANDLER( sms_vdp_vcount_r )
+{
+ smsvdp_t *smsvdp = get_safe_token(device);
+ int vpos = device->machine().primary_screen->vpos();
+
+ if (device->machine().primary_screen->hpos() < VCOUNT_CHANGE_HPOS)
+ {
+ vpos--;
+ if (vpos < 0)
+ vpos += device->machine().primary_screen->height();
+ }
+
+ return (smsvdp->sms_frame_timing[INIT_VCOUNT] + vpos) & 0xff;
+}
+
+
+READ8_DEVICE_HANDLER( sms_vdp_hcount_latch_r )
+{
+ smsvdp_t *smsvdp = get_safe_token(device);
+
+ return smsvdp->hcounter;
+}
+
+WRITE8_DEVICE_HANDLER( sms_vdp_hcount_latch_w )
+{
+ smsvdp_t *smsvdp = get_safe_token(device);
+
+ smsvdp->hcounter = data;
+}
+
+
+void sms_vdp_set_ggsmsmode( device_t *device, int mode )
+{
+ smsvdp_t *smsvdp = get_safe_token(device);
+
+ smsvdp->gg_sms_mode = mode;
+ smsvdp->cram_mask = (IS_GAMEGEAR_VDP && !smsvdp->gg_sms_mode) ? (GG_CRAM_SIZE - 1) : (SMS_CRAM_SIZE - 1);
+}
+
+
+static TIMER_CALLBACK( smsvdp_set_status )
+{
+ smsvdp_t *smsvdp = (smsvdp_t *) ptr;
+
+ smsvdp->status |= (UINT8) param;
+}
+
+
+static TIMER_CALLBACK( smsvdp_check_hint )
+{
+ smsvdp_t *smsvdp = (smsvdp_t *) ptr;
+
+ if (smsvdp->line_counter == 0x00)
+ {
+ smsvdp->line_counter = smsvdp->reg[0x0a];
+ smsvdp->status |= STATUS_HINT;
+ }
+ else
+ {
+ smsvdp->line_counter--;
+ }
+
+ if ((smsvdp->status & STATUS_HINT) && (smsvdp->reg[0x00] & 0x10))
+ {
+ smsvdp->irq_state = 1;
+
+ if (smsvdp->int_callback)
+ smsvdp->int_callback(machine, ASSERT_LINE);
+ }
+}
+
+
+static TIMER_CALLBACK( smsvdp_check_vint )
+{
+ smsvdp_t *smsvdp = (smsvdp_t *) ptr;
+
+ if ((smsvdp->status & STATUS_VINT) && (smsvdp->reg[0x01] & 0x20))
+ {
+ smsvdp->irq_state = 1;
+
+ if (smsvdp->int_callback)
+ smsvdp->int_callback(machine, ASSERT_LINE);
+ }
+}
+
+
+static TIMER_CALLBACK( smsvdp_display_callback )
+{
+ smsvdp_t *smsvdp = (smsvdp_t *) ptr;
+
+ rectangle rec;
+ int vpos = machine.primary_screen->vpos();
+ int vpos_limit = smsvdp->sms_frame_timing[VERTICAL_BLANKING] + smsvdp->sms_frame_timing[TOP_BLANKING]
+ + smsvdp->sms_frame_timing[TOP_BORDER] + smsvdp->sms_frame_timing[ACTIVE_DISPLAY_V]
+ + smsvdp->sms_frame_timing[BOTTOM_BORDER] + smsvdp->sms_frame_timing[BOTTOM_BLANKING];
+
+ rec.min_y = rec.max_y = vpos;
+
+ /* Check if we're on the last line of a frame */
+ if (vpos == vpos_limit - 1)
+ {
+ smsvdp->line_counter = smsvdp->reg[0x0a];
+ if (smsvdp->pause_callback)
+ smsvdp->pause_callback(machine);
+
+ return;
+ }
+
+ vpos_limit -= smsvdp->sms_frame_timing[BOTTOM_BLANKING];
+
+ /* Check if we're below the bottom border */
+ if (vpos >= vpos_limit)
+ {
+ smsvdp->line_counter = smsvdp->reg[0x0a];
+ return;
+ }
+
+ vpos_limit -= smsvdp->sms_frame_timing[BOTTOM_BORDER];
+
+ /* Check if we're in the bottom border area */
+ if (vpos >= vpos_limit)
+ {
+ if (vpos == vpos_limit)
+ {
+ machine.scheduler().timer_set(machine.primary_screen->time_until_pos(vpos, HINT_HPOS), FUNC(smsvdp_check_hint),0 ,smsvdp);
+ }
+ else
+ {
+ smsvdp->line_counter = smsvdp->reg[0x0a];
+ }
+
+ if (vpos == vpos_limit + 1)
+ {
+ machine.scheduler().timer_set(machine.primary_screen->time_until_pos(vpos, VINT_FLAG_HPOS), FUNC(smsvdp_set_status), (int)STATUS_VINT, smsvdp);
+ machine.scheduler().timer_set(machine.primary_screen->time_until_pos(vpos, VINT_HPOS), FUNC(smsvdp_check_vint), 0, smsvdp);
+ }
+
+ sms_update_palette(smsvdp);
+
+ /* Draw left border */
+ rec.min_x = LBORDER_START;
+ rec.max_x = LBORDER_START + LBORDER_X_PIXELS - 1;
+ bitmap_fill(smsvdp->tmpbitmap, &rec, machine.pens[smsvdp->current_palette[BACKDROP_COLOR]]);
+
+ /* Draw right border */
+ rec.min_x = LBORDER_START + LBORDER_X_PIXELS + 256;
+ rec.max_x = rec.min_x + RBORDER_X_PIXELS - 1;
+ bitmap_fill(smsvdp->tmpbitmap, &rec, machine.pens[smsvdp->current_palette[BACKDROP_COLOR]]);
+
+ /* Draw middle of the border */
+ /* We need to do this through the regular drawing function so it will */
+ /* be included in the gamegear scaling functions */
+ sms_refresh_line(machine, smsvdp, smsvdp->tmpbitmap, LBORDER_START + LBORDER_X_PIXELS, vpos_limit - smsvdp->sms_frame_timing[ACTIVE_DISPLAY_V], vpos - (vpos_limit - smsvdp->sms_frame_timing[ACTIVE_DISPLAY_V]));
+ return;
+ }
+
+ vpos_limit -= smsvdp->sms_frame_timing[ACTIVE_DISPLAY_V];
+
+ /* Check if we're in the active display area */
+ if (vpos >= vpos_limit)
+ {
+ if (vpos == vpos_limit)
+ {
+ smsvdp->reg9copy = smsvdp->reg[0x09];
+ }
+
+ machine.scheduler().timer_set(machine.primary_screen->time_until_pos(vpos, HINT_HPOS), FUNC(smsvdp_check_hint),0, smsvdp);
+
+ sms_update_palette(smsvdp);
+
+ /* Draw left border */
+ rec.min_x = LBORDER_START;
+ rec.max_x = LBORDER_START + LBORDER_X_PIXELS - 1;
+ bitmap_fill(smsvdp->tmpbitmap, &rec, machine.pens[smsvdp->current_palette[BACKDROP_COLOR]]);
+
+ /* Draw right border */
+ rec.min_x = LBORDER_START + LBORDER_X_PIXELS + 256;
+ rec.max_x = rec.min_x + RBORDER_X_PIXELS - 1;
+ bitmap_fill(smsvdp->tmpbitmap, &rec, machine.pens[smsvdp->current_palette[BACKDROP_COLOR]]);
+
+ sms_refresh_line(machine, smsvdp, smsvdp->tmpbitmap, LBORDER_START + LBORDER_X_PIXELS, vpos_limit, vpos - vpos_limit);
+
+ return;
+ }
+
+ vpos_limit -= smsvdp->sms_frame_timing[TOP_BORDER];
+
+ /* Check if we're in the top border area */
+ if (vpos >= vpos_limit)
+ {
+ smsvdp->line_counter = smsvdp->reg[0x0a];
+ sms_update_palette(smsvdp);
+
+ /* Draw left border */
+ rec.min_x = LBORDER_START;
+ rec.max_x = LBORDER_START + LBORDER_X_PIXELS - 1;
+ bitmap_fill(smsvdp->tmpbitmap, &rec, machine.pens[smsvdp->current_palette[BACKDROP_COLOR]]);
+
+ /* Draw right border */
+ rec.min_x = LBORDER_START + LBORDER_X_PIXELS + 256;
+ rec.max_x = rec.min_x + RBORDER_X_PIXELS - 1;
+ bitmap_fill(smsvdp->tmpbitmap, &rec, machine.pens[smsvdp->current_palette[BACKDROP_COLOR]]);
+
+ /* Draw middle of the border */
+ /* We need to do this through the regular drawing function so it will */
+ /* be included in the gamegear scaling functions */
+ sms_refresh_line(machine, smsvdp, smsvdp->tmpbitmap, LBORDER_START + LBORDER_X_PIXELS, vpos_limit + smsvdp->sms_frame_timing[TOP_BORDER], vpos - (vpos_limit + smsvdp->sms_frame_timing[TOP_BORDER]));
+ return;
+ }
+
+ /* we're in the vertical or top blanking area */
+ smsvdp->line_counter = smsvdp->reg[0x0a];
+}
+
+
+READ8_DEVICE_HANDLER( sms_vdp_data_r )
+{
+ smsvdp_t *smsvdp = get_safe_token(device);
+ UINT8 temp;
+
+ /* SMS 2 & GG behaviour. Seems like the latched data is passed straight through */
+ /* to the address register when in the middle of doing a command. */
+ /* Cosmic Spacehead needs this, among others */
+ /* Clear pending write flag */
+ smsvdp->pending = 0;
+
+ /* Return read buffer contents */
+ temp = smsvdp->buffer;
+
+ /* Load read buffer */
+ smsvdp->buffer = smsvdp->VRAM->u8((smsvdp->addr & 0x3fff));
+
+ /* Bump internal address register */
+ smsvdp->addr += 1;
+ return temp;
+}
+
+
+READ8_DEVICE_HANDLER( sms_vdp_ctrl_r )
+{
+ smsvdp_t *smsvdp = get_safe_token(device);
+
+ UINT8 temp = smsvdp->status;
+
+ /* Clear pending write flag */
+ smsvdp->pending = 0;
+
+ smsvdp->status &= ~(STATUS_VINT | STATUS_SPROVR | STATUS_SPRCOL | STATUS_HINT);
+
+ if (smsvdp->irq_state == 1)
+ {
+ smsvdp->irq_state = 0;
+
+ if (smsvdp->int_callback)
+ smsvdp->int_callback(device->machine(), CLEAR_LINE);
+ }
+
+ /* low 5 bits return non-zero data (it fixes PGA Tour Golf course map introduction) */
+ return temp | 0x1f;
+}
+
+
+WRITE8_DEVICE_HANDLER( sms_vdp_data_w )
+{
+ smsvdp_t *smsvdp = get_safe_token(device);
+ int address;
+
+ /* SMS 2 & GG behaviour. Seems like the latched data is passed straight through */
+ /* to the address register when in the middle of doing a command. */
+ /* Cosmic Spacehead needs this, among others */
+ /* Clear pending write flag */
+ smsvdp->pending = 0;
+
+ switch(smsvdp->addrmode)
+ {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ address = (smsvdp->addr & 0x3fff);
+ smsvdp->VRAM->u8(address) = data;
+ break;
+
+ case 0x03:
+ address = smsvdp->addr & smsvdp->cram_mask;
+ if (data != smsvdp->CRAM->u8(address))
+ {
+ smsvdp->CRAM->u8(address) = data;
+ smsvdp->cram_dirty = 1;
+ }
+ break;
+ }
+
+ smsvdp->buffer = data;
+ smsvdp->addr += 1;
+}
+
+
+WRITE8_DEVICE_HANDLER( sms_vdp_ctrl_w )
+{
+ smsvdp_t *smsvdp = get_safe_token(device);
+
+ int reg_num;
+
+ if (smsvdp->pending == 0)
+ {
+ smsvdp->addr = (smsvdp->addr & 0xff00) | data;
+ smsvdp->pending = 1;
+ }
+ else
+ {
+ /* Clear pending write flag */
+ smsvdp->pending = 0;
+
+ smsvdp->addrmode = (data >> 6) & 0x03;
+ smsvdp->addr = (data << 8) | (smsvdp->addr & 0xff);
+ switch (smsvdp->addrmode)
+ {
+ case 0: /* VRAM reading mode */
+ smsvdp->buffer = smsvdp->VRAM->u8(smsvdp->addr & 0x3fff);
+ smsvdp->addr += 1;
+ break;
+
+ case 1: /* VRAM writing mode */
+ break;
+
+ case 2: /* VDP register write */
+ reg_num = data & 0x0f;
+ smsvdp->reg[reg_num] = smsvdp->addr & 0xff;
+ if (reg_num == 0 && smsvdp->addr & 0x02)
+ logerror("overscan enabled.\n");
+
+ if (reg_num == 0 || reg_num == 1)
+ set_display_settings(device);
+
+ if (reg_num == 1)
+ {
+ device->machine().scheduler().timer_set(device->machine().primary_screen->time_until_pos(device->machine().primary_screen->vpos(), VINT_HPOS), FUNC(smsvdp_check_vint),0 ,smsvdp);
+ }
+ smsvdp->addrmode = 0;
+ break;
+
+ case 3: /* CRAM writing mode */
+ break;
+ }
+ }
+}
+
+
+static void sms_refresh_line_mode4( smsvdp_t *smsvdp, int *line_buffer, int *priority_selected, int line )
+{
+ int tile_column;
+ int x_scroll, y_scroll, x_scroll_start_column;
+ int pixel_x, pixel_plot_x;
+ int bit_plane_0, bit_plane_1, bit_plane_2, bit_plane_3;
+ int scroll_mod;
+ UINT16 name_table_address;
+ UINT8 *name_table;
+
+ if (smsvdp->y_pixels != 192)
+ {
+ name_table_address = ((smsvdp->reg[0x02] & 0x0c) << 10) | 0x0700;
+ scroll_mod = 256;
+ }
+ else
+ {
+ name_table_address = (smsvdp->reg[0x02] << 10) & 0x3800;
+ scroll_mod = 224;
+ }
+
+ if (IS_SMS1_VDP)
+ name_table_address = name_table_address & (((smsvdp->reg[0x02] & 0x01) << 10) | 0x3bff);
+
+ /* if top 2 rows of screen not affected by horizontal scrolling, then x_scroll = 0 */
+ /* else x_scroll = reg[0x08] */
+ x_scroll = (((smsvdp->reg[0x00] & 0x40) && (line < 16)) ? 0 : 0x0100 - smsvdp->reg[0x08]);
+
+ x_scroll_start_column = (x_scroll >> 3); /* x starting column tile */
+
+ /* Draw background layer */
+ for (tile_column = 0; tile_column < 33; tile_column++)
+ {
+ UINT16 tile_data;
+ int tile_selected, palette_selected, horiz_selected, vert_selected, priority_select;
+ int tile_line;
+
+ /* Rightmost 8 columns for SMS (or 2 columns for GG) not affected by */
+ /* vertical scrolling when bit 7 of reg[0x00] is set */
+ y_scroll = ((smsvdp->reg[0x00] & 0x80) && (tile_column > 23)) ? 0 : smsvdp->reg9copy;
+
+ name_table = smsvdp->VRAM->base() + name_table_address + ((((line + y_scroll) % scroll_mod) >> 3) << 6);
+
+ tile_line = ((tile_column + x_scroll_start_column) & 0x1f) * 2;
+ tile_data = name_table[tile_line] | (name_table[tile_line + 1] << 8);
+
+ tile_selected = (tile_data & 0x01ff);
+ priority_select = tile_data & PRIORITY_BIT;
+ palette_selected = (tile_data >> 11) & 0x01;
+ vert_selected = (tile_data >> 10) & 0x01;
+ horiz_selected = (tile_data >> 9) & 0x01;
+
+ tile_line = line - ((0x07 - (y_scroll & 0x07)) + 1);
+ if (vert_selected)
+ tile_line = 0x07 - tile_line;
+
+ bit_plane_0 = smsvdp->VRAM->u8(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x00);
+ bit_plane_1 = smsvdp->VRAM->u8(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x01);
+ bit_plane_2 = smsvdp->VRAM->u8(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x02);
+ bit_plane_3 = smsvdp->VRAM->u8(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x03);
+
+ for (pixel_x = 0; pixel_x < 8 ; pixel_x++)
+ {
+ UINT8 pen_bit_0, pen_bit_1, pen_bit_2, pen_bit_3;
+ UINT8 pen_selected;
+
+ pen_bit_0 = (bit_plane_0 >> (7 - pixel_x)) & 0x01;
+ pen_bit_1 = (bit_plane_1 >> (7 - pixel_x)) & 0x01;
+ pen_bit_2 = (bit_plane_2 >> (7 - pixel_x)) & 0x01;
+ pen_bit_3 = (bit_plane_3 >> (7 - pixel_x)) & 0x01;
+
+ pen_selected = (pen_bit_3 << 3 | pen_bit_2 << 2 | pen_bit_1 << 1 | pen_bit_0);
+ if (palette_selected)
+ pen_selected |= 0x10;
+
+
+ if (!horiz_selected)
+ {
+ pixel_plot_x = pixel_x;
+ }
+ else
+ {
+ pixel_plot_x = 7 - pixel_x;
+ }
+ pixel_plot_x = (0 - (x_scroll & 0x07) + (tile_column << 3) + pixel_plot_x);
+ if (pixel_plot_x >= 0 && pixel_plot_x < 256)
+ {
+// logerror("%x %x\n", pixel_plot_x + pixel_offset_x, pixel_plot_y);
+ line_buffer[pixel_plot_x] = smsvdp->current_palette[pen_selected];
+ priority_selected[pixel_plot_x] = priority_select | (pen_selected & 0x0f);
+ }
+ }
+ }
+}
+
+static void sms_refresh_mode4_sprites( running_machine &machine, smsvdp_t *smsvdp, int *line_buffer, int *priority_selected, int pixel_plot_y, int line )
+{
+ int sprite_index;
+ int pixel_x, pixel_plot_x;
+ int sprite_x, sprite_y, sprite_line, sprite_tile_selected, sprite_height, sprite_zoom;
+ int sprite_col_occurred, sprite_col_x;
+ int sprite_buffer[8], sprite_buffer_count, sprite_buffer_index;
+ int bit_plane_0, bit_plane_1, bit_plane_2, bit_plane_3;
+ UINT8 *sprite_table = smsvdp->VRAM->base() + ((smsvdp->reg[0x05] << 7) & 0x3f00);
+
+ /* Draw sprite layer */
+ sprite_height = (smsvdp->reg[0x01] & 0x02 ? 16 : 8);
+ sprite_zoom = 1;
+
+ if (smsvdp->reg[0x01] & 0x01) /* sprite doubling */
+ sprite_zoom = 2;
+
+ sprite_buffer_count = 0;
+ for (sprite_index = 0; (sprite_index < 64) && (sprite_table[sprite_index] != 0xd0 || smsvdp->y_pixels != 192) && (sprite_buffer_count < 9); sprite_index++)
+ {
+ sprite_y = sprite_table[sprite_index] + 1; /* sprite y position starts at line 1 */
+
+ if (sprite_y > 240)
+ sprite_y -= 256; /* wrap from top if y position is > 240 */
+
+ if ((line >= sprite_y) && (line < (sprite_y + sprite_height * sprite_zoom)))
+ {
+ if (sprite_buffer_count < 8)
+ {
+ sprite_buffer[sprite_buffer_count] = sprite_index;
+ }
+ else if (line >= 0 && line < smsvdp->sms_frame_timing[ACTIVE_DISPLAY_V])
+ {
+ /* Too many sprites per line */
+ machine.scheduler().timer_set(machine.primary_screen->time_until_pos(pixel_plot_y + line, SPROVR_HPOS), FUNC(smsvdp_set_status), (int)STATUS_SPROVR, smsvdp);
+ }
+ sprite_buffer_count++;
+ }
+ }
+
+ /* Check if display is disabled */
+ if (!(smsvdp->reg[0x01] & 0x40))
+ return;
+
+ if (sprite_buffer_count > 8)
+ sprite_buffer_count = 8;
+
+ memset(smsvdp->collision_buffer, 0, SMS_X_PIXELS);
+ sprite_buffer_count--;
+
+ for (sprite_buffer_index = sprite_buffer_count; sprite_buffer_index >= 0; sprite_buffer_index--)
+ {
+ sprite_index = sprite_buffer[sprite_buffer_index];
+ sprite_y = sprite_table[sprite_index] + 1; /* sprite y position starts at line 1 */
+
+ if (sprite_y > 240)
+ sprite_y -= 256; /* wrap from top if y position is > 240 */
+
+ sprite_x = sprite_table[0x80 + (sprite_index << 1)];
+
+ if (smsvdp->reg[0x00] & 0x08)
+ sprite_x -= 0x08; /* sprite shift */
+
+ sprite_tile_selected = sprite_table[0x81 + (sprite_index << 1)];
+
+ if (smsvdp->reg[0x06] & 0x04)
+ sprite_tile_selected += 256; /* pattern table select */
+
+ if (smsvdp->reg[0x01] & 0x02)
+ sprite_tile_selected &= 0x01fe; /* force even index */
+
+ sprite_line = (line - sprite_y) / sprite_zoom;
+
+ if (sprite_line > 0x07)
+ sprite_tile_selected += 1;
+
+ bit_plane_0 = smsvdp->VRAM->u8(((sprite_tile_selected << 5) + ((sprite_line & 0x07) << 2)) + 0x00);
+ bit_plane_1 = smsvdp->VRAM->u8(((sprite_tile_selected << 5) + ((sprite_line & 0x07) << 2)) + 0x01);
+ bit_plane_2 = smsvdp->VRAM->u8(((sprite_tile_selected << 5) + ((sprite_line & 0x07) << 2)) + 0x02);
+ bit_plane_3 = smsvdp->VRAM->u8(((sprite_tile_selected << 5) + ((sprite_line & 0x07) << 2)) + 0x03);
+
+ sprite_col_occurred = 0;
+ sprite_col_x = 0;
+
+ for (pixel_x = 0; pixel_x < 8 ; pixel_x++)
+ {
+ UINT8 pen_bit_0, pen_bit_1, pen_bit_2, pen_bit_3;
+ int pen_selected;
+
+ pen_bit_0 = (bit_plane_0 >> (7 - pixel_x)) & 0x01;
+ pen_bit_1 = (bit_plane_1 >> (7 - pixel_x)) & 0x01;
+ pen_bit_2 = (bit_plane_2 >> (7 - pixel_x)) & 0x01;
+ pen_bit_3 = (bit_plane_3 >> (7 - pixel_x)) & 0x01;
+
+ pen_selected = (pen_bit_3 << 3 | pen_bit_2 << 2 | pen_bit_1 << 1 | pen_bit_0) | 0x10;
+
+ if (pen_selected == 0x10) /* Transparent palette so skip draw */
+ {
+ continue;
+ }
+
+ if (smsvdp->reg[0x01] & 0x01)
+ {
+ /* sprite doubling is enabled */
+ pixel_plot_x = sprite_x + (pixel_x << 1);
+
+ /* check to prevent going outside of active display area */
+ if (pixel_plot_x < 0 || pixel_plot_x > 255)
+ {
+ continue;
+ }
+
+ if (!(priority_selected[pixel_plot_x] & PRIORITY_BIT))
+ {
+ line_buffer[pixel_plot_x] = smsvdp->current_palette[pen_selected];
+ line_buffer[pixel_plot_x + 1] = smsvdp->current_palette[pen_selected];
+ }
+ else
+ {
+ if (priority_selected[pixel_plot_x] == PRIORITY_BIT)
+ {
+ line_buffer[pixel_plot_x] = smsvdp->current_palette[pen_selected];
+ }
+ if (priority_selected[pixel_plot_x + 1] == PRIORITY_BIT)
+ {
+ line_buffer[pixel_plot_x + 1] = smsvdp->current_palette[pen_selected];
+ }
+ }
+ if (smsvdp->collision_buffer[pixel_plot_x] != 1)
+ {
+ smsvdp->collision_buffer[pixel_plot_x] = 1;
+ }
+ else
+ {
+ if (!sprite_col_occurred)
+ {
+ sprite_col_occurred = 1;
+ sprite_col_x = pixel_plot_x;
+ }
+ }
+ if (smsvdp->collision_buffer[pixel_plot_x + 1] != 1)
+ {
+ smsvdp->collision_buffer[pixel_plot_x + 1] = 1;
+ }
+ else
+ {
+ if (!sprite_col_occurred)
+ {
+ sprite_col_occurred = 1;
+ sprite_col_x = pixel_plot_x;
+ }
+ }
+ }
+ else
+ {
+ pixel_plot_x = sprite_x + pixel_x;
+
+ /* check to prevent going outside of active display area */
+ if (pixel_plot_x < 0 || pixel_plot_x > 255)
+ {
+ continue;
+ }
+
+ if (!(priority_selected[pixel_plot_x] & PRIORITY_BIT))
+ {
+ line_buffer[pixel_plot_x] = smsvdp->current_palette[pen_selected];
+ }
+ else
+ {
+ if (priority_selected[pixel_plot_x] == PRIORITY_BIT)
+ {
+ line_buffer[pixel_plot_x] = smsvdp->current_palette[pen_selected];
+ }
+ }
+ if (smsvdp->collision_buffer[pixel_plot_x] != 1)
+ {
+ smsvdp->collision_buffer[pixel_plot_x] = 1;
+ }
+ else
+ {
+ if (!sprite_col_occurred)
+ {
+ sprite_col_occurred = 1;
+ sprite_col_x = pixel_plot_x;
+ }
+ }
+ }
+ }
+ if (sprite_col_occurred)
+ machine.scheduler().timer_set(machine.primary_screen->time_until_pos(pixel_plot_y + line, SPRCOL_BASEHPOS + sprite_col_x), FUNC(smsvdp_set_status), (int)STATUS_SPRCOL, smsvdp);
+ }
+
+ /* Fill column 0 with overscan color from reg[0x07] */
+ if (smsvdp->reg[0x00] & 0x20)
+ {
+ line_buffer[0] = smsvdp->current_palette[BACKDROP_COLOR];
+ line_buffer[1] = smsvdp->current_palette[BACKDROP_COLOR];
+ line_buffer[2] = smsvdp->current_palette[BACKDROP_COLOR];
+ line_buffer[3] = smsvdp->current_palette[BACKDROP_COLOR];
+ line_buffer[4] = smsvdp->current_palette[BACKDROP_COLOR];
+ line_buffer[5] = smsvdp->current_palette[BACKDROP_COLOR];
+ line_buffer[6] = smsvdp->current_palette[BACKDROP_COLOR];
+ line_buffer[7] = smsvdp->current_palette[BACKDROP_COLOR];
+ }
+}
+
+
+static void sms_refresh_tms9918_sprites( running_machine &machine, smsvdp_t *smsvdp, int *line_buffer, int pixel_plot_y, int line )
+{
+ int pixel_plot_x;
+ int sprite_col_occurred, sprite_col_x;
+ int sprite_height, sprite_buffer_count, sprite_index, sprite_buffer[5], sprite_buffer_index;
+ UINT8 *sprite_table, *sprite_pattern_table;
+
+ /* Draw sprite layer */
+ sprite_table = smsvdp->VRAM->base() + ((smsvdp->reg[0x05] & 0x7f) << 7);
+ sprite_pattern_table = smsvdp->VRAM->base() + ((smsvdp->reg[0x06] & 0x07) << 11);
+ sprite_height = 8;
+
+ if (smsvdp->reg[0x01] & 0x02) /* Check if SI is set */
+ sprite_height = sprite_height * 2;
+ if (smsvdp->reg[0x01] & 0x01) /* Check if MAG is set */
+ sprite_height = sprite_height * 2;
+
+ sprite_buffer_count = 0;
+ for (sprite_index = 0; (sprite_index < 32 * 4) && (sprite_table[sprite_index] != 0xd0) && (sprite_buffer_count < 5); sprite_index += 4)
+ {
+ int sprite_y = sprite_table[sprite_index] + 1;
+
+ if (sprite_y > 240)
+ sprite_y -= 256;
+
+ if ((line >= sprite_y) && (line < (sprite_y + sprite_height)))
+ {
+ if (sprite_buffer_count < 5)
+ {
+ sprite_buffer[sprite_buffer_count] = sprite_index;
+ }
+ else if (line >= 0 && line < smsvdp->sms_frame_timing[ACTIVE_DISPLAY_V])
+ {
+ /* Too many sprites per line */
+ machine.scheduler().timer_set(machine.primary_screen->time_until_pos(pixel_plot_y + line, SPROVR_HPOS), FUNC(smsvdp_set_status), (int)STATUS_SPROVR, smsvdp);
+ }
+ sprite_buffer_count++;
+ }
+ }
+
+ /* Check if display is disabled */
+ if (!(smsvdp->reg[0x01] & 0x40))
+ return;
+
+ if (sprite_buffer_count > 4)
+ sprite_buffer_count = 4;
+
+ memset(smsvdp->collision_buffer, 0, SMS_X_PIXELS);
+ sprite_buffer_count--;
+
+ for (sprite_buffer_index = sprite_buffer_count; sprite_buffer_index >= 0; sprite_buffer_index--)
+ {
+ int pen_selected;
+ int sprite_line, pixel_x, sprite_x, sprite_tile_selected;
+ int sprite_y;
+ UINT8 pattern;
+
+ sprite_index = sprite_buffer[sprite_buffer_index];
+ sprite_y = sprite_table[sprite_index] + 1;
+
+ if (sprite_y > 240)
+ sprite_y -= 256;
+
+ sprite_x = sprite_table[sprite_index + 1];
+ pen_selected = sprite_table[sprite_index + 3] & 0x0f;
+
+ if (IS_GAMEGEAR_VDP)
+ pen_selected |= 0x10;
+
+ if (sprite_table[sprite_index + 3] & 0x80)
+ sprite_x -= 32;
+
+ sprite_tile_selected = sprite_table[sprite_index + 2];
+ sprite_line = line - sprite_y;
+
+ if (smsvdp->reg[0x01] & 0x01)
+ sprite_line >>= 1;
+
+ if (smsvdp->reg[0x01] & 0x02)
+ {
+ sprite_tile_selected &= 0xfc;
+
+ if (sprite_line > 0x07)
+ {
+ sprite_tile_selected += 1;
+ sprite_line -= 8;
+ }
+ }
+
+ pattern = sprite_pattern_table[sprite_tile_selected * 8 + sprite_line];
+
+ sprite_col_occurred = 0;
+ sprite_col_x = 0;
+
+ for (pixel_x = 0; pixel_x < 8; pixel_x++)
+ {
+ if (smsvdp->reg[0x01] & 0x01)
+ {
+ pixel_plot_x = sprite_x + pixel_x * 2;
+ if (pixel_plot_x < 0 || pixel_plot_x > 255)
+ {
+ continue;
+ }
+
+ if (pen_selected && (pattern & (1 << (7 - pixel_x))))
+ {
+ line_buffer[pixel_plot_x] = smsvdp->current_palette[pen_selected];
+
+ if (smsvdp->collision_buffer[pixel_plot_x] != 1)
+ {
+ smsvdp->collision_buffer[pixel_plot_x] = 1;
+ }
+ else
+ {
+ if (!sprite_col_occurred)
+ {
+ sprite_col_occurred = 1;
+ sprite_col_x = pixel_plot_x;
+ }
+ }
+
+ line_buffer[pixel_plot_x+1] = smsvdp->current_palette[pen_selected];
+
+ if (smsvdp->collision_buffer[pixel_plot_x + 1] != 1)
+ {
+ smsvdp->collision_buffer[pixel_plot_x + 1] = 1;
+ }
+ else
+ {
+ if (!sprite_col_occurred)
+ {
+ sprite_col_occurred = 1;
+ sprite_col_x = pixel_plot_x;
+ }
+ }
+ }
+ }
+ else
+ {
+ pixel_plot_x = sprite_x + pixel_x;
+
+ if (pixel_plot_x < 0 || pixel_plot_x > 255)
+ {
+ continue;
+ }
+
+ if (pen_selected && (pattern & (1 << (7 - pixel_x))))
+ {
+ line_buffer[pixel_plot_x] = smsvdp->current_palette[pen_selected];
+
+ if (smsvdp->collision_buffer[pixel_plot_x] != 1)
+ {
+ smsvdp->collision_buffer[pixel_plot_x] = 1;
+ }
+ else
+ {
+ if (!sprite_col_occurred)
+ {
+ sprite_col_occurred = 1;
+ sprite_col_x = pixel_plot_x;
+ }
+ }
+ }
+ }
+ }
+
+ if (smsvdp->reg[0x01] & 0x02)
+ {
+ sprite_tile_selected += 2;
+ pattern = sprite_pattern_table[sprite_tile_selected * 8 + sprite_line];
+ sprite_x += (smsvdp->reg[0x01] & 0x01 ? 16 : 8);
+
+ for (pixel_x = 0; pixel_x < 8; pixel_x++)
+ {
+ if (smsvdp->reg[0x01] & 0x01)
+ {
+ pixel_plot_x = sprite_x + pixel_x * 2;
+
+ if (pixel_plot_x < 0 || pixel_plot_x > 255)
+ {
+ continue;
+ }
+
+ if (pen_selected && (pattern & (1 << (7 - pixel_x))))
+ {
+ line_buffer[pixel_plot_x] = smsvdp->current_palette[pen_selected];
+
+ if (smsvdp->collision_buffer[pixel_plot_x] != 1)
+ {
+ smsvdp->collision_buffer[pixel_plot_x] = 1;
+ }
+ else
+ {
+ if (!sprite_col_occurred)
+ {
+ sprite_col_occurred = 1;
+ sprite_col_x = pixel_plot_x;
+ }
+ }
+
+ line_buffer[pixel_plot_x+1] = smsvdp->current_palette[pen_selected];
+
+ if (smsvdp->collision_buffer[pixel_plot_x + 1] != 1)
+ {
+ smsvdp->collision_buffer[pixel_plot_x + 1] = 1;
+ }
+ else
+ {
+ if (!sprite_col_occurred)
+ {
+ sprite_col_occurred = 1;
+ sprite_col_x = pixel_plot_x;
+ }
+ }
+ }
+ }
+ else
+ {
+ pixel_plot_x = sprite_x + pixel_x;
+
+ if (pixel_plot_x < 0 || pixel_plot_x > 255)
+ {
+ continue;
+ }
+
+ if (pen_selected && (pattern & (1 << (7 - pixel_x))))
+ {
+ line_buffer[pixel_plot_x] = smsvdp->current_palette[pen_selected];
+
+ if (smsvdp->collision_buffer[pixel_plot_x] != 1)
+ {
+ smsvdp->collision_buffer[pixel_plot_x] = 1;
+ }
+ else
+ {
+ if (!sprite_col_occurred)
+ {
+ sprite_col_occurred = 1;
+ sprite_col_x = pixel_plot_x;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (sprite_col_occurred)
+ machine.scheduler().timer_set(machine.primary_screen->time_until_pos(pixel_plot_y + line, SPRCOL_BASEHPOS + sprite_col_x), FUNC(smsvdp_set_status), (int)STATUS_SPRCOL, smsvdp);
+ }
+}
+
+
+static void sms_refresh_line_mode2( smsvdp_t *smsvdp, int *line_buffer, int line )
+{
+ int tile_column;
+ int pixel_x, pixel_plot_x;
+ UINT8 *name_table, *color_table, *pattern_table;
+ int pattern_mask, color_mask, pattern_offset;
+
+ /* Draw background layer */
+ name_table = smsvdp->VRAM->base() + ((smsvdp->reg[0x02] & 0x0f) << 10) + ((line >> 3) * 32);
+ color_table = smsvdp->VRAM->base() + ((smsvdp->reg[0x03] & 0x80) << 6);
+ color_mask = ((smsvdp->reg[0x03] & 0x7f) << 3) | 0x07;
+ pattern_table = smsvdp->VRAM->base() + ((smsvdp->reg[0x04] & 0x04) << 11);
+ pattern_mask = ((smsvdp->reg[0x04] & 0x03) << 8) | 0xff;
+ pattern_offset = (line & 0xc0) << 2;
+
+ for (tile_column = 0; tile_column < 32; tile_column++)
+ {
+ UINT8 pattern;
+ UINT8 colors;
+
+ pattern = pattern_table[(((pattern_offset + name_table[tile_column]) & pattern_mask) * 8) + (line & 0x07)];
+ colors = color_table[(((pattern_offset + name_table[tile_column]) & color_mask) * 8) + (line & 0x07)];
+
+ for (pixel_x = 0; pixel_x < 8; pixel_x++)
+ {
+ UINT8 pen_selected;
+
+ if (pattern & (1 << (7 - pixel_x)))
+ {
+ pen_selected = colors >> 4;
+ }
+ else
+ {
+ pen_selected = colors & 0x0f;
+ }
+
+ if (!pen_selected)
+ pen_selected = BACKDROP_COLOR;
+
+ pixel_plot_x = (tile_column << 3) + pixel_x;
+
+ if (IS_GAMEGEAR_VDP)
+ pen_selected |= 0x10;
+
+ line_buffer[pixel_plot_x] = smsvdp->current_palette[pen_selected];
+ }
+ }
+}
+
+
+static void sms_refresh_line_mode0( smsvdp_t *smsvdp, int *line_buffer, int line)
+{
+ int tile_column;
+ int pixel_x, pixel_plot_x;
+ UINT8 *name_table, *color_table, *pattern_table;
+
+ /* Draw background layer */
+ name_table = smsvdp->VRAM->base() + ((smsvdp->reg[0x02] & 0x0f) << 10) + ((line >> 3) * 32);
+ color_table = smsvdp->VRAM->base() + ((smsvdp->reg[0x03] << 6) & (VRAM_SIZE - 1));
+ pattern_table = smsvdp->VRAM->base() + ((smsvdp->reg[0x04] << 11) & (VRAM_SIZE - 1));
+
+ for (tile_column = 0; tile_column < 32; tile_column++)
+ {
+ UINT8 pattern;
+ UINT8 colors;
+
+ pattern = pattern_table[(name_table[tile_column] * 8) + (line & 0x07)];
+ colors = color_table[name_table[tile_column] >> 3];
+
+ for (pixel_x = 0; pixel_x < 8; pixel_x++)
+ {
+ int pen_selected;
+
+ if (pattern & (1 << (7 - pixel_x)))
+ pen_selected = colors >> 4;
+ else
+ pen_selected = colors & 0x0f;
+
+ if (IS_GAMEGEAR_VDP)
+ pen_selected |= 0x10;
+
+ pixel_plot_x = (tile_column << 3) + pixel_x;
+ line_buffer[pixel_plot_x] = smsvdp->current_palette[pen_selected];
+ }
+ }
+}
+
+
+static void sms_refresh_line( running_machine &machine, smsvdp_t *smsvdp, bitmap_t *bitmap, int pixel_offset_x, int pixel_plot_y, int line )
+{
+ int x;
+ int *blitline_buffer = smsvdp->line_buffer;
+ int priority_selected[256];
+
+ switch( smsvdp->vdp_mode )
+ {
+ case 0:
+ if (line >= 0 && line < smsvdp->sms_frame_timing[ACTIVE_DISPLAY_V])
+ sms_refresh_line_mode0(smsvdp, blitline_buffer, line);
+ sms_refresh_tms9918_sprites(machine, smsvdp, blitline_buffer, pixel_plot_y, line);
+ break;
+
+ case 2:
+ if (line >= 0 && line < smsvdp->sms_frame_timing[ACTIVE_DISPLAY_V])
+ sms_refresh_line_mode2(smsvdp, blitline_buffer, line);
+ sms_refresh_tms9918_sprites(machine, smsvdp, blitline_buffer, pixel_plot_y, line);
+ break;
+
+ case 4:
+ default:
+ memset(priority_selected, 0, sizeof(priority_selected));
+ if (line >= 0 && line < smsvdp->sms_frame_timing[ACTIVE_DISPLAY_V])
+ sms_refresh_line_mode4(smsvdp, blitline_buffer, priority_selected, line);
+ sms_refresh_mode4_sprites(machine, smsvdp, blitline_buffer, priority_selected, pixel_plot_y, line);
+ break;
+ }
+
+ /* Check if display is disabled or we're below/above active area */
+ if (!(smsvdp->reg[0x01] & 0x40) || line < 0 || line >= smsvdp->sms_frame_timing[ACTIVE_DISPLAY_V])
+ {
+ for (x = 0; x < 256; x++)
+ {
+ blitline_buffer[x] = smsvdp->current_palette[BACKDROP_COLOR];
+ }
+ }
+
+ if (IS_GAMEGEAR_VDP && smsvdp->gg_sms_mode)
+ {
+ int *combineline_buffer = smsvdp->line_buffer + ((line & 0x03) + 1) * 256;
+ int plot_x = 48;
+
+ /* Do horizontal scaling */
+ for (x = 8; x < 248;)
+ {
+ int combined;
+
+ /* Take red and green from first pixel, and blue from second pixel */
+ combined = (blitline_buffer[x] & 0x00ff) | (blitline_buffer[x + 1] & 0x0f00);
+ combineline_buffer[plot_x] = combined;
+
+ /* Take red from second pixel, and green and blue from third pixel */
+ combined = (blitline_buffer[x + 1] & 0x000f) | (blitline_buffer[x + 2] & 0x0ff0);
+ combineline_buffer[plot_x + 1] = combined;
+ x += 3;
+ plot_x += 2;
+ }
+
+ /* Do vertical scaling for a screen with 192 or 224 lines
+ Lines 0-2 and 221-223 have no effect on the output on the GG screen.
+ We will calculate the gamegear lines as follows:
+ GG_0 = 1/6 * SMS_3 + 1/3 * SMS_4 + 1/3 * SMS_5 + 1/6 * SMS_6
+ GG_1 = 1/6 * SMS_4 + 1/3 * SMS_5 + 1/3 * SMS_6 + 1/6 * SMS_7
+ GG_2 = 1/6 * SMS_6 + 1/3 * SMS_7 + 1/3 * SMS_8 + 1/6 * SMS_9
+ GG_3 = 1/6 * SMS_7 + 1/3 * SMS_8 + 1/3 * SMS_9 + 1/6 * SMS_10
+ GG_4 = 1/6 * SMS_9 + 1/3 * SMS_10 + 1/3 * SMS_11 + 1/6 * SMS_12
+ .....
+ GG_142 = 1/6 * SMS_216 + 1/3 * SMS_217 + 1/3 * SMS_218 + 1/6 * SMS_219
+ GG_143 = 1/6 * SMS_217 + 1/3 * SMS_218 + 1/3 * SMS_219 + 1/6 * SMS_220
+ */
+ {
+ int gg_line;
+ int my_line = pixel_plot_y + line - (TBORDER_START + NTSC_224_TBORDER_Y_PIXELS);
+ int *line1, *line2, *line3, *line4;
+
+ /* First make sure there's enough data to draw anything */
+ /* We need one more line of data if we're on line 8, 11, 14, 17, etc */
+ if (my_line < 6 || my_line > 220 || ((my_line - 8) % 3 == 0))
+ {
+ return;
+ }
+
+ gg_line = ((my_line - 6) / 3) * 2;
+
+ /* If we're on SMS line 7, 10, 13, etc we're on an odd GG line */
+ if (my_line % 3)
+ {
+ gg_line++;
+ }
+
+ /* Calculate the line we will be drawing on */
+ pixel_plot_y = TBORDER_START + NTSC_192_TBORDER_Y_PIXELS + 24 + gg_line;
+
+ /* Setup our source lines */
+ line1 = smsvdp->line_buffer + (((my_line - 3) & 0x03) + 1) * 256;
+ line2 = smsvdp->line_buffer + (((my_line - 2) & 0x03) + 1) * 256;
+ line3 = smsvdp->line_buffer + (((my_line - 1) & 0x03) + 1) * 256;
+ line4 = smsvdp->line_buffer + (((my_line - 0) & 0x03) + 1) * 256;
+
+ for (x = 0 + 48; x < 160 + 48; x++)
+ {
+ rgb_t c1 = machine.pens[line1[x]];
+ rgb_t c2 = machine.pens[line2[x]];
+ rgb_t c3 = machine.pens[line3[x]];
+ rgb_t c4 = machine.pens[line4[x]];
+ *BITMAP_ADDR32(bitmap, pixel_plot_y, pixel_offset_x + x) =
+ MAKE_RGB((RGB_RED(c1) / 6 + RGB_RED(c2) / 3 + RGB_RED(c3) / 3 + RGB_RED(c4) / 6 ),
+ (RGB_GREEN(c1) / 6 + RGB_GREEN(c2) / 3 + RGB_GREEN(c3) / 3 + RGB_GREEN(c4) / 6 ),
+ (RGB_BLUE(c1) / 6 + RGB_BLUE(c2) / 3 + RGB_BLUE(c3) / 3 + RGB_BLUE(c4) / 6 ) );
+ }
+ return;
+ }
+ blitline_buffer = combineline_buffer;
+ }
+
+ for (x = 0; x < 256; x++)
+ {
+ *BITMAP_ADDR32(bitmap, pixel_plot_y + line, pixel_offset_x + x) = machine.pens[blitline_buffer[x]];
+ }
+}
+
+
+// This is only used by Light Phaser. Should it be moved elsewhere?
+int sms_vdp_check_brightness(device_t *device, int x, int y)
+{
+ /* brightness of the lightgray color in the frame drawn by Light Phaser games */
+ const UINT8 sensor_min_brightness = 0x7f;
+
+ /* TODO: Check how Light Phaser behaves for border areas. For Gangster Town, should */
+ /* a shot at right border (HC~=0x90) really appear at active scr, near to left border? */
+ if (x < LBORDER_START + LBORDER_X_PIXELS || x >= LBORDER_START + LBORDER_X_PIXELS + 256)
+ return 0;
+
+ smsvdp_t *smsvdp = get_safe_token(device);
+ rgb_t color = *BITMAP_ADDR32(smsvdp->tmpbitmap, y, x);
+
+ /* reference: http://www.w3.org/TR/AERT#color-contrast */
+ UINT8 brightness = (RGB_RED(color) * 0.299) + (RGB_GREEN(color) * 0.587) + (RGB_BLUE(color) * 0.114);
+ //printf ("color brightness: %2X for x %d y %d\n", brightness, x, y);
+
+ return (brightness >= sensor_min_brightness) ? 1 : 0;
+}
+
+
+static void sms_update_palette( smsvdp_t *smsvdp )
+{
+ int i;
+
+ /* Exit if palette is has no changes */
+ if (smsvdp->cram_dirty == 0)
+ {
+ return;
+ }
+ smsvdp->cram_dirty = 0;
+
+ if (smsvdp->vdp_mode != 4 && ! IS_GAMEGEAR_VDP)
+ {
+ for(i = 0; i < 16; i++)
+ {
+ smsvdp->current_palette[i] = 64 + i;
+ }
+ return;
+ }
+
+ if (IS_GAMEGEAR_VDP)
+ {
+ if (smsvdp->gg_sms_mode)
+ {
+ for (i = 0; i < 32; i++)
+ {
+ smsvdp->current_palette[i] = ((smsvdp->CRAM->u8(i) & 0x30) << 6) | ((smsvdp->CRAM->u8(i) & 0x0c ) << 4) | ((smsvdp->CRAM->u8(i) & 0x03) << 2);
+ }
+ }
+ else
+ {
+ for (i = 0; i < 32; i++)
+ {
+ smsvdp->current_palette[i] = ((smsvdp->CRAM->u8(i * 2 + 1) << 8) | smsvdp->CRAM->u8(i * 2)) & 0x0fff;
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < 32; i++)
+ {
+ smsvdp->current_palette[i] = smsvdp->CRAM->u8(i) & 0x3f;
+ }
+ }
+}
+
+
+UINT32 sms_vdp_update( device_t *device, bitmap_t *bitmap, const rectangle *cliprect )
+{
+ smsvdp_t *smsvdp = get_safe_token(device);
+ copybitmap(bitmap, smsvdp->tmpbitmap, 0, 0, 0, 0, cliprect);
+ return 0;
+}
+
+
+/*****************************************************************************
+ DEVICE INTERFACE
+*****************************************************************************/
+
+static DEVICE_START( smsvdp )
+{
+ smsvdp_t *smsvdp = get_safe_token(device);
+ const smsvdp_interface *intf = get_interface(device);
+
+ screen_device *screen = device->machine().first_screen();
+ int width = screen->width();
+ int height = screen->height();
+
+ smsvdp->features = intf->model;
+ smsvdp->int_callback = intf->int_callback;
+ smsvdp->pause_callback = intf->pause_callback;
+
+ /* Allocate video RAM */
+ smsvdp->VRAM = device->machine().region_alloc("vdp_vram", VRAM_SIZE, 1, ENDIANNESS_LITTLE);
+ smsvdp->CRAM = device->machine().region_alloc("vdp_cram", MAX_CRAM_SIZE, 1, ENDIANNESS_LITTLE);
+ smsvdp->line_buffer = auto_alloc_array(device->machine(), int, 256 * 5);
+
+ smsvdp->collision_buffer = auto_alloc_array(device->machine(), UINT8, SMS_X_PIXELS);
+ smsvdp->sms_frame_timing = auto_alloc_array(device->machine(), UINT8, 7);
+
+ /* Make temp bitmap for rendering */
+ smsvdp->tmpbitmap = auto_bitmap_alloc(device->machine(), width, height, BITMAP_FORMAT_INDEXED32);
+
+ smsvdp->smsvdp_display_timer = device->machine().scheduler().timer_alloc(FUNC(smsvdp_display_callback), smsvdp);
+ smsvdp->smsvdp_display_timer->adjust(screen->time_until_pos(0, DISPLAY_CB_HPOS), 0, screen->scan_period());
+
+ device->save_item(NAME(smsvdp->status));
+ device->save_item(NAME(smsvdp->reg9copy));
+ device->save_item(NAME(smsvdp->addrmode));
+ device->save_item(NAME(smsvdp->addr));
+ device->save_item(NAME(smsvdp->cram_mask));
+ device->save_item(NAME(smsvdp->cram_dirty));
+ device->save_item(NAME(smsvdp->pending));
+ device->save_item(NAME(smsvdp->buffer));
+ device->save_item(NAME(smsvdp->gg_sms_mode));
+ device->save_item(NAME(smsvdp->irq_state));
+ device->save_item(NAME(smsvdp->vdp_mode));
+ device->save_item(NAME(smsvdp->y_pixels));
+ device->save_item(NAME(smsvdp->line_counter));
+ device->save_item(NAME(smsvdp->hcounter));
+ device->save_item(NAME(smsvdp->reg));
+ device->save_item(NAME(smsvdp->current_palette));
+ device->save_pointer(NAME(smsvdp->line_buffer), 256 * 5);
+ device->save_pointer(NAME(smsvdp->collision_buffer), SMS_X_PIXELS);
+ device->save_item(NAME(*smsvdp->tmpbitmap));
+}
+
+static DEVICE_RESET( smsvdp )
+{
+ smsvdp_t *smsvdp = get_safe_token(device);
+ int i;
+
+ /* Most register are 0x00 at power-up */
+ for (i = 0; i < NUM_OF_REGISTER; i++)
+ smsvdp->reg[i] = 0x00;
+
+ smsvdp->reg[0x02] = 0x0e;
+ smsvdp->reg[0x0a] = 0xff;
+
+ smsvdp->status = 0;
+ smsvdp->reg9copy = 0;
+ smsvdp->addrmode = 0;
+ smsvdp->addr = 0;
+ smsvdp->gg_sms_mode = 0;
+ smsvdp->cram_mask = (IS_GAMEGEAR_VDP && !smsvdp->gg_sms_mode) ? (GG_CRAM_SIZE - 1) : (SMS_CRAM_SIZE - 1);
+ smsvdp->cram_dirty = 1;
+ smsvdp->pending = 0;
+ smsvdp->buffer = 0;
+ smsvdp->irq_state = 0;
+ smsvdp->line_counter = 0;
+ smsvdp->hcounter = 0;
+
+ for (i = 0; i < 0x20; i++)
+ smsvdp->current_palette[i] = 0;
+
+ set_display_settings(device);
+
+ /* Clear RAM */
+ memset(smsvdp->VRAM->base(), 0, VRAM_SIZE);
+ memset(smsvdp->CRAM->base(), 0, MAX_CRAM_SIZE);
+ memset(smsvdp->line_buffer, 0, 256 * 5 * sizeof(int));
+}
+
+DEVICE_GET_INFO( smsvdp )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(smsvdp_t); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(smsvdp); break;
+ case DEVINFO_FCT_STOP: /* Nothing */ break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(smsvdp); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Sega Master SYstem / Game Gear VDP"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Sega MS VDP"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright MAME / MESS Team"); break;
+ }
+}
+
+DEFINE_LEGACY_DEVICE(SMSVDP, smsvdp);
diff --git a/src/mame/video/smsvdp.h b/src/mame/video/smsvdp.h
new file mode 100644
index 00000000000..92a20832e54
--- /dev/null
+++ b/src/mame/video/smsvdp.h
@@ -0,0 +1,86 @@
+/*************************************************************************
+
+ smsvdp.h
+
+ Implementation of Sega VDP chip used in Master System and Game Gear
+
+**************************************************************************/
+
+#ifndef __SMSVDP_H__
+#define __SMSVDP_H__
+
+#include "devcb.h"
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+
+#define MODEL_315_5124 0x0001
+#define MODEL_315_5246 0x0002
+#define MODEL_315_5378 0x0004
+
+#define SMS_X_PIXELS 342 /* 342 pixels */
+#define NTSC_Y_PIXELS 262 /* 262 lines */
+#define PAL_Y_PIXELS 313 /* 313 lines */
+#define LBORDER_START (1 + 2 + 14 + 8)
+#define LBORDER_X_PIXELS (0x0d) /* 13 pixels */
+#define RBORDER_X_PIXELS (0x0f) /* 15 pixels */
+#define TBORDER_START (3 + 13)
+#define NTSC_192_TBORDER_Y_PIXELS (0x1b) /* 27 lines */
+#define NTSC_192_BBORDER_Y_PIXELS (0x18) /* 24 lines */
+#define NTSC_224_TBORDER_Y_PIXELS (0x0b) /* 11 lines */
+#define NTSC_224_BBORDER_Y_PIXELS (0x08) /* 8 lines */
+#define PAL_192_TBORDER_Y_PIXELS (0x36) /* 54 lines */
+#define PAL_192_BBORDER_Y_PIXELS (0x30) /* 48 lines */
+#define PAL_224_TBORDER_Y_PIXELS (0x26) /* 38 lines */
+#define PAL_224_BBORDER_Y_PIXELS (0x20) /* 32 lines */
+#define PAL_240_TBORDER_Y_PIXELS (0x1e) /* 30 lines */
+#define PAL_240_BBORDER_Y_PIXELS (0x18) /* 24 lines */
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef void (*smsvdp_int_cb)( running_machine &machine, int state );
+typedef void (*smsvdp_pause_cb)( running_machine &machine );
+
+typedef struct _smsvdp_interface smsvdp_interface;
+struct _smsvdp_interface
+{
+ UINT32 model; /* Select model/features for the emulation */
+ smsvdp_int_cb int_callback; /* Interrupt callback function */
+ smsvdp_pause_cb pause_callback; /* Pause callback function */
+};
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+***************************************************************************/
+
+DECLARE_LEGACY_DEVICE(SMSVDP, smsvdp);
+
+#define MCFG_SMSVDP_ADD(_tag, _interface) \
+ MCFG_DEVICE_ADD(_tag, SMSVDP, 0) \
+ MCFG_DEVICE_CONFIG(_interface)
+
+
+/***************************************************************************
+ DEVICE I/O FUNCTIONS
+***************************************************************************/
+
+/* prototypes */
+
+UINT32 sms_vdp_update( device_t *device, bitmap_t *bitmap, const rectangle *cliprect );
+READ8_DEVICE_HANDLER( sms_vdp_vcount_r );
+READ8_DEVICE_HANDLER( sms_vdp_hcount_latch_r );
+WRITE8_DEVICE_HANDLER( sms_vdp_hcount_latch_w );
+READ8_DEVICE_HANDLER( sms_vdp_data_r );
+WRITE8_DEVICE_HANDLER( sms_vdp_data_w );
+READ8_DEVICE_HANDLER( sms_vdp_ctrl_r );
+WRITE8_DEVICE_HANDLER( sms_vdp_ctrl_w );
+void sms_vdp_set_ggsmsmode( device_t *device, int mode );
+int sms_vdp_check_brightness( device_t *device, int x, int y );
+
+#endif /* __SMSVDP_H__ */