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, ¶m, NULL, hashfile->preloaded_hashes[i]->hashes)) + return hashfile->preloaded_hashes[i]; + } + + hashfile_parse(hashfile, singular_selector_proc, singular_use_proc, + hashfile->error_proc, (void *) ¶m); + 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(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(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->ByteCountDataLength) + { + /* 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; iword_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; istop_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; iform.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(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<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( 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( 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(); + 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(); + 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(); + 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(); + + 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 +#include +#include + +#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, §or_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 m_maincpu; + required_device 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 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 m_fdc; + required_device m_centronics; + required_device 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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(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("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(&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("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(); + 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(); + + 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(&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(&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(); + 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(); + 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("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(); + 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(); + 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(); + 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(); + + 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(); + 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(); + 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(); + UINT8 data = 0x00; + UINT32 cpu_cycles = downcast(&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(); + + if (state->m_has_fm) + state->m_fm_detect = (data & 0x01); +} + + +READ8_HANDLER( sms_fm_detect_r ) +{ + sms_state *state = space->machine().driver_data(); + + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + //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(); + return state->m_sscope_state; +} + + +WRITE8_HANDLER( sms_sscope_w ) +{ + sms_state *state = space->machine().driver_data(); + state->m_sscope_state = data; +} + + +READ8_HANDLER( sms_mapper_r ) +{ + sms_state *state = space->machine().driver_data(); + 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(); + 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(); + return state->m_tvdraw_data; +} + + +WRITE8_HANDLER( sms_mapper_w ) +{ + sms_state *state = space->machine().driver_data(); + 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(); + + 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(); + + 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(); + + 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(); + 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(); + 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(); + + 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(); + 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(); + 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(); + 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(); + + /* 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(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(); + 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(); + + 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(); + 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(); + 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(); + 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(); + 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(); + + 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(); + + 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(); + 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(); + return state->m_store_control; +} + + +WRITE8_HANDLER( sms_store_control_w ) +{ + sms_state *state = space->machine().driver_data(); + logerror("0x%04X: sms_store_control write 0x%02X\n", cpu_get_pc(&space->device()), data); + if (data & 0x02) + { + space->machine().device("maincpu")->resume(SUSPEND_REASON_HALT); + } + else + { + /* Pull reset line of CPU #0 low */ + space->machine().device("maincpu")->reset(); + space->machine().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(); + 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(); + 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_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_set_zero_flag(machine); + state->m_has_bios_full = 1; +} + + +DRIVER_INIT( smsj ) +{ + sms_state *state = machine.driver_data(); + 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_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_set_zero_flag(machine); + state->m_is_gamegear = 1; +} + + +DRIVER_INIT( gamegeaj ) +{ + sms_state *state = machine.driver_data(); + 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(); + 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(); + 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_vdp_update(state->m_vdp, bitmap, cliprect); + return 0; +} + +VIDEO_START( gamegear ) +{ + sms_state *state = machine.driver_data(); + 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(); + 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(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__ */