From 84c86102a73d81bdc0d72f78e3db765945e45b77 Mon Sep 17 00:00:00 2001 From: Toni Wilen Date: Sun, 20 Jan 2013 15:37:53 +0200 Subject: [PATCH] 2600b2 --- a2091.cpp | 41 +- blkdev.cpp | 130 +++- blkdev_cdimage.cpp | 11 +- cdtv.cpp | 4 +- cfgfile.cpp | 91 ++- consolehook.cpp | 3 +- cpummu.cpp | 208 +++++- cpummu30.cpp | 326 +++++++-- custom.cpp | 2 +- debug.cpp | 12 +- expansion.cpp | 2 +- filesys.cpp | 71 +- gayle.cpp | 728 +++++++++++++-------- gencpu.cpp | 293 +++++++-- hardfile.cpp | 32 +- include/a2091.h | 3 +- include/autoconf.h | 3 +- include/blkdev.h | 4 + include/cdtv.h | 2 +- include/cpummu.h | 90 ++- include/cpummu030.h | 123 +++- include/gui.h | 3 +- include/mmu_common.h | 10 +- include/options.h | 9 +- include/scsi.h | 8 +- memory.cpp | 5 +- newcpu.cpp | 26 +- od-win32/gencomp_msvc/gencomp_msvc.vcxproj | 2 +- od-win32/mman.cpp | 21 +- od-win32/resources/RCa02620 | Bin 0 -> 212576 bytes od-win32/resources/RCa06412 | Bin 0 -> 212124 bytes od-win32/resources/RCb02620 | Bin 0 -> 212576 bytes od-win32/resources/RCb06412 | Bin 0 -> 212124 bytes od-win32/resources/resource.h | 6 +- od-win32/resources/winuae.rc | 27 +- od-win32/win32.h | 4 +- od-win32/win32gui.cpp | 121 +++- od-win32/winuaechangelog.txt | 25 + rommgr.cpp | 2 +- scsi.cpp | 74 ++- 40 files changed, 1865 insertions(+), 657 deletions(-) create mode 100644 od-win32/resources/RCa02620 create mode 100644 od-win32/resources/RCa06412 create mode 100644 od-win32/resources/RCb02620 create mode 100644 od-win32/resources/RCb06412 diff --git a/a2091.cpp b/a2091.cpp index d19dcfcf..e9b1c3b6 100644 --- a/a2091.cpp +++ b/a2091.cpp @@ -9,7 +9,7 @@ #define A2091_DEBUG 0 #define A3000_DEBUG 0 -#define WD33C93_DEBUG 1 +#define WD33C93_DEBUG 0 #define WD33C93_DEBUG_PIO 0 #include "sysconfig.h" @@ -340,7 +340,7 @@ static TCHAR *scsitostring (void) p = buf; p[0] = 0; - for (i = 0; i < gettc () && i < sizeof wd_data; i++) { + for (i = 0; i < scsi->offset && i < sizeof wd_data; i++) { if (i > 0) { _tcscat (p, _T(".")); p++; @@ -418,7 +418,7 @@ static bool do_dma (void) static bool wd_do_transfer_out (void) { #if WD33C93_DEBUG > 0 - write_log (_T("%s SCSI O [%02X] %d/%d %s\n"), WD33C93, wdregs[WD_COMMAND_PHASE], wd_dataoffset, gettc (), scsitostring ()); + write_log (_T("%s SCSI O [%02X] %d/%d TC=%d %s\n"), WD33C93, wdregs[WD_COMMAND_PHASE], scsi->offset, scsi->data_len, gettc (), scsitostring ()); #endif if (wdregs[WD_COMMAND_PHASE] < 0x20) { /* message was sent */ @@ -775,6 +775,9 @@ static void wd_cmd_reset (bool irq) wdregs[0] &= ~(0x08 | 0x10); sasr = 0; wd_selected = false; + scsi = NULL; + scsidelay_irq[0] = 0; + scsidelay_irq[1] = 0; if (irq) set_status ((wdregs[0] & 0x08) ? 1 : 0, 200); } @@ -840,7 +843,7 @@ void wdscsi_put (uae_u8 d) #endif } else if (sasr == WD_DATA) { #if WD33C93_DEBUG_PIO - write_log (_T("%s WD_DATA WRITE %02x %d/%d,%d\n"), WD33C93, d, wd_dataoffset, scsi->len, scsi->data_len); + write_log (_T("%s WD_DATA WRITE %02x %d/%d\n"), WD33C93, d, scsi->offset, scsi->data_len); #endif if (!wd_data_avail) { write_log (_T("%s WD_DATA WRITE without data request!?\n"), WD33C93); @@ -913,7 +916,7 @@ uae_u8 wdscsi_get (void) } int status = scsi_receive_data (scsi, &v); #if WD33C93_DEBUG_PIO - write_log (_T("%s WD_DATA READ %02x %d/%d,%d\n"), WD33C93, v, wd_dataoffset, scsi->len, scsi->data_len); + write_log (_T("%s WD_DATA READ %02x %d/%d\n"), WD33C93, v, scsi->offset, scsi->data_len); #endif if (wd_dataoffset < sizeof wd_data) wd_data[wd_dataoffset] = v; @@ -1532,7 +1535,7 @@ static void freescsi (struct scsi_data *sd) scsi_free (sd); } -int addscsi (int ch, struct hd_hardfiledata *hfd, struct uaedev_config_info *ci, int scsi_level) +int add_scsi_hd (int ch, struct hd_hardfiledata *hfd, struct uaedev_config_info *ci, int scsi_level) { freescsi (scsis[ch]); scsis[ch] = NULL; @@ -1543,7 +1546,15 @@ int addscsi (int ch, struct hd_hardfiledata *hfd, struct uaedev_config_info *ci, if (!hdf_hd_open (hfd)) return 0; hfd->ansi_version = scsi_level; - scsis[ch] = scsi_alloc (ch, hfd); + scsis[ch] = scsi_alloc_hd (ch, hfd); + return scsis[ch] ? 1 : 0; +} + +int add_scsi_cd (int ch, int unitnum) +{ + device_func_init (0); + freescsi (scsis[ch]); + scsis[ch] = scsi_alloc_cd (ch, unitnum); return scsis[ch] ? 1 : 0; } @@ -1605,7 +1616,10 @@ static void addnativescsi (void) int a3000_add_scsi_unit (int ch, struct uaedev_config_info *ci) { - return addscsi (ch, NULL, ci, 2); + if (ci->cd_emu_unit >= 0) + return add_scsi_cd (ch, ci->cd_emu_unit); + else + return add_scsi_hd (ch, NULL, ci, 2); } void a3000scsi_reset (void) @@ -1621,7 +1635,10 @@ void a3000scsi_free (void) int a2091_add_scsi_unit (int ch, struct uaedev_config_info *ci) { - return addscsi (ch, NULL, ci, 1); + if (ci->cd_emu_unit >= 0) + return add_scsi_cd (ch, ci->cd_emu_unit); + else + return add_scsi_hd (ch, NULL, ci, 1); } @@ -1759,6 +1776,8 @@ uae_u8 *save_scsi_hd (int num, int *len, uae_u8 *dstptr) if (!scsis[num]) return NULL; s = scsis[num]; + if (s->hfd == NULL) + return NULL; if (dstptr) dstbak = dst = dstptr; else @@ -1794,7 +1813,7 @@ uae_u8 *restore_scsi_hd (uae_u8 *src) num = restore_u32 (); hfd = xcalloc (struct hd_hardfiledata, 1); - s = scsis[num] = scsi_alloc (num, hfd); + s = scsis[num] = scsi_alloc_hd (num, hfd); restore_u32 (); size = restore_u64 (); path = restore_string (); @@ -1812,7 +1831,7 @@ uae_u8 *restore_scsi_hd (uae_u8 *src) s->hfd->ansi_version = restore_u32 (); if (size) { - addscsi (num, hfd, NULL, s->hfd->ansi_version); + add_scsi_hd (num, hfd, NULL, s->hfd->ansi_version); } xfree (path); return src; diff --git a/blkdev.cpp b/blkdev.cpp index a8d62c04..3b1490b9 100644 --- a/blkdev.cpp +++ b/blkdev.cpp @@ -43,6 +43,7 @@ static TCHAR newimagefiles[MAX_TOTAL_SCSI_DEVICES][256]; static int imagechangetime[MAX_TOTAL_SCSI_DEVICES]; static bool cdimagefileinuse[MAX_TOTAL_SCSI_DEVICES]; static int wasopen[MAX_TOTAL_SCSI_DEVICES]; +static bool dev_init; /* convert minutes, seconds and frames -> logical sector number */ int msf2lsn (int msf) @@ -324,16 +325,16 @@ static int getunitinfo (int unitnum, int drive, cd_standard_unit csu, int *isaud return 0; } -static int get_standard_cd_unit2 (cd_standard_unit csu) +static int get_standard_cd_unit2 (struct uae_prefs *p, cd_standard_unit csu) { int unitnum = 0; int isaudio = 0; - if (currprefs.cdslots[unitnum].name[0] || currprefs.cdslots[unitnum].inuse) { - if (currprefs.cdslots[unitnum].name[0]) { + if (p->cdslots[unitnum].name[0] || p->cdslots[unitnum].inuse) { + if (p->cdslots[unitnum].name[0]) { device_func_init (SCSI_UNIT_IOCTL); - if (!sys_command_open_internal (unitnum, currprefs.cdslots[unitnum].name, csu)) { + if (!sys_command_open_internal (unitnum, p->cdslots[unitnum].name, csu)) { device_func_init (SCSI_UNIT_IMAGE); - if (!sys_command_open_internal (unitnum, currprefs.cdslots[unitnum].name, csu)) + if (!sys_command_open_internal (unitnum, p->cdslots[unitnum].name, csu)) goto fallback; } } else { @@ -371,7 +372,7 @@ fallback: int get_standard_cd_unit (cd_standard_unit csu) { - int unitnum = get_standard_cd_unit2 (csu); + int unitnum = get_standard_cd_unit2 (&currprefs, csu); if (unitnum < 0) return -1; #ifdef RETROPLATFORM @@ -396,6 +397,12 @@ int sys_command_isopen (int unitnum) int sys_command_open (int unitnum) { + blkdev_fix_prefs (&currprefs); + if (!dev_init) { + device_func_init (0); + dev_init = true; + } + if (openlist[unitnum]) { openlist[unitnum]++; return -1; @@ -449,6 +456,23 @@ int device_func_init (int flags) return 1; } +bool blkdev_get_info (struct uae_prefs *p, int unitnum, struct device_info *di) +{ + bool open = true, opened = true, ok = false; + if (!openlist[unitnum]) { + blkdev_fix_prefs (p); + install_driver (0); + opened = true; + open = sys_command_open_internal (unitnum, p->cdslots[unitnum].name[0] ? p->cdslots[unitnum].name : NULL, CD_STANDARD_UNIT_DEFAULT) != 0; + } + if (open) { + ok = sys_command_info (unitnum, di, true) != 0; + } + if (open && opened) + sys_command_close_internal (unitnum); + return ok; +} + void blkdev_entergui (void) { for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) { @@ -1121,7 +1145,7 @@ static int scsi_read_cd (int unitnum, uae_u8 *cmd, uae_u8 *data, struct device_i return sys_command_cd_rawread (unitnum, data, start, len, 0, (cmd[1] >> 2) & 7, cmd[9], subs); } -static int scsi_emulate (int unitnum, uae_u8 *cmdbuf, int scsi_cmd_len, +int scsi_cd_emulate (int unitnum, uae_u8 *cmdbuf, int scsi_cmd_len, uae_u8 *scsi_data, int *data_len, uae_u8 *r, int *reply_len, uae_u8 *s, int *sense_len) { uae_u64 len, offset; @@ -1150,14 +1174,21 @@ static int scsi_emulate (int unitnum, uae_u8 *cmdbuf, int scsi_cmd_len, scsi_cmd_len, scsi_data, *data_len); switch (cmdbuf[0]) { + case 0x03: /* REQUEST SENSE */ + break; + case 0x1e: /* PREVENT/ALLOW MEDIUM REMOVAL */ + scsi_len = 0; + break; case 0x12: /* INQUIRY */ { if ((cmdbuf[1] & 1) || cmdbuf[2] != 0) goto err; len = cmdbuf[4]; - if (cmdbuf[1] >> 5) - goto err; - r[0] = 5; // CDROM + if (cmdbuf[1] >> 5) { + r[0] = 0x7f; + } else { + r[0] = 5; // CDROM + } r[1] |= 0x80; // removable r[2] = 2; /* supports SCSI-2 */ r[3] = 2; /* response data format */ @@ -1414,6 +1445,61 @@ static int scsi_emulate (int unitnum, uae_u8 *cmdbuf, int scsi_cmd_len, break; case 0xaa: /* WRITE (12) */ goto readprot; + case 0x51: /* READ DISC INFORMATION */ + { + struct cd_toc_head ttoc; + int maxlen = (cmdbuf[7] << 8) | cmdbuf[8]; + if (nodisk (&di)) + goto nodisk; + if (!sys_command_cd_toc (unitnum, &ttoc)) + goto readerr; + struct cd_toc_head *toc = &ttoc; + uae_u8 *p = scsi_data; + p[0] = 0; + p[1] = 34 - 2; + p[2] = 2 | (3 << 2); // complete cd rom, last session is complete + p[3] = toc->first_track; + p[4] = 1; + p[5] = toc->first_track; + p[6] = toc->last_track; + wl (p + 16, lsn2msf (toc->lastaddress)); + wl (p + 20, 0x00ffffff); + scsi_len = p[1] + 2; + if (scsi_len > maxlen) + scsi_len = maxlen; + } + break; + case 0x52: /* READ TRACK INFORMATION */ + { + struct cd_toc_head ttoc; + int maxlen = (cmdbuf[7] << 8) | cmdbuf[8]; + if (nodisk (&di)) + goto nodisk; + if (!sys_command_cd_toc (unitnum, &ttoc)) + goto readerr; + struct cd_toc_head *toc = &ttoc; + uae_u8 *p = scsi_data; + int lsn; + if (cmdbuf[1] & 1) { + int track = cmdbuf[5]; + lsn = toc->toc[track].address; + } else { + lsn = rl (p + 2); + } + struct cd_toc *t = gettoc (toc, lsn); + p[0] = 0; + p[1] = 28 - 2; + p[2] = t->track; + p[3] = 1; + p[5] = t->control; + p[6] = 0; // data mode, fixme + wl (p + 8, t->address); + wl (p + 24, t[1].address - t->address); + scsi_len = p[1] + 2; + if (scsi_len > maxlen) + scsi_len = maxlen; + } + break; case 0x43: // READ TOC { if (nodisk (&di)) @@ -1425,6 +1511,7 @@ static int scsi_emulate (int unitnum, uae_u8 *cmdbuf, int scsi_cmd_len, if (format >= 3) goto errreq; int maxlen = (cmdbuf[7] << 8) | cmdbuf[8]; + int maxlen2 = maxlen; struct cd_toc_head ttoc; if (!sys_command_cd_toc (unitnum, &ttoc)) goto readerr; @@ -1458,23 +1545,24 @@ static int scsi_emulate (int unitnum, uae_u8 *cmdbuf, int scsi_cmd_len, maxlen -= 4; if (format == 2) { if (!addtocentry (&p2, &maxlen, 0xa0, -1, msf, p, toc)) - goto errreq; + break; if (!addtocentry (&p2, &maxlen, 0xa1, -1, msf, p, toc)) - goto errreq; + break; if (!addtocentry (&p2, &maxlen, 0xa2, -1, msf, p, toc)) - goto errreq; + break; } while (strack < 100) { if (!addtocentry (&p2, &maxlen, strack, -1, msf, p, toc)) - goto errreq; + break; strack++; } - if (!addtocentry (&p2, &maxlen, 0xa2, 0xaa, msf, p, toc)) - goto errreq; + addtocentry (&p2, &maxlen, 0xa2, 0xaa, msf, p, toc); int tlen = p2 - (p + 2); p[0] = tlen >> 8; p[1] = tlen >> 0; - scsi_len = tlen + 2 + 4; + scsi_len = tlen + 2; + if (scsi_len > maxlen2) + scsi_len = maxlen2; } } break; @@ -1524,10 +1612,6 @@ static int scsi_emulate (int unitnum, uae_u8 *cmdbuf, int scsi_cmd_len, scsiemudrv (unitnum, cmdbuf); scsi_len = 0; break; - case 0x1e: // PREVENT/ALLOW MEDIA REMOVAL - // do nothing - scsi_len = 0; - break; case 0x4e: // STOP PLAY/SCAN if (nodisk (&di)) goto nodisk; @@ -1635,7 +1719,7 @@ static int scsi_emulate (int unitnum, uae_u8 *cmdbuf, int scsi_cmd_len, goto nodisk; int start = rl (cmdbuf + 2); int len; - if (cmd = 0xa5) + if (cmd == 0xa5) len = rl (cmdbuf + 6); else len = rw (cmdbuf + 7); @@ -1762,7 +1846,7 @@ static int execscsicmd_direct (int unitnum, struct amigascsi *as) if (as->sense_len > 32) as->sense_len = 32; - as->status = scsi_emulate (unitnum, cmd, as->cmd_len, scsi_datap, &datalen, replydata, &replylen, as->sensedata, &senselen); + as->status = scsi_cd_emulate (unitnum, cmd, as->cmd_len, scsi_datap, &datalen, replydata, &replylen, as->sensedata, &senselen); as->cmdactual = as->status != 0 ? 0 : as->cmd_len; /* fake scsi_CmdActual */ if (as->status) { diff --git a/blkdev_cdimage.cpp b/blkdev_cdimage.cpp index 23c527a2..ffdbfafe 100644 --- a/blkdev_cdimage.cpp +++ b/blkdev_cdimage.cpp @@ -86,8 +86,7 @@ struct cdunit { play_status_callback cdda_statusfunc; int cdda_delay, cdda_delay_frames; - int imagechange; - TCHAR newfile[MAX_DPATH]; + TCHAR imgname[MAX_DPATH]; uae_sem_t sub_sem; struct device_info di; chd_file *chd_f; @@ -1705,7 +1704,7 @@ static struct device_info *info_device (int unitnum, struct device_info *di, int di->sectorspertrack = (int)(cdu->cdsize / di->bytespersector); if (ismedia (unitnum, 1)) { di->media_inserted = 1; - _tcscpy (di->mediapath, currprefs.cdslots[unitnum].name); + _tcscpy (di->mediapath, cdu->imgname); } memset (&di->toc, 0, sizeof (struct cd_toc_head)); command_toc (unitnum, &di->toc); @@ -1718,6 +1717,9 @@ static struct device_info *info_device (int unitnum, struct device_info *di, int } else { _tcscpy (di->label, _T("IMG:")); } + _tcscpy (di->vendorid, _T("UAE")); + _stprintf (di->productid, _T("SCSICD%d"), unitnum); + _tcscpy (di->revision, _T("1.0")); di->backend = _T("IMAGE"); return di; } @@ -1737,8 +1739,10 @@ static void unload_image (struct cdunit *cdu) xfree (t->extrainfo); } cdrom_close (cdu->chd_cdf); + cdu->chd_cdf = NULL; if (cdu->chd_f) cdu->chd_f->close(); + cdu->chd_f = NULL; memset (cdu->toc, 0, sizeof cdu->toc); cdu->tracks = 0; cdu->cdsize = 0; @@ -1752,6 +1756,7 @@ static int open_device (int unitnum, const TCHAR *ident, int flags) if (!cdu->open) { uae_sem_init (&cdu->sub_sem, 0, 1); + _tcscpy (cdu->imgname, ident); parse_image (cdu, ident); cdu->open = true; cdu->enabled = true; diff --git a/cdtv.cpp b/cdtv.cpp index d8254914..e49c9a80 100644 --- a/cdtv.cpp +++ b/cdtv.cpp @@ -1656,9 +1656,9 @@ uae_u8 cdtv_battram_read (int addr) return v; } -int cdtv_add_scsi_unit (int ch, struct uaedev_config_info *ci) +int cdtv_add_scsi_hd_unit (int ch, struct uaedev_config_info *ci) { - return addscsi (ch, NULL, ci, 1); + return add_scsi_hd (ch, NULL, ci, 1); } void cdtv_free (void) diff --git a/cfgfile.cpp b/cfgfile.cpp index 09e4acea..b9437a63 100644 --- a/cfgfile.cpp +++ b/cfgfile.cpp @@ -533,7 +533,7 @@ static void write_filesys_config (struct uae_prefs *p, struct zfile *f) if (ci->donotmount) bp = -129; str = cfgfile_put_multipath (&p->path_hardfile, ci->rootdir); - if (!uci->ishdf) { + if (ci->type == UAEDEV_DIR) { _stprintf (tmp, _T("%s,%s:%s:%s,%d"), ci->readonly ? _T("ro") : _T("rw"), ci->devname ? ci->devname : _T(""), ci->volname, str, bp); cfgfile_write_str (f, _T("filesystem2"), tmp); @@ -542,7 +542,7 @@ static void write_filesys_config (struct uae_prefs *p, struct zfile *f) uci->volname, str); zfile_fputs (f, tmp2); #endif - } else { + } else if (ci->type == UAEDEV_HDF || ci->type == UAEDEV_CD) { _stprintf (tmp, _T("%s,%s:%s,%d,%d,%d,%d,%d,%s,%s"), ci->readonly ? _T("ro") : _T("rw"), ci->devname ? ci->devname : _T(""), str, @@ -556,7 +556,8 @@ static void write_filesys_config (struct uae_prefs *p, struct zfile *f) _stprintf (s, _T(",%d/%d/%d"), ci->pcyls, ci->pheads, ci->psecs); } } - cfgfile_write_str (f, _T("hardfile2"), tmp); + if (ci->type == UAEDEV_HDF) + cfgfile_write_str (f, _T("hardfile2"), tmp); #if 0 _stprintf (tmp2, _T("hardfile=%s,%d,%d,%d,%d,%s"), uci->readonly ? "ro" : "rw", uci->sectors, @@ -565,7 +566,11 @@ static void write_filesys_config (struct uae_prefs *p, struct zfile *f) #endif } _stprintf (tmp2, _T("uaehf%d"), i); - cfgfile_write (f, tmp2, _T("%s,%s"), uci->ishdf ? _T("hdf") : _T("dir"), tmp); + if (ci->type == UAEDEV_CD) { + cfgfile_write (f, tmp2, _T("cd%d,%s"), ci->cd_emu_unit, tmp); + } else { + cfgfile_write (f, tmp2, _T("%s,%s"), ci->type == UAEDEV_HDF ? _T("hdf") : _T("dir"), tmp); + } xfree (str); } } @@ -2309,7 +2314,7 @@ static struct uaedev_config_data *getuci (struct uae_prefs *p) return NULL; } -struct uaedev_config_data *add_filesys_config (struct uae_prefs *p, int index, struct uaedev_config_info *ci, bool hdf) +struct uaedev_config_data *add_filesys_config (struct uae_prefs *p, int index, struct uaedev_config_info *ci) { struct uaedev_config_data *uci; int i; @@ -2320,8 +2325,18 @@ struct uaedev_config_data *add_filesys_config (struct uae_prefs *p, int index, s return 0; } } + if (ci->type == UAEDEV_CD) { + if (ci->controller > HD_CONTROLLER_SCSI6 || ci->controller < HD_CONTROLLER_IDE0) + return NULL; + } if (index < 0) { + if (ci->type == UAEDEV_CD) { + for (i = 0; i < p->mountitems; i++) { + if (p->mountconfig[i].ci.type == UAEDEV_CD) + return 0; + } + } uci = getuci (p); uci->configoffset = -1; } else { @@ -2331,18 +2346,17 @@ struct uaedev_config_data *add_filesys_config (struct uae_prefs *p, int index, s return 0; memcpy (&uci->ci, ci, sizeof (struct uaedev_config_info)); - uci->ishdf = hdf; validatedevicename (uci->ci.devname); validatevolumename (uci->ci.volname); if (uci->ci.bootpri < -128) uci->ci.donotmount = true; else if (uci->ci.bootpri >= -127) uci->ci.autoboot = true; - if (!uci->ci.devname[0]) { + if (!uci->ci.devname[0] && ci->type != UAEDEV_CD) { TCHAR base[32]; TCHAR base2[32]; int num = 0; - if (uci->ci.rootdir[0] == 0 && !uci->ishdf) + if (uci->ci.rootdir[0] == 0 && ci->type == UAEDEV_DIR) _tcscpy (base, _T("RDH")); else _tcscpy (base, _T("DH")); @@ -2358,7 +2372,7 @@ struct uaedev_config_data *add_filesys_config (struct uae_prefs *p, int index, s _tcscpy (uci->ci.devname, base2); validatedevicename (uci->ci.devname); } - if (!uci->ishdf) { + if (ci->type == UAEDEV_DIR) { TCHAR *s = filesys_createvolname (uci->ci.volname, uci->ci.rootdir, _T("Harddrive")); _tcscpy (uci->ci.volname, s); xfree (s); @@ -2496,6 +2510,8 @@ static bool parse_geo (const TCHAR *tname, struct uaedev_config_info *uci, struc uci->flags = v; if (!_tcsicmp (key, _T("priority"))) uci->priority = v; + if (!_tcsicmp (key, _T("forceload"))) + uci->forceload = v; if (!_tcsicmp (key, _T("bootpri"))) { uci->bootpri = v; uci->donotmount = false; @@ -2540,7 +2556,7 @@ bool get_hd_geometry (struct uaedev_config_info *uci) return false; } -static int cfgfile_parse_newfilesys (struct uae_prefs *p, int nr, bool hdf, TCHAR *value) +static int cfgfile_parse_newfilesys (struct uae_prefs *p, int nr, int type, TCHAR *value, int unit, bool uaehfentry) { struct uaedev_config_info uci; TCHAR *tmpp = _tcschr (value, ','), *tmpp2; @@ -2563,7 +2579,8 @@ static int cfgfile_parse_newfilesys (struct uae_prefs *p, int nr, bool hdf, TCHA goto invalid_fs; value = tmpp; - if (!hdf) { + if (type == 0) { + uci.type = UAEDEV_DIR; tmpp = _tcschr (value, ':'); if (tmpp == 0) goto empty_fs; @@ -2585,7 +2602,7 @@ static int cfgfile_parse_newfilesys (struct uae_prefs *p, int nr, bool hdf, TCHA _tcscpy (uci.devname, devname); if (! getintval (&tmpp, &uci.bootpri, 0)) goto empty_fs; - } else { + } else if (type == 1 || (type == 2 && uaehfentry)) { tmpp = _tcschr (value, ':'); if (tmpp == 0) goto invalid_fs; @@ -2624,6 +2641,16 @@ static int cfgfile_parse_newfilesys (struct uae_prefs *p, int nr, bool hdf, TCHA } } } + if (type == 2) { + uci.cd_emu_unit = unit; + uci.blocksize = 2048; + uci.readonly = true; + uci.type = UAEDEV_CD; + } else { + uci.type = UAEDEV_HDF; + } + } else { + goto invalid_fs; } empty_fs: uci.autoboot = uci.bootpri >= -127; @@ -2637,13 +2664,13 @@ empty_fs: _tcscpy (uci.rootdir, str); } #ifdef FILESYS - add_filesys_config (p, nr, &uci, hdf); + add_filesys_config (p, nr, &uci); #endif xfree (str); return 1; invalid_fs: - write_log (_T("Invalid filesystem/hardfile specification.\n")); + write_log (_T("Invalid filesystem/hardfile/cd specification.\n")); return 1; } @@ -2656,21 +2683,29 @@ static int cfgfile_parse_filesys (struct uae_prefs *p, const TCHAR *option, TCHA _stprintf (tmp, _T("uaehf%d"), i); if (_tcscmp (option, tmp) == 0) { for (;;) { - bool hdf = false; + int type = -1; + int unit = -1; TCHAR *tmpp = _tcschr (value, ','); if (tmpp == NULL) return 1; *tmpp++ = 0; - if (strcasecmp (value, _T("hdf")) == 0) { - hdf = true; - } else if (strcasecmp (value, _T("dir")) != 0) { - return 1; + if (_tcsicmp (value, _T("hdf")) == 0) { + type = 1; + return 1; /* ignore for now */ + } else if (_tcsnicmp (value, _T("cd"), 2) == 0 && (value[2] == 0 || value[3] == 0)) { + unit = 0; + if (value[2] > 0) + unit = value[2] - '0'; + if (unit >= 0 && unit <= MAX_TOTAL_SCSI_DEVICES) { + type = 2; + } + } else if (_tcsicmp (value, _T("dir")) != 0) { + type = 0; + return 1; /* ignore for now */ } -#if 0 // not yet - return cfgfile_parse_newfilesys (p, i, hdf, tmpp); -#else + if (type >= 0) + return cfgfile_parse_newfilesys (p, -1, type, tmpp, unit, true); return 1; -#endif } return 1; } else if (!_tcsncmp (option, tmp, _tcslen (tmp)) && option[_tcslen (tmp)] == '_') { @@ -2744,7 +2779,8 @@ static int cfgfile_parse_filesys (struct uae_prefs *p, const TCHAR *option, TCHA } str = cfgfile_subst_path_load (UNEXPANDED, &p->path_hardfile, uci.rootdir, true); #ifdef FILESYS - add_filesys_config (p, -1, &uci, hdf); + uci.type = hdf ? UAEDEV_HDF : UAEDEV_DIR; + add_filesys_config (p, -1, &uci); #endif xfree (str); return 1; @@ -2755,9 +2791,9 @@ invalid_fs: } if (_tcscmp (option, _T("filesystem2")) == 0) - return cfgfile_parse_newfilesys (p, -1, false, value); + return cfgfile_parse_newfilesys (p, -1, 0, value, -1, false); if (_tcscmp (option, _T("hardfile2")) == 0) - return cfgfile_parse_newfilesys (p, -1, true, value); + return cfgfile_parse_newfilesys (p, -1, 1, value, -1, false); return 0; } @@ -3749,7 +3785,8 @@ static void parse_filesys_spec (struct uae_prefs *p, bool readonly, const TCHAR _tcscpy (uci.volname, buf); _tcscpy (uci.rootdir, s2); uci.readonly = readonly; - add_filesys_config (p, -1, &uci, false); + uci.type = UAEDEV_DIR; + add_filesys_config (p, -1, &uci); #endif } else { write_log (_T("Usage: [-m | -M] VOLNAME:mount_point\n")); diff --git a/consolehook.cpp b/consolehook.cpp index a81f8e18..28f76f44 100644 --- a/consolehook.cpp +++ b/consolehook.cpp @@ -54,7 +54,8 @@ void consolehook_config (struct uae_prefs *p) _tcscpy (ci.volname, _T("CLIBOOT")); _tcscpy (ci.devname, _T("DH0")); ci.bootpri = 15; - add_filesys_config (p, -1, &ci, false); + ci.type = UAEDEV_DIR; + add_filesys_config (p, -1, &ci); } static void *console_thread (void *v) diff --git a/cpummu.cpp b/cpummu.cpp index a65db594..64b52a94 100644 --- a/cpummu.cpp +++ b/cpummu.cpp @@ -42,11 +42,17 @@ uae_u32 mmu_is_super; -uae_u32 mmu_tagmask, mmu_pagemask; +uae_u32 mmu_tagmask, mmu_pagemask, mmu_pagemaski; struct mmu_atc_line mmu_atc_array[ATC_TYPE][ATC_WAYS][ATC_SLOTS]; -int mmu060_state; bool mmu_pagesize_8k; +int mmu060_state; +uae_u16 mmu060_opcode; +static bool locked_rmw_cycle; + +int mmu040_movem; +uaecptr mmu040_movem_ea; + static void mmu_dump_ttr(const TCHAR * label, uae_u32 ttr) { DUNUSED(label); @@ -247,7 +253,7 @@ static void mmu_bus_error(uaecptr addr, int fc, bool write, int size, uae_u32 st if (currprefs.mmu_model == 68040) { uae_u16 ssw = 0; - ssw |= fc & MMU_SSW_TM; /* Copy TM */ + ssw |= fc & MMU_SSW_TM; /* TM = FC */ switch (size) { case sz_byte: ssw |= MMU_SSW_SIZE_B; @@ -258,21 +264,45 @@ static void mmu_bus_error(uaecptr addr, int fc, bool write, int size, uae_u32 st case sz_long: ssw |= MMU_SSW_SIZE_L; break; + case 16: // MOVE16 + ssw |= MMU_SSW_SIZE_L; // ?? + ssw |= MMU_SSW_TT0; + break; } regs.wb3_status = write ? 0x80 | ssw : 0; if (!write) ssw |= MMU_SSW_RW; + if (mmu040_movem) { + ssw |= MMU_SSW_CM; + addr = mmu040_movem_ea; + mmu040_movem = 0; + write_log (_T("040 MMU_SSW_CM!\n")); + } + if (locked_rmw_cycle) { + ssw |= MMU_SSW_LK; + locked_rmw_cycle = false; + write_log (_T("040 MMU_SSW_LK!\n")); + } + regs.mmu_ssw = ssw | MMU_SSW_ATC; -#if MMUDEBUG > 2 +#if MMUDEBUG > 0 write_log(_T("040 BUS ERROR: fc=%d w=%d logical=%08x ssw=%04x PC=%08x\n"), fc, write, addr, ssw, m68k_getpc()); #endif } else { uae_u32 fslw = 0; fslw |= write ? MMU_FSLW_W : MMU_FSLW_R; +#if 0 + // read-modify-write? + if (table68k[mmu060_opcode].rmw) { + fslw |= MMU_FSLW_W | MMU_FSLW_R; + } +#endif + + fslw |= fc << 16; /* MMU_FSLW_TM */ switch (size) { case sz_byte: fslw |= MMU_FSLW_SIZE_B; @@ -286,24 +316,39 @@ static void mmu_bus_error(uaecptr addr, int fc, bool write, int size, uae_u32 st case 16: // MOVE16 addr &= ~15; fslw |= MMU_FSLW_SIZE_D; + fslw |= MMU_FSLW_TT_16; break; } - if (mmu060_state == 0) { - fslw |= MMU_FSLW_IO; // opword fetch - } else if ((fc & 2)) { - fslw |= MMU_FSLW_IO | MMU_FSLW_MA; // extension word + if ((fc & 3) == 2) { + if (mmu060_state == 0) { + fslw |= MMU_FSLW_IO; // opword fetch + } else { + fslw |= MMU_FSLW_IO | MMU_FSLW_MA; // extension word + } + } + if (locked_rmw_cycle) { + fslw |= MMU_FSLW_LK; + locked_rmw_cycle = false; + write_log (_T("060 MMU_FSLW_LK!\n")); } fslw |= status; regs.mmu_fslw = fslw; -#if MMUDEBUG > 2 +#if MMUDEBUG > 0 write_log(_T("060 BUS ERROR: fc=%d w=%d logical=%08x ssw=%08x PC=%08x\n"), fc, write, addr, fslw, m68k_getpc()); #endif } regs.mmu_fault_addr = addr; +#if 0 + if (addr == 0x00002180) { + write_log (_T("*")); + extern void activate_debugger(void); + activate_debugger (); + } +#endif THROW(2); } @@ -351,10 +396,7 @@ static uaecptr mmu_fill_atc(uaecptr addr, bool super, bool data, bool write, str l->global = 0; } else { l->valid = 1; - if (mmu_pagesize_8k) - l->phys = (desc & ~0x1fff); - else - l->phys = (desc & ~0xfff); + l->phys = desc & mmu_pagemaski; l->global = (desc & MMU_MMUSR_G) != 0; l->modified = (desc & MMU_MMUSR_M) != 0; l->write_protect = (desc & MMU_MMUSR_W) != 0; @@ -375,7 +417,7 @@ static ALWAYS_INLINE bool mmu_fill_atc_try(uaecptr addr, bool super, bool data, if (write) { if (l1->write_protect) { *status |= MMU_FSLW_WP; -#if MMUDEBUG > 1 +#if MMUDEBUG > 0 write_log(_T("MMU: write protected %lx by atc \n"), addr); #endif mmu_dump_atc(); @@ -398,14 +440,14 @@ uaecptr REGPARAM2 mmu_translate(uaecptr addr, bool super, bool data, bool write) mmu_user_lookup(addr, super, data, write, &l); mmu_fill_atc(addr, super, data, write, l, &status); - if (!l->valid) { -#if MMUDEBUG > 0 + if (!l->valid || (write && l->write_protect)) { +#if MMUDEBUG > 2 write_log(_T("[MMU] mmu_translate error")); #endif THROW(2); } - return l->phys | (addr & (mmu_pagesize_8k?0x00001fff:0x00000fff)); + return l->phys | (addr & mmu_pagemask); } @@ -536,6 +578,7 @@ uae_u32 REGPARAM2 mmu_get_long_unaligned(uaecptr addr, bool data) CATCH(prb) { RESTORE_EXCEPTION; regs.mmu_fault_addr = addr; + regs.mmu_fslw |= MMU_FSLW_MA; regs.mmu_ssw |= MMU_SSW_MA; THROW_AGAIN(prb); } ENDTRY @@ -559,6 +602,63 @@ uae_u32 REGPARAM2 mmu_get_long_unaligned(uaecptr addr, bool data) return res; } +uae_u16 REGPARAM2 mmu_get_rmw_word_unaligned(uaecptr addr) +{ + uae_u16 res; + + res = (uae_u16)mmu_get_user_byte(addr, regs.s != 0, false, true, sz_word) << 8; + SAVE_EXCEPTION; + TRY(prb) { + res |= mmu_get_user_byte(addr + 1, regs.s != 0, false, true, sz_word); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + regs.mmu_fault_addr = addr; + regs.mmu_fslw |= MMU_FSLW_MA; + regs.mmu_ssw |= MMU_SSW_MA; + THROW_AGAIN(prb); + } ENDTRY + return res; +} + +uae_u32 REGPARAM2 mmu_get_rmw_long_unaligned(uaecptr addr) +{ + uae_u32 res; + + if (likely(!(addr & 1))) { + res = (uae_u32)mmu_get_user_word(addr, regs.s != 0, false, true, sz_long) << 16; + SAVE_EXCEPTION; + TRY(prb) { + res |= mmu_get_user_word(addr + 2, regs.s != 0, false, true, sz_long); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + regs.mmu_fault_addr = addr; + regs.mmu_fslw |= MMU_FSLW_MA; + regs.mmu_ssw |= MMU_SSW_MA; + THROW_AGAIN(prb); + } ENDTRY + } else { + res = (uae_u32)mmu_get_user_byte(addr, regs.s != 0, false, true, sz_long) << 8; + SAVE_EXCEPTION; + TRY(prb) { + res = (res | mmu_get_user_byte(addr + 1, regs.s != 0, false, true, sz_long)) << 8; + res = (res | mmu_get_user_byte(addr + 2, regs.s != 0, false, true, sz_long)) << 8; + res |= mmu_get_user_byte(addr + 3, regs.s != 0, false, true, sz_long); + RESTORE_EXCEPTION; + } + CATCH(prb) { + RESTORE_EXCEPTION; + regs.mmu_fault_addr = addr; + regs.mmu_fslw |= MMU_FSLW_MA; + regs.mmu_ssw |= MMU_SSW_MA; + THROW_AGAIN(prb); + } ENDTRY + } + return res; +} uae_u8 REGPARAM2 mmu_get_byte_slow(uaecptr addr, bool super, bool data, int size, struct mmu_atc_line *cl) { @@ -682,13 +782,13 @@ uae_u32 REGPARAM2 sfc_get_long(uaecptr addr) uae_u32 res; if (likely(!is_unaligned(addr, 4))) - return mmu_get_user_long(addr, super, data, sz_long); + return mmu_get_user_long(addr, super, data, false, sz_long); if (likely(!(addr & 1))) { - res = (uae_u32)mmu_get_user_word(addr, super, data, sz_long) << 16; + res = (uae_u32)mmu_get_user_word(addr, super, data, false, sz_long) << 16; SAVE_EXCEPTION; TRY(prb) { - res |= mmu_get_user_word(addr + 2, super, data, sz_long); + res |= mmu_get_user_word(addr + 2, super, data, false, sz_long); RESTORE_EXCEPTION; } CATCH(prb) { @@ -699,12 +799,12 @@ uae_u32 REGPARAM2 sfc_get_long(uaecptr addr) THROW_AGAIN(prb); } ENDTRY } else { - res = (uae_u32)mmu_get_user_byte(addr, super, data, sz_long) << 8; + res = (uae_u32)mmu_get_user_byte(addr, super, data, false, sz_long) << 8; SAVE_EXCEPTION; TRY(prb) { - res = (res | mmu_get_user_byte(addr + 1, super, data, sz_long)) << 8; - res = (res | mmu_get_user_byte(addr + 2, super, data, sz_long)) << 8; - res |= mmu_get_user_byte(addr + 3, super, data, sz_long); + res = (res | mmu_get_user_byte(addr + 1, super, data, false, sz_long)) << 8; + res = (res | mmu_get_user_byte(addr + 2, super, data, false, sz_long)) << 8; + res |= mmu_get_user_byte(addr + 3, super, data, false, sz_long); RESTORE_EXCEPTION; } CATCH(prb) { @@ -725,12 +825,12 @@ uae_u16 REGPARAM2 sfc_get_word(uaecptr addr) uae_u16 res; if (likely(!is_unaligned(addr, 2))) - return mmu_get_user_word(addr, super, data, sz_word); + return mmu_get_user_word(addr, super, data, false, sz_word); - res = (uae_u16)mmu_get_user_byte(addr, super, data, sz_word) << 8; + res = (uae_u16)mmu_get_user_byte(addr, super, data, false, sz_word) << 8; SAVE_EXCEPTION; TRY(prb) { - res |= mmu_get_user_byte(addr + 1, super, data, sz_word); + res |= mmu_get_user_byte(addr + 1, super, data, false, sz_word); RESTORE_EXCEPTION; } CATCH(prb) { @@ -748,7 +848,7 @@ uae_u8 REGPARAM2 sfc_get_byte(uaecptr addr) bool super = (regs.sfc & 4) != 0; bool data = (regs.sfc & 3) != 2; - return mmu_get_user_byte(addr, super, data, sz_byte); + return mmu_get_user_byte(addr, super, data, false, sz_byte); } void REGPARAM2 dfc_put_long(uaecptr addr, uae_u32 val) @@ -842,13 +942,13 @@ void REGPARAM2 mmu_op_real(uae_u32 opcode, uae_u16 extra) if (opcode & 16) { #if MMUDEBUG > 1 - write_log(_T("pflusha(%u,%u)\n"), glob, regs.dfc); + write_log(_T("pflusha(%u,%u) PC=%08x\n"), glob, regs.dfc, m68k_getpc ()); #endif mmu_flush_atc_all(glob); } else { addr = m68k_areg(regs, regno); #if MMUDEBUG > 1 - write_log(_T("pflush(%u,%u,%x)\n"), glob, regs.dfc, addr); + write_log(_T("pflush(%u,%u,%x) PC=%08x\n"), glob, regs.dfc, addr, m68k_getpc ()); #endif mmu_flush_atc(addr, super, glob); } @@ -959,8 +1059,9 @@ void REGPARAM2 mmu_set_tc(uae_u16 tc) { regs.mmu_enabled = (tc & 0x8000) != 0; mmu_pagesize_8k = (tc & 0x4000) != 0; - mmu_tagmask = mmu_pagesize_8k ? 0xFFFF0000 : 0xFFFF8000; + mmu_tagmask = mmu_pagesize_8k ? 0xFFFF0000 : 0xFFFF8000; mmu_pagemask = mmu_pagesize_8k ? 0x00001FFF : 0x00000FFF; + mmu_pagemaski = ~mmu_pagemask; regs.mmu_page_size = mmu_pagesize_8k ? 8192 : 4096; mmu_flush_atc_all(true); @@ -984,6 +1085,10 @@ void m68k_do_rte_mmu040 (uaecptr a7) // skip this word put_long_mmu040 (dst_a7 + 8, get_long_mmu040 (src_a7 + 8)); } + if (ssr & MMU_SSW_CM) { + mmu040_movem = 1; + mmu040_movem_ea = get_long_mmu040 (a7 + 8); + } } void flush_mmu040 (uaecptr addr, int n) @@ -1016,6 +1121,47 @@ void m68k_do_bsr_mmu060 (uaecptr oldpc, uae_s32 offset) m68k_incpci (offset); } +void uae_mmu_put_rmw (uaecptr addr, uae_u32 v, int size, int type) +{ + locked_rmw_cycle = true; + if (size == sz_byte) { + mmu_put_byte(addr, v, true, sz_byte); + } else if (size == sz_word) { + if (unlikely(is_unaligned(addr, 2))) { + mmu_put_word_unaligned(addr, v, true); + } else { + mmu_put_word(addr, v, true, sz_word); + } + } else { + if (unlikely(is_unaligned(addr, 4))) + mmu_put_long_unaligned(addr, v, true); + else + mmu_put_long(addr, v, true, sz_long); + } + locked_rmw_cycle = false; +} +uae_u32 uae_mmu_get_rmw (uaecptr addr, int size, int type) +{ + uae_u32 v; + locked_rmw_cycle = true; + if (size == sz_byte) { + v = mmu_get_user_byte(addr, regs.s != 0, true, true, sz_byte); + } else if (size == sz_word) { + if (unlikely(is_unaligned(addr, 2))) { + v = mmu_get_rmw_word_unaligned(addr); + } else { + v = mmu_get_user_word(addr, regs.s != 0, true, true, sz_word); + } + } else { + if (unlikely(is_unaligned(addr, 4))) + v = mmu_get_rmw_long_unaligned(addr); + else + v = mmu_get_user_long(addr, regs.s != 0, true, true, sz_long); + } + locked_rmw_cycle = false; + return v; +} + #ifndef __cplusplus jmp_buf __exbuf; int __exvalue; diff --git a/cpummu30.cpp b/cpummu30.cpp index 584cc255..a056e6a7 100644 --- a/cpummu30.cpp +++ b/cpummu30.cpp @@ -40,8 +40,19 @@ #define MMU030_ATC_DBG_MSG 0 #define MMU030_REG_DBG_MSG 0 +#define TT_FC_MASK 0x00000007 +#define TT_FC_BASE 0x00000070 +#define TT_RWM 0x00000100 +#define TT_RW 0x00000200 +#define TT_CI 0x00000400 +#define TT_ENABLE 0x00008000 + +#define TT_ADDR_MASK 0x00FF0000 +#define TT_ADDR_BASE 0xFF000000 + static int bBusErrorReadWrite; static int atcindextable[32]; +static int tt_enabled; int mmu030_idx; @@ -266,6 +277,7 @@ void mmu_op30_pmove (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra) if (!fd && !rw && !(preg==0x18)) { mmu030_flush_atc_all(); } + tt_enabled = (tt0_030 & TT_ENABLE) || (tt1_030 & TT_ENABLE); } void mmu_op30_ptest (uaecptr pc, uae_u32 opcode, uae_u16 next, uaecptr extra) @@ -488,17 +500,6 @@ void mmu030_flush_atc_all(void) { * */ - -#define TT_FC_MASK 0x00000007 -#define TT_FC_BASE 0x00000070 -#define TT_RWM 0x00000100 -#define TT_RW 0x00000200 -#define TT_CI 0x00000400 -#define TT_ENABLE 0x00008000 - -#define TT_ADDR_MASK 0x00FF0000 -#define TT_ADDR_BASE 0xFF000000 - /* TT comparision results */ #define TT_NO_MATCH 0x1 #define TT_OK_MATCH 0x2 @@ -514,10 +515,12 @@ TT_info mmu030_decode_tt(uae_u32 TT) { ret.addr_base = TT & TT_ADDR_BASE; ret.addr_mask = ~(((TT&TT_ADDR_MASK)<<8)|0x00FFFFFF); +#if 0 if ((TT&TT_ENABLE) && !(TT&TT_RWM)) { write_log(_T("MMU Warning: Transparent translation of read-modify-write cycle is not correctly handled!\n")); } - +#endif + #if MMU030_REG_DBG_MSG /* enable or disable debugging messages */ write_log(_T("\n")); write_log(_T("TRANSPARENT TRANSLATION: %08X\n"), TT); @@ -571,6 +574,27 @@ int mmu030_match_ttr(uaecptr addr, uae_u32 fc, bool write) return (tt0|tt1); } +int mmu030_match_ttr_access(uaecptr addr, uae_u32 fc, bool write) +{ + int tt0, tt1; + if (!tt_enabled) + return 0; + tt0 = mmu030_do_match_ttr(tt0_030, mmu030.transparent.tt0, addr, fc, write); + tt1 = mmu030_do_match_ttr(tt1_030, mmu030.transparent.tt1, addr, fc, write); + return (tt0|tt1) & TT_OK_MATCH; +} + +/* Read-Modify-Write */ +int mmu030_match_rmw_ttr_access(uaecptr addr, uae_u32 fc) +{ + int tt0, tt1; + + if (!tt_enabled) + return 0; + tt0 = mmu030_do_match_rmw_ttr(tt0_030, mmu030.transparent.tt0, addr, fc); + tt1 = mmu030_do_match_rmw_ttr(tt1_030, mmu030.transparent.tt1, addr, fc); + return (tt0|tt1) & TT_OK_MATCH; +} /* This function checks if an address matches a transparent * translation register */ @@ -604,6 +628,23 @@ int mmu030_do_match_ttr(uae_u32 tt, TT_info comp, uaecptr addr, uae_u32 fc, bool return TT_NO_MATCH; } +int mmu030_do_match_rmw_ttr(uae_u32 tt, TT_info comp, uaecptr addr, uae_u32 fc) +{ + if ((tt & TT_ENABLE) && (tt & TT_RWM)) { /* transparent translation enabled */ + + /* Compare actual function code with function code base using mask */ + if ((comp.fc_base&comp.fc_mask)==(fc&comp.fc_mask)) { + + /* Compare actual address with address base using mask */ + if ((comp.addr_base&comp.addr_mask)==(addr&comp.addr_mask)) { + + return TT_NO_READ; /* TODO: check this! */ + } + } + } + return TT_NO_MATCH; +} + /* Translation Control Register: @@ -1510,18 +1551,22 @@ uae_u32 mmu030_ptest_table_search(uaecptr logical_addr, uae_u32 fc, bool write, #define ATC030_PHYS_CI 0x04000000 #define ATC030_PHYS_BE 0x08000000 -void mmu030_page_fault(uaecptr addr, bool read, int size, uae_u32 fc) { +static void mmu030_page_fault(uaecptr addr, bool read, int flags, uae_u32 fc) { regs.mmu_fault_addr = addr; regs.mmu_ssw = (fc & 1) ? MMU030_SSW_DF | (MMU030_SSW_DF << 1) : (MMU030_SSW_FB | MMU030_SSW_RB); regs.mmu_ssw |= read ? MMU030_SSW_RW : 0; - regs.mmu_ssw |= size; + regs.mmu_ssw |= flags; regs.mmu_ssw |= fc; bBusErrorReadWrite = read; mm030_stageb_address = addr; #if 1 write_log(_T("MMU: page fault (logical addr=%08X SSW=%04x read=%d size=%d fc=%d pc=%08x)\n"), - addr, regs.mmu_ssw, read, (size & MMU030_SSW_SIZE_B) ? 1 : (size & MMU030_SSW_SIZE_W) ? 2 : 4, fc, regs.instruction_pc); + addr, regs.mmu_ssw, read, (flags & MMU030_SSW_SIZE_B) ? 1 : (flags & MMU030_SSW_SIZE_W) ? 2 : 4, fc, regs.instruction_pc); #endif + +// extern void activate_debugger(void); +// activate_debugger (); + THROW(2); } @@ -1537,7 +1582,7 @@ void mmu030_put_long_atc(uaecptr addr, uae_u32 val, int l, uae_u32 fc) { physical_addr += page_index; if (mmu030.atc[l].physical.bus_error || mmu030.atc[l].physical.write_protect) { - mmu030_page_fault(addr, 0, MMU030_SSW_SIZE_L, fc); + mmu030_page_fault(addr, false, MMU030_SSW_SIZE_L, fc); return; } @@ -1556,7 +1601,7 @@ void mmu030_put_word_atc(uaecptr addr, uae_u16 val, int l, uae_u32 fc) { physical_addr += page_index; if (mmu030.atc[l].physical.bus_error || mmu030.atc[l].physical.write_protect) { - mmu030_page_fault(addr, 0, MMU030_SSW_SIZE_W, fc); + mmu030_page_fault(addr, false, MMU030_SSW_SIZE_W, fc); return; } @@ -1575,7 +1620,7 @@ void mmu030_put_byte_atc(uaecptr addr, uae_u8 val, int l, uae_u32 fc) { physical_addr += page_index; if (mmu030.atc[l].physical.bus_error || mmu030.atc[l].physical.write_protect) { - mmu030_page_fault(addr, 0, MMU030_SSW_SIZE_B, fc); + mmu030_page_fault(addr, false, MMU030_SSW_SIZE_B, fc); return; } @@ -1594,7 +1639,7 @@ uae_u32 mmu030_get_long_atc(uaecptr addr, int l, uae_u32 fc) { physical_addr += page_index; if (mmu030.atc[l].physical.bus_error) { - mmu030_page_fault(addr, 1, MMU030_SSW_SIZE_L, fc); + mmu030_page_fault(addr, true, MMU030_SSW_SIZE_L, fc); return 0; } @@ -1613,7 +1658,7 @@ uae_u16 mmu030_get_word_atc(uaecptr addr, int l, uae_u32 fc) { physical_addr += page_index; if (mmu030.atc[l].physical.bus_error) { - mmu030_page_fault(addr, 1, MMU030_SSW_SIZE_W, fc); + mmu030_page_fault(addr, true, MMU030_SSW_SIZE_W, fc); return 0; } @@ -1632,13 +1677,60 @@ uae_u8 mmu030_get_byte_atc(uaecptr addr, int l, uae_u32 fc) { physical_addr += page_index; if (mmu030.atc[l].physical.bus_error) { - mmu030_page_fault(addr, 1, MMU030_SSW_SIZE_B, fc); + mmu030_page_fault(addr, true, MMU030_SSW_SIZE_B, fc); return 0; } return phys_get_byte(physical_addr); } +/* Generic versions of above */ +void mmu030_put_atc_generic(uaecptr addr, uae_u32 val, int l, uae_u32 fc, int size, int flags) { + uae_u32 page_index = addr & mmu030.translation.page.mask; + uae_u32 addr_mask = mmu030.translation.page.imask; + + uae_u32 physical_addr = mmu030.atc[l].physical.addr & addr_mask; +#if MMU030_ATC_DBG_MSG + write_log(_T("ATC match(%i): page addr = %08X, index = %08X (bput %02X)\n"), + l, physical_addr, page_index, val); +#endif + physical_addr += page_index; + + if (mmu030.atc[l].physical.write_protect || mmu030.atc[l].physical.bus_error) { + mmu030_page_fault(addr, false, flags, fc); + return; + } + if (size == sz_byte) + phys_put_byte(physical_addr, val); + else if (size == sz_word) + phys_put_word(physical_addr, val); + else + phys_put_long(physical_addr, val); + +} +uae_u32 mmu030_get_atc_generic(uaecptr addr, int l, uae_u32 fc, int size, int flags, bool checkwrite) { + uae_u32 page_index = addr & mmu030.translation.page.mask; + uae_u32 addr_mask = mmu030.translation.page.imask; + + uae_u32 physical_addr = mmu030.atc[l].physical.addr & addr_mask; +#if MMU030_ATC_DBG_MSG + write_log(_T("ATC match(%i): page addr = %08X, index = %08X (bget %02X)\n"), l, + physical_addr, page_index, phys_get_byte(physical_addr+page_index)); +#endif + physical_addr += page_index; + + if (mmu030.atc[l].physical.bus_error || (checkwrite && mmu030.atc[l].physical.write_protect)) { + mmu030_page_fault(addr, true, flags, fc); + return 0; + } + if (size == sz_byte) + return phys_get_byte(physical_addr); + else if (size == sz_word) + return phys_get_word(physical_addr); + return phys_get_long(physical_addr); +} + + /* This function checks if a certain logical address is in the ATC * by comparing the logical address and function code to the values * stored in the ATC entries. If a matching entry is found it sets @@ -1704,10 +1796,10 @@ void mmu030_atc_handle_history_bit(int entry_num) { * create a new ATC entry and then look up the physical address. */ -void mmu030_put_long(uaecptr addr, uae_u32 val, uae_u32 fc, int size) { +void mmu030_put_long(uaecptr addr, uae_u32 val, uae_u32 fc) { // addr,super,write - if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,true)&TT_OK_MATCH) || (fc==7)) { + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr,fc,true)) || (fc==7)) { phys_put_long(addr,val); return; } @@ -1722,10 +1814,10 @@ void mmu030_put_long(uaecptr addr, uae_u32 val, uae_u32 fc, int size) { } } -void mmu030_put_word(uaecptr addr, uae_u16 val, uae_u32 fc, int size) { +void mmu030_put_word(uaecptr addr, uae_u16 val, uae_u32 fc) { // addr,super,write - if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,true)&TT_OK_MATCH) || (fc==7)) { + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr,fc,true)) || (fc==7)) { phys_put_word(addr,val); return; } @@ -1740,10 +1832,10 @@ void mmu030_put_word(uaecptr addr, uae_u16 val, uae_u32 fc, int size) { } } -void mmu030_put_byte(uaecptr addr, uae_u8 val, uae_u32 fc, int size) { +void mmu030_put_byte(uaecptr addr, uae_u8 val, uae_u32 fc) { // addr,super,write - if ((!mmu030.enabled) || (mmu030_match_ttr(addr, fc, true)&TT_OK_MATCH) || (fc==7)) { + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr, fc, true)) || (fc==7)) { phys_put_byte(addr,val); return; } @@ -1758,10 +1850,10 @@ void mmu030_put_byte(uaecptr addr, uae_u8 val, uae_u32 fc, int size) { } } -uae_u32 mmu030_get_long(uaecptr addr, uae_u32 fc, int size) { +uae_u32 mmu030_get_long(uaecptr addr, uae_u32 fc) { // addr,super,write - if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,false)&TT_OK_MATCH) || (fc==7)) { + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr,fc,false)) || (fc==7)) { return phys_get_long(addr); } @@ -1775,10 +1867,10 @@ uae_u32 mmu030_get_long(uaecptr addr, uae_u32 fc, int size) { } } -uae_u16 mmu030_get_word(uaecptr addr, uae_u32 fc, int size) { +uae_u16 mmu030_get_word(uaecptr addr, uae_u32 fc) { // addr,super,write - if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,false)&TT_OK_MATCH) || (fc==7)) { + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr,fc,false)) || (fc==7)) { return phys_get_word(addr); } @@ -1792,10 +1884,10 @@ uae_u16 mmu030_get_word(uaecptr addr, uae_u32 fc, int size) { } } -uae_u8 mmu030_get_byte(uaecptr addr, uae_u32 fc, int size) { +uae_u8 mmu030_get_byte(uaecptr addr, uae_u32 fc) { // addr,super,write - if ((!mmu030.enabled) || (mmu030_match_ttr(addr,fc,false)&TT_OK_MATCH) || (fc==7)) { + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr,fc,false)) || (fc==7)) { return phys_get_byte(addr); } @@ -1810,14 +1902,128 @@ uae_u8 mmu030_get_byte(uaecptr addr, uae_u32 fc, int size) { } -uae_u16 REGPARAM2 mmu030_get_word_unaligned(uaecptr addr, uae_u32 fc) +/* Not commonly used access function */ +void mmu030_put_generic(uaecptr addr, uae_u32 val, uae_u32 fc, int size, int accesssize, int flags) { + + // addr,super,write + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr, fc, true)) || (fc==7)) { + if (size == sz_byte) + phys_put_byte(addr, val); + else if (size == sz_word) + phys_put_word(addr, val); + else + phys_put_long(addr, val); + return; + } + + int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); + if (atc_line_num>=0) { + mmu030_put_atc_generic(addr, val, atc_line_num, fc, size, flags); + } else { + mmu030_table_search(addr, fc, true, 0); + atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); + if (accesssize == sz_byte) + flags |= MMU030_SSW_SIZE_B; + else if (accesssize == sz_word) + flags |= MMU030_SSW_SIZE_W; + mmu030_put_atc_generic(addr, val, atc_line_num, fc, size, flags); + } +} +static uae_u32 mmu030_get_generic_rmw(uaecptr addr, uae_u32 fc, int size, int accesssize, int flags) { + + // addr,super,write + if ((!mmu030.enabled) || (mmu030_match_rmw_ttr_access(addr,fc)) || (fc==7)) { + if (size == sz_byte) + return phys_get_byte(addr); + else if (size == sz_word) + return phys_get_word(addr); + return phys_get_long(addr); + } + + int atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); + if (atc_line_num>=0) { + return mmu030_get_atc_generic(addr, atc_line_num, fc, size, flags, true); + } else { + mmu030_table_search(addr, fc, true, 0); + atc_line_num = mmu030_logical_is_in_atc(addr, fc, true); + if (accesssize == sz_byte) + flags |= MMU030_SSW_SIZE_B; + else if (accesssize == sz_word) + flags |= MMU030_SSW_SIZE_W; + return mmu030_get_atc_generic(addr, atc_line_num, fc, size, flags, true); + } +} +uae_u32 mmu030_get_generic(uaecptr addr, uae_u32 fc, int size, int accesssize, int flags) { + if (flags & MMU030_SSW_RM) { + return mmu030_get_generic_rmw(addr, fc, size, accesssize, flags); + } + // addr,super,write + if ((!mmu030.enabled) || (mmu030_match_ttr_access(addr,fc,false)) || (fc==7)) { + if (size == sz_byte) + return phys_get_byte(addr); + else if (size == sz_word) + return phys_get_word(addr); + return phys_get_long(addr); + } + + int atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); + if (atc_line_num>=0) { + return mmu030_get_atc_generic(addr, atc_line_num, fc, size, flags, false); + } else { + mmu030_table_search(addr, fc, false, 0); + atc_line_num = mmu030_logical_is_in_atc(addr, fc, false); + if (accesssize == sz_byte) + flags |= MMU030_SSW_SIZE_B; + else if (accesssize == sz_word) + flags |= MMU030_SSW_SIZE_W; + return mmu030_get_atc_generic(addr, atc_line_num, fc, size, flags, false); + } +} + + +/* RMW is rarely used */ +uae_u32 uae_mmu030_get_rmw(uaecptr addr, int size) +{ + uae_u32 fc = (regs.s ? 4 : 0) | 1; + if (size == sz_byte) { + return mmu030_get_generic(addr, fc, size, size, MMU030_SSW_RM); + } else if (size == sz_word) { + if (unlikely(is_unaligned(addr, 2))) + return mmu030_get_word_unaligned(addr, fc, MMU030_SSW_RM); + else + return mmu030_get_generic(addr, fc, size, size, MMU030_SSW_RM); + } else { + if (unlikely(is_unaligned(addr, 4))) + return mmu030_get_long_unaligned(addr, fc, MMU030_SSW_RM); + else + return mmu030_get_generic(addr, fc, size, size, MMU030_SSW_RM); + } +} +void uae_mmu030_put_rmw(uaecptr addr, uae_u32 val, int size) +{ + uae_u32 fc = (regs.s ? 4 : 0) | 1; + if (size == sz_byte) { + mmu030_put_generic(addr, val, fc, size, size, MMU030_SSW_RM); + } else if (size == sz_word) { + if (unlikely(is_unaligned(addr, 2))) + mmu030_put_word_unaligned(addr, val, fc, MMU030_SSW_RM); + else + mmu030_put_generic(addr, val, fc, size, size, MMU030_SSW_RM); + } else { + if (unlikely(is_unaligned(addr, 4))) + mmu030_put_long_unaligned(addr, val, fc, MMU030_SSW_RM); + else + mmu030_put_generic(addr, val, fc, size, size, MMU030_SSW_RM); + } +} +uae_u16 REGPARAM2 mmu030_get_word_unaligned(uaecptr addr, uae_u32 fc, int flags) { uae_u16 res; - res = (uae_u16)mmu030_get_byte(addr, fc, sz_word) << 8; + res = (uae_u16)mmu030_get_generic(addr, fc, sz_byte, sz_word, flags) << 8; SAVE_EXCEPTION; TRY(prb) { - res |= mmu030_get_byte(addr + 1, fc, sz_word); + res |= mmu030_get_generic(addr + 1, fc, sz_byte, sz_word, flags); RESTORE_EXCEPTION; } CATCH(prb) { @@ -1827,15 +2033,15 @@ uae_u16 REGPARAM2 mmu030_get_word_unaligned(uaecptr addr, uae_u32 fc) return res; } -uae_u32 REGPARAM2 mmu030_get_long_unaligned(uaecptr addr, uae_u32 fc) +uae_u32 REGPARAM2 mmu030_get_long_unaligned(uaecptr addr, uae_u32 fc, int flags) { uae_u32 res; if (likely(!(addr & 1))) { - res = (uae_u32)mmu030_get_word(addr, fc, sz_long) << 16; + res = (uae_u32)mmu030_get_generic(addr, fc, sz_word, sz_long, flags) << 16; SAVE_EXCEPTION; TRY(prb) { - res |= mmu030_get_word(addr + 2, fc, sz_long); + res |= mmu030_get_generic(addr + 2, fc, sz_word, sz_long, flags); RESTORE_EXCEPTION; } CATCH(prb) { @@ -1843,12 +2049,12 @@ uae_u32 REGPARAM2 mmu030_get_long_unaligned(uaecptr addr, uae_u32 fc) THROW_AGAIN(prb); } ENDTRY } else { - res = (uae_u32)mmu030_get_byte(addr, fc, sz_long) << 8; + res = (uae_u32)mmu030_get_generic(addr, fc, sz_byte, sz_long, flags) << 8; SAVE_EXCEPTION; TRY(prb) { - res = (res | mmu030_get_byte(addr + 1, fc, sz_long)) << 8; - res = (res | mmu030_get_byte(addr + 2, fc, sz_long)) << 8; - res |= mmu030_get_byte(addr + 3, fc, sz_long); + res = (res | mmu030_get_generic(addr + 1, fc, sz_byte, sz_long, flags)) << 8; + res = (res | mmu030_get_generic(addr + 2, fc, sz_byte, sz_long, flags)) << 8; + res |= mmu030_get_generic(addr + 3, fc, sz_byte, sz_long, flags); RESTORE_EXCEPTION; } CATCH(prb) { @@ -1860,18 +2066,18 @@ uae_u32 REGPARAM2 mmu030_get_long_unaligned(uaecptr addr, uae_u32 fc) } -void REGPARAM2 mmu030_put_long_unaligned(uaecptr addr, uae_u32 val, uae_u32 fc) +void REGPARAM2 mmu030_put_long_unaligned(uaecptr addr, uae_u32 val, uae_u32 fc, int flags) { SAVE_EXCEPTION; TRY(prb) { if (likely(!(addr & 1))) { - mmu030_put_word(addr, val >> 16, fc, sz_long); - mmu030_put_word(addr + 2, val, fc, sz_long); + mmu030_put_generic(addr, val >> 16, fc, sz_word, sz_long, flags); + mmu030_put_generic(addr + 2, val, fc, sz_word, sz_long, flags); } else { - mmu030_put_byte(addr, val >> 24, fc, sz_long); - mmu030_put_byte(addr + 1, val >> 16, fc, sz_long); - mmu030_put_byte(addr + 2, val >> 8, fc, sz_long); - mmu030_put_byte(addr + 3, val, fc, sz_long); + mmu030_put_generic(addr, val >> 24, fc, sz_byte, sz_long, flags); + mmu030_put_generic(addr + 1, val >> 16, fc, sz_byte, sz_long, flags); + mmu030_put_generic(addr + 2, val >> 8, fc, sz_byte, sz_long, flags); + mmu030_put_generic(addr + 3, val, fc, sz_byte, sz_long, flags); } RESTORE_EXCEPTION; } @@ -1882,12 +2088,12 @@ void REGPARAM2 mmu030_put_long_unaligned(uaecptr addr, uae_u32 val, uae_u32 fc) } ENDTRY } -void REGPARAM2 mmu030_put_word_unaligned(uaecptr addr, uae_u16 val, uae_u32 fc) +void REGPARAM2 mmu030_put_word_unaligned(uaecptr addr, uae_u16 val, uae_u32 fc, int flags) { SAVE_EXCEPTION; TRY(prb) { - mmu030_put_byte(addr, val >> 8, fc, sz_word); - mmu030_put_byte(addr + 1, val, fc, sz_word); + mmu030_put_generic(addr, val >> 8, fc, sz_byte, sz_word, flags); + mmu030_put_generic(addr + 1, val, fc, sz_byte, sz_word, flags); RESTORE_EXCEPTION; } CATCH(prb) { @@ -1906,7 +2112,7 @@ static uaecptr mmu030_get_addr_atc(uaecptr addr, int l, uae_u32 fc, bool write) uae_u32 physical_addr = mmu030.atc[l].physical.addr&addr_mask; physical_addr += page_index; - if (mmu030.atc[l].physical.bus_error) { + if (mmu030.atc[l].physical.bus_error || (write && mmu030.atc[l].physical.write_protect)) { mmu030_page_fault(addr, write == 0, MMU030_SSW_SIZE_B, fc); return 0; } @@ -1927,7 +2133,6 @@ uaecptr mmu030_translate(uaecptr addr, bool super, bool data, bool write) mmu030_table_search(addr, fc, false, 0); return mmu030_get_addr_atc(addr, mmu030_logical_is_in_atc(addr,fc,write), fc, write); } - } /* MMU Reset */ @@ -1965,6 +2170,9 @@ void m68k_do_rte_mmu030 (uaecptr a7) mmu030_disp_store[0] = get_long_mmu030 (a7 + 0x1c); mmu030_disp_store[1] = get_long_mmu030 (a7 + 0x1c + 4); + // Rerun "mmu030_opcode" using restored state. + mmu030_retry = true; + if (frame == 0xb) { uae_u16 idxsize = get_word_mmu030 (a7 + 0x36); for (int i = 0; i < idxsize + 1; i++) { @@ -1975,7 +2183,11 @@ void m68k_do_rte_mmu030 (uaecptr a7) // did we have data fault but DF bit cleared? if (ssw & (MMU030_SSW_DF << 1) && !(ssw & MMU030_SSW_DF)) { // DF not set: mark access as done - if (mmu030_state[1] & MMU030_STATEFLAG1_MOVEM1) { + if (ssw & MMU030_SSW_RM) { + // Read-Modify-Write: whole instruction is considered done + write_log (_T("Read-Modify-Write and DF bit cleared! PC=%08x\n"), regs.instruction_pc); + mmu030_retry = false; + } else if (mmu030_state[1] & MMU030_STATEFLAG1_MOVEM1) { // if movem, skip next move mmu030_state[1] |= MMU030_STATEFLAG1_MOVEM2; } else { @@ -2003,8 +2215,6 @@ void m68k_do_rte_mmu030 (uaecptr a7) } else { m68k_areg (regs, 7) += 32; } - // Rerun "mmu030_opcode" using restored state. - mmu030_retry = true; } void flush_mmu030 (uaecptr addr, int n) diff --git a/custom.cpp b/custom.cpp index a1256a44..0aec4b1d 100644 --- a/custom.cpp +++ b/custom.cpp @@ -2977,7 +2977,7 @@ void compute_framesync (void) int start = hbstrt; int stop = hbstop; - gfxvidinfo.drawbuffer.inwidth = (((start > stop ? (maxhpos - (maxhpos - start + stop)) : (maxhpos - (stop - start) + 2)) * 2) << res2); + gfxvidinfo.drawbuffer.inwidth = (((start > stop ? (maxhpos - (maxhpos - start + stop)) : (maxhpos - (stop - start) + 2)) * 2) << res2); gfxvidinfo.drawbuffer.inxoffset = ((stop + 1) & ~1) * 2; gfxvidinfo.drawbuffer.extrawidth = 0; diff --git a/debug.cpp b/debug.cpp index cd3416f3..42c74411 100644 --- a/debug.cpp +++ b/debug.cpp @@ -188,9 +188,9 @@ uae_u32 get_byte_debug (uaecptr addr) regs.s = (debug_mmu_mode & 4) != 0; TRY(p) { if (currprefs.mmu_model == 68030) { - v = mmu030_get_byte (addr, debug_mmu_mode, sz_byte); + v = mmu030_get_byte (addr, debug_mmu_mode); } else { - v = mmu_get_byte (addr, (debug_mmu_mode & 1) ? true : false, sz_byte); + v = mmu_get_user_byte (addr, regs.s != 0, (debug_mmu_mode & 1) ? true : false, false, sz_byte); } } CATCH(p) { } @@ -208,9 +208,9 @@ uae_u32 get_word_debug (uaecptr addr) regs.s = (debug_mmu_mode & 4) != 0; TRY(p) { if (currprefs.mmu_model == 68030) { - v = mmu030_get_word (addr, debug_mmu_mode, sz_word); + v = mmu030_get_word (addr, debug_mmu_mode); } else { - v = mmu_get_word (addr, (debug_mmu_mode & 1) ? true : false, sz_word); + v = mmu_get_user_word (addr, regs.s != 0, (debug_mmu_mode & 1) ? true : false, false, sz_word); } } CATCH(p) { } @@ -228,9 +228,9 @@ uae_u32 get_long_debug (uaecptr addr) regs.s = (debug_mmu_mode & 4) != 0; TRY(p) { if (currprefs.mmu_model == 68030) { - v = mmu030_get_long (addr, debug_mmu_mode, sz_long); + v = mmu030_get_long (addr, debug_mmu_mode); } else { - v = mmu_get_long (addr, (debug_mmu_mode & 1) ? true : false, sz_long); + v = mmu_get_user_long (addr, regs.s != 0, (debug_mmu_mode & 1) ? true : false, false, sz_long); } } CATCH(p) { } diff --git a/expansion.cpp b/expansion.cpp index dbeb9cf5..df2b5472 100644 --- a/expansion.cpp +++ b/expansion.cpp @@ -953,7 +953,7 @@ static void expamem_map_fastcard (void) static void expamem_init_fastcard (void) { uae_u16 mid = (currprefs.cs_a2091 || currprefs.uae_hide) ? commodore : uae_id; - uae_u8 pid = (currprefs.cs_a2091 || currprefs.uae_hide) ? commodore_a2091_ram : 1; + uae_u8 pid = (currprefs.cs_a2091 || currprefs.uae_hide) ? commodore_a2091_ram : (currprefs.maprom ? 1 : 81); expamem_init_clear (); if (allocated_fastmem == 0x100000) diff --git a/filesys.cpp b/filesys.cpp index 8f18ba0c..73f099da 100644 --- a/filesys.cpp +++ b/filesys.cpp @@ -295,9 +295,10 @@ int get_filesys_unitconfig (struct uae_prefs *p, int index, struct mountedinfo * memset (mi, 0, sizeof (struct mountedinfo)); memset (&uitmp, 0, sizeof uitmp); + _tcscpy (mi->rootdir, uci->ci.rootdir); if (!ui) { ui = &uitmp; - if (!uci->ishdf) { + if (uci->ci.type == UAEDEV_DIR) { mi->ismounted = 1; if (uci->ci.rootdir && _tcslen (uci->ci.rootdir) == 0) return FILESYS_VIRTUAL; @@ -309,7 +310,7 @@ int get_filesys_unitconfig (struct uae_prefs *p, int index, struct mountedinfo * return -1; mi->ismedia = true; return FILESYS_VIRTUAL; - } else { + } else if (uci->ci.type == UAEDEV_HDF) { ui->hf.ci.readonly = true; ui->hf.ci.blocksize = uci->ci.blocksize; if (!hdf_open (&ui->hf, uci->ci.rootdir)) { @@ -326,23 +327,41 @@ int get_filesys_unitconfig (struct uae_prefs *p, int index, struct mountedinfo * if (ui->hf.drive_empty) mi->ismedia = 0; hdf_close (&ui->hf); + } else if (uci->ci.type == UAEDEV_CD) { + struct device_info di; + ui->hf.ci.readonly = true; + ui->hf.ci.blocksize = uci->ci.blocksize; + mi->size = -1; + mi->ismounted = true; + if (blkdev_get_info (p, ui->hf.ci.cd_emu_unit, &di)) { + mi->ismedia = di.media_inserted; + _tcscpy (mi->rootdir, di.label); + } +#if 0 + if (ui->hf.ci.cd_emu_unit == 0) + _tcscpy (mi->rootdir, _T("CD")); + else + _stprintf (mi->rootdir, _T("CD %d"), ui->hf.ci.cd_emu_unit); +#endif } } else { if (!ui->controller || (ui->controller && p->cs_ide)) { mi->ismounted = 1; - if (uci->ishdf) + if (uci->ci.type == UAEDEV_HDF) mi->ismedia = ui->hf.drive_empty ? false : true; else mi->ismedia = true; } } + if (mi->size < 0) + return -1; mi->size = ui->hf.virtsize; if (uci->ci.highcyl) { uci->ci.cyls = mi->nrcyls = uci->ci.highcyl; } else { uci->ci.cyls = mi->nrcyls = (int)(uci->ci.sectors * uci->ci.surfaces ? (ui->hf.virtsize / uci->ci.blocksize) / (uci->ci.sectors * uci->ci.surfaces) : 0); } - if (!uci->ishdf) + if (uci->ci.type == UAEDEV_DIR) return FILESYS_VIRTUAL; if (uci->ci.reserved == 0 && uci->ci.sectors == 0 && uci->ci.surfaces == 0) { if (ui->hf.flags & 1) @@ -499,6 +518,7 @@ void uci_set_defaults (struct uaedev_config_info *uci, bool rdb) uci->stacksize = 4000; uci->priority = -129; uci->sectorsperblock = 1; + uci->cd_emu_unit = -1; } static int set_filesys_unit_1 (int nr, struct uaedev_config_info *ci) @@ -757,7 +777,7 @@ static void initialize_mountinfo (void) #endif } else if (currprefs.cs_cdtvscsi) { #ifdef CDTV - cdtv_add_scsi_unit (uci->controller - HD_CONTROLLER_SCSI0, uci); + cdtv_add_scsi_hd_unit (uci->controller - HD_CONTROLLER_SCSI0, uci); allocuci (&currprefs, nr, -1); #endif } @@ -6051,6 +6071,14 @@ static uae_u32 REGPARAM2 filesys_dev_bootfilesys (TrapContext *context) } fsnode = get_long (fsnode); } + if (type == FILESYS_HARDFILE) { + uae_u32 pf = get_long (parmpacket + PP_FSHDSTART + 8); // fse_PatchFlags + for (int i = 0; i < 32; i++) { + if (pf & (1 << i)) + put_long (devicenode + 4 + i * 4, get_long (parmpacket + PP_FSHDSTART + 8 + 4 + i * 4)); + } + put_long (devicenode + 4 + 7 * 4, 0); // seglist + } return 0; } @@ -6563,8 +6591,10 @@ static void addfakefilesys (uaecptr parmpacket, uae_u32 dostype, int ver, int re flags = 0x180; for (i = 0; i < 140; i++) put_byte (parmpacket + PP_FSHDSTART + i, 0); - put_long (parmpacket + 80, dostype); - put_long (parmpacket + PP_FSHDSTART, dostype); + if (dostype) { + put_long (parmpacket + 80, dostype); + put_long (parmpacket + PP_FSHDSTART, dostype); + } if (ver >= 0 && rev >= 0) put_long (parmpacket + PP_FSHDSTART + 4, (ver << 16) | rev); @@ -6603,17 +6633,18 @@ static int dofakefilesys (UnitInfo *uip, uaecptr parmpacket, struct uaedev_confi struct zfile *zf; int ver = -1, rev = -1; uae_u32 dostype; - uaecptr seg; // we already have custom filesystem loaded for earlier hardfile? - seg = getfakefilesysseg (uip); - if (seg) { - // yes, re-use it. - put_long (parmpacket + PP_FSSIZE, 0); - put_long (parmpacket + PP_FSPTR, seg); - put_long (parmpacket + PP_ADDTOFSRES, 0); - write_log (_T("HDF: faked RDB filesystem '%s' reused\n"), uip->filesysdir); - return FILESYS_HARDFILE; + if (!ci->forceload) { + uaecptr seg = getfakefilesysseg (uip); + if (seg) { + // yes, re-use it. + put_long (parmpacket + PP_FSSIZE, 0); + put_long (parmpacket + PP_FSPTR, seg); + put_long (parmpacket + PP_ADDTOFSRES, 0); + write_log (_T("HDF: faked RDB filesystem '%s' reused\n"), uip->filesysdir); + return FILESYS_HARDFILE; + } } if (!ci->dostype) { @@ -6623,8 +6654,10 @@ static int dofakefilesys (UnitInfo *uip, uaecptr parmpacket, struct uaedev_confi } else { dostype = ci->dostype; } - if (dostype == 0) + if (dostype == 0) { + addfakefilesys (parmpacket, dostype, ver, rev, ci); return FILESYS_HARDFILE; + } tmp[0] = 0; if (uip->filesysdir && _tcslen (uip->filesysdir) > 0) { _tcscpy (tmp, uip->filesysdir); @@ -6637,6 +6670,7 @@ static int dofakefilesys (UnitInfo *uip, uaecptr parmpacket, struct uaedev_confi } if (tmp[0] == 0) { write_log (_T("RDB: no filesystem for dostype 0x%08X (%s)\n"), dostype, dostypes (dostype)); + addfakefilesys (parmpacket, dostype, ver, rev, ci); if ((dostype & 0xffffff00) == 0x444f5300) return FILESYS_HARDFILE; write_log (_T("RDB: mounted without filesys\n")); @@ -6645,6 +6679,7 @@ static int dofakefilesys (UnitInfo *uip, uaecptr parmpacket, struct uaedev_confi write_log (_T("RDB: fakefilesys, trying to load '%s', dostype 0x%08X (%s)\n"), tmp, dostype, dostypes (dostype)); zf = zfile_fopen (tmp, _T("rb"), ZFD_NORMAL); if (!zf) { + addfakefilesys (parmpacket, dostype, ver, rev, ci); write_log (_T("RDB: filesys not found\n")); if ((dostype & 0xffffff00) == 0x444f5300) return FILESYS_HARDFILE; @@ -6764,6 +6799,8 @@ static uae_u32 REGPARAM2 filesys_dev_storeinfo (TrapContext *context) uaecptr parmpacket = m68k_areg (regs, 0); struct uaedev_config_info *ci = &uip[unit_no].hf.ci; + put_long (parmpacket + PP_ADDTOFSRES, 0); + put_long (parmpacket + PP_FSSIZE, 0); if (iscd) { TCHAR *cdname = NULL; uaecptr cdname_amiga; diff --git a/gayle.cpp b/gayle.cpp index 17be2859..a9671e37 100644 --- a/gayle.cpp +++ b/gayle.cpp @@ -26,6 +26,8 @@ #include "gui.h" #include "a2091.h" #include "ncr_scsi.h" +#include "blkdev.h" +#include "scsi.h" #define PCMCIA_SRAM 1 #define PCMCIA_IDE 2 @@ -75,7 +77,7 @@ read 1 byte to stop reset */ /* IDE drive registers */ #define IDE_DATA 0x00 #define IDE_ERROR 0x01 /* see err-bits */ -#define IDE_NSECTOR 0x02 /* nr of sectors to read/write */ +#define IDE_NSECTOR 0x02 /* sector count, nr of sectors to read/write */ #define IDE_SECTOR 0x03 /* starting sector */ #define IDE_LCYL 0x04 /* starting cylinder */ #define IDE_HCYL 0x05 /* high byte of starting cyl */ @@ -84,12 +86,13 @@ read 1 byte to stop reset */ #define IDE_DEVCON 0x0406 #define IDE_DRVADDR 0x0407 /* STATUS bits */ -#define IDE_STATUS_ERR 0x01 -#define IDE_STATUS_IDX 0x02 -#define IDE_STATUS_DRQ 0x08 -#define IDE_STATUS_DSC 0x10 -#define IDE_STATUS_DRDY 0x40 -#define IDE_STATUS_BSY 0x80 +#define IDE_STATUS_ERR 0x01 // 0 +#define IDE_STATUS_IDX 0x02 // 1 +#define IDE_STATUS_DRQ 0x08 // 3 +#define IDE_STATUS_DSC 0x10 // 4 +#define IDE_STATUS_DRDY 0x40 // 6 +#define IDE_STATUS_BSY 0x80 // 7 +#define ATAPI_STATUS_CHK IDE_STATUS_ERR /* ERROR bits */ #define IDE_ERR_UNC 0x40 #define IDE_ERR_MC 0x20 @@ -97,6 +100,11 @@ read 1 byte to stop reset */ #define IDE_ERR_MCR 0x08 #define IDE_ERR_ABRT 0x04 #define IDE_ERR_NM 0x02 +#define ATAPI_ERR_EOM 0x02 +#define ATAPI_ERR_ILI 0x01 +/* ATAPI interrupt reason (Sector Count) */ +#define ATAPI_IO 0x02 +#define ATAPI_CD 0x01 /* * These are at different offsets from the base @@ -165,13 +173,16 @@ struct ide_registers { uae_u8 ide_select, ide_nsector, ide_sector, ide_lcyl, ide_hcyl, ide_devcon, ide_error, ide_feat; uae_u8 ide_nsector2, ide_sector2, ide_lcyl2, ide_hcyl2, ide_feat2; - uae_u8 ide_drv; + uae_u8 ide_status; }; struct ide_hdf { struct hd_hardfiledata hdhfd; - struct ide_registers *regs; + struct ide_registers regs; + struct ide_registers *regs0; + struct ide_registers *regs1; + struct ide_hdf *pair; uae_u8 secbuf[SECBUF_SIZE]; int data_offset; @@ -179,14 +190,19 @@ struct ide_hdf int data_multi; int lba48; uae_u8 multiple_mode; - uae_u8 status; int irq_delay; int irq; int num; int type; int blocksize; int maxtransferstate; + int ide_drv; + bool atapi; + int cd_unit_num; + int packet_cnt; + int packet_data_size; + struct scsi_data scsi; }; #define TOTAL_IDE 3 @@ -194,7 +210,7 @@ struct ide_hdf #define PCMCIA_IDE_ID 2 static struct ide_hdf *idedrive[TOTAL_IDE * 2]; -static struct ide_registers ideregs[TOTAL_IDE]; +static struct ide_registers ideregs[TOTAL_IDE * 2]; struct hd_hardfiledata *pcmcia_sram; static int pcmcia_card; @@ -251,22 +267,30 @@ static uae_u8 checkpcmciaideirq (void) return 0; } +static bool isdrive (struct ide_hdf *drv) +{ + return drv->hdhfd.size != 0 || drv->atapi; +} + static uae_u8 checkgayleideirq (void) { int i; + bool irq = false; + if (!idedrive) return 0; for (i = 0; i < 2; i++) { - if (ideregs[i].ide_devcon & 2) - continue; - if (idedrive[i]->irq || idedrive[i + 2]->irq) { - /* IDE killer feature. Do not eat interrupt to make booting faster. */ - if (idedrive[i]->irq && idedrive[i]->hdhfd.size == 0) - idedrive[i]->irq = 0; - return GAYLE_IRQ_IDE; + if (idedrive[i]->ide_drv == i) { + if (!(idedrive[i]->regs.ide_devcon & 2) && (idedrive[i]->irq || idedrive[i + 2]->irq)) + irq = true; } + /* IDE killer feature. Do not eat interrupt to make booting faster. */ + if (idedrive[i]->irq && !isdrive (idedrive[i])) + idedrive[i]->irq = 0; + if (idedrive[i + 2]->irq && !isdrive (idedrive[i + 2])) + idedrive[i + 2]->irq = 0; } - return 0; + return irq ? GAYLE_IRQ_IDE : 0; } void rethink_gayle (void) @@ -405,26 +429,25 @@ static uae_u8 read_gayle_cs (void) static void ide_interrupt (void) { - if (ide->regs->ide_devcon & 2) - return; - //ide->status |= IDE_STATUS_BSY; ide->irq_delay = 2; } static void ide_interrupt_do (struct ide_hdf *ide) { - ide->status &= ~IDE_STATUS_BSY; + ide->regs.ide_status &= ~IDE_STATUS_BSY; ide->irq_delay = 0; + if (ide->regs.ide_devcon & 2) + return; ide->irq = 1; rethink_gayle (); } static void ide_fail_err (uae_u8 err) { - ide->regs->ide_error |= err; - if (ide->regs->ide_drv == 1 && idedrive[ide2 + 1]->hdhfd.size == 0) - idedrive[ide2]->status |= IDE_STATUS_ERR; - ide->status |= IDE_STATUS_ERR; + ide->regs.ide_error |= err; + if (ide->ide_drv == 1 && !isdrive (ide + 1)) + idedrive[ide2]->regs.ide_status |= IDE_STATUS_ERR; + ide->regs.ide_status |= IDE_STATUS_ERR; ide_interrupt (); } static void ide_fail (void) @@ -436,7 +459,7 @@ static void ide_data_ready (void) { memset (ide->secbuf, 0, ide->blocksize); ide->data_offset = 0; - ide->status |= IDE_STATUS_DRQ; + ide->regs.ide_status |= IDE_STATUS_DRQ; ide->data_size = ide->blocksize; ide->data_multi = 1; ide_interrupt (); @@ -445,8 +468,8 @@ static void ide_data_ready (void) static void ide_recalibrate (void) { write_log (_T("IDE%d recalibrate\n"), ide->num); - ide->regs->ide_sector = 0; - ide->regs->ide_lcyl = ide->regs->ide_hcyl = 0; + ide->regs.ide_sector = 0; + ide->regs.ide_lcyl = ide->regs.ide_hcyl = 0; ide_interrupt (); } static void ide_identify_drive (void) @@ -457,7 +480,7 @@ static void ide_identify_drive (void) TCHAR tmp[100]; bool atapi = ide->atapi; - if (ide->hdhfd.size == 0) { + if (!isdrive (ide)) { ide_fail (); return; } @@ -477,8 +500,11 @@ static void ide_identify_drive (void) pw (20, 3); pw (21, ide->blocksize); pw (22, 4); - ps (23, _T("0.4"), 8); /* firmware revision */ - _stprintf (tmp, _T("UAE-IDE %s"), ide->hdhfd.hfd.product_id); + ps (23, _T("0.5"), 8); /* firmware revision */ + if (ide->atapi) + _tcscpy (tmp, _T("UAE-ATAPI")); + else + _stprintf (tmp, _T("UAE-IDE %s"), ide->hdhfd.hfd.product_id); ps (27, tmp, 40); /* model */ pw (47, MAX_IDE_MULTIPLE_SECTORS >> (ide->blocksize / 512 - 1)); /* max sectors in multiple mode */ pw (48, 1); @@ -492,9 +518,9 @@ static void ide_identify_drive (void) totalsecs = ide->hdhfd.cyls * ide->hdhfd.heads * ide->hdhfd.secspertrack; pw (57, (uae_u16)totalsecs); pw (58, (uae_u16)(totalsecs >> 16)); - v = idedrive[ide->regs->ide_drv]->multiple_mode; + v = idedrive[ide->ide_drv]->multiple_mode; pw (59, (v > 0 ? 0x100 : 0) | v); - totalsecs = ide->hdhfd.size / ide->blocksize; + totalsecs = ide->blocksize ? ide->hdhfd.size / ide->blocksize : 0; if (totalsecs > 0x0fffffff) totalsecs = 0x0fffffff; pw (60, (uae_u16)totalsecs); @@ -527,27 +553,31 @@ static void ide_identify_drive (void) static void ide_execute_drive_diagnostics (bool irq) { - ide->regs->ide_error = 1; + ide->regs.ide_error = 0x01; // device ok if (ide->atapi) { - ide->regs->ide_sector = ide->regs->ide_nsector = 1; - ide->regs->ide_lcyl = 0x14; - ide->regs->ide_hcyl = 0xeb; + ide->regs.ide_nsector = 1; + ide->regs.ide_sector = 1; + ide->regs.ide_lcyl = 0x14; + ide->regs.ide_hcyl = 0xeb; + ide->regs.ide_status = IDE_STATUS_BSY; } else { - ide->regs->ide_sector = ide->regs->ide_nsector = 1; - ide->regs->ide_select = 0; - ide->regs->ide_lcyl = ide->regs->ide_hcyl = 0; + ide->regs.ide_nsector = 1; + ide->regs.ide_sector = 1; + ide->regs.ide_lcyl = 0; + ide->regs.ide_hcyl = 0; + ide->regs.ide_status = IDE_STATUS_BSY | IDE_STATUS_DRDY; } if (irq) ide_interrupt (); else - ide->status &= ~IDE_STATUS_BSY; + ide->regs.ide_status &= ~IDE_STATUS_BSY; } static void ide_initialize_drive_parameters (void) { if (ide->hdhfd.size) { - ide->hdhfd.secspertrack = ide->regs->ide_nsector == 0 ? 256 : ide->regs->ide_nsector; - ide->hdhfd.heads = (ide->regs->ide_select & 15) + 1; + ide->hdhfd.secspertrack = ide->regs.ide_nsector == 0 ? 256 : ide->regs.ide_nsector; + ide->hdhfd.heads = (ide->regs.ide_select & 15) + 1; ide->hdhfd.cyls = (ide->hdhfd.size / ide->blocksize) / (ide->hdhfd.secspertrack * ide->hdhfd.heads); if (ide->hdhfd.heads * ide->hdhfd.cyls * ide->hdhfd.secspertrack > 16515072 || ide->lba48) { ide->hdhfd.cyls = ide->hdhfd.cyls_def; @@ -555,8 +585,8 @@ static void ide_initialize_drive_parameters (void) ide->hdhfd.secspertrack = ide->hdhfd.secspertrack_def; } } else { - ide->regs->ide_error |= IDE_ERR_ABRT; - ide->status |= IDE_STATUS_ERR; + ide->regs.ide_error |= IDE_ERR_ABRT; + ide->regs.ide_status |= IDE_STATUS_ERR; } write_log (_T("IDE%d initialize drive parameters, CYL=%d,SPT=%d,HEAD=%d\n"), ide->num, ide->hdhfd.cyls, ide->hdhfd.secspertrack, ide->hdhfd.heads); @@ -564,33 +594,33 @@ static void ide_initialize_drive_parameters (void) } static void ide_set_multiple_mode (void) { - write_log (_T("IDE%d drive multiple mode = %d\n"), ide->num, ide->regs->ide_nsector); - ide->multiple_mode = ide->regs->ide_nsector; + write_log (_T("IDE%d drive multiple mode = %d\n"), ide->num, ide->regs.ide_nsector); + ide->multiple_mode = ide->regs.ide_nsector; ide_interrupt (); } static void ide_set_features (void) { - int type = ide->regs->ide_nsector >> 3; - int mode = ide->regs->ide_nsector & 7; + int type = ide->regs.ide_nsector >> 3; + int mode = ide->regs.ide_nsector & 7; - write_log (_T("IDE%d set features %02X (%02X)\n"), ide->num, ide->regs->ide_feat, ide->regs->ide_nsector); + write_log (_T("IDE%d set features %02X (%02X)\n"), ide->num, ide->regs.ide_feat, ide->regs.ide_nsector); ide_fail (); } static void get_lbachs (struct ide_hdf *ide, uae_u64 *lbap, unsigned int *cyl, unsigned int *head, unsigned int *sec, int lba48) { - if (lba48 && (ide->regs->ide_select & 0x40)) { + if (lba48 && (ide->regs.ide_select & 0x40)) { uae_u64 lba; - lba = (ide->regs->ide_hcyl << 16) | (ide->regs->ide_lcyl << 8) | ide->regs->ide_sector; - lba |= ((ide->regs->ide_hcyl2 << 16) | (ide->regs->ide_lcyl2 << 8) | ide->regs->ide_sector2) << 24; + lba = (ide->regs.ide_hcyl << 16) | (ide->regs.ide_lcyl << 8) | ide->regs.ide_sector; + lba |= ((ide->regs.ide_hcyl2 << 16) | (ide->regs.ide_lcyl2 << 8) | ide->regs.ide_sector2) << 24; *lbap = lba; } else { - if (ide->regs->ide_select & 0x40) { - *lbap = ((ide->regs->ide_select & 15) << 24) | (ide->regs->ide_hcyl << 16) | (ide->regs->ide_lcyl << 8) | ide->regs->ide_sector; + if (ide->regs.ide_select & 0x40) { + *lbap = ((ide->regs.ide_select & 15) << 24) | (ide->regs.ide_hcyl << 16) | (ide->regs.ide_lcyl << 8) | ide->regs.ide_sector; } else { - *cyl = (ide->regs->ide_hcyl << 8) | ide->regs->ide_lcyl; - *head = ide->regs->ide_select & 15; - *sec = ide->regs->ide_sector; + *cyl = (ide->regs.ide_hcyl << 8) | ide->regs.ide_lcyl; + *head = ide->regs.ide_select & 15; + *sec = ide->regs.ide_sector; *lbap = (((*cyl) * ide->hdhfd.heads + (*head)) * ide->hdhfd.secspertrack) + (*sec) - 1; } } @@ -599,22 +629,22 @@ static void get_lbachs (struct ide_hdf *ide, uae_u64 *lbap, unsigned int *cyl, u static int get_nsec (int lba48) { if (lba48) - return (ide->regs->ide_nsector == 0 && ide->regs->ide_nsector2 == 0) ? 65536 : (ide->regs->ide_nsector2 * 256 + ide->regs->ide_nsector); + return (ide->regs.ide_nsector == 0 && ide->regs.ide_nsector2 == 0) ? 65536 : (ide->regs.ide_nsector2 * 256 + ide->regs.ide_nsector); else - return ide->regs->ide_nsector == 0 ? 256 : ide->regs->ide_nsector; + return ide->regs.ide_nsector == 0 ? 256 : ide->regs.ide_nsector; } static int dec_nsec (int lba48, int v) { if (lba48) { uae_u16 nsec; - nsec = ide->regs->ide_nsector2 * 256 + ide->regs->ide_nsector; - ide->regs->ide_nsector -= v; - ide->regs->ide_nsector2 = nsec >> 8; - ide->regs->ide_nsector = nsec & 0xff; - return (ide->regs->ide_nsector2 << 8) | ide->regs->ide_nsector; + nsec = ide->regs.ide_nsector2 * 256 + ide->regs.ide_nsector; + ide->regs.ide_nsector -= v; + ide->regs.ide_nsector2 = nsec >> 8; + ide->regs.ide_nsector = nsec & 0xff; + return (ide->regs.ide_nsector2 << 8) | ide->regs.ide_nsector; } else { - ide->regs->ide_nsector -= v; - return ide->regs->ide_nsector; + ide->regs.ide_nsector -= v; + return ide->regs.ide_nsector; } } @@ -622,21 +652,21 @@ static void put_lbachs (struct ide_hdf *ide, uae_u64 lba, unsigned int cyl, unsi { if (lba48) { lba += inc; - ide->regs->ide_hcyl = (lba >> 16) & 0xff; - ide->regs->ide_lcyl = (lba >> 8) & 0xff; - ide->regs->ide_sector = lba & 0xff; + ide->regs.ide_hcyl = (lba >> 16) & 0xff; + ide->regs.ide_lcyl = (lba >> 8) & 0xff; + ide->regs.ide_sector = lba & 0xff; lba >>= 24; - ide->regs->ide_hcyl2 = (lba >> 16) & 0xff; - ide->regs->ide_lcyl2 = (lba >> 8) & 0xff; - ide->regs->ide_sector2 = lba & 0xff; + ide->regs.ide_hcyl2 = (lba >> 16) & 0xff; + ide->regs.ide_lcyl2 = (lba >> 8) & 0xff; + ide->regs.ide_sector2 = lba & 0xff; } else { - if (ide->regs->ide_select & 0x40) { + if (ide->regs.ide_select & 0x40) { lba += inc; - ide->regs->ide_select &= ~15; - ide->regs->ide_select |= (lba >> 24) & 15; - ide->regs->ide_hcyl = (lba >> 16) & 0xff; - ide->regs->ide_lcyl = (lba >> 8) & 0xff; - ide->regs->ide_sector = lba & 0xff; + ide->regs.ide_select &= ~15; + ide->regs.ide_select |= (lba >> 24) & 15; + ide->regs.ide_hcyl = (lba >> 16) & 0xff; + ide->regs.ide_lcyl = (lba >> 8) & 0xff; + ide->regs.ide_sector = lba & 0xff; } else { sec += inc; while (sec >= ide->hdhfd.secspertrack) { @@ -647,11 +677,11 @@ static void put_lbachs (struct ide_hdf *ide, uae_u64 lba, unsigned int cyl, unsi cyl++; } } - ide->regs->ide_select &= ~15; - ide->regs->ide_select |= head; - ide->regs->ide_sector = sec; - ide->regs->ide_hcyl = cyl >> 8; - ide->regs->ide_lcyl = (uae_u8)cyl; + ide->regs.ide_select &= ~15; + ide->regs.ide_select |= head; + ide->regs.ide_sector = sec; + ide->regs.ide_hcyl = cyl >> 8; + ide->regs.ide_lcyl = (uae_u8)cyl; } } } @@ -660,7 +690,7 @@ static void check_maxtransfer (int state) { if (state == 1) { // transfer was started - if (ide->maxtransferstate < 2 && ide->regs->ide_nsector == 0) { + if (ide->maxtransferstate < 2 && ide->regs.ide_nsector == 0) { ide->maxtransferstate = 1; } else if (ide->maxtransferstate == 2) { // second transfer was started (part of split) @@ -702,7 +732,7 @@ static void ide_read_sectors (int flags) } ide->data_multi = multi ? ide->multiple_mode : 1; ide->data_offset = 0; - ide->status |= IDE_STATUS_DRQ; + ide->regs.ide_status |= IDE_STATUS_DRQ; ide->data_size = nsec * ide->blocksize; ide_interrupt (); } @@ -718,7 +748,7 @@ static void ide_write_sectors (int flags) ide_fail (); return; } - check_maxtransfer(1); + check_maxtransfer (1); gui_flicker_led (LED_HD, ide->num, 2); nsec = get_nsec (lba48); get_lbachs (ide, &lba, &cyl, &head, &sec, lba48); @@ -738,18 +768,24 @@ static void ide_write_sectors (int flags) } ide->data_multi = multi ? ide->multiple_mode : 1; ide->data_offset = 0; - ide->status |= IDE_STATUS_DRQ; + ide->regs.ide_status |= IDE_STATUS_DRQ; ide->data_size = nsec * ide->blocksize; } static void atapi_packet (void) { - ide->regs->ide_error = 1; /* C/D = 1 */ - ide->status = IDE_STATUS_DRQ; - ide->data_size = (ide->regs->ide_hcyl << 8) | ide->regs->ide_lcyl; + ide->data_size = (ide->regs.ide_hcyl << 8) | ide->regs.ide_lcyl; if (ide->data_size == 65535) ide->data_size = 65534; - + ide->packet_data_size = (ide->data_size + 1) & ~1; + ide->data_size = 12; + write_log (_T("ATAPI packet command\n")); + ide->packet_cnt = 1; + ide->data_multi = 1; + ide->data_offset = 0; + ide->regs.ide_status = IDE_STATUS_DRQ; + ide->regs.ide_nsector = ATAPI_CD; + ide->regs.ide_error = 0; } static void ide_do_command (uae_u8 cmd) @@ -758,63 +794,120 @@ static void ide_do_command (uae_u8 cmd) if (IDE_LOG > 1) write_log (_T("**** IDE%d command %02X\n"), ide->num, cmd); - ide->status &= ~ (IDE_STATUS_DRDY | IDE_STATUS_DRQ | IDE_STATUS_ERR); - ide->regs->ide_error = 0; + ide->regs.ide_status &= ~ (IDE_STATUS_DRDY | IDE_STATUS_DRQ | IDE_STATUS_ERR); + ide->regs.ide_error = 0; if (ide->atapi) { + if (cmd == 0x08) { /* device reset */ - ide_execute_drive_diagnostics (false); + ide_execute_drive_diagnostics (true); } else if (cmd == 0xa1) { /* identify packet device */ ide_identify_drive (); } else if (cmd == 0xa0) { /* packet */ atapi_packet (); + } else { + ide_execute_drive_diagnostics (false); + ide_fail (); + write_log (_T("IDE%d: unknown ATAPI command 0x%02x\n"), ide->num, cmd); } - } - if (cmd == 0x10) { /* recalibrate */ - ide_recalibrate (); - } else if (cmd == 0xec) { /* identify drive */ - ide_identify_drive (); - } else if (cmd == 0x90) { /* execute drive diagnostics */ - ide_execute_drive_diagnostics (true); - } else if (cmd == 0x91) { /* initialize drive parameters */ - ide_initialize_drive_parameters (); - } else if (cmd == 0xc6) { /* set multiple mode */ - if (ide->atapi) - ide_fail (); - else + } else { + + if (cmd == 0x10) { /* recalibrate */ + ide_recalibrate (); + } else if (cmd == 0xec) { /* identify drive */ + ide_identify_drive (); + } else if (cmd == 0x90) { /* execute drive diagnostics */ + ide_execute_drive_diagnostics (true); + } else if (cmd == 0x91) { /* initialize drive parameters */ + ide_initialize_drive_parameters (); + } else if (cmd == 0xc6) { /* set multiple mode */ ide_set_multiple_mode (); - } else if (cmd == 0x20 || cmd == 0x21) { /* read sectors */ - ide_read_sectors (0); - } else if (cmd == 0x24 && lba48) { /* read sectors ext */ - ide_read_sectors (2); - } else if (cmd == 0xc4) { /* read multiple */ - ide_read_sectors (1); - } else if (cmd == 0x29 && lba48) { /* read multiple ext */ - ide_read_sectors (1|2); - } else if (cmd == 0x30 || cmd == 0x31) { /* write sectors */ - ide_write_sectors (0); - } else if (cmd == 0x34 && lba48) { /* write sectors ext */ - ide_write_sectors (2); - } else if (cmd == 0xc5) { /* write multiple */ - ide_write_sectors (1); - } else if (cmd == 0x39 && lba48) { /* write multiple ext */ - ide_write_sectors (1|2); - } else if (cmd == 0x50) { /* format track (nop) */ - ide_interrupt (); - } else if (cmd == 0xef) { /* set features */ - ide_set_features (); - } else if (cmd == 0x00) { /* nop */ - ide_fail (); - } else if (cmd == 0xe0 || cmd == 0xe1 || cmd == 0xe7 || cmd == 0xea) { /* standby now/idle/flush cache/flush cache ext */ - ide_interrupt (); - } else if (cmd == 0xe5) { /* check power mode */ - ide->regs->ide_nsector = 0xff; - ide_interrupt (); + } else if (cmd == 0x20 || cmd == 0x21) { /* read sectors */ + ide_read_sectors (0); + } else if (cmd == 0x24 && lba48) { /* read sectors ext */ + ide_read_sectors (2); + } else if (cmd == 0xc4) { /* read multiple */ + ide_read_sectors (1); + } else if (cmd == 0x29 && lba48) { /* read multiple ext */ + ide_read_sectors (1|2); + } else if (cmd == 0x30 || cmd == 0x31) { /* write sectors */ + ide_write_sectors (0); + } else if (cmd == 0x34 && lba48) { /* write sectors ext */ + ide_write_sectors (2); + } else if (cmd == 0xc5) { /* write multiple */ + ide_write_sectors (1); + } else if (cmd == 0x39 && lba48) { /* write multiple ext */ + ide_write_sectors (1|2); + } else if (cmd == 0x50) { /* format track (nop) */ + ide_interrupt (); + } else if (cmd == 0xef) { /* set features */ + ide_set_features (); + } else if (cmd == 0x00) { /* nop */ + ide_fail (); + } else if (cmd == 0xe0 || cmd == 0xe1 || cmd == 0xe7 || cmd == 0xea) { /* standby now/idle/flush cache/flush cache ext */ + ide_interrupt (); + } else if (cmd == 0xe5) { /* check power mode */ + ide->regs.ide_nsector = 0xff; + ide_interrupt (); + } else { + ide_fail (); + write_log (_T("IDE%d: unknown ATA command 0x%02x\n"), ide->num, cmd); + } + } +} + +static void atapi_data_done (void) +{ + ide->regs.ide_nsector = ATAPI_IO | ATAPI_CD; + ide->regs.ide_status = IDE_STATUS_DRDY; +} + +static void do_packet_command (void) +{ + memcpy (ide->scsi.cmd, ide->secbuf, 12); + ide->scsi.cmd_len = 12; + if (IDE_LOG > 0) { + uae_u8 *c = ide->scsi.cmd; + write_log (_T("SCSI %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x\n"), + c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8], c[9], c[10], c[11], c[12]); + } + ide->packet_cnt = 0; + scsi_emulate_analyze (&ide->scsi); + if (ide->scsi.direction <= 0) { + // data in + scsi_emulate_cmd (&ide->scsi); + ide->data_size = ide->scsi.data_len; + if (ide->data_size > ide->packet_data_size) + ide->data_size = ide->packet_data_size; + ide->data_offset = 0; + ide->regs.ide_status = 0; + if (ide->scsi.status) { + // error + ide->regs.ide_status = ATAPI_STATUS_CHK; + ide->regs.ide_error = ide->scsi.status << 4; + atapi_data_done (); + } else if (ide->scsi.data_len) { + // data + ide->regs.ide_status = IDE_STATUS_DRQ; + memcpy (ide->secbuf, ide->scsi.buffer, ide->scsi.data_len); + ide->packet_cnt = -1; + ide->regs.ide_nsector = ATAPI_IO; + } else { + // no data + atapi_data_done (); + } } else { - ide_fail (); - write_log (_T("IDE%d: unknown command %x\n"), ide->num, cmd); + // data out + ide->regs.ide_status = IDE_STATUS_DRQ; + ide->packet_cnt = -1; + ide->regs.ide_nsector = 0; + ide->data_size = ide->scsi.data_len; + if (ide->data_size > ide->packet_data_size) + ide->data_size = ide->packet_data_size; } + ide->regs.ide_lcyl = ide->data_size & 0xff; + ide->regs.ide_hcyl = ide->data_size >> 8; } static uae_u16 ide_get_data (void) @@ -829,49 +922,65 @@ static uae_u16 ide_get_data (void) write_log (_T("IDE%d DATA read\n"), ide->num); if (ide->data_size == 0) { if (IDE_LOG > 0) - write_log (_T("IDE%d DATA read without DRQ!?\n"), ide->num); - if (ide->hdhfd.size == 0) + write_log (_T("IDE%d DATA read without DRQ!? PC=%08X\n"), ide->num, m68k_getpc ()); + if (!isdrive (ide)) return 0xffff; return 0; } - nsec = 0; - if (ide->data_offset == 0 && ide->data_size >= 0) { - get_lbachs (ide, &lba, &cyl, &head, &sec, ide->lba48); - nsec = get_nsec (ide->lba48); - if (nsec * ide->blocksize > ide->hdhfd.size - lba * ide->blocksize) - nsec = (ide->hdhfd.size - lba * ide->blocksize) / ide->blocksize; - if (nsec <= 0) { - ide_data_ready (); - ide_fail_err (IDE_ERR_IDNF); - return 0; + if (ide->packet_cnt) { + v = ide->secbuf[ide->data_offset + 1] | (ide->secbuf[ide->data_offset + 0] << 8); + ide->data_offset += 2; + if (ide->data_size < 0) + ide->data_size += 2; + else + ide->data_size -= 2; + if (ide->data_size == 0) { + ide->packet_cnt = 0; + atapi_data_done (); + if (IDE_LOG > 1) + write_log (_T("IDE%d ATAPI read finished\n"), ide->num); + irq = true; } - if (nsec > ide->data_multi) - nsec = ide->data_multi; - hdf_read (&ide->hdhfd.hfd, ide->secbuf, lba * ide->blocksize, nsec * ide->blocksize); - if (!dec_nsec (ide->lba48, nsec)) - last = true; - if (IDE_LOG > 1) - write_log (_T("IDE%d read, read %d bytes to buffer\n"), ide->num, nsec * ide->blocksize); - } - - v = ide->secbuf[ide->data_offset + 1] | (ide->secbuf[ide->data_offset + 0] << 8); - ide->data_offset += 2; - if (ide->data_size < 0) { - ide->data_size += 2; } else { - ide->data_size -= 2; - if (((ide->data_offset % ide->blocksize) == 0) && ((ide->data_offset / ide->blocksize) % ide->data_multi) == 0) { - irq = true; - ide->data_offset = 0; + nsec = 0; + if (ide->data_offset == 0 && ide->data_size >= 0) { + get_lbachs (ide, &lba, &cyl, &head, &sec, ide->lba48); + nsec = get_nsec (ide->lba48); + if (nsec * ide->blocksize > ide->hdhfd.size - lba * ide->blocksize) + nsec = (ide->hdhfd.size - lba * ide->blocksize) / ide->blocksize; + if (nsec <= 0) { + ide_data_ready (); + ide_fail_err (IDE_ERR_IDNF); + return 0; + } + if (nsec > ide->data_multi) + nsec = ide->data_multi; + hdf_read (&ide->hdhfd.hfd, ide->secbuf, lba * ide->blocksize, nsec * ide->blocksize); + if (!dec_nsec (ide->lba48, nsec)) + last = true; + if (IDE_LOG > 1) + write_log (_T("IDE%d read, read %d bytes to buffer\n"), ide->num, nsec * ide->blocksize); + } + + v = ide->secbuf[ide->data_offset + 1] | (ide->secbuf[ide->data_offset + 0] << 8); + ide->data_offset += 2; + if (ide->data_size < 0) { + ide->data_size += 2; + } else { + ide->data_size -= 2; + if (((ide->data_offset % ide->blocksize) == 0) && ((ide->data_offset / ide->blocksize) % ide->data_multi) == 0) { + irq = true; + ide->data_offset = 0; + } + } + if (ide->data_size == 0) { + ide->regs.ide_status &= ~IDE_STATUS_DRQ; + if (IDE_LOG > 1) + write_log (_T("IDE%d read finished\n"), ide->num); + } + if (nsec) { + put_lbachs (ide, lba, cyl, head, sec, last ? nsec - 1 : nsec, ide->lba48); } - } - if (ide->data_size == 0) { - ide->status &= ~IDE_STATUS_DRQ; - if (IDE_LOG > 1) - write_log (_T("IDE%d read finished\n"), ide->num); - } - if (nsec) { - put_lbachs (ide, lba, cyl, head, sec, last ? nsec - 1 : nsec, ide->lba48); } if (irq) { ide_interrupt (); @@ -898,28 +1007,44 @@ static void ide_write_drive (bool last) static void ide_put_data (uae_u16 v) { - int irq = 0; + bool irq = false; if (IDE_LOG > 4) write_log (_T("IDE%d DATA write %04x %d/%d\n"), ide->num, v, ide->data_offset, ide->data_size); if (ide->data_size == 0) { if (IDE_LOG > 0) - write_log (_T("IDE%d DATA write without DRQ!?\n"), ide->num); + write_log (_T("IDE%d DATA write without DRQ!? PC=%08X\n"), ide->num, m68k_getpc ()); return; } ide->secbuf[ide->data_offset + 1] = v & 0xff; ide->secbuf[ide->data_offset + 0] = v >> 8; ide->data_offset += 2; ide->data_size -= 2; - if (ide->data_size == 0) { - irq = 1; - ide_write_drive (true); - ide->status &= ~IDE_STATUS_DRQ; - if (IDE_LOG > 1) - write_log (_T("IDE%d write finished\n"), ide->num); - } else if (((ide->data_offset % ide->blocksize) == 0) && ((ide->data_offset / ide->blocksize) % ide->data_multi) == 0) { - irq = 1; - ide_write_drive (false); + if (ide->packet_cnt) { + if (ide->data_size == 0) { + if (ide->packet_cnt > 0) { + do_packet_command (); + } else if (ide->packet_cnt < 0) { + ide->packet_cnt = 0; + memcpy (&ide->scsi.buffer, ide->secbuf, ide->data_size); + ide->scsi.data_len = ide->data_size; + scsi_emulate_cmd (&ide->scsi); + if (IDE_LOG > 1) + write_log (_T("IDE%d ATAPI write finished\n"), ide->num); + } + irq = true; + } + } else { + if (ide->data_size == 0) { + irq = true; + ide_write_drive (true); + ide->regs.ide_status &= ~IDE_STATUS_DRQ; + if (IDE_LOG > 1) + write_log (_T("IDE%d write finished\n"), ide->num); + } else if (((ide->data_offset % ide->blocksize) == 0) && ((ide->data_offset / ide->blocksize) % ide->data_multi) == 0) { + irq = 1; + ide_write_drive (false); + } } if (irq) ide_interrupt (); @@ -929,7 +1054,7 @@ static int get_gayle_ide_reg (uaecptr addr) { uaecptr a = addr; addr &= 0xffff; - if (addr >= 0x3020 && addr <= 0x3021 && currprefs.cs_ide == IDE_A4000) + if (addr >= GAYLE_IRQ_4000 && addr <= GAYLE_IRQ_4000 + 1 && currprefs.cs_ide == IDE_A4000) return -1; addr &= ~0x2020; addr >>= 2; @@ -940,74 +1065,84 @@ static int get_gayle_ide_reg (uaecptr addr) addr &= ~0x400; } } - ide = idedrive[ide2 + (ide->regs->ide_drv ? 1 : 0)]; + ide = idedrive[ide2 + (ide->ide_drv ? 1 : 0)]; return addr; } static uae_u32 ide_read_reg (int ide_reg) { uae_u8 v = 0; - bool isdrive = ide->hdhfd.size != 0; + bool isdrv = isdrive (ide); + + if (ide->regs.ide_status & IDE_STATUS_BSY) + ide_reg = IDE_STATUS; + if (!isdrive (ide)) { + v = 0; + goto end; + } switch (ide_reg) { case IDE_DRVADDR: - v = ((ide->regs->ide_drv ? 2 : 1) | ((ide->regs->ide_select & 15) << 2)) ^ 0xff; + v = ((ide->ide_drv ? 2 : 1) | ((ide->regs.ide_select & 15) << 2)) ^ 0xff; break; case IDE_DATA: break; case IDE_ERROR: - v = ide->regs->ide_error; + v = ide->regs.ide_error; break; case IDE_NSECTOR: - if (isdrive) { - if (ide->regs->ide_devcon & 0x80) - v = ide->regs->ide_nsector2; + if (isdrv) { + if (ide->regs.ide_devcon & 0x80) + v = ide->regs.ide_nsector2; else - v = ide->regs->ide_nsector; + v = ide->regs.ide_nsector; } break; case IDE_SECTOR: - if (isdrive) { - if (ide->regs->ide_devcon & 0x80) - v = ide->regs->ide_sector2; + if (isdrv) { + if (ide->regs.ide_devcon & 0x80) + v = ide->regs.ide_sector2; else - v = ide->regs->ide_sector; + v = ide->regs.ide_sector; check_maxtransfer (2); } break; case IDE_LCYL: - if (isdrive) { - if (ide->regs->ide_devcon & 0x80) - v = ide->regs->ide_lcyl2; + if (isdrv) { + if (ide->regs.ide_devcon & 0x80) + v = ide->regs.ide_lcyl2; else - v = ide->regs->ide_lcyl; + v = ide->regs.ide_lcyl; } break; case IDE_HCYL: - if (isdrive) { - if (ide->regs->ide_devcon & 0x80) - v = ide->regs->ide_hcyl2; + if (isdrv) { + if (ide->regs.ide_devcon & 0x80) + v = ide->regs.ide_hcyl2; else - v = ide->regs->ide_hcyl; + v = ide->regs.ide_hcyl; } break; case IDE_SELECT: - v = ide->regs->ide_select; + v = ide->regs.ide_select; break; case IDE_STATUS: - ide->irq = 0; /* fall through */ + ide->irq = 0; + /* fall through */ case IDE_DEVCON: /* ALTSTATUS when reading */ - if (!isdrive) { + if (!isdrv) { v = 0; - if (ide->regs->ide_error) + if (ide->regs.ide_error) v |= IDE_STATUS_ERR; } else { - v = ide->status; - v |= IDE_STATUS_DRDY | IDE_STATUS_DSC; + v = ide->regs.ide_status; + if (!ide->atapi) + v |= IDE_STATUS_DRDY | IDE_STATUS_DSC; } break; } +end: if (IDE_LOG > 2 && ide_reg > 0 && (1 || ide->num > 0)) write_log (_T("IDE%d GET register %d->%02X\n"), ide->num, ide_reg, (uae_u32)v & 0xff); return v; @@ -1015,7 +1150,7 @@ static uae_u32 ide_read_reg (int ide_reg) static void ide_write_reg (int ide_reg, uae_u32 val) { - ide->regs->ide_devcon &= ~0x80; /* clear HOB */ + ide->regs.ide_devcon &= ~0x80; /* clear HOB */ if (IDE_LOG > 2 && ide_reg > 0 && (1 || ide->num > 0)) write_log (_T("IDE%d PUT register %d=%02X\n"), ide->num, ide_reg, (uae_u32)val & 0xff); switch (ide_reg) @@ -1023,39 +1158,45 @@ static void ide_write_reg (int ide_reg, uae_u32 val) case IDE_DRVADDR: break; case IDE_DEVCON: - if ((ide->regs->ide_devcon & 4) == 0 && (val & 4) != 0) + if ((ide->regs.ide_devcon & 4) == 0 && (val & 4) != 0) ide_execute_drive_diagnostics (false); - ide->regs->ide_devcon = val; + ide->regs.ide_devcon = val; break; case IDE_DATA: break; case IDE_ERROR: - ide->regs->ide_feat2 = ide->regs->ide_feat; - ide->regs->ide_feat = val; + ide->regs.ide_feat2 = ide->regs.ide_feat; + ide->regs.ide_feat = val; break; case IDE_NSECTOR: - ide->regs->ide_nsector2 = ide->regs->ide_nsector; - ide->regs->ide_nsector = val; + ide->regs.ide_nsector2 = ide->regs.ide_nsector; + ide->regs.ide_nsector = val; break; case IDE_SECTOR: - ide->regs->ide_sector2 = ide->regs->ide_sector; - ide->regs->ide_sector = val; + ide->regs.ide_sector2 = ide->regs.ide_sector; + ide->regs.ide_sector = val; break; case IDE_LCYL: - ide->regs->ide_lcyl2 = ide->regs->ide_lcyl; - ide->regs->ide_lcyl = val; + ide->regs.ide_lcyl2 = ide->regs.ide_lcyl; + ide->regs.ide_lcyl = val; break; case IDE_HCYL: - ide->regs->ide_hcyl2 = ide->regs->ide_hcyl; - ide->regs->ide_hcyl = val; + ide->regs.ide_hcyl2 = ide->regs.ide_hcyl; + ide->regs.ide_hcyl = val; break; case IDE_SELECT: - ide->regs->ide_select = val; - ide->regs->ide_drv = (val & 0x10) ? 1 : 0; + ide->regs0->ide_select = val; + ide->regs1->ide_select = val; +#if IDE_LOG > 2 + if (ide->ide_drv != (val & 0x10) ? 1 : 0) + write_log (_T("DRIVE=%d\n"), (val & 0x10) ? 1 : 0); +#endif + ide->pair->ide_drv = ide->ide_drv = (val & 0x10) ? 1 : 0; break; case IDE_STATUS: ide->irq = 0; - ide_do_command (val); + if (isdrive (ide)) + ide_do_command (val); break; } } @@ -1066,7 +1207,7 @@ static uae_u32 gayle_read2 (uaecptr addr) uae_u8 v = 0; addr &= 0xffff; - if ((IDE_LOG > 2 && (addr != 0x2000 && addr != 0x2001 && addr != 0x2020 && addr != 0x2021 && addr != GAYLE_IRQ_1200)) || IDE_LOG > 4) + if ((IDE_LOG > 3 && (addr != 0x2000 && addr != 0x2001 && addr != 0x3020 && addr != 0x3021 && addr != GAYLE_IRQ_1200)) || IDE_LOG > 5) write_log (_T("IDE_READ %08X PC=%X\n"), addr, M68K_GETPC); if (currprefs.cs_ide <= 0) { if (addr == 0x201c) // AR1200 IDE detection hack @@ -1092,7 +1233,7 @@ static uae_u32 gayle_read2 (uaecptr addr) } ide_reg = get_gayle_ide_reg (addr); /* Emulated "ide killer". Prevents long KS boot delay if no drives installed */ - if (idedrive[0]->hdhfd.size == 0 && idedrive[2]->hdhfd.size == 0) { + if (!isdrive (idedrive[0]) && !isdrive (idedrive[1]) && !isdrive (idedrive[2]) && !isdrive (idedrive[3])) { if (ide_reg == IDE_STATUS) return 0x7f; return 0xff; @@ -1104,7 +1245,7 @@ static void gayle_write2 (uaecptr addr, uae_u32 val) { int ide_reg; - if ((IDE_LOG > 2 && (addr != 0x2000 && addr != 0x2001 && addr != 0x2020 && addr != 0x2021 && addr != GAYLE_IRQ_1200)) || IDE_LOG > 4) + if ((IDE_LOG > 3 && (addr != 0x2000 && addr != 0x2001 && addr != 0x2020 && addr != 0x2021 && addr != GAYLE_IRQ_1200)) || IDE_LOG > 5) write_log (_T("IDE_WRITE %08X=%02X PC=%X\n"), addr, (uae_u32)val & 0xff, M68K_GETPC); if (currprefs.cs_ide <= 0) return; @@ -1562,8 +1703,10 @@ static void alloc_ide_mem (struct ide_hdf **ide, int max) int i; for (i = 0; i < max; i++) { - if (!ide[i]) + if (!ide[i]) { ide[i] = xcalloc (struct ide_hdf, 1); + ide[i]->cd_unit_num = -1; + } } } @@ -1575,14 +1718,38 @@ static struct ide_hdf *add_ide_unit (int ch, struct uaedev_config_info *ci) ide = idedrive[ch]; if (ci) memcpy (&ide->hdhfd.hfd.ci, ci, sizeof (struct uaedev_config_info)); - if (!hdf_hd_open (&ide->hdhfd)) - return NULL; - ide->blocksize = ide->hdhfd.hfd.ci.blocksize; - ide->lba48 = ide->hdhfd.size >= 128 * (uae_u64)0x40000000 ? 1 : 0; - ide->status = 0; + if (ci->cd_emu_unit >= 0) { + device_func_init (0); + ide->cd_unit_num = ci->cd_emu_unit; + if (!sys_command_open (ide->cd_unit_num)) { + write_log (_T("IDE: CD EMU unit %d failed to open\n"), ide->cd_unit_num); + return NULL; + } + ide->atapi = true; + ide->blocksize = 512; + gui_flicker_led (LED_CD, ch, -1); + ide->scsi.cd_emu_unit = ide->cd_unit_num; + + write_log (_T("IDE%d CD %d\n"), ch, ide->cd_unit_num); + + } else { + if (!hdf_hd_open (&ide->hdhfd)) + return NULL; + ide->blocksize = ide->hdhfd.hfd.ci.blocksize; + ide->lba48 = ide->hdhfd.size >= 128 * (uae_u64)0x40000000 ? 1 : 0; + gui_flicker_led (LED_HD, ch, -1); + ide->cd_unit_num = -1; + + write_log (_T("IDE%d HD '%s', LCHS=%d/%d/%d. PCHS=%d/%d/%d %uM. LBA48=%d\n"), + ch, ide->hdhfd.hfd.ci.rootdir, + ide->hdhfd.cyls, ide->hdhfd.heads, ide->hdhfd.secspertrack, + ide->hdhfd.hfd.ci.pcyls, ide->hdhfd.hfd.ci.pheads, ide->hdhfd.hfd.ci.psecs, + (int)(ide->hdhfd.size / (1024 * 1024)), ide->lba48); + + } + ide->regs.ide_status = 0; ide->data_offset = 0; ide->data_size = 0; - gui_flicker_led (LED_HD, ch, -1); return ide; } @@ -1610,7 +1777,7 @@ static int get_pcmcmia_ide_reg (uaecptr addr, int width) addr |= 1; } ide = idedrive[PCMCIA_IDE_ID * 2]; - if (ide->regs->ide_drv) + if (ide->ide_drv) ide = idedrive[PCMCIA_IDE_ID * 2 + 1]; if (pcmcia_configured == 1) { // IO mapped linear @@ -2260,8 +2427,13 @@ void gayle_free_units (void) for (i = 0; i < TOTAL_IDE * 2; i++) { struct ide_hdf *ide = idedrive[i]; if (ide) { - hdf_hd_close (&ide->hdhfd); + if (ide->cd_unit_num >= 0) { + sys_command_close (ide->cd_unit_num); + } else { + hdf_hd_close (&ide->hdhfd); + } memset (ide, 0, sizeof (struct ide_hdf)); + ide->cd_unit_num = -1; } } freepcmcia (1); @@ -2296,11 +2468,6 @@ int gayle_add_ide_unit (int ch, struct uaedev_config_info *ci) ide = add_ide_unit (ch, ci); if (ide == NULL) return 0; - write_log (_T("GAYLE_IDE%d '%s', LCHS=%d/%d/%d. PCHS=%d/%d/%d %uM. LBA48=%d\n"), - ch, ide->hdhfd.hfd.ci.rootdir, - ide->hdhfd.cyls, ide->hdhfd.heads, ide->hdhfd.secspertrack, - ide->hdhfd.hfd.ci.pcyls, ide->hdhfd.hfd.ci.pheads, ide->hdhfd.hfd.ci.psecs, - (int)(ide->hdhfd.size / (1024 * 1024)), ide->lba48); ide->type = IDE_GAYLE; //dumphdf (&ide->hdhfd.hfd); return 1; @@ -2340,19 +2507,20 @@ static void initide (void) if (isrestore ()) return; for (i = 0; i < TOTAL_IDE; i++) { - ideregs[i].ide_error = 1; - ideregs[i].ide_sector = ideregs[i].ide_nsector = 1; - ideregs[i].ide_select = 0; - ideregs[i].ide_lcyl = ideregs[i].ide_hcyl = ideregs[i].ide_devcon = ideregs[i].ide_feat = 0; - idedrive[i * 2 + 0]->regs = &ideregs[i]; - idedrive[i * 2 + 1]->regs = &ideregs[i]; ide = idedrive[i * 2 + 0]; + ide->regs0 = &ide->regs; + ide->regs1 = &idedrive[i * 2 + 1]->regs; + ide->pair = idedrive[i * 2 + 1]; ide_execute_drive_diagnostics (false); + ide = idedrive[i * 2 + 1]; + ide->regs1 = &ide->regs; + ide->regs0 = &idedrive[i * 2 + 0]->regs; + ide->pair = idedrive[i * 2 + 0]; ide_execute_drive_diagnostics (false); } ide_splitter = 0; - if (idedrive[2]->hdhfd.size) { + if (isdrive (idedrive[2]) || isdrive(idedrive[3])) { ide_splitter = 1; write_log (_T("IDE splitter enabled\n")); } @@ -2440,19 +2608,19 @@ uae_u8 *save_ide (int num, int *len, uae_u8 *dstptr) save_u32 (ide->hdhfd.cyls); save_u32 (ide->hdhfd.heads); save_u32 (ide->hdhfd.secspertrack); - save_u8 (ide->regs->ide_select); - save_u8 (ide->regs->ide_nsector); - save_u8 (ide->regs->ide_nsector2); - save_u8 (ide->regs->ide_sector); - save_u8 (ide->regs->ide_sector2); - save_u8 (ide->regs->ide_lcyl); - save_u8 (ide->regs->ide_lcyl2); - save_u8 (ide->regs->ide_hcyl); - save_u8 (ide->regs->ide_hcyl2); - save_u8 (ide->regs->ide_feat); - save_u8 (ide->regs->ide_feat2); - save_u8 (ide->regs->ide_error); - save_u8 (ide->regs->ide_devcon); + save_u8 (ide->regs.ide_select); + save_u8 (ide->regs.ide_nsector); + save_u8 (ide->regs.ide_nsector2); + save_u8 (ide->regs.ide_sector); + save_u8 (ide->regs.ide_sector2); + save_u8 (ide->regs.ide_lcyl); + save_u8 (ide->regs.ide_lcyl2); + save_u8 (ide->regs.ide_hcyl); + save_u8 (ide->regs.ide_hcyl2); + save_u8 (ide->regs.ide_feat); + save_u8 (ide->regs.ide_feat2); + save_u8 (ide->regs.ide_error); + save_u8 (ide->regs.ide_devcon); save_u64 (ide->hdhfd.hfd.virtual_size); save_u32 (ide->hdhfd.hfd.ci.sectors); save_u32 (ide->hdhfd.hfd.ci.surfaces); @@ -2481,19 +2649,19 @@ uae_u8 *restore_ide (uae_u8 *src) ide->hdhfd.cyls = restore_u32 (); ide->hdhfd.heads = restore_u32 (); ide->hdhfd.secspertrack = restore_u32 (); - ide->regs->ide_select = restore_u8 (); - ide->regs->ide_nsector = restore_u8 (); - ide->regs->ide_sector = restore_u8 (); - ide->regs->ide_lcyl = restore_u8 (); - ide->regs->ide_hcyl = restore_u8 (); - ide->regs->ide_feat = restore_u8 (); - ide->regs->ide_nsector2 = restore_u8 (); - ide->regs->ide_sector2 = restore_u8 (); - ide->regs->ide_lcyl2 = restore_u8 (); - ide->regs->ide_hcyl2 = restore_u8 (); - ide->regs->ide_feat2 = restore_u8 (); - ide->regs->ide_error = restore_u8 (); - ide->regs->ide_devcon = restore_u8 (); + ide->regs.ide_select = restore_u8 (); + ide->regs.ide_nsector = restore_u8 (); + ide->regs.ide_sector = restore_u8 (); + ide->regs.ide_lcyl = restore_u8 (); + ide->regs.ide_hcyl = restore_u8 (); + ide->regs.ide_feat = restore_u8 (); + ide->regs.ide_nsector2 = restore_u8 (); + ide->regs.ide_sector2 = restore_u8 (); + ide->regs.ide_lcyl2 = restore_u8 (); + ide->regs.ide_hcyl2 = restore_u8 (); + ide->regs.ide_feat2 = restore_u8 (); + ide->regs.ide_error = restore_u8 (); + ide->regs.ide_devcon = restore_u8 (); ide->hdhfd.hfd.virtual_size = restore_u64 (); ide->hdhfd.hfd.ci.sectors = restore_u32 (); ide->hdhfd.hfd.ci.surfaces = restore_u32 (); diff --git a/gencpu.cpp b/gencpu.cpp index 2858dac1..d58a05a9 100644 --- a/gencpu.cpp +++ b/gencpu.cpp @@ -52,6 +52,8 @@ static int optimized_flags; #define GF_FC 32 #define GF_MOVE 64 #define GF_IR2IRC 128 +#define GF_LRMW 256 +#define GF_NOFAULTPC 512 /* For the current opcode, the next lower level that will have different code. * Initialized to -1 for each opcode. If it remains unchanged, indicates we @@ -78,6 +80,8 @@ static int mmudisp020cnt; static char *srcl, *dstl; static char *srcw, *dstw; static char *srcb, *dstb; +static char *srcbrmw, *srcwrmw, *srclrmw; +static char *dstbrmw, *dstwrmw, *dstlrmw; static char *prefetch_long, *prefetch_word; static char *srcli, *srcwi, *srcbi, *nextl, *nextw, *nextb; static char *srcld, *dstld; @@ -472,6 +476,26 @@ static void sync_m68k_pc (void) m68k_pc_offset = 0; } +static void addmmufixup (char *reg) +{ + if (!using_mmu) + return; + if (using_mmu == 68040 && (mmufixupstate || mmufixupcnt > 0)) + return; + printf ("\tmmufixup[%d].reg = %s;\n", mmufixupcnt, reg); + printf ("\tmmufixup[%d].value = m68k_areg (regs, %s);\n", mmufixupcnt, reg); + mmufixupstate |= 1 << mmufixupcnt; + mmufixupcnt++; +} + +static void clearmmufixup (int cnt) +{ + if (mmufixupstate & (1 << cnt)) { + printf ("\tmmufixup[%d].reg = -1;\n", cnt); + mmufixupstate &= ~(1 << cnt); + } +} + static void gen_set_fault_pc (void) { if (using_mmu != 68040) @@ -479,8 +503,20 @@ static void gen_set_fault_pc (void) sync_m68k_pc (); printf ("\tregs.instruction_pc = m68k_getpci ();\n"); m68k_pc_offset = 0; + clearmmufixup (0); } +static void add_mmu040_movem (int movem) +{ + if (movem != 3) + return; + printf ("\tif (mmu040_movem) {\n"); + printf ("\t\tsrca = mmu040_movem_ea;\n"); + printf ("\t} else\n"); + start_brace (); +} + + static void syncmovepc (int getv, int flags) { #if 0 @@ -494,26 +530,6 @@ static void syncmovepc (int getv, int flags) } -static void addmmufixup (char *reg) -{ - if (!using_mmu) - return; - if (using_mmu == 68040 && (mmufixupstate || mmufixupcnt > 0)) - return; - printf ("\tmmufixup[%d].reg = %s;\n", mmufixupcnt, reg); - printf ("\tmmufixup[%d].value = m68k_areg (regs, %s);\n", mmufixupcnt, reg); - mmufixupstate |= 1 << mmufixupcnt; - mmufixupcnt++; -} - -static void clearmmufixup (int cnt) -{ - if (mmufixupstate & (1 << cnt)) { - printf ("\tmmufixup[%d].reg = -1;\n", cnt); - mmufixupstate &= ~(1 << cnt); - } -} - /* getv == 1: fetch data; getv != 0: check for odd address. If movem != 0, * the calling routine handles Apdi and Aipi modes. * gb-- movem == 2 means the same thing but for a MOVE16 instruction */ @@ -618,6 +634,7 @@ static void genamode2 (amodes mode, char *reg, wordsizes size, char *name, int g if (next_cpu_level < 1) next_cpu_level = 1; sync_m68k_pc (); + add_mmu040_movem (movem); start_brace (); /* This would ordinarily be done in gen_nextiword, which we bypass. */ insn_n_cycles += 4; @@ -633,7 +650,9 @@ static void genamode2 (amodes mode, char *reg, wordsizes size, char *name, int g } break; case PC16: // (d16,PC,Xn) - printf ("\tuaecptr %sa = m68k_getpc () + %d;\n", name, m68k_pc_offset); + printf ("\tuaecptr %sa;\n", name); + add_mmu040_movem (movem); + printf ("\t%sa = m68k_getpc () + %d;\n", name, m68k_pc_offset); printf ("\t%sa += (uae_s32)(uae_s16)%s;\n", name, gen_nextiword (flags)); break; case PC8r: // (d8,PC,Xn) @@ -643,6 +662,7 @@ static void genamode2 (amodes mode, char *reg, wordsizes size, char *name, int g if (next_cpu_level < 1) next_cpu_level = 1; sync_m68k_pc (); + add_mmu040_movem (movem); start_brace (); /* This would ordinarily be done in gen_nextiword, which we bypass. */ insn_n_cycles += 4; @@ -774,9 +794,9 @@ static void genamode2 (amodes mode, char *reg, wordsizes size, char *name, int g } } else { switch (size) { - case sz_byte: insn_n_cycles += 4; printf ("\tuae_s8 %s = %s (%sa);\n", name, srcb, name); break; - case sz_word: insn_n_cycles += 4; printf ("\tuae_s16 %s = %s (%sa);\n", name, srcw, name); break; - case sz_long: insn_n_cycles += 8; printf ("\tuae_s32 %s = %s (%sa);\n", name, srcl, name); break; + case sz_byte: insn_n_cycles += 4; printf ("\tuae_s8 %s = %s (%sa);\n", name, (flags & GF_LRMW) ? srcbrmw : srcb, name); break; + case sz_word: insn_n_cycles += 4; printf ("\tuae_s16 %s = %s (%sa);\n", name, (flags & GF_LRMW) ? srcwrmw : srcw, name); break; + case sz_long: insn_n_cycles += 8; printf ("\tuae_s32 %s = %s (%sa);\n", name, (flags & GF_LRMW) ? srclrmw : srcl, name); break; default: abort (); } } @@ -817,6 +837,10 @@ static void genamode2 (amodes mode, char *reg, wordsizes size, char *name, int g default: break; } + + if (movem == 3) { + close_brace (); + } } @@ -869,7 +893,8 @@ static void genastore_2 (char *from, amodes mode, char *reg, wordsizes size, cha case absl: case PC16: case PC8r: - gen_set_fault_pc (); + if (!(flags & GF_NOFAULTPC)) + gen_set_fault_pc (); if (using_ce020) { switch (size) { case sz_byte: @@ -922,7 +947,7 @@ static void genastore_2 (char *from, amodes mode, char *reg, wordsizes size, cha if (flags & GF_FC) printf ("\tdfc%s_put_byte (%sa, %s);\n", mmu_postfix, to, from); else - printf ("\t%s (%sa, %s);\n", dstb, to, from); + printf ("\t%s (%sa, %s);\n", (flags & GF_LRMW) ? dstbrmw : dstb, to, from); break; case sz_word: insn_n_cycles += 4; @@ -931,7 +956,7 @@ static void genastore_2 (char *from, amodes mode, char *reg, wordsizes size, cha if (flags & GF_FC) printf ("\tdfc%s_put_word (%sa, %s);\n", mmu_postfix, to, from); else - printf ("\t%s (%sa, %s);\n", dstw, to, from); + printf ("\t%s (%sa, %s);\n", (flags & GF_LRMW) ? dstwrmw :dstw, to, from); break; case sz_long: insn_n_cycles += 8; @@ -940,7 +965,7 @@ static void genastore_2 (char *from, amodes mode, char *reg, wordsizes size, cha if (flags & GF_FC) printf ("\tdfc%s_put_long (%sa, %s);\n", mmu_postfix, to, from); else - printf ("\t%s (%sa, %s);\n", dstl, to, from); + printf ("\t%s (%sa, %s);\n", (flags & GF_LRMW) ? dstlrmw : dstl, to, from); break; default: abort (); @@ -987,6 +1012,14 @@ static void genastore (char *from, amodes mode, char *reg, wordsizes size, char { genastore_2 (from, mode, reg, size, to, 0, 0); } +static void genastore_tas (char *from, amodes mode, char *reg, wordsizes size, char *to) +{ + genastore_2 (from, mode, reg, size, to, 0, GF_LRMW); +} +static void genastore_cas (char *from, amodes mode, char *reg, wordsizes size, char *to) +{ + genastore_2 (from, mode, reg, size, to, 0, GF_LRMW | GF_NOFAULTPC); +} static void genastore_rev (char *from, amodes mode, char *reg, wordsizes size, char *to) { genastore_2 (from, mode, reg, size, to, 1, 0); @@ -996,6 +1029,93 @@ static void genastore_fc (char *from, amodes mode, char *reg, wordsizes size, ch genastore_2 (from, mode, reg, size, to, 1, GF_FC); } +static void movem_mmu060 (const char *code, int size, bool put, bool aipi, bool apdi) +{ + char *index; + int dphase, aphase; + if (apdi) { + dphase = 1; aphase = 0; + index = "movem_index2"; + } else { + dphase = 0; aphase = 1; + index = "movem_index1"; + } + + for (int i = 0; i < 2; i++) { + char reg; + if (i == dphase) + reg = 'd'; + else + reg = 'a'; + printf ("\twhile (%cmask) {\n", reg); + if (apdi) + printf ("\t\tsrca -= %d;\n", size); + if (put) { + printf ("\t\t%s, m68k_%creg (regs, %s[%cmask]));\n", code, reg, index, reg); + } else { + printf ("\t\tm68k_%creg (regs, %s[%cmask]) = %s;\n", reg, index, reg, code); + } + if (!apdi) + printf ("\t\tsrca += %d;\n", size); + printf ("\t\t%cmask = movem_next[%cmask];\n", reg, reg); + printf ("\t}\n"); + } + if (aipi || apdi) + printf ("\tm68k_areg (regs, dstreg) = srca;\n"); +} + +static bool mmu040_special_movem (uae_u16 opcode) +{ + if (using_mmu != 68040) + return false; + return (((((opcode >> 3) & 7) == 7) && ((opcode & 7) == 2 || (opcode & 7) == 3)) || ((opcode >> 3) & 7) == 6); +} + +static void movem_mmu040 (const char *code, int size, bool put, bool aipi, bool apdi, uae_u16 opcode) +{ + char *index; + int dphase, aphase; + bool mvm = false; + + if (apdi) { + dphase = 1; aphase = 0; + index = "movem_index2"; + } else { + dphase = 0; aphase = 1; + index = "movem_index1"; + } + + if (mmu040_special_movem (opcode)) { + printf ("\tmmu040_movem = 1;\n"); + printf ("\tmmu040_movem_ea = srca;\n"); + mvm = true; + } + + for (int i = 0; i < 2; i++) { + char reg; + if (i == dphase) + reg = 'd'; + else + reg = 'a'; + printf ("\twhile (%cmask) {\n", reg); + if (apdi) + printf ("\t\tsrca -= %d;\n", size); + if (put) { + printf ("\t\t%s, m68k_%creg (regs, %s[%cmask]));\n", code, reg, index, reg); + } else { + printf ("\t\tm68k_%creg (regs, %s[%cmask]) = %s;\n", reg, index, reg, code); + } + if (!apdi) + printf ("\t\tsrca += %d;\n", size); + printf ("\t\t%cmask = movem_next[%cmask];\n", reg, reg); + printf ("\t}\n"); + } + if (aipi || apdi) + printf ("\tm68k_areg (regs, dstreg) = srca;\n"); + if (mvm) + printf ("\tmmu040_movem = 0;\n"); +} + /* 68030 MMU does not restore register state if it bus faults. * (also there wouldn't be enough space in stack frame to store all registers) */ @@ -1057,10 +1177,14 @@ static void genmovemel (uae_u16 opcode) count_read += table68k[opcode].size == sz_long ? 2 : 1; printf ("\tuae_u16 mask = %s;\n", gen_nextiword (0)); printf ("\tuae_u32 dmask = mask & 0xff, amask = (mask >> 8) & 0xff;\n"); - genamode (table68k[opcode].dmode, "dstreg", table68k[opcode].size, "src", 2, 1, 0); + genamode (table68k[opcode].dmode, "dstreg", table68k[opcode].size, "src", 2, mmu040_special_movem (opcode) ? 3 : 1, 0); start_brace (); if (using_mmu == 68030) { movem_mmu030 (getcode, size, false, table68k[opcode].dmode == Aipi, false); + } else if (using_mmu == 68060) { + movem_mmu060 (getcode, size, false, table68k[opcode].dmode == Aipi, false); + } else if (using_mmu == 68040) { + movem_mmu040 (getcode, size, false, table68k[opcode].dmode == Aipi, false, opcode); } else { printf ("\twhile (dmask) { m68k_dreg (regs, movem_index1[dmask]) = %s; srca += %d; dmask = movem_next[dmask]; }\n", getcode, size); @@ -1117,14 +1241,19 @@ static void genmovemle (uae_u16 opcode) count_write += table68k[opcode].size == sz_long ? 2 : 1; printf ("\tuae_u16 mask = %s;\n", gen_nextiword (0)); - genamode (table68k[opcode].dmode, "dstreg", table68k[opcode].size, "src", 2, 1, 0); + genamode (table68k[opcode].dmode, "dstreg", table68k[opcode].size, "src", 2, mmu040_special_movem (opcode) ? 3 : 1, 0); start_brace (); - if (using_mmu == 68030) { + if (using_mmu >= 68030) { if (table68k[opcode].dmode == Apdi) printf ("\tuae_u16 amask = mask & 0xff, dmask = (mask >> 8) & 0xff;\n"); else printf ("\tuae_u16 dmask = mask & 0xff, amask = (mask >> 8) & 0xff;\n"); - movem_mmu030 (putcode, size, true, false, table68k[opcode].dmode == Apdi); + if (using_mmu == 68030) + movem_mmu030 (putcode, size, true, false, table68k[opcode].dmode == Apdi); + else if (using_mmu == 68060) + movem_mmu060 (putcode, size, true, false, table68k[opcode].dmode == Apdi); + else if (using_mmu == 68040) + movem_mmu040 (putcode, size, true, false, table68k[opcode].dmode == Apdi, opcode); } else { if (table68k[opcode].dmode == Apdi) { printf ("\tuae_u16 amask = mask & 0xff, dmask = (mask >> 8) & 0xff;\n"); @@ -1132,9 +1261,12 @@ static void genmovemle (uae_u16 opcode) printf ("\tint type = get_cpu_model () >= 68020;\n"); printf ("\twhile (amask) {\n"); printf ("\t\tsrca -= %d;\n", size); - if (!using_mmu) - printf ("\t\tif (type) m68k_areg (regs, dstreg) = srca;\n"); - printf ("\t\t%s, m68k_areg (regs, movem_index2[amask]));\n", putcode); + + printf ("\t\tif (!type || movem_index2[amask] != dstreg)\n"); + printf ("\t\t\t%s, m68k_areg (regs, movem_index2[amask]));\n", putcode); + printf ("\t\telse\n"); + printf ("\t\t\t%s, m68k_areg (regs, movem_index2[amask]) - %d);\n", putcode, size); + printf ("\t\tamask = movem_next[amask];\n"); printf ("\t}\n"); printf ("\twhile (dmask) { srca -= %d; %s, m68k_dreg (regs, movem_index2[dmask])); dmask = movem_next[dmask]; }\n", @@ -1523,6 +1655,12 @@ static void gen_opcode (unsigned long int opcode) do_cycles = "do_cycles"; srcwd = srcld = NULL; dstwd = dstld = NULL; + srcbrmw = NULL; + srcwrmw = NULL; + srclrmw = NULL; + dstbrmw = NULL; + dstwrmw = NULL; + dstlrmw = NULL; if (using_indirect) { // tracer @@ -1621,6 +1759,12 @@ static void gen_opcode (unsigned long int opcode) dstw = "put_word_mmu030_state"; srcb = "get_byte_mmu030_state"; dstb = "put_byte_mmu030_state"; + srcbrmw = "get_rmw_byte_mmu030_state"; + srcwrmw = "get_rmw_word_mmu030_state"; + srclrmw = "get_rmw_long_mmu030_state"; + dstbrmw = "put_rmw_byte_mmu030_state"; + dstwrmw = "put_rmw_word_mmu030_state"; + dstlrmw = "put_rmw_long_mmu030_state"; srcld = "get_long_mmu030"; srcwd = "get_word_mmu030"; dstld = "put_long_mmu030"; @@ -1641,6 +1785,12 @@ static void gen_opcode (unsigned long int opcode) dstw = "put_word_mmu040"; srcb = "get_byte_mmu040"; dstb = "put_byte_mmu040"; + srcbrmw = "get_rmw_byte_mmu040"; + srcwrmw = "get_rmw_word_mmu040"; + srclrmw = "get_rmw_long_mmu040"; + dstbrmw = "put_rmw_byte_mmu040"; + dstwrmw = "put_rmw_word_mmu040"; + dstlrmw = "put_rmw_long_mmu040"; } else if (using_mmu) { // 68060 MMU disp020 = "x_get_disp_ea_020"; @@ -1657,6 +1807,12 @@ static void gen_opcode (unsigned long int opcode) dstw = "put_word_mmu060"; srcb = "get_byte_mmu060"; dstb = "put_byte_mmu060"; + srcbrmw = "get_rmw_byte_mmu060"; + srcwrmw = "get_rmw_word_mmu060"; + srclrmw = "get_rmw_long_mmu060"; + dstbrmw = "put_rmw_byte_mmu060"; + dstwrmw = "put_rmw_word_mmu060"; + dstlrmw = "put_rmw_long_mmu060"; } else if (using_ce) { // 68000 ce prefetch_word = "get_word_ce000_prefetch"; @@ -1702,6 +1858,14 @@ static void gen_opcode (unsigned long int opcode) srcld = srcl; if (!srcwd) srcwd = srcw; + if (!srcbrmw) { + srcbrmw = srcb; + srcwrmw = srcw; + srclrmw = srcl; + dstbrmw = dstb; + dstwrmw = dstw; + dstlrmw = dstl; + } insn_n_cycles020 = 0; @@ -3258,10 +3422,14 @@ static void gen_opcode (unsigned long int opcode) case i_CAS: { int old_brace_level; - genamode (curi->smode, "srcreg", curi->size, "src", 1, 0, 0); - genamode (curi->dmode, "dstreg", curi->size, "dst", 1, 0, 0); + genamode (curi->smode, "srcreg", curi->size, "src", 1, 0, GF_LRMW); + genamode (curi->dmode, "dstreg", curi->size, "dst", 1, 0, GF_LRMW); if (cpu_level == 5 && curi->size > 0) { - printf ("\tif ((dsta & %d) && currprefs.cpu_compatible && get_cpu_model () == 68060) {\n", curi->size == 1 ? 1 : 3); + if (!using_mmu) { + printf ("\tif ((dsta & %d) && currprefs.cpu_compatible && get_cpu_model () == 68060) {\n", curi->size == 1 ? 1 : 3); + } else { + printf ("\tif ((dsta & %d) && currprefs.cpu_compatible) {\n", curi->size == 1 ? 1 : 3); + } if (curi->dmode == Aipi || curi->dmode == Apdi) printf ("\t\tm68k_areg (regs, dstreg) %c= %d;\n", curi->dmode == Aipi ? '-' : '+', 1 << curi->size); printf ("\t\top_unimpl ();\n"); @@ -3275,40 +3443,49 @@ static void gen_opcode (unsigned long int opcode) printf ("\tint rc = src & 7;\n"); genflags (flag_cmp, curi->size, "newv", "m68k_dreg (regs, rc)", "dst"); sync_m68k_pc (); - printf ("\tif (GET_ZFLG ())"); + gen_set_fault_pc (); + printf ("\tif (GET_ZFLG ()) "); old_brace_level = n_braces; start_brace (); - genastore ("(m68k_dreg (regs, ru))", curi->dmode, "dstreg", curi->size, "dst"); + printf ("\n\t"); + genastore_cas ("(m68k_dreg (regs, ru))", curi->dmode, "dstreg", curi->size, "dst"); + printf ("\t"); pop_braces (old_brace_level); printf ("else"); start_brace (); + printf ("\n"); switch (curi->size) { case sz_byte: - printf ("\tm68k_dreg(regs, rc) = (m68k_dreg(regs, rc) & ~0xff) | (dst & 0xff);\n"); + printf ("\t\tm68k_dreg(regs, rc) = (m68k_dreg(regs, rc) & ~0xff) | (dst & 0xff);\n"); break; case sz_word: - printf ("\tm68k_dreg(regs, rc) = (m68k_dreg(regs, rc) & ~0xffff) | (dst & 0xffff);\n"); + printf ("\t\tm68k_dreg(regs, rc) = (m68k_dreg(regs, rc) & ~0xffff) | (dst & 0xffff);\n"); break; default: - printf ("\tm68k_dreg(regs, rc) = dst;\n"); + printf ("\t\tm68k_dreg(regs, rc) = dst;\n"); break; } + if (cpu_level >= 4) { + // apparently 68040/060 needs to always write at the end of RMW cycle + printf ("\t"); + genastore_cas ("dst", curi->dmode, "dstreg", curi->size, "dst"); + } pop_braces (old_brace_level); } break; case i_CAS2: - genamode (curi->smode, "srcreg", curi->size, "extra", 1, 0, 0); + genamode (curi->smode, "srcreg", curi->size, "extra", 1, 0, GF_LRMW); printf ("\tuae_u32 rn1 = regs.regs[(extra >> 28) & 15];\n"); printf ("\tuae_u32 rn2 = regs.regs[(extra >> 12) & 15];\n"); if (curi->size == sz_word) { int old_brace_level = n_braces; - printf ("\tuae_u16 dst1 = %s (rn1), dst2 = %s (rn2);\n", srcw, srcw); + printf ("\tuae_u16 dst1 = %s (rn1), dst2 = %s (rn2);\n", srcwrmw, srcwrmw); genflags (flag_cmp, curi->size, "newv", "m68k_dreg (regs, (extra >> 16) & 7)", "dst1"); printf ("\tif (GET_ZFLG ()) {\n"); genflags (flag_cmp, curi->size, "newv", "m68k_dreg (regs, extra & 7)", "dst2"); printf ("\tif (GET_ZFLG ()) {\n"); - printf ("\t%s (rn1, m68k_dreg (regs, (extra >> 22) & 7));\n", dstw); - printf ("\t%s (rn2, m68k_dreg (regs, (extra >> 6) & 7));\n", dstw); + printf ("\t%s (rn1, m68k_dreg (regs, (extra >> 22) & 7));\n", dstwrmw); + printf ("\t%s (rn2, m68k_dreg (regs, (extra >> 6) & 7));\n", dstwrmw); printf ("\t}}\n"); pop_braces (old_brace_level); printf ("\tif (! GET_ZFLG ()) {\n"); @@ -3317,13 +3494,13 @@ static void gen_opcode (unsigned long int opcode) printf ("\t}\n"); } else { int old_brace_level = n_braces; - printf ("\tuae_u32 dst1 = %s (rn1), dst2 = %s (rn2);\n", srcl, srcl); + printf ("\tuae_u32 dst1 = %s (rn1), dst2 = %s (rn2);\n", srclrmw, srclrmw); genflags (flag_cmp, curi->size, "newv", "m68k_dreg (regs, (extra >> 16) & 7)", "dst1"); printf ("\tif (GET_ZFLG ()) {\n"); genflags (flag_cmp, curi->size, "newv", "m68k_dreg (regs, extra & 7)", "dst2"); printf ("\tif (GET_ZFLG ()) {\n"); - printf ("\t%s (rn1, m68k_dreg (regs, (extra >> 22) & 7));\n", dstl); - printf ("\t%s (rn2, m68k_dreg (regs, (extra >> 6) & 7));\n", dstl); + printf ("\t%s (rn1, m68k_dreg (regs, (extra >> 22) & 7));\n", dstlrmw); + printf ("\t%s (rn2, m68k_dreg (regs, (extra >> 6) & 7));\n", dstlrmw); printf ("\t}}\n"); pop_braces (old_brace_level); printf ("\tif (! GET_ZFLG ()) {\n"); @@ -3533,7 +3710,7 @@ static void gen_opcode (unsigned long int opcode) } break; case i_TAS: - genamode (curi->smode, "srcreg", curi->size, "src", 1, 0, 0); + genamode (curi->smode, "srcreg", curi->size, "src", 1, 0, GF_LRMW); genflags (flag_logical, curi->size, "src", "", ""); if (!isreg (curi->smode)) addcycles000 (2); @@ -3542,7 +3719,7 @@ static void gen_opcode (unsigned long int opcode) if (cpu_level >= 2 || curi->smode == Dreg || !using_ce) { if (next_cpu_level < 2) next_cpu_level = 2 - 1; - genastore ("src", curi->smode, "srcreg", curi->size, "src"); + genastore_tas ("src", curi->smode, "srcreg", curi->size, "src"); } else { printf ("\tif (!is_cycle_ce ()) {\n"); genastore ("src", curi->smode, "srcreg", curi->size, "src"); @@ -3618,9 +3795,9 @@ static void gen_opcode (unsigned long int opcode) printf ("\tuaecptr mems = m68k_areg (regs, srcreg) & ~15, memd;\n"); printf ("\tdstreg = (%s >> 12) & 7;\n", gen_nextiword (0)); printf ("\tmemd = m68k_areg (regs, dstreg) & ~15;\n"); - if (using_mmu == 68060) { - printf ("\tget_move16_mmu060 (mems, v);\n"); - printf ("\tput_move16_mmu060 (memd, v);\n"); + if (using_mmu >= 68040) { + printf ("\tget_move16_mmu (mems, v);\n"); + printf ("\tput_move16_mmu (memd, v);\n"); } else { printf ("\tv[0] = %s (mems);\n", srcl); printf ("\tv[1] = %s (mems + 4);\n", srcl); @@ -3641,9 +3818,9 @@ static void gen_opcode (unsigned long int opcode) genamode (curi->dmode, "dstreg", curi->size, "memd", 0, 2, 0); printf ("\tmemsa &= ~15;\n"); printf ("\tmemda &= ~15;\n"); - if (using_mmu == 68060) { - printf ("\tget_move16_mmu060 (memsa, v);\n"); - printf ("\tput_move16_mmu060 (memda, v);\n"); + if (using_mmu >= 68040) { + printf ("\tget_move16_mmu (memsa, v);\n"); + printf ("\tput_move16_mmu (memda, v);\n"); } else { printf ("\tv[0] = %s (memsa);\n", srcl); printf ("\tv[1] = %s (memsa + 4);\n", srcl); diff --git a/hardfile.cpp b/hardfile.cpp index 350dcfd5..ddbe7a71 100644 --- a/hardfile.cpp +++ b/hardfile.cpp @@ -1031,6 +1031,9 @@ int hdf_read (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len) { int v; + hf_log3 (_T("cmd_read: %p %04x-%08x (%d) %08x (%d)\n"), + buffer, (uae_u32)(offset >> 32), (uae_u32)offset, (uae_u32)(offset / hfd->ci.blocksize), (uae_u32)len, (uae_u32)(len / hfd->ci.blocksize)); + if (!hfd->adide) { v = hdf_cache_read (hfd, buffer, offset, len); } else { @@ -1047,6 +1050,9 @@ int hdf_write (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len) { int v; + hf_log3 (_T("cmd_write: %p %04x-%08x (%d) %08x (%d)\n"), + buffer, (uae_u32)(offset >> 32), (uae_u32)offset, (uae_u32)(offset / hfd->ci.blocksize), (uae_u32)len, (uae_u32)(len / hfd->ci.blocksize)); + if (hfd->byteswap) hdf_byteswap (buffer, len); if (!hfd->adide) { @@ -1065,8 +1071,6 @@ int hdf_write (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len) static uae_u64 cmd_readx (struct hardfiledata *hfd, uae_u8 *dataptr, uae_u64 offset, uae_u64 len) { gui_flicker_led (LED_HD, hfd->unitnum, 1); - hf_log3 (_T("cmd_read: %p %04x-%08x (%d) %08x (%d)\n"), - dataptr, (uae_u32)(offset >> 32), (uae_u32)offset, (uae_u32)(offset / hfd->ci.blocksize), (uae_u32)len, (uae_u32)(len / hfd->ci.blocksize)); return hdf_read (hfd, dataptr, offset, len); } static uae_u64 cmd_read (struct hardfiledata *hfd, uaecptr dataptr, uae_u64 offset, uae_u64 len) @@ -1079,8 +1083,6 @@ static uae_u64 cmd_read (struct hardfiledata *hfd, uaecptr dataptr, uae_u64 offs static uae_u64 cmd_writex (struct hardfiledata *hfd, uae_u8 *dataptr, uae_u64 offset, uae_u64 len) { gui_flicker_led (LED_HD, hfd->unitnum, 2); - hf_log3 (_T("cmd_write: %p %04x-%08x (%d) %08x (%d)\n"), - dataptr, (uae_u32)(offset >> 32), (uae_u32)offset, (uae_u32)(offset / hfd->ci.blocksize), (uae_u32)len, (uae_u32)(len / hfd->ci.blocksize)); return hdf_write (hfd, dataptr, offset, len); } @@ -1108,7 +1110,7 @@ static int nodisk (struct hardfiledata *hfd) return 0; } -int scsi_emulate (struct hardfiledata *hfd, struct hd_hardfiledata *hdhfd, uae_u8 *cmdbuf, int scsi_cmd_len, +int scsi_hd_emulate (struct hardfiledata *hfd, struct hd_hardfiledata *hdhfd, uae_u8 *cmdbuf, int scsi_cmd_len, uae_u8 *scsi_data, int *data_len, uae_u8 *r, int *reply_len, uae_u8 *s, int *sense_len) { uae_u64 len, offset; @@ -1122,7 +1124,7 @@ int scsi_emulate (struct hardfiledata *hfd, struct hd_hardfiledata *hdhfd, uae_u memset (r, 0, 256); memset (s, 0, 256); lun = cmdbuf[1] >> 5; - if (lun) { + if (cmdbuf[0] != 0x03 && cmdbuf[0] != 0x12 && lun) { status = 2; /* CHECK CONDITION */ s[0] = 0x70; s[2] = 5; /* ILLEGAL REQUEST */ @@ -1173,9 +1175,14 @@ int scsi_emulate (struct hardfiledata *hfd, struct hd_hardfiledata *hdhfd, uae_u if ((cmdbuf[1] & 1) || cmdbuf[2] != 0) goto err; int alen = (cmdbuf[3] << 8) | cmdbuf[4]; - if (hfd->drive_empty) { - r[1] |= 0x80; // removable.. - r[0] |= 0x20; // not present + if (lun != 0) { + r[0] = 0x7f; + } else { + r[0] = 0; + if (hfd->drive_empty) { + r[1] |= 0x80; // removable.. + r[0] |= 0x20; // not present + } } r[2] = 2; /* supports SCSI-2 */ r[3] = 2; /* response data format */ @@ -1416,6 +1423,9 @@ int scsi_emulate (struct hardfiledata *hfd, struct hd_hardfiledata *hdhfd, uae_u s[12] = 0x1c; /* DEFECT LIST NOT FOUND */ ls = 12; break; + case 0x1b: /* START/STOP UNIT */ + scsi_len = 0; + break; readprot: status = 2; /* CHECK CONDITION */ s[0] = 0x70; @@ -1433,7 +1443,7 @@ nodisk: default: err: - write_log (_T("UAEHF: unsupported scsi command 0x%02X\n"), cmdbuf[0]); + write_log (_T("UAEHF: unsupported scsi command 0x%02X LUN=%d\n"), cmdbuf[0], lun); errreq: lr = -1; status = 2; /* CHECK CONDITION */ @@ -1495,7 +1505,7 @@ static int handle_scsi (uaecptr request, struct hardfiledata *hfd) } scsi_log (_T("\n")); - status = scsi_emulate (hfd, NULL, cmdbuf, scsi_cmd_len, scsi_data_ptr, &scsi_len, reply, &reply_len, sense, &sense_len); + status = scsi_hd_emulate (hfd, NULL, cmdbuf, scsi_cmd_len, scsi_data_ptr, &scsi_len, reply, &reply_len, sense, &sense_len); put_word (acmd + 18, status != 0 ? 0 : scsi_cmd_len); /* fake scsi_CmdActual */ put_byte (acmd + 21, status); /* scsi_Status */ diff --git a/include/a2091.h b/include/a2091.h index a2225999..d00edd49 100644 --- a/include/a2091.h +++ b/include/a2091.h @@ -26,6 +26,7 @@ extern struct scsi_data *scsis[8]; extern int a2091_add_scsi_unit (int ch, struct uaedev_config_info *ci); extern int a3000_add_scsi_unit (int ch, struct uaedev_config_info *ci); -extern int addscsi (int ch, struct hd_hardfiledata *hfd, struct uaedev_config_info *ci, int scsi_level); +extern int add_scsi_hd (int ch, struct hd_hardfiledata *hfd, struct uaedev_config_info *ci, int scsi_level); +extern int add_scsi_cd (int ch, int unitnum); #endif diff --git a/include/autoconf.h b/include/autoconf.h index 6638e56b..6162786d 100644 --- a/include/autoconf.h +++ b/include/autoconf.h @@ -52,10 +52,11 @@ extern uaecptr need_uae_boot_rom (void); struct mountedinfo { - uae_u64 size; + uae_s64 size; bool ismounted; bool ismedia; int nrcyls; + TCHAR rootdir[MAX_DPATH]; }; extern int add_filesys_unitconfig (struct uae_prefs *p, int index, TCHAR *error); diff --git a/include/blkdev.h b/include/blkdev.h index 52f13e4e..a3d72927 100644 --- a/include/blkdev.h +++ b/include/blkdev.h @@ -174,6 +174,7 @@ extern int sys_command_scsi_direct_native (int unitnum, struct amigascsi *as); extern int sys_command_scsi_direct (int unitnum, uaecptr request); extern int sys_command_ismedia (int unitnum, int quick); extern struct device_info *sys_command_info_session (int unitnum, struct device_info *di, int, int); +extern bool blkdev_get_info (struct uae_prefs *p, int unitnum, struct device_info *di); extern void scsi_atapi_fixup_pre (uae_u8 *scsi_cmd, int *len, uae_u8 **data, int *datalen, int *parm); extern void scsi_atapi_fixup_post (uae_u8 *scsi_cmd, int len, uae_u8 *olddata, uae_u8 *data, int *datalen, int parm); @@ -181,6 +182,9 @@ extern void scsi_atapi_fixup_post (uae_u8 *scsi_cmd, int len, uae_u8 *olddata, u extern void scsi_log_before (uae_u8 *cdb, int cdblen, uae_u8 *data, int datalen); extern void scsi_log_after (uae_u8 *data, int datalen, uae_u8 *sense, int senselen); +extern int scsi_cd_emulate (int unitnum, uae_u8 *cmdbuf, int scsi_cmd_len, + uae_u8 *scsi_data, int *data_len, uae_u8 *r, int *reply_len, uae_u8 *s, int *sense_len); + extern void blkdev_vsync (void); extern int msf2lsn (int msf); diff --git a/include/cdtv.h b/include/cdtv.h index c63bf69e..63d2e206 100644 --- a/include/cdtv.h +++ b/include/cdtv.h @@ -14,7 +14,7 @@ uae_u8 cdtv_battram_read (int addr); extern void cdtv_loadcardmem (uae_u8*, int); extern void cdtv_savecardmem (uae_u8*, int); -extern int cdtv_add_scsi_unit (int ch, struct uaedev_config_info *ci); +extern int cdtv_add_scsi_hd_unit (int ch, struct uaedev_config_info *ci); extern void cdtv_getdmadata (uae_u32*); diff --git a/include/cpummu.h b/include/cpummu.h index 0e5d25b4..7c8fe43b 100644 --- a/include/cpummu.h +++ b/include/cpummu.h @@ -44,6 +44,11 @@ static __inline void flush_internals (void) { } extern int mmu060_state; +extern uae_u16 mmu060_opcode; + +extern int mmu040_movem; +extern uaecptr mmu040_movem_ea; + extern bool mmu_pagesize_8k; //typedef uae_u8 flagtype; @@ -342,6 +347,9 @@ extern void REGPARAM3 dfc_put_byte(uaecptr addr, uae_u8 val) REGPARAM; #define dfc060_put_word dfc_put_word #define dfc060_put_byte dfc_put_byte +extern void uae_mmu_put_rmw (uaecptr addr, uae_u32 v, int size, int type); +extern uae_u32 uae_mmu_get_rmw (uaecptr addr, int size, int type); + extern void REGPARAM3 mmu_flush_atc(uaecptr addr, bool super, bool global) REGPARAM; extern void REGPARAM3 mmu_flush_atc_all(bool global) REGPARAM; extern void REGPARAM3 mmu_op_real(uae_u32 opcode, uae_u16 extra) REGPARAM; @@ -355,7 +363,7 @@ static ALWAYS_INLINE uaecptr mmu_get_real_address(uaecptr addr, struct mmu_atc_l return cl->phys | (addr & mmu_pagemask); } -static ALWAYS_INLINE void mmu060_get_move16(uaecptr addr, uae_u32 *v, bool data, int size) +static ALWAYS_INLINE void mmu_get_move16(uaecptr addr, uae_u32 *v, bool data, int size) { struct mmu_atc_line *cl; for (int i = 0; i < 4; i++) { @@ -421,7 +429,7 @@ static ALWAYS_INLINE void mmu_put_long(uaecptr addr, uae_u32 val, bool data, int mmu_put_long_slow(addr, val, regs.s != 0, data, size, cl); } -static ALWAYS_INLINE void mmu060_put_move16(uaecptr addr, uae_u32 *val, bool data, int size) +static ALWAYS_INLINE void mmu_put_move16(uaecptr addr, uae_u32 *val, bool data, int size) { struct mmu_atc_line *cl; for (int i = 0; i < 4; i++) { @@ -466,38 +474,38 @@ static ALWAYS_INLINE void mmu_put_byte(uaecptr addr, uae_u8 val, bool data, int mmu_put_byte_slow(addr, val, regs.s != 0, data, size, cl); } -static ALWAYS_INLINE uae_u32 mmu_get_user_long(uaecptr addr, bool super, bool data, int size) +static ALWAYS_INLINE uae_u32 mmu_get_user_long(uaecptr addr, bool super, bool data, bool write, int size) { struct mmu_atc_line *cl; // addr,super,data if ((!regs.mmu_enabled) || (mmu_match_ttr(addr,super,data)!=TTR_NO_MATCH)) return phys_get_long(addr); - if (likely(mmu_user_lookup(addr, super, data, false, &cl))) + if (likely(mmu_user_lookup(addr, super, data, write, &cl))) return phys_get_long(mmu_get_real_address(addr, cl)); return mmu_get_long_slow(addr, super, data, size, cl); } -static ALWAYS_INLINE uae_u16 mmu_get_user_word(uaecptr addr, bool super, bool data, int size) +static ALWAYS_INLINE uae_u16 mmu_get_user_word(uaecptr addr, bool super, bool data, bool write, int size) { struct mmu_atc_line *cl; // addr,super,data if ((!regs.mmu_enabled) || (mmu_match_ttr(addr,super,data)!=TTR_NO_MATCH)) return phys_get_word(addr); - if (likely(mmu_user_lookup(addr, super, data, false, &cl))) + if (likely(mmu_user_lookup(addr, super, data, write, &cl))) return phys_get_word(mmu_get_real_address(addr, cl)); return mmu_get_word_slow(addr, super, data, size, cl); } -static ALWAYS_INLINE uae_u8 mmu_get_user_byte(uaecptr addr, bool super, bool data, int size) +static ALWAYS_INLINE uae_u8 mmu_get_user_byte(uaecptr addr, bool super, bool data, bool write, int size) { struct mmu_atc_line *cl; // addr,super,data if ((!regs.mmu_enabled) || (mmu_match_ttr(addr,super,data)!=TTR_NO_MATCH)) return phys_get_byte(addr); - if (likely(mmu_user_lookup(addr, super, data, false, &cl))) + if (likely(mmu_user_lookup(addr, super, data, write, &cl))) return phys_get_byte(mmu_get_real_address(addr, cl)); return mmu_get_byte_slow(addr, super, data, size, cl); } @@ -658,10 +666,10 @@ static ALWAYS_INLINE uae_u8 uae_mmu060_get_byte(uaecptr addr) { return mmu_get_byte(addr, true, sz_byte); } -static ALWAYS_INLINE void uae_mmu060_get_move16(uaecptr addr, uae_u32 *val) +static ALWAYS_INLINE void uae_mmu_get_move16(uaecptr addr, uae_u32 *val) { // move16 is always aligned - mmu060_get_move16(addr, val, true, 16); + mmu_get_move16(addr, val, true, 16); } static ALWAYS_INLINE void uae_mmu060_put_long(uaecptr addr, uae_u32 val) @@ -682,10 +690,10 @@ static ALWAYS_INLINE void uae_mmu060_put_byte(uaecptr addr, uae_u8 val) { mmu_put_byte(addr, val, true, sz_byte); } -static ALWAYS_INLINE void uae_mmu060_put_move16(uaecptr addr, uae_u32 *val) +static ALWAYS_INLINE void uae_mmu_put_move16(uaecptr addr, uae_u32 *val) { // move16 is always aligned - mmu060_put_move16(addr, val, true, 16); + mmu_put_move16(addr, val, true, 16); } @@ -739,13 +747,63 @@ STATIC_INLINE uae_u32 get_long_mmu060 (uaecptr addr) return uae_mmu060_get_long (addr); } -STATIC_INLINE void get_move16_mmu060 (uaecptr addr, uae_u32 *v) +STATIC_INLINE void get_move16_mmu (uaecptr addr, uae_u32 *v) +{ + return uae_mmu_get_move16 (addr, v); +} +STATIC_INLINE void put_move16_mmu (uaecptr addr, uae_u32 *v) +{ + return uae_mmu_put_move16 (addr, v); +} + +STATIC_INLINE void put_rmw_byte_mmu060 (uaecptr addr, uae_u32 v) +{ + uae_mmu_put_rmw (addr, v, sz_byte, 1); +} +STATIC_INLINE void put_rmw_word_mmu060 (uaecptr addr, uae_u32 v) +{ + uae_mmu_put_rmw (addr, v, sz_word, 1); +} +STATIC_INLINE void put_rmw_long_mmu060 (uaecptr addr, uae_u32 v) +{ + uae_mmu_put_rmw (addr, v, sz_long, 1); +} +STATIC_INLINE uae_u32 get_rmw_byte_mmu060 (uaecptr addr) +{ + return uae_mmu_get_rmw (addr, sz_byte, 1); +} +STATIC_INLINE uae_u32 get_rmw_word_mmu060 (uaecptr addr) +{ + return uae_mmu_get_rmw (addr, sz_word, 1); +} +STATIC_INLINE uae_u32 get_rmw_long_mmu060 (uaecptr addr) +{ + return uae_mmu_get_rmw (addr, sz_long, 1); +} + +STATIC_INLINE void put_rmw_byte_mmu040 (uaecptr addr, uae_u32 v) +{ + uae_mmu_put_rmw (addr, v, sz_byte, 0); +} +STATIC_INLINE void put_rmw_word_mmu040 (uaecptr addr, uae_u32 v) +{ + uae_mmu_put_rmw (addr, v, sz_word, 0); +} +STATIC_INLINE void put_rmw_long_mmu040 (uaecptr addr, uae_u32 v) +{ + uae_mmu_put_rmw (addr, v, sz_long, 0); +} +STATIC_INLINE uae_u32 get_rmw_byte_mmu040 (uaecptr addr) +{ + return uae_mmu_get_rmw (addr, sz_byte, 0); +} +STATIC_INLINE uae_u32 get_rmw_word_mmu040 (uaecptr addr) { - return uae_mmu060_get_move16 (addr, v); + return uae_mmu_get_rmw (addr, sz_word, 0); } -STATIC_INLINE void put_move16_mmu060 (uaecptr addr, uae_u32 *v) +STATIC_INLINE uae_u32 get_rmw_long_mmu040 (uaecptr addr) { - return uae_mmu060_put_move16 (addr, v); + return uae_mmu_get_rmw (addr, sz_long, 0); } STATIC_INLINE uae_u32 get_ibyte_mmu040 (int o) diff --git a/include/cpummu030.h b/include/cpummu030.h index 565e37da..0ae8b857 100644 --- a/include/cpummu030.h +++ b/include/cpummu030.h @@ -71,89 +71,95 @@ void mmu030_reset(int hardreset); uaecptr mmu030_translate(uaecptr addr, bool super, bool data, bool write); int mmu030_match_ttr(uaecptr addr, uae_u32 fc, bool write); +int mmu030_match_ttr_access(uaecptr addr, uae_u32 fc, bool write); +int mmu030_match_rmw_ttr(uaecptr addr, uae_u32 fc); int mmu030_do_match_ttr(uae_u32 tt, TT_info masks, uaecptr addr, uae_u32 fc, bool write); +int mmu030_do_match_rmw_ttr(uae_u32 tt, TT_info masks, uaecptr addr, uae_u32 fc); -void mmu030_put_long(uaecptr addr, uae_u32 val, uae_u32 fc, int size); -void mmu030_put_word(uaecptr addr, uae_u16 val, uae_u32 fc, int size); -void mmu030_put_byte(uaecptr addr, uae_u8 val, uae_u32 fc, int size); -uae_u32 mmu030_get_long(uaecptr addr, uae_u32 fc, int size); -uae_u16 mmu030_get_word(uaecptr addr, uae_u32 fc, int size); -uae_u8 mmu030_get_byte(uaecptr addr, uae_u32 fc, int size); +void mmu030_put_long(uaecptr addr, uae_u32 val, uae_u32 fc); +void mmu030_put_word(uaecptr addr, uae_u16 val, uae_u32 fc); +void mmu030_put_byte(uaecptr addr, uae_u8 val, uae_u32 fc); +uae_u32 mmu030_get_long(uaecptr addr, uae_u32 fc); +uae_u16 mmu030_get_word(uaecptr addr, uae_u32 fc); +uae_u8 mmu030_get_byte(uaecptr addr, uae_u32 fc); -void mmu030_page_fault(uaecptr addr, bool read, int size); +uae_u32 uae_mmu030_get_rmw(uaecptr addr, int size); +void uae_mmu030_put_rmw(uaecptr addr, uae_u32 val, int size); -extern uae_u16 REGPARAM3 mmu030_get_word_unaligned(uaecptr addr, uae_u32 fc) REGPARAM; -extern uae_u32 REGPARAM3 mmu030_get_long_unaligned(uaecptr addr, uae_u32 fc) REGPARAM; -extern void REGPARAM3 mmu030_put_word_unaligned(uaecptr addr, uae_u16 val, uae_u32 fc) REGPARAM; -extern void REGPARAM3 mmu030_put_long_unaligned(uaecptr addr, uae_u32 val, uae_u32 fc) REGPARAM; +extern uae_u16 REGPARAM3 mmu030_get_word_unaligned(uaecptr addr, uae_u32 fc, int flags) REGPARAM; +extern uae_u32 REGPARAM3 mmu030_get_long_unaligned(uaecptr addr, uae_u32 fc, int flags) REGPARAM; +extern uae_u16 REGPARAM3 mmu030_get_rmw_word_unaligned(uaecptr addr, uae_u32 fc, int flags) REGPARAM; +extern uae_u32 REGPARAM3 mmu030_get_rmw_long_unaligned(uaecptr addr, uae_u32 fc, int flags) REGPARAM; +extern void REGPARAM3 mmu030_put_word_unaligned(uaecptr addr, uae_u16 val, uae_u32 fc, int flags) REGPARAM; +extern void REGPARAM3 mmu030_put_long_unaligned(uaecptr addr, uae_u32 val, uae_u32 fc, int flags) REGPARAM; static ALWAYS_INLINE uae_u32 uae_mmu030_get_ilong(uaecptr addr) { uae_u32 fc = (regs.s ? 4 : 0) | 2; if (unlikely(is_unaligned(addr, 4))) - return mmu030_get_long_unaligned(addr, fc); - return mmu030_get_long(addr, fc, sz_long); + return mmu030_get_long_unaligned(addr, fc, 0); + return mmu030_get_long(addr, fc); } static ALWAYS_INLINE uae_u16 uae_mmu030_get_iword(uaecptr addr) { uae_u32 fc = (regs.s ? 4 : 0) | 2; if (unlikely(is_unaligned(addr, 2))) - return mmu030_get_word_unaligned(addr, fc); - return mmu030_get_word(addr, fc, sz_word); + return mmu030_get_word_unaligned(addr, fc, 0); + return mmu030_get_word(addr, fc); } static ALWAYS_INLINE uae_u16 uae_mmu030_get_ibyte(uaecptr addr) { uae_u32 fc = (regs.s ? 4 : 0) | 2; - return mmu030_get_byte(addr, fc, sz_byte); + return mmu030_get_byte(addr, fc); } static ALWAYS_INLINE uae_u32 uae_mmu030_get_long(uaecptr addr) { uae_u32 fc = (regs.s ? 4 : 0) | 1; if (unlikely(is_unaligned(addr, 4))) - return mmu030_get_long_unaligned(addr, fc); - return mmu030_get_long(addr, fc, sz_long); + return mmu030_get_long_unaligned(addr, fc, 0); + return mmu030_get_long(addr, fc); } static ALWAYS_INLINE uae_u16 uae_mmu030_get_word(uaecptr addr) { uae_u32 fc = (regs.s ? 4 : 0) | 1; if (unlikely(is_unaligned(addr, 2))) - return mmu030_get_word_unaligned(addr, fc); - return mmu030_get_word(addr, fc, sz_word); + return mmu030_get_word_unaligned(addr, fc, 0); + return mmu030_get_word(addr, fc); } static ALWAYS_INLINE uae_u8 uae_mmu030_get_byte(uaecptr addr) { uae_u32 fc = (regs.s ? 4 : 0) | 1; - return mmu030_get_byte(addr, fc, sz_byte); + return mmu030_get_byte(addr, fc); } static ALWAYS_INLINE void uae_mmu030_put_long(uaecptr addr, uae_u32 val) { uae_u32 fc = (regs.s ? 4 : 0) | 1; if (unlikely(is_unaligned(addr, 4))) - mmu030_put_long_unaligned(addr, val, fc); + mmu030_put_long_unaligned(addr, val, fc, 0); else - mmu030_put_long(addr, val, fc, sz_long); + mmu030_put_long(addr, val, fc); } static ALWAYS_INLINE void uae_mmu030_put_word(uaecptr addr, uae_u16 val) { uae_u32 fc = (regs.s ? 4 : 0) | 1; if (unlikely(is_unaligned(addr, 2))) - mmu030_put_word_unaligned(addr, val, fc); + mmu030_put_word_unaligned(addr, val, fc, 0); else - mmu030_put_word(addr, val, fc, sz_word); + mmu030_put_word(addr, val, fc); } static ALWAYS_INLINE void uae_mmu030_put_byte(uaecptr addr, uae_u8 val) { uae_u32 fc = (regs.s ? 4 : 0) | 1; - mmu030_put_byte(addr, val, fc, sz_byte); + mmu030_put_byte(addr, val, fc); } static ALWAYS_INLINE uae_u32 sfc030_get_long(uaecptr addr) @@ -163,8 +169,8 @@ static ALWAYS_INLINE uae_u32 sfc030_get_long(uaecptr addr) write_log(_T("sfc030_get_long: FC = %i\n"),fc); #endif if (unlikely(is_unaligned(addr, 4))) - return mmu030_get_long_unaligned(addr, fc); - return mmu030_get_long(addr, fc, sz_long); + return mmu030_get_long_unaligned(addr, fc, 0); + return mmu030_get_long(addr, fc); } static ALWAYS_INLINE uae_u16 sfc030_get_word(uaecptr addr) @@ -174,8 +180,8 @@ static ALWAYS_INLINE uae_u16 sfc030_get_word(uaecptr addr) write_log(_T("sfc030_get_word: FC = %i\n"),fc); #endif if (unlikely(is_unaligned(addr, 2))) - return mmu030_get_word_unaligned(addr, fc); - return mmu030_get_word(addr, fc, sz_word); + return mmu030_get_word_unaligned(addr, fc, 0); + return mmu030_get_word(addr, fc); } static ALWAYS_INLINE uae_u8 sfc030_get_byte(uaecptr addr) @@ -184,7 +190,7 @@ static ALWAYS_INLINE uae_u8 sfc030_get_byte(uaecptr addr) #if MMUDEBUG > 1 write_log(_T("sfc030_get_byte: FC = %i\n"),fc); #endif - return mmu030_get_byte(addr, fc, sz_byte); + return mmu030_get_byte(addr, fc); } static ALWAYS_INLINE void dfc030_put_long(uaecptr addr, uae_u32 val) @@ -194,9 +200,9 @@ static ALWAYS_INLINE void dfc030_put_long(uaecptr addr, uae_u32 val) write_log(_T("dfc030_put_long: FC = %i\n"),fc); #endif if (unlikely(is_unaligned(addr, 4))) - mmu030_put_long_unaligned(addr, val, fc); + mmu030_put_long_unaligned(addr, val, fc, 0); else - mmu030_put_long(addr, val, fc, sz_long); + mmu030_put_long(addr, val, fc); } static ALWAYS_INLINE void dfc030_put_word(uaecptr addr, uae_u16 val) @@ -206,9 +212,9 @@ static ALWAYS_INLINE void dfc030_put_word(uaecptr addr, uae_u16 val) write_log(_T("dfc030_put_word: FC = %i\n"),fc); #endif if (unlikely(is_unaligned(addr, 2))) - mmu030_put_word_unaligned(addr, val, fc); + mmu030_put_word_unaligned(addr, val, fc, 0); else - mmu030_put_word(addr, val, fc, sz_word); + mmu030_put_word(addr, val, fc); } static ALWAYS_INLINE void dfc030_put_byte(uaecptr addr, uae_u8 val) @@ -217,7 +223,7 @@ static ALWAYS_INLINE void dfc030_put_byte(uaecptr addr, uae_u8 val) #if MMUDEBUG > 1 write_log(_T("dfc030_put_byte: FC = %i\n"),fc); #endif - mmu030_put_byte(addr, val, fc, sz_byte); + mmu030_put_byte(addr, val, fc); } #define ACCESS_CHECK_PUT \ @@ -260,18 +266,37 @@ STATIC_INLINE void put_byte_mmu030_state (uaecptr addr, uae_u32 v) uae_mmu030_put_byte (addr, v); ACCESS_EXIT_PUT } +STATIC_INLINE void put_rmw_byte_mmu030_state (uaecptr addr, uae_u32 v) +{ + ACCESS_CHECK_PUT + uae_mmu030_put_rmw (addr, v, sz_byte); + ACCESS_EXIT_PUT +} STATIC_INLINE void put_word_mmu030_state (uaecptr addr, uae_u32 v) { ACCESS_CHECK_PUT uae_mmu030_put_word (addr, v); ACCESS_EXIT_PUT } +STATIC_INLINE void put_rmw_word_mmu030_state (uaecptr addr, uae_u32 v) +{ + ACCESS_CHECK_PUT + uae_mmu030_put_rmw (addr, v, sz_word); + ACCESS_EXIT_PUT +} STATIC_INLINE void put_long_mmu030_state (uaecptr addr, uae_u32 v) { ACCESS_CHECK_PUT uae_mmu030_put_long (addr, v); ACCESS_EXIT_PUT } +STATIC_INLINE void put_rmw_long_mmu030_state (uaecptr addr, uae_u32 v) +{ + ACCESS_CHECK_PUT + uae_mmu030_put_rmw (addr, v, sz_long); + ACCESS_EXIT_PUT +} + STATIC_INLINE uae_u32 get_byte_mmu030_state (uaecptr addr) { uae_u32 v; @@ -280,6 +305,15 @@ STATIC_INLINE uae_u32 get_byte_mmu030_state (uaecptr addr) ACCESS_EXIT_GET return v; } +STATIC_INLINE uae_u32 get_rmw_byte_mmu030_state (uaecptr addr) +{ + uae_u32 v; + ACCESS_CHECK_GET + v = uae_mmu030_get_rmw (addr, sz_byte); + ACCESS_EXIT_GET + return v; +} + STATIC_INLINE uae_u32 get_word_mmu030_state (uaecptr addr) { uae_u32 v; @@ -288,6 +322,14 @@ STATIC_INLINE uae_u32 get_word_mmu030_state (uaecptr addr) ACCESS_EXIT_GET return v; } +STATIC_INLINE uae_u32 get_rmw_word_mmu030_state (uaecptr addr) +{ + uae_u32 v; + ACCESS_CHECK_GET + v = uae_mmu030_get_rmw (addr, sz_word); + ACCESS_EXIT_GET + return v; +} STATIC_INLINE uae_u32 get_long_mmu030_state (uaecptr addr) { uae_u32 v; @@ -296,6 +338,15 @@ STATIC_INLINE uae_u32 get_long_mmu030_state (uaecptr addr) ACCESS_EXIT_GET return v; } +STATIC_INLINE uae_u32 get_rmw_long_mmu030_state (uaecptr addr) +{ + uae_u32 v; + ACCESS_CHECK_GET + v = uae_mmu030_get_rmw (addr, sz_long); + ACCESS_EXIT_GET + return v; +} + STATIC_INLINE uae_u32 get_ibyte_mmu030_state (int o) { uae_u32 v; diff --git a/include/gui.h b/include/gui.h index be46aa46..fe7a0342 100644 --- a/include/gui.h +++ b/include/gui.h @@ -79,6 +79,7 @@ typedef enum { NUMSG_MODRIP_NOTFOUND, NUMSG_MODRIP_FINISHED, NUMSG_MODRIP_SAVE, NUMSG_KS68EC020, NUMSG_KS68020, NUMSG_KS68030, NUMSG_ROMNEED, NUMSG_EXPROMNEED, NUMSG_NOZLIB, NUMSG_STATEHD, - NUMSG_NOCAPS, NUMSG_OLDCAPS, NUMSG_KICKREP, NUMSG_KICKREPNO + NUMSG_NOCAPS, NUMSG_OLDCAPS, NUMSG_KICKREP, NUMSG_KICKREPNO, + NUMSG_KS68030PLUS } notify_user_msg; diff --git a/include/mmu_common.h b/include/mmu_common.h index 2738f460..a681a131 100644 --- a/include/mmu_common.h +++ b/include/mmu_common.h @@ -49,12 +49,14 @@ typedef int m68k_exception; #define MMU_FSLW_LK 0x02000000 #define MMU_FSLW_R 0x01000000 #define MMU_FSLW_W 0x00800000 -#define MMU_FSLW_SIZE_B 0x00000000 +#define MMU_FSLW_SIZE_L 0x00000000 /* Note: wrong in mc68060 manual! */ +#define MMU_FSLW_SIZE_B 0x00200000 #define MMU_FSLW_SIZE_W 0x00400000 -#define MMU_FSLW_SIZE_L 0x00200000 #define MMU_FSLW_SIZE_D 0x00600000 #define MMU_FSLW_TT 0x00180000 -#define MMU_FSLW_TM 0x00070000 +#define MMU_FSLW_TT_N 0x00000000 /* Normal access */ +#define MMU_FSLW_TT_16 0x00080000 /* MOVE16 */ +#define MMU_FSLW_TM 0x00070000 /* = function code */ #define MMU_FSLW_IO 0x00008000 #define MMU_FSLW_PBE 0x00004000 #define MMU_FSLW_SBE 0x00002000 @@ -73,6 +75,8 @@ typedef int m68k_exception; /* 68040 */ #define MMU_SSW_TM 0x0007 #define MMU_SSW_TT 0x0018 +#define MMU_SSW_TT1 0x0010 +#define MMU_SSW_TT0 0x0008 #define MMU_SSW_SIZE 0x0060 #define MMU_SSW_SIZE_B 0x0020 #define MMU_SSW_SIZE_W 0x0040 diff --git a/include/options.h b/include/options.h index c69892c0..0680b784 100644 --- a/include/options.h +++ b/include/options.h @@ -108,7 +108,11 @@ struct wh { }; #define MOUNT_CONFIG_SIZE 30 +#define UAEDEV_DIR 0 +#define UAEDEV_HDF 1 +#define UAEDEV_CD 2 struct uaedev_config_info { + int type; TCHAR devname[MAX_DPATH]; TCHAR volname[MAX_DPATH]; TCHAR rootdir[MAX_DPATH]; @@ -138,6 +142,8 @@ struct uaedev_config_info { int unit; int interleave; int sectorsperblock; + int forceload; + int cd_emu_unit; }; @@ -145,7 +151,6 @@ struct uaedev_config_data { struct uaedev_config_info ci; int configoffset; - bool ishdf; }; enum { CP_GENERIC = 1, CP_CDTV, CP_CD32, CP_A500, CP_A500P, CP_A600, CP_A1000, @@ -571,7 +576,7 @@ extern void cfgfile_target_write_str (struct zfile *f, const TCHAR *option, cons extern void cfgfile_target_dwrite_str (struct zfile *f, const TCHAR *option, const TCHAR *value); extern void cfgfile_backup (const TCHAR *path); -extern struct uaedev_config_data *add_filesys_config (struct uae_prefs *p, int index, struct uaedev_config_info*, bool hdf); +extern struct uaedev_config_data *add_filesys_config (struct uae_prefs *p, int index, struct uaedev_config_info*); extern bool get_hd_geometry (struct uaedev_config_info *); extern void uci_set_defaults (struct uaedev_config_info *uci, bool rdb); diff --git a/include/scsi.h b/include/scsi.h index 9e80cf02..b6152c04 100644 --- a/include/scsi.h +++ b/include/scsi.h @@ -18,10 +18,12 @@ struct scsi_data uae_u8 buffer[SCSI_DATA_BUFFER_SIZE]; struct hd_hardfiledata *hfd; int nativescsiunit; + int cd_emu_unit; }; -extern struct scsi_data *scsi_alloc(int,struct hd_hardfiledata*); -extern struct scsi_data *scsi_alloc_native(int,int); +extern struct scsi_data *scsi_alloc_hd(int, struct hd_hardfiledata*); +extern struct scsi_data *scsi_alloc_cd(int, int); +extern struct scsi_data *scsi_alloc_native(int, int); extern void scsi_free(struct scsi_data*); extern void scsi_reset(void); @@ -30,6 +32,6 @@ extern int scsi_send_data(struct scsi_data*, uae_u8); extern int scsi_receive_data(struct scsi_data*, uae_u8*); extern void scsi_emulate_cmd(struct scsi_data *sd); -extern int scsi_emulate(struct hardfiledata *hfd, struct hd_hardfiledata *hdhfd, uae_u8 *cmdbuf, int scsi_cmd_len, +extern int scsi_hd_emulate(struct hardfiledata *hfd, struct hd_hardfiledata *hdhfd, uae_u8 *cmdbuf, int scsi_cmd_len, uae_u8 *scsi_data, int *data_len, uae_u8 *r, int *reply_len, uae_u8 *s, int *sense_len); extern void scsi_emulate_analyze (struct scsi_data*); \ No newline at end of file diff --git a/memory.cpp b/memory.cpp index 6afcd6df..8c2d6e6d 100644 --- a/memory.cpp +++ b/memory.cpp @@ -2490,7 +2490,10 @@ void memory_reset (void) struct romdata *rd = getromdatabydata (kickmemory, kickmem_size); if (rd) { write_log (_T("Known ROM '%s' loaded\n"), rd->name); - if ((rd->cpu & 3) == 3 && changed_prefs.cpu_model != 68030) { + if ((rd->cpu & 8) && changed_prefs.cpu_model < 68030) { + notify_user (NUMSG_KS68030PLUS); + uae_restart (-1, NULL); + } else if ((rd->cpu & 3) == 3 && changed_prefs.cpu_model != 68030) { notify_user (NUMSG_KS68030); uae_restart (-1, NULL); } else if ((rd->cpu & 3) == 1 && changed_prefs.cpu_model < 68020) { diff --git a/newcpu.cpp b/newcpu.cpp index aadbdc17..376ba923 100644 --- a/newcpu.cpp +++ b/newcpu.cpp @@ -4267,7 +4267,7 @@ retry: for (;;) { pc = regs.instruction_pc = m68k_getpc (); mmu060_state = 0; - opcode = x_prefetch (0); + mmu060_opcode = opcode = x_prefetch (0); mmu060_state = 1; count_instr (opcode); do_cycles (cpu_cycles); @@ -4317,6 +4317,14 @@ retry: TRY (prb) { for (;;) { pc = regs.instruction_pc = m68k_getpc (); + +#if 0 + if (pc == 0x000fa01c) { + write_log (_T("*")); + //activate_debugger (); + } +#endif + opcode = x_prefetch (0); count_instr (opcode); do_cycles (cpu_cycles); @@ -4375,8 +4383,8 @@ insretry: pc = regs.instruction_pc = m68k_getpc (); mmu030_state[0] = mmu030_state[1] = mmu030_state[2] = 0; -#if 1 - if (pc == 0x1000) { +#if 0 + if (pc == 0x00109FFC) { write_log (_T("*")); //activate_debugger (); } @@ -5052,13 +5060,16 @@ void m68k_disasm_2 (TCHAR *buf, int bufsize, uaecptr pc, uaecptr *nextpc, int cn } pc += 2; } else if (lookup->mnemo == i_MVMEL) { + uae_u16 mask = get_word_debug (pc); + pc += 2; pc = ShowEA (0, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, deaddr, safemode); _tcscat (instrname, _T(",")); - movemout (instrname, get_word_debug (pc), dp->dmode); + movemout (instrname, mask, dp->dmode); pc += 2; } else if (lookup->mnemo == i_MVMLE) { - movemout (instrname, get_word_debug (pc), dp->dmode); + uae_u16 mask = get_word_debug (pc); pc += 2; + movemout (instrname, mask, dp->dmode); _tcscat (instrname, _T(",")); pc = ShowEA (0, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, deaddr, safemode); } else { @@ -5085,12 +5096,13 @@ void m68k_disasm_2 (TCHAR *buf, int bufsize, uaecptr pc, uaecptr *nextpc, int cn buf = buf_out (buf, &bufsize, _T(" ]")); if (ccpt != 0) { + uaecptr addr2 = deaddr2 ? deaddr2 : seaddr2; if (deaddr) *deaddr = pc; if (cctrue (dp->cc)) - buf = buf_out (buf, &bufsize, _T(" == $%08x (T)"), seaddr2); + buf = buf_out (buf, &bufsize, _T(" == $%08x (T)"), addr2); else - buf = buf_out (buf, &bufsize, _T(" == $%08x (F)"), seaddr2); + buf = buf_out (buf, &bufsize, _T(" == $%08x (F)"), addr2); } else if ((opcode & 0xff00) == 0x6100) { /* BSR */ if (deaddr) *deaddr = pc; diff --git a/od-win32/gencomp_msvc/gencomp_msvc.vcxproj b/od-win32/gencomp_msvc/gencomp_msvc.vcxproj index 263c93e7..b55431e0 100644 --- a/od-win32/gencomp_msvc/gencomp_msvc.vcxproj +++ b/od-win32/gencomp_msvc/gencomp_msvc.vcxproj @@ -45,7 +45,7 @@ Application false false - Unicode + MultiByte v110 diff --git a/od-win32/mman.cpp b/od-win32/mman.cpp index 7c63a1c1..526c9acd 100644 --- a/od-win32/mman.cpp +++ b/od-win32/mman.cpp @@ -180,8 +180,8 @@ bool preinit_shm (void) } natmem_size = (max_allowed_mman + 1) * 1024 * 1024; - if (natmem_size < 256 * 1024 * 1024) - natmem_size = 256 * 1024 * 1024; + if (natmem_size < 17 * 1024 * 1024) + natmem_size = 17 * 1024 * 1024; write_log (_T("Total physical RAM %lluM, all RAM %lluM. Attempting to reserve: %uM.\n"), totalphys64 >> 20, total64 >> 20, natmem_size >> 20); natmem_offset = 0; @@ -203,12 +203,21 @@ bool preinit_shm (void) break; natmem_size -= 128 * 1024 * 1024; if (!natmem_size) { - write_log (_T("Can't allocate 256M of virtual address space!?\n")); - return false; + write_log (_T("Can't allocate 257M of virtual address space!?\n")); + natmem_size = 17 * 1024 * 1024; + natmem_offset = (uae_u8*)VirtualAlloc (NULL, natmem_size, MEM_RESERVE | (VAMODE == 1 ? MEM_WRITE_WATCH : 0), PAGE_READWRITE); + if (!natmem_size) { + write_log (_T("Can't allocate 17M of virtual address space!? Something is seriously wrong\n")); + return false; + } + break; } } } - max_z3fastmem = natmem_size; + if (natmem_size <= 257 * 1024 * 1024) + max_z3fastmem = 0; + else + max_z3fastmem = natmem_size; write_log (_T("Reserved: 0x%p-0x%p (%08x %dM)\n"), natmem_offset, (uae_u8*)natmem_offset + natmem_size, natmem_size, natmem_size >> 20); @@ -737,6 +746,8 @@ void *shmat (int shmid, void *shmaddr, int shmflg) size += BARRIER; } else if(!_tcscmp (shmids[shmid].name, _T("ramsey_low"))) { shmaddr=natmem_offset + a3000lmem_start; + if (!a3000hmem_start) + size += BARRIER; got = TRUE; } else if(!_tcscmp (shmids[shmid].name, _T("ramsey_high"))) { shmaddr=natmem_offset + a3000hmem_start; diff --git a/od-win32/resources/RCa02620 b/od-win32/resources/RCa02620 new file mode 100644 index 0000000000000000000000000000000000000000..9bdff419dceee9a4027625663fb81d74c748a213 GIT binary patch literal 212576 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 hHs)#6Hv)2(KXh-K8c<_HL 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 literal 0 HcmV?d00001 diff --git a/od-win32/resources/RCb02620 b/od-win32/resources/RCb02620 new file mode 100644 index 0000000000000000000000000000000000000000..9bdff419dceee9a4027625663fb81d74c748a213 GIT binary patch literal 212576 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 hHs)#6Hv)2(KXh-K8c<_HL zetf^umWw47Z-iHj)~i2B;Y3YaPh%cy>4zdw;PTpw-tI9I)8+gGnb!Nwm*d;D9y1Xq z`KaXP;2GA|qQ+;1;=R@;`XzbL+;~acA8koImKr6jg+70fY-i-1EIQZWj4DqwVx2SF zS7eJy+7a9Mc3t0z2Mx~=r)0iuR>;16uvByFOh>8{YHH4(Gouvladzp)g_jOW#{P}X z&)iVUN6+~@M!&%?`8RIH^l#LJ5@X^yem)J%=pCOw(s_nn)$M~(uVXaK$P3mh! z%Z{GDEbw$!0cahs1MRkKjko@H53xG$HsOVhc{A{KJJu<_L5fowt6ONPNyk(9ykyT1 zWm3)W$MIeJx0B*^((}XTmsexe$}DmuhTqBLW#9P2;?E`j!cTC9k>0Ls;XZRSXXAuJ z=OP)u0=jy_-6n{-BKgq+^50~Sa6V4fFX08+cd7 z=7c~J3VBpVIM7zpD%mAiT#Zq$7e32a579(+#)|2yk^4p(=sZStl+G^T)HODU;SRny z+37P&eithNi&NUc28TNj;+-D?!pbgaX(j2%w6dL^n2pQ{OSv8ovMcCOlsch}y;>T8 zu_pl?&clQ?$%2pK`!%qE3l;rRw`2CWYQKT_YXowYgF_LkS3Fv z*eu$IXf8C1UEJb8U3ao~5D_g)vj4=->S+v5D9H;fAYx#D58o3l1sN3`>F2`4ebzcZT$vasiTdl1^%gHNQJ)3uWPw z%ODzL>>SCvnaW_OSNRc-iaul>=P3wk@Q3*UU5!3b=K6oYF^Km|E1pBz=*Qqb z_i=?kks6{L%MasE)FoS)XN<-EEW_t0rjlO+UTlrD9^taJ-g0P;fJY=Ls*}#zWu4GJkKKS(DyWG3pOl}}^M=Yupp(#Z=K4{bf!~YoTg!=Y(%P?S zpq@YJ0g8d^@WKycp6&Bt4JsNytdv}&pEuqp)nVLu(nl-gWwNqMC0lzc-*Ropcy+Wx zr>DP}Ph{;UoiKn4761^!wvczz(sR2m`@Fe|v~>HbXGheWe>95fICj+f{~&UR8SuTbZU)gYPOA~EVTEjlhW_0El;l{=S|M4*O&Y)8Iri-DQFdX zkk{4jd^^S*L#**d9M0d@^3~r#Wsxthb;>^DhK(WLBi*%y`#=tn#}OFr4m z!t*y}HYAU9DDuzJH22+7c~864k>ow#;NPb+@Yti^M)|79Ye_)#bnY1WtKW&T_M>5E zOZK8M)Z+kn%+0AB1;zQqtInNFi(PsP{|N~T?<;mEO`j{nVxVHgwm?50{N@D?I||V(;W0i53tW1NTz5`%*b`L34yW?QSWb$V z%l!T{qWSxE9GGespF-4>g{*oQI3-sW-%ghEbj9-++U_rq^-sS1qoO%9J7+ufDGQKj zzkvhSoV)mB5NHE)WV)&EZP1%6KWQvtQNpD-le}r9zu(k(kFm-Mkkg7%!|%vks?q9v|Mf$D~?VbnABkkL^_8bGVxNbY6v}ka62v0rh+FP5Ssq z=)0S5?cYK#{Fm8q`;I^!8PZ{&_qXrb=Z)>I^Q0aB;XEq<1{#f-&O6%r{hY?S{ehRg zPrkoeI0n9v&a3~b8Rn(kojrSXEmgy*woa>UtKrlSgYF#nW3BD(=SC!dWPU_t#E(Z* zg|W}Q=es}kRpITr`cW8@ycD z`H)ol(lZ=Aa}czO1xk4d^2q)EH9mHH?Gdm_v{XA*-W*RiIM>Ch-~RK!E*T>yWt@}PtM21K zmtgUsH;8-V9q{kc$Y2CK)4Qr2oi*mzU<&IOjoFT$=SoZSxZ^=TM>`8UkzZNwU~UHY zXIHK*J(l4is=A!TirvW!Phwm=c)BAmN?~A)=lL5kdbyKvt!7KJ(uAOBwX!rAS_me&4ZoevWOrh2% zar)b?>L}Mf%bY9t0R^NZ(;LMck}m&epom2s1J*3Nakigns}`2{kNK{pd= zd#y4%&W^0B(JtE_O&;YiFhDMVA`k0uK%O>suHi*DXJQCH#lL-TUf=0~{?=_X)TSfB z{I*wO>p-!*zLV(uIIjZ2^({R~X&y-)THe%S>^UmAG&cLB!dG{=jK^lzL?p59cNx2` z+pv596GSS!_U#x!;-Ks4^!j`QRo#+bZ8LxY`*You>fmS7Ba5T$qJ<@?SS^K88Yn`61d*QTq_qf*#J^HXE zsMkX7K=b5b^%`N#NbL+7@98GTO;O~2)Wid^Mfn0P=4)K}8P;jO(y7H-ns`|x*Iza> z;h)hF4hm!Lyxm>>i-_+*XFe5fskR-9PK;E`1I?5n;6+{Gi8y{=Ns>7k%Y8%p$ynxp zR;1CF8%g#kv*3kZ& z*aY6-lDa8Ddmc_VgB$nJMJ?XYWJEZqQ-&6DWmsm!Zi%HU*1Lp%^+wWa+uH%C+jv)Y z#YjYf+i~2(lh_EWQAG(AhQu@TqpoK7Te`K2esgWtOspm1&3I=-6f+KR927yPFZpfw zAMA)cgMXGMeX8s(bJv1$ZkG8KUsis{+wv?Hnlq=A%YZCnMC1milt|`&E#tRz+9o>0 z%g>WKsLA_S3$I%-Z+@Vv)L`B_`_Y~cgJS70;T{y|5>NIlu6!3ZIq|OQ)?k{yMhA}=*b4Q ziUR@HZKDBhubJKer1=O2R9T5X%oW9xMyMCUX zAiITiJwbon`dfC)2@g>b&Z<F;~p03{&+rRY!iopFXZ|fVfb1E-T8@<7B$) z+hyD<@jlt>zb!fuzW~U*2@jV$fxikHUGX5eK>g;3|8WZvb?N_&kIsTeZ>oDToeQyl zp2QpMgf0n0gz@rpqo=>|bM7sMrB4)!2!-D1PkDq-vL@*~f?bC;keq%M@dTr^NE6|RS@_&T^uT613h5W`~xwB|1J zeH}Bd8UGKP{a~N$GCp*H2kM*0(d3tdw*DHLRNTf6`ELD6w9|P>yN3FDI`-|?^P@^y zpfPuq9~YC#Egh$jkWV6xmc9RLYS*mZppAHaqWtLRwZStt4@hM5(N++dzO*azX--C}npWj3OzDDT-thWGa=` zFLWNOF3)wpz*(RF`04I@-Z226vJ6Hd5V6CDGmlmP93ID*@8Y|_W2^Q3 zPd9#ycbn_|{Nx?J^+SRY-`?KX+c?-b-1vH9cjNZPgN>t&{f+DK?ryxkzj1ftyLjgy z{=c_zE#A2suf1zlbpC$4I{)6>xEcFxLi~q-*{hA8WB+dhR>0vfcKvPqe>--)6Z-@n z*W>-m@vnJ4<0tYcApEzJ@8#1tf6(%Zge&g?PQs2w@uH@W&pr@b*^HBZ8I%c+ISzbi zpZIZ$nl5czJ$dhWob~3$Kb=5MTwG3)E}?$g-IrJ#e)2By4ewqnJhf9d7&P5Jl{ZuAQhgc)orG~%XJDbIVG^{IKHWU z9LL|Rgi%=!-(l1j@q;Y+_2ko^0QVC6+8*ddL9`0v}fj>fcw|I^JNIkpZRgX{9VtiH)Fr| zg8%Hrx$g(u4|-NGf7ZVQjD_W#^iMtWqu-3p&ED5H|NR8_)nnYW{ohM?wi6sXpK#Z! z`TGao(OJ9k-1DMK4gIWs-Up$DBXPeDxcASz$h@x-bsYt+zwX_&XGfQQ7?QVp1#?PDMu_g7;q1_J(BYA~R!;K1~`%_uTsd z@dv#)w#=@v)L!-C81m#^%)B2qYM!P2YW|)dho*-G-o4*H1w}FDoseSE)4SCo$h~Nbu*$ z8o^uAy@DxnoR!R9!Rx@bewa&Bz7gZz4EyeO&>CKX_RsuzrORV0JWKwW7rlEQnzvlZ z{53Fp|Gaoez`6T@aY&ZcNwlZoEdl4SYGnP?r(Rl|pNDpDTkd>WnqG}p=ihi!9>>qq z1Y3Fjz%hT$?{bG@&=UHq1Vje8IIEF;#25*8n5QU$!j4_{&wnGhJ@C)#UTl6W;ylLq{Qb`_ zHa~jPjcOlWL-+ofzrA<9Gk5;2fcjR?-Wjhs|E*Z*-Ck%dGXLJJ`GtSr`(EHh`^@I= ze|y&a+sSwEAh7Ow=XuSYpIF9j(h^FY=gW;N={@UE=kIr^t+Bez*v0eES@APJd%yOk zF8Afem+3RG3Qg-y;8)XX#7_dxcn}-`9eXqMKh9bIU)lI7;t~8!tVK~LIKX!&w#ROq z%u)LGP8#KU%yTJvgxectwny*qUgygviCP78G@=b={k3 zwQvez)xfd-*@{tfEMzl1eJ{=WG^}0XE<^#3(~RZ<><#^zQ~NkcM^?g_puv50g0=8_ z?XsTHf3ZJB1M;l4wf?P|4|v~uKN>SK&!w2><2e5>((g+r_5tu!T)un1-h;-p)yQbb zTSj9iU!+}K4Xf(o*yFW;5%vJz*^A%yT!Ksf)EoiN!_EnY7)SgJUNEeI5_7)kcwYDd zjG0+n4h+F#NeuH^;y%1zTbo<3*6*i1UQK%?hRN)`1P$%Wt+V`Wg=fh*>NG$9&7G#8T%RTTy%IbS%*7ATJWX>P zz6(Y+e?*6`-=E;q@G`v%4qirG|HsDvitp_N_j#J$C(27Sbu)00813JKQ_4U0N!avc zk;q&2eB=9M-*3h{jEjZ;Yx<@}s&N&++1|-IK28#1E5^r{PK5Yn!fVl@sSQY6k9FON z=fvHh$u9c&IAjji&(-ul_%0rDIn6T;ql57L5tDuz6oe0yd?vUtv1H;M_K`?XfD9bIZrFwC`x#$#Dv zkw}td-oniMg~Tw;vGb|&2fYMYV4i56#i)$;TarQEYt6SZ*J972#(7fcM#3CrC*ko! zZ|T+`+9#Re{aKp3d5+c?eezANY5AH*ZlC1miP!ifg)i3wpU5#G_V^$$tJD@2mGOBE z{ScNcoGa(|gG!XEc^Fg!4@U~1-IGy}p9!!p3PPdD$22}yY z(h@mayXfpJoE)jGF%?bydcr?`11a%&PV3`zURX8Q8Ip6-KJ2HN!|xUM_c@}4Gs;fP zm@$KS(BvdfK2P)8c4%53%4f7K;it%+f~v4l@d)0G*Na*kVD+&5b|dMDFOnR0B)r!9 zYhIxJ@&D%uzSFqCREeq+M8}1I-V?+6(Poi$2rpnU0z@9!&yPShFQzZv}n%f(H4wxEKBquE`nOzc_ zTx0wSNU1rp>APYEB#`zaTTR&5KgS$Axsf;-x~gKI>zEI@;A~M>?EBb@6jiAUiLU{0pZQ-_psqtR^C1Ggm%|c*mx>F3ljD?k zKUt;9f*qnd@I;o@XUX1LEvJ^wE4fRa5EPc(?jFV1Xt?*|RsQ7T1^rsJ zE?;WNwdY-!EP9m%C%VO+vjs4`YpiH|pVS^u!9qm}`*%GIo>V+jcol&GRhuOguNc)>{Xl_RkCm645>EFQ5gGsx^0y8>`+=W zr&{iC3U|k2UGH#6SFqf=`W~V{7S227-^lk+Ve>dzIo9OA2%%wHxw;j46>mkUaq7RN^K z-~oKA#)_$Sy|KE4lJNpaff1jb@I`hx=9M3(wGxtfcD%eq@$vPoR+3!~+mDs|+@*0# zijCWzJHZpIg($rc3*867NK3p$fTP_jjkVZb|QY-Q(wbH&(39Aol|5NPrUJXp_{u+x7;F zJOlo2xTWIbygTib@269y4!efZ-IxX29u1H9{@n?50R7gM{5LJ0Tqdgs@Gf`b9 z-1D@xIAvl2NqOv$+ScNx%4_W;YrTA*bMg7uyE`VUMfsBE=u2hK9_V{KgAQB$IR3p2 zUZcJga8K-Va-y1+xz;VKPMN@#mR>oWRQ8uTzx2g@$zo)Mj{DX%oYSMe%*Hs#@c+z) z{VyRe%F3@SBb+Z*=XH<^SLUe=`{>I!6}XT8*70JrapfhC+l4z}4N_HG>-)79#@NaX z%J$4O3wzi%)o&w`&u}{^MwoaOoaKw;MNrI4yPnHeya(j$y?`M3Y2qu=VI})BS1C2o zFY4_s~4o&*OZ7=Q8IyE`zsEynSVL z@@9B%blPMUf^DUC==BQb_6BMu{JA${)vNRZDpszXVB!Pv=t>_P$6I&;vH2mqg}ce` z03|8TES+kK*KbjtgswyO{n_Czu!Lrp;Y!|xv6rkkiQtoy9-5q%QYDlrK%H^=o1yku}s8@zZS#9@sr+* zlO;nPOP)xkDr?Yb;DXlEh9sZQn;dG+`83X(na=XOWo<2c-s}R|DGgkF)qgE^-hCbu z>W@l~iTgKB_mz+faK3yc%V)?-$Dix<5bR1mRC^zwV*X{{Ips|^WJxs?2T;6Bwu`bTr+Uk^1N?gJ9f)TS*DQ|jpy>gZy@AAO*E$bd zQd%osllEH0Z`QF^WezR2R&d5R`^|3qS*tQ^7F#QPy}efO ze?4pcGU=Xk*NWuG_6m6rbhVTmZ^x!N?{t*e_GFy&Bld12q@+e+&;Cl~jiD%dibxxBi!a1hD%g z#YR7gSGf*S{i2B%_)IuM;Cm2HJqwG6j85eakooW;)xLwrsz<|L2yW*a7mniu=_ra_ z!kroU6Kgl+Z0-t($MNZgdSr@Pj2Uo#MnVI|_J0=tEAv+4)n_U=)Hy3)?-+;Zrl#&J zQ^{STWUk1cLnHShy6$rl?jtX}L%jo62E+AiH@wJJz!qp$)zjy(>}fBE)KJpMrvW9& z(HfgGC+dq}8BgJul#a_vSA;%l<2cACpM6e^} z6A?u|4u0@BxWd!mE_C};?spqPnafY(u#3T|sT~<2$}^h}k^aac^~_ZND7*W#^A*?^ zo-lb}*_UkAsnHSB*Hl^ptF9(~r|vB1@nin;sXjlgCFf1xy@9jJhXZev_re*DtBr3O z61PKMfyE`});xtQMfizUr%F}X-s2WyIjUwPN5UDEr;R;uMDXQm+K+P3*&SSB3UBVB zcs%pcC6?vc@GJ1meA|72$9-mA!HuLrqDzn=3WqA;U!N+JB->g3u`dED;2T&shT+m5 zkzw6(%>6aAF1q!SWkZK?B=1h@vkR}bKNhT4M(=RfF7nsdMXBA->A^LRGP~9-ZsY12 zRWG=9LLRMgJR(#4>%o)Z9CRM?*{ByrEhVQ(HgqL&NlQG?{HO1)5$#zcnkt)-pZqjy zAb{ceJT^!cavxIGPJEj+iN8yb=lEy4Zb~1+v~QzYDzD;UFI(3yG`v6eu^&zjH}_Azu(Qd zw>lzzwy@{q(@Bn)i&>(<$KYF2BpUj(ZZgFF_sLI=?uS3U4`kp z#i*xMC(T7Yh26dl*>}*5BJZ-D!K-PKb|*<%>}I0nSkHK$@C7zG2DeQdZu~KJf&X(| zyMX4PemttvcVW&~_eCssAZ}nDw@U9)J^^#Cd22`4p{p5{+0CxE)Uc@28XY-S3v)0= z8NR|CG?qCqr|UAFKdLuNNK=asrXMDqd<@bQ>ekIfOJK!8$bQAGxEZa#cdavWkfGve z!Z)e$$QyYae9c`b@KMqQwd5D9hyFXc4!{nX|-8E8;r*<2;WlC(+!RiZ<9iEmhv zN)!jSfE&Pc95=7Ozoow1$@S1{)ESY{z<248SR&RSr_Rvshb}lVlJeKl7+7<;@~OTl zjg|FX>A1+HD*+Ygbl4Xq$hvoIXn)$bzfX~oU9XgoHPRXv$R4l1ob)zvIN{!EIGn~6 zhts%=YjI$0$>A6Y{p~~1*d|IpJcE)0X)A4@}nE*<`%AyOF(W%6}u`&yJ=> z_}Y8P7(@hKkaJIRWx>n4qJBJca?i$b;C}esehJ@W6Bo#I=VqGQDF?Id-|Rkh&Rg*c z(W^R5VK@2Ar}!~T2xV{P=SgD(KJbJul3uZ1OMjfhlsCDNt*(gUlkf;Deo8z^a8wRN z`8@gR-2CP^W&Reran8nuf~Y##Ot-U<35&mMGeveZw(?{-qdLP{x3Z|OCnqC)5eTA_ zS-wxzPs_EFX{UUC?x@pkrDOY41;l3ZqT@S%5#ReL;;uL1-yh=j(7t$*S^|2;5zoCG z-?%Hh^s6&9riIieJXFFT)SrJ^`X;+KmZoZRhkP~lF! zdVRjA@2@NNgM~syzTz~tR5>xNxi8+^tUY#SiTvH%8ay@TOE&|}b$(*L=!NBK1UcS4 z{rii@;~4Zhg%j4iI_DS6(QU{6&NuO0^a$cuP|e+leo~V{uJK-s02h)6 z)1MIG5Q|U0EVt(-_CnmcHdMeybVqcN5cM_yJvXR;hN4?9gF6l?N0DqcxJv|gp++2 zCyq71rx)ViH}S9D@7VeB&Glfhy!`WbDqTKf?AJm2NX3I#>9u6(ly_{G`$JCFPp@`o z_H39!osT{}AC`{cFE{=rof)<#R9fDE2p59e=4w#7oKsKH+$X%3%BP*AGh9oW$hvkb zx}3jT_r@$Sb&MyuqPqzdvi8N*fCi9*LtE=8N5z8X<{j=5=Z2-9lSPXat2$g&-sd%4 zDEK7jq5gQB6ZZVi@%OX%CO!_WV4P<a)Ac36YO}y{&U3mhm2U>Mr&GPE$^27Ppze$9zqXRpDW%PYfN^&wX{o=VccbZJ zX}i{u*JD+p<9aF;MPL*7E6P_^E4TwcsI^xlA9sw^w=b(&no3M!`@WA+R>9Of8I7F1 zzU&r%0;3XPk`Ml@{l49?daXU@U1BNPTPB#)5&O9?ahyq5AnzA(CakZs%a9z$E!z#` z5o7mXh<{k&(!I*8^jcEO+Le}t_gYq`EF>Z#rPbN(VZt9FieyINEPBrUcs<>>1PL$bTFmt?)PI?n443fbC@YnB+7cLirF?+v!MMKNx7 z@&E@N*BXvx)_Qp}&)u+ulaJYktEw$h#{tvS#lUa&IJL5i#CE8?OzKx0;qqy@@$QyZlcC8CgQtk!kc> zk8FA^edA!oY1bTMOM~I&NA6iNFCQV}Lb)HsaMX`PT*5b)^T|3g5lTKyH_ad7M?KaK z0;j1uuTP;qUuE6sPAkqAZz|EVY3JKtRi2^49co0*h#tU&xd-L!&K&M0{b$)zFZbmR z@?ezcMcpjcfA)AC;aza5rU%>fM#KoaYCpx4hze0(#a-Fpr1TCn?yP%IbqWSdvB!Q4 z-v2Uwl$n5!1D{DV67IF239^#wG5=vi^ssZWOIb&T7E+aeWA@p6y{nv`nZb9W5|ZPh z*kPN%8e16&n^PGz4%34;Q7HInz!Hh{H2EIL$Z)J;3UtxM_TIy|tZ5W<+4=fmtW^MG z_jeGiRi$&*kiH#wD_GMfP<|if?e#&ppO!KIQVy5O4SQ z>Vvd9Ze_TNOURBD7Oe-Wo;mXe`3j`{3`208T;Z4C0I!215bMr7_jrKU68q0Qz*P!@ zpy+Wu5Ijg(2YZRbNZQ)l0@oRa;A!($q!pC^>i`_H%)&Q|)RN@NT-7kej#?K?o2w|f z>Un}Mb`^eo^)kT=DjwTU=~R1bcZRI}mdfI^S9Cp)`7~s?I!^_hgOVLz@Naj$ z%T`SH&aAh>)yODCm(E)3BKF>VjW(~0HQ1`rTGT^qz5I5W6*nUx&^J~nXT@*0>3UZ` zPIwAK#zcw$gkCRu!Fl`kS3WcDJ6w;CE$`EQ5-@bSznAM9ay%@mdRJB{Rl)Wexk13uEoFK4mtjy9x9tmiqOnk3$bRR zD?W|!we&@0@tUBYIVww+H!`!`H*0!q7X9EKCpJ4AWh}7?ZRUP>tI3V0dm;A(Abpg# zf0*R+)5v+O^Y#zp9o;2?T*HDTpOefb` zr*~quoK;eKA^NiP`};gWPiP24x3J;CTR4dG_bv66FXp-ob`9R5Q}4#Ut|v?#r>grw zRYakh)?rE2(yu>DE76U(Q!K%$TE3sl6;tx`pwTPt<@^)$(XooD@g48RZjZx@gI<|) zriS*<36Qq|XH#ClI`&U)iF(%Ej&pa6fEgt}oRhO!XpkNFGIFMe_OJ6-PqXRgKh-iR zX z6Pg%ViZ4#!+Q;35vB zm9dAV3ICH6m|jUlhGyNB7)c3t^9;aO_`28(cRR3xn?i0UxojOxaunVPH=kP%br5*h ztcA*RSpQ>OoylUecqjBVAh-JGsMcv#?3DYFL|S%FXw%#a*qkZv3XMbY>BaVi#3dIO zKXi$0NW8lV8_@EoChLb@pH7b5SnA~1w6=2L_ERfy!SnN0iMxFFWo4Gny$={mi+DHn z6v`{GhoL;Znm@_9%33O3IL$t;kx|!(7SbK3D7m1bS^V!RN-$`K0D)jnF+upL3CgonY~{OwZu&Cxf;p>`NCGyGdanj0&%{i{{44_?lG_s$JB zY4N88w{f{gEJwFP=*CoG9=9#Lifqsl!|6Yu4%;)Qy8xDf@(47dqlo%kn6622qK+u*knjw2us=uKl~=$a))+SI~`7VNzmw(33!m8Ani+W9&B#$SaiK1t$rGm(3{+A=FK3dV%D#Ms@B8V z(Pg+R1#M&hJ#RLLLh^$nr^DIDEy-pES_#9FKh?XUvPqJQ?7rn#Uuzt&D%)kR>~Kd`IxqgQ%j~Nxy46<~-;w!xMV$ zwDyFY631I?S7p>#6|(B=MMW~Vk0Hg&+7e4@@=A^OpM)O3`*bd#@)&u6v{yy4`WkEo z_u5JCz6nWA#;-?G-L$IUyP%2%q}p+GS?FXjZX)VITd8x8*nPVybDMGhYY7r zgBG5K9Zs!CZAohPHC><1F7b;<`)k(0XrJSn#Dk=n)ml7xZk!wb7bG#6CHyu8X058^ z)3sJGl^c34avKf{*(hBpcRp~{vi#5M;*8_)x*fRR4J|`T{%ZLu;OEGrbwCUsR zyy9Y<6LV&Wv7&WBY+S~wZx9+Nb<*-Q;u(RzJWDoy%{NM&RIv|5bsd3{53ucX9AAlX zZFoNJPGw%tr@EN-d{Z$To7`Qy@_8A*Rguo=fj{^y&;!FT)y+dMDcKT6H>6j}q1 zke~Pp@E~l)-Ab>*YlPK`3`O7I^Zpv9CMSTenJUR%o$ldNhSFwyqvqYU7ZE;*-oPyD z@K>p)-^I{1{}}W^Up^ullHb@Z`1DST3ry)LY)>{T-VV=+S1af!L;5s0*4(uky7Upl z%P+o)o-J==*1FfrZO6TVhk^glgS*@0zio?xvvkl^{~+jvzQ6Ly(PiXWP+3_W*7Toc zoP!(7ql{J}T9O?r3q;vP<7?iJHQ>ji8byA?@|}9!?R8>xAo=^(iM624cbu1He|x3l zx>{aK^B$g>?%BUQhl&BMR$ns}UFx8F#SzXc-LvA7?&TdkpLAbBw@vr3rOaG}`hiWG`UMkvTvtu~tiDbC*8jVypLKRo|6(gW2p@>J)a!sD8OnGy#Id?R zgVS19x!mATxvI?)TfLiomzCf*Uf)|*>EK<yF(es|nF+3Bc?p^aV zM?hTV?7Gw`mgqQV{Wkg{ZpZw@aqx<$&)Kg5XZ4!LJJilE5%hl-(x-pj=ylS)^2(*h z=atWo9niW zE}FRkdF@S331wxyziVH^x(NH~;zr#&n~PyrIx);0&rZhd60F1%8lZq;&Y zF}&*D*DzkOuWr2R+SxoyyNv(Nv#sH~qE~fgB(gRw%6czxeOdZ+%A{xJlR-OMGvV`s z(>uo&F2AanJibw|n}`k-R>Z03J%i5xIpVpgGn94ccylurKkhNR7c%f!@@U{Ov)7}P zaF!}Z@i~4m^Z8OD} z)C*oQ>1_9xolk4cFXLAUgVo|y&XScjX8xOF-^2U75!j;+$?TQ;V(|Q_Gwqq?7*$@#L^d!E{@TXeaST~_4$e;2#Gp1w^+4LP}Vl;l04c2u;J-=tof;x##!Gk+p0neWl_ME3=Ce-mkCIixP2 zd6X+L4=Z>OoUF_?1SjFZu5A*8$?&F|>9w@l@_BZU>rRwUQNFVoxS%+z>hIpB3@G(+ zw#~K^Jc&F`t@f3PFkFg(>FyL&Cnz6h+AY|1JJzoI0x& zFXN50Uosk{N6IYCcNp$WPZe1{aW| z;U*X12j4pkoB?~NG^1;$E6H>w{*;?Gum+J-U`qWd-?^Epz0`-`W{hU+^9agEWfY#_ z{w?b3zD;YXN4uBkn7P1Nu$hdE5=Z)~+4?hEr`NgR`$t|~bV1ooD_IYPG-uDXOQw(#o0}L5 z7aYc8)vmEKxY1txaEB4x5KhTnjgeEZb{rR>Br@Q{$;g6I&X%+rv}xod{$x&fx!vP+ zIPbgXgq+{%iX~z8xRu$*w@Zx35Nz@{bT~!LHG~d>3Mm3jkO!*18BHy&v^&mTU-OCM2@-c)?!;9aix=cP%FjO8 zAevtdPqbt_ILU1~?=&wNxC(lgR&Xb9|L_Cgb*A|~Ue{;wHmjXEZ|In)AzB9>f(OYT z@NK+<{p0`1+JnY66Sw*8S-kRs);^(Zu}tyb7?wFhcq?E_?4KSfc!=FG0c!_N>RW;F z&e;PjOV^)p9%J|XvP#`md_T$T`mSG(gjOM6x+Hu3lz2;@cShb?{2S^hMCKV1R=y9# zVw8hxALCx~q<};95578JLHz~3WIT5CNhi;AGe*z-bcTC1s&+`6kIojd4TP2WoVxph z+D`$-<5|eLB6KLrw_$Qq94kDIpP_71_q4#zB&$Su4^!e`oZ6JENUt?#M6qA#IqX}n z*IjKk2GR2FMdieR@4u!P!4wEOBgdH|tAwDe9o5wr5-US@&Qehwl?_SxG{@((wlrl{>sonsdNT-;i_K2#kf=5}kv{~+ z5c974PkG0R7}C*#oYA~*S3K7Ho+|G>I{L|<6K;4%?hdYT*NYkBnlBXtz75#f0GkP? zYijm>&~fvg7OSn5mfF-tO*_~^(g?FY28S5uK?iZdzBBkn$W$^LhV)N3gyQ+chm>ta zW(>1ALfglch&Ap+)YiGaSZa#g>*fh`YH4@&>VVBdoit=I=n0^VW)Cgiecvag_9)iy zn;&P$E7wipbOXUV$n9lRf;sC$^_K4VGOq)ncs;xySgRw1qV`kxhr0Yxo91(E^DST` ze-E)$NgQ!v!(YE06P(Ju-05w6cK1gsXU8(MWd#=0vyWr)oxt2*BXa@>ya^t*8G8gH z$FW&Cd>NZ_1Zs+vawkq-Gf8?PE!7c?J*;1nP35k6op1_wCRAqG`CO-E88AF;hz{q2 zkp+1zFwA*`Wvn;D`6%Qg&#jt6=(KVUGF z@>#&%={_(xlk`uh_k3q_Id=p}gH+m&edg@1vj0QQZPgQ!x69>$l_SZkOl*N1U7ggo ziTfIYno&Kev2j=(Ic2)z2N@@M)yylqb;9p_YrAs5>bhjUX+3c>q&2?G)#Q$i^JQt3 z)J}EX^jOl(r4J~xRh^8^fKp6Im75=NTRF{@>*FXKJuFk>*w^a6}%70J$Qj+ZL=zB{6yv9A(NiY z<;{99`E$@F$uK7)-X0IuqR!&ln|#owim6<8dSO%~xb9{swbS_&b1ksvRqRlGk)Ch-J@#Ag z5-$n0KHT>s|8A3Rq)iC77NeZ@9GP~e?{+Gk3Rm-O46-BSyF~xi-)O1gL#4erKs*twXLb1*>#ou-->3%u>=U#NeWsiB*(ZA_91e8n> zoP%R0f{|~+>z8KZT|0p4(CHIhVP;7e*KzUQSe@&-Sj4$rJ9?F0sMHix;Pe)&k&v5 zF>vWr<2%?52)+y&{yBKXB(&-DBNp+$%y2r&r8t z@JlxhI9_F$GBuvD6A*bB@E~8yBa<6_Ke!s!Klr46rQI`3rw;jHb*qo?Gp~MGNiu%= zH!=J+=YmR~V7CACgQK69-fmS8kjJ7b;#{rS_JcQ{ z-3qG;Yvf(v9sIQIKL+OO@n(eX4kvleAMIHPjz3ftPHb}XAoKELVv z9oH=Jf}jO2HvaXvkxOSf2BGWG%~{?dxVvoWt~t9kD*U+0JFrFt{E_?8WxF&g?38w8 z4c?Of)TPFI%amHy6kTdNyh8Ye+`CjdK7E_II$WMFU8_%e%U|IhPRQC-`6;?&`)5gh zcup)makg>UKF)ooMe;2_WZ~=oshqXVx3G%g(#r9(M|tXa!#k-)=5^TcWObKvp!WW9R^XI@Yu_* zgx9`?5#92;zU`-UvlVww{vlvQ1aCKX_)WYrG|`gOUpd1wf4Uv#Z1_8JK&NSZ_?zi} zvQWrwCoNi8elij$gGwFuDVkvW>pS$%Ll7fBveM40=(g%A()}!GA;0`gq zRi2+t#5nw~0UJ&L9jV0j?cjAfff~m(`g-9hf9PCza@yOm7eNt?;;$73ch%(4T^kJ^ zOvIWXSD=O#T*(|kI4O7$jjSOCj+5b4!%M4LAG~V%FKx)_^j~Ajd=5~5-`LvZ0r!)I zM8uC6{;zRT#3N+M!C`($=kQ~^G6rY9RKrxC%kOkQcqsCB-4Hg7&x0^IK-32tD+L2G zK%qH2hsYv2ykjq#g8vw=fDKvEqF%*JUnkoV$f<6E8-G+q08T=|w<4k~{`f<}Wxi*s&fCA7l@5b+slB{D~sGzwHsHJPBKU9+3P?&Z` zbLvpg0?>5LG3K4lPGI5Vfa%ldv`<%Os>+e?qJYB$CuMy}?gKCGx2)C> zM%+$Zq1|US=ivj%Z{oXe5*1Lxrt!J|QKzSwzz@!t+@6{Ob|S*1+8#2o$-_RJNCDl@ zt#4i{^^G}N2wE-rhda5-V>#~2+6hQM54c*sU4MVWa-Ew|YlQM_sCL;mGfuIx;EPit z`0CxMJ_YPbHi*0q_RKP$Gj)dZd6YNb-$k?5EU+>%$F2ZH;7xjnb-U@gN+@+g>prmrFHtp2dPyP&DVCaHG z$8mPCTA#ABqs31gkrh6V!|P!{iTw~|mshYGT00GXLIw=qgTtfsDo@E{ybgZxDl~Pt z`OgWOaB|KAzKkCHFX=UUay`2^{$}D>C7d~pH}NCg2bqi$;hBJkLy0ihrz+r)n{ENNfcK85PK)4VgAj4&C56y=Zvv) zZsrxl)JMbmCR)v_lr|2 zpkAHYQf09%LmkUX1-#di&UoGNJJ-;6K59E#MHwrS1Mm`C>P`KlX&Vm73#w+Yj}zENZFtajIl@GAq>hN*d0q+50vlUGxD%Blz9!ai)RxS;4 z1lYkDU5kojoJLRRzpC{U)pV_*d8WRySPbR6@Ui;zn?4j) zNT25nK~8=(5Uh0_Ywu`Sj^7H)rpntUtZYX$-#082qx&_a5?0TPke=50_v3G1BYnrZ zxAc}8Ufo!UrXvcfC0!euV8wc%N#3e(&baqw&?{6s6`t7xC0vs)^!EX!Hs0jS-t3<% z<&$rsq<5Gx7zo++QFidNz+*H_NjcqagRI0Z;JsIcrU5n|#Hk@``3C-((r?^`HTv$l zVQt*4Vci!=AbXY-bw?7o@n~wVKV+J??{7VpFwQfPCT;utAh4vaGyXWiogN>u@fi7D z@_1lVs87ObGd<*`KxSGeGBu7%>T8L|%g(|w;BPDgX`f#QWl)Dm_RWiUbr=)_C83F1 zZ!M{#wxQt*swTE9$Ik_3jzinByc}Kd#Nwx8X?-|zMrP-lRv?EK$d^gPJqg_&jZrv7TXOTsBA0F0q(c@b>~V9U_Q65 zvW)Lcg+G?W>i6NCXrDkCL7SQxU1AAQy+5Xy6COENe9I1UCLbpRH$9Y`02fw@Xn)D82p)3ODEl-v4j%g}_--Qo~dGg$PR-Vru{y*Jk zoih%2)xB~u64tkJ=eS->y20)OT~-xNSEX*|sG zf|E0HF)~8hr$MzX-MRM@?a=v#C)a~fSwEb&Cpu=ndCJ?na)tb1WW7^S zhi8zkqU+h0V_@}Jjn^k`$Jrt~E=6wf7g%pIXOBu!6<7Z9a+N z)%(2GvwGQ5^J%`!nakA)SVZ^YWW*7=&Vrfv(UYs*6TL^}`_h>x5;~n5BOlP>k${)O zN6_AZpU~&)TEFGsBI{S?9TkwAMfVPPX1yoi7~I?)WS-RIPs1{Ciq<_(CK{J473c1x zPbzK)ADku?d2(GCX1O?bSNMTG33<((toSXF2g*8ZV;WVodU9R6uoRrTi=6K`#Cy5n zoNSx*tUgPQxvPf=HEj9 zBhKa=?CzaqsX`vr-iqla?=8!gd3z({?q2-E4sY+E{B&KXl4Z%fmE-mGkF#xV#EB@A zuusw-gqMNaqKWEqM+G|ix^|Z3$-JGFdSuQ3CKL6`(8TDza^d9F)lR8N#QmUQvis-~->!72 z^P2=o(K2d>ZgsJ1d5!4{y?>2^>gd4E(^Qs0cEot{xc{EG-EeBFKT)uUfowN%FV z3{)$jPgMt;d;XL?{B`#d9ssIpa+N2#$L)Aew;|;3g3pU|zZW;FZ^;~&b`L_9zYUF^ z|Ec}KWB)q1^_%!xl|jSu4l1;)!@^LVv|-ej=gVo4h0{JQZmR++)K%vS3`uhSM>5%; zHyleBtQBns1IO;(q1J+q-Rk68-gZE}&Fy$!cQovVjJlq%WRd#$p&he3(>;3ynzGxb zMdi1XUPj(sd1hTR(%nWi$cx$l$`CgMfX|(PieRqKZUoej=q1iKb6}jkgPrQmC;4_8 z;BC5TW{Pjk*kd!3c`N8!w+|u9fR!QWo~QP0u&gL}7cXZ?Li>+i}>geZ$|mv6)ybl00{{UG=`P zK9P)h=V)ELH;t1wPy9IOoO)gI)p=!!J;vwZWZWzbH=-MD){543*6u1j_pFC@OsCIg zZdup)*1M(q5S>sL<$B#1?UT*>oOiNXcYTUf6j0|R`hGG&vQhPWJ2YMOt)7yBgRejm zzuk3YI)4X;rHjcjoc-e%k=$NRk^2dituE_fTwgehm5@2!4HGIv zr}Ne>s2#;lPK&#`1G)QgTW6liUBM4$u^>VFFcMGCUdRb#Bc5ftf|H@7s{6B{KP-3Q zM}0H;bo!&`0W?8H5BE?`p~U(IO04c_a?JHsxwio`wxzYHANbPa<0!#>T2|z$IGd?n zH>1>4KTco!0mmAy+T{>$Z|_>WmmNATw^p@tWSY3R_KuH8*2q*_49k;>Z+g5aD_kc| zrdTrrw!LfjT7}l8o%x)&jkpDS6d!lQP&hnCnv_&C1&4i$EL>d>=C`~THH61IWq@C% zSYuY!{Xa@^13pJbJdWsC?O`{}UC9dv!Mh+%!m6a5+7d2r0>MJSlW6O{OD{4vn9^Ot zYss_@9%F&924(ECHhdqdo*u54F8vF0x@;^4b2?l65I=Cu;}Iz9?pni{+{CklGWybu z_yO+O@_`yy*J(h!&xeag4-B#o7yAsloa4L2v;EtvhV1g`B8#TbS%zYH7mfPAV~NTZ zS7(N4ooxKnDsQHmzZ-m6_CMP#AmBo?It**1H>I zYixG=$drQyCdz?AI9bC)*&ygiAF`r3t1*c^A3wOQ4bLTc56nvLf@WVGZm9bfz2_E#M8=TQfy8zN30Itr zQOP(*qrb@Ol$S^B4L?V1UuUXC7VV|{wai6T&FtQo*{3p37JFo(cUS;$LGx+hzh^lA zlItMl_S3)Q(#C(?_;0a~Zf))$X;gc~Ua74`UVE>CW}jsVqT&=-XRnrUEO=ju_CC({+r=`#eV86ygcRU zS??p-sJu(z2$qTW=Zg2UKD%*?<5tiHkw$e>r*azL1*_D@%t!#6EVTg5*mZnnx-i>ozfeI0xj?n)FIKTc_(Td&~vT)bTkS5gk{S#c%j82fhl z#ZMZ)cX`riQD_+JKI@V%ji0nC|IXs1xe@g&X_}~*ydKM+HG8SgTKNLYo%Oh-xgB&& zo}g}K*$;^=nx-F1d3%BA98I9KJk8CNpU7PyNWMk1C2FPdwCSc5>{#TyZan$wJ(SO0 zzaHjLcLI`61D@P9q+A^|BYd2AG^rjptow8->N|*(Sxd@#c?}`pyjTvow6HoxCOs=- z%TVmgXQP{8oIeUJ`0S^{D8s`Z@n&63sikGul(=%1P{76&rYeiJgu-0!OL@MM<%~@C zV6gtY&_KqKzTD${yU?)b%K4?@A^2F?=Q2Fx+wgD@w}5k4-*Rn$a-2S#C~ArKbd}hT zU13RK_pm!KL|I(tV^700Kh2)}HuiZo@?1JJ(p=iBA-lNl#$F`PeP*if_sEOs>EqN# zUc7yU@(N~o4UMrsSCZo2G0&dMt;niZz6Lk&fs=R@9UsGIY%k-G%&vVL)X-ox*YB>< zLpJVdy&GB&5$ZnqT<)7{Wn#zdTU|)eh0zE zoMFn$_>$2Zltl_R0-L&L?~WB=B}h+j79yuhO$Odla{aHz>5~(MFY-akhvjBAaLE6I zy~;@unUN%E%BWw}e1gWJnj)GKSMuBadH zs_GRI3Blvb8tZ7WGoY1?ExAdo?8}5rWiH$l-q*%cMV3xY zH{kKOxAs<|ajatc@sgjwjYULghwx672+9!@w+8c87ja-!Hv&r7+*t>MOQSI=>s*$a z>$rxa4(rFzdZo?trzZ!|PGso83XqwKttofLH-?Hr`e|3Nf`ZBKBd7UAA-A{V{PFps zLzB^rjO-eJYJ5acCg%no@0?U|JJ;ndro}F{0_K9&$#7tY%dP>L4dwqXph(^%GL8y7 zX%yB^$jpVC(4ly$UV;{^D%#cQCH)#jN%y6kKOdcM9tJg#ch5b}_!uRrO%2o_)^ZvN zEmY`r#RQL|36DTXVyYEEvs*OrW3G1h4izZdXzwkEL0)0sO- zJZZqZdCtI9vWWdC*BAO8%i$cCAA7M5ud97mUWt26sG~ukd9PZkuvhCtbMKMq@7d75 z6W2PZBgHy)ljA+od!@zO9@-0;+N9c1@fx zZ*UI==h$jBr^6lk)>U%|t?+btL@j-q+T2#~)~U4Ewa0bQqq+jGmm=HySua&;Ps`IK zcgoOVo-RjZPu0G#nv0|9K>wyp_;>c;uTo;8htUm^-+vAcO&3DTZ9J@^O7kNB9+oDn zk=*?&E2-0K{7|X0dzb5>PjXaiiD{l|jVfEZO^$>8I89=9?P#48>Rn|DEq668Ih7NR zmEwEbb9Z>O>*M%&74r0Xa9ro*)Oo=*?E+ymFK9RW?eSbsgL18XptU^+EgjD-bZ_kw z-QypzLR1{cl_QQmqPGLxaT~vRo}4>*1f|7KE@cz$bO^d1Co2C!j;g|$iQG?{oyY*$ z3MEF(Spgjon9mUc$9BtA^hF4afwsU^A$=$IKh^$TN0$Pug2UtqSq*_7f5{`o z8OrRv?ufvu?fWi2{5YPX#Z#F;CtJM5+=)ybEZx<+Jjx~4@X4B2AmM>CzF2e$JU;ki zOiQ*Ar{OSOMCu6@y;yE#oH?gJe2O*c&XT^hP0fGu{;elLF^nz=;VA9Z9-Lc9!5lK9Kz*}vSf(n@1WL`-Y(*>qyXWh8?s|;Y-J_dtt|#JIueM2PXtgEh%osw(6B&b<+75#klZDvq z#KB!b)L$r{0xAW^3^{+Q{*1sI{38xtciZrD%Cu7b22Uz;Q>Wl((5Aa$aXQVZy*9`r zk=`KxEO`geScwyTy~D?~>TamgADv7x;*MxkbV*V~jr;}tPsk>;8e|hIl4iAv`H%w2 zD`wZs!Cca5K8`aRj$@8XY~-S93Cq2MMbdHVt{vJPmWt}`r|Xb^3Lk84QNtqmR<2r5b~NNPv94T2iF>|!y$P3Vasm_q;yMvuLlTJ`4F4=1NTGvga()p))Z)NbYQ?pU zB^MgErm|(d8TRN{m!48vmUW5uR^3A2%Xm5Xtn|k+Y8N#Ij(f_??E?4dSlIW_g zD(ix~>zd?0gL{9RC?TJ%_}O^%7P;zAVihk2v(oWQLTzMgV~;E1H0B?j%1yg>sESi$ zA<{)2QpsfT*g5rDiiyp+pJiOe6EQ_gbeHk_i7$#%Y{v6_*le~iD;0o2zT z9YNV3zN?7-&iGO2R}PPT61(3Fs9%hzz~AE)8Vy~5@vXp<9^cK7t<0|8sBk7~ z572vblk#T#Ev*EpO$_~`@b+q4zTI3$-%K^nHxyQXqa%@fow~9HgoENC<9OZgpROxj zYCfr`8yfHF;khtPl>Bw*l=#F{oou|f4@6YAUc4XYUO<-n>!j6wAJ|2m7_~&3kyC_p z+z%X7SAB2@-E1et+{hky6}5}P&{A4;a{%!VuvHvR-3QI%(B#k}$8q6W%&40Rpo;Fb zm#e99LLM;rY2CvNtz!!J8_vfq=Ny9oIB_lhk)NQ-r?crV35A$8RKbjm$T^J1jOZcp zOJKudry#$})g*;%_f9#7ag3&ipYWrfYmA>2UFpCa;i`HuWL#BTZ5&toS1`u6;0{=F zKAkDBSx!QG$0Y|d&tf%jZ+I6gIcu)f*12|*WF!l+w#BeOAI#KS z({ChwJ!gR;nU#gf=ocFrnBMzSvRl_>PjNEnb?9`~v4p8pY^iL4kM~CKZvJ!vu{^JN z>QoAOn9dE&`IC@B^c&!{{QxDMYR-otb(~4c?Bo!UsRZ{zZ?>gAm0qUv^gKWFE*2+N z&A$63i~p0KR_689)wNi9`F;ZVc682DPxgA;J0ggd(7e9!$C#ITY2P%*)V7s!M zWh!S8jLP%TidBP#uZF+RbFS|&RbT3$>PrDXtnT$vwQuE+4n6hk58~wQSDQxF>;`-g zH^iT)I#l7tdZ_wRWI48}S{8!4dSuLBt)>RXYW}mLYIk@j>1~`}k5M(}SW>ml(XmdQ z;~c8KRB>HS15H}C+*wogIJBSxDmTzo?cuBQoJ!uWonDXjo*yjLMK0KyWi@-Hf+78WY#;>g_1Md3W7X z-7Bu-O3fOgoFzV`$D%5X=;B44EmGxK%4jC8%f8oBXEd%6j8`4FFT3zCTSu&V9>?g$ z5u{>$dxPxXXJG{kT4Yc|EA*Km+xs9@XHpq+tCEIgjS<$BWER?=tMDBUw`JDManRpe z8-J^0diT1Kjp|V)okMj?RANj2L3#}7=p}B|wl0wEqdR(E@VV&@dLgHYJC)sqXKIZF z67IA+sXF-i*2jE{JgNfgNi~n6=us&*>nuXFnQ-2e`4jQ&_S~*Nt(|gKCX?s-I z%_7xI&XGmy1Cyoap<6TJD)#D7LGEfveFCohE+=Yp8(P4fx$mzuCpj zgoR)tc#hrlMWU&@@f7lg`{?mb(z%*Q=EIQIx8pRRN^x;NscPGjeqC|vC$NFw*2oUD zCG->O@TeHNbpp+~yU$_B5+90?I4B9hSf1_qqdA z90pz~Nco;!gYVD+XI`q@n4!uHaAzQ5OAK=xvpN>4-^S!lb=IEAMzcpt04(H1B zdjT=J(*hgaORxGxjct2YvgNF(V*Z{i-Cg66sX;!d4{Im#u%o5qxGuCU|M-Wpmm9%- ztp#%)?27CsC{cKJwwl*6ycfPJ9n!r=#C;V7?6a9m?rENIet7R(s^feQP9&>OSFu)i z9)o9nDAbm8x%>(%;7-tkZ<@0{mEo-J z7H7e+YHv^76A21Xgsr@Zs7uF&8s|dAd{3C?Q>tx=e=La9`f-5WRgD+BxPv z^}J8|q~DDnqRH#pvpUPn-*dAI1ohOMHYce4Zzjy<+;tzZbFF3gWj4e?kwsW8`` zc{=w(!fc10S$7C?cS`97cxlnq&fXRnx31CTySmN{^(1iRpyJhKw#^j1dk&=|;ehx3 z@P?~@3VdH365#^yFZVhh=3?Ug=5edEx5i;i7x5qBNBW&*!w`hQoX-*ltsll@4s&}M zxt%)!X}Bu$OCHUE@%nAsaaePU9R1zs8sHvE*qKAnCSy#JaQ)D(^;Ps4Ro)A`@eP6B zUeEc0{w3x`HF9&eOv!gHdkMl z&YhV1ZL%M@(~s3`Ca+DGu4(^A*LUs(-?^N=i)Bhjip|hFZpZ&pfxiT$Z}SlO4U_NibA9)|WzpH9iG)!PW_ z2#RH7kC?P+br2fQO?wyc#Cwl7?|Iar-~;$;yaJa>YXJJehl+(yof=y5YApfp#8Sq+ z%CwVl8X#XE+yi5NYOGz6eETDv!6qyRcGAkHV)^$0t0(b~J5Ya0vwj+wj*rA$JdNA! zCpQyq=5^9>vi@clUe_nV?WcDF{)$qs-s;SqdWBPp+_|6aRGfkbQQ1^tl;J{mP%u1fJobU-+psI& zza9`)e?mcvC+Bj^c`(bfuM=jzvKXy(o2$bV^Th8dgE4&vtJ2l!y zuLxC_{??;y<2X^Cd=sM9^*R0Xa@#d=Aa->(ESh`4QOVjP9;}*3;zD;%ZiG~x5}o&{ zpKd-S`l5c07*zgtcRWCvYK|)s6LjAIu~&v2)z-9$fA<4N5sc1|Gniy~I0V?3N%^Kg~N{Po@s7()5KOZp4a zXEWf&t>bl#I4h>VfU6>h`2l<%Pb~d;SIrI1S5F4Tqh1ELS1if*XLNT6J3fU9aVU;_ zZSq}j#d%RbM2z%)oCZHz@w$2MB$+fwQ}zAJ{TZ57Gj(SR@TPwr&xS?zGT9+73T+;Z z4Jk+uYvdGE*_@s6$2}HJE}OA5cUOQDFavx1EM}9pf_H~JZlZQQaS(9Ci-rs>xj;LA zU^Oddg{&CZd?;(i`eMz>GgvpA`?MLjmn=YQGnTdET8yl3tQj&xUyE=kP6Q1f?Na#z$d^@S0HQg&H(7gz z@iph$bxMw8+$-fJcNCCWK#mR>DSf+|r(t%_n_MZ6G^G<3CjuWTvv|h27J3NYUcQI__lKm9WNrnI>+1?K zPXF8CAtT#HYs)>f%?*6RH@#{cb*nRtSEEZwV^BBvdi;Kj=R5G}3+ML7tMl*8utYgU z$3px*ziV3tZBMeC`b&BQV39hHSk@zwjuFdGZ1FH65*l7rKY^Zr){2%!jTld=&)05H z2zuc7^B(y|jK3W`k;*Wl40ld&@k(In-8Ay=62{_%C8JL{dg@wKt}u@yT)y=F+emC; zIYUohES~0`6=i9k2YFqE;*o}FJsW3NLWQw~% z(L5!O>(}wk>nCG&k5Xf*zkNAu!7t+9#q>{Ws=v<;)nN=D`-|Y>k5VSFM;M#`Zmj-U z`X;$muOptuKAwg4rhGW|@c+h6)Rpy%=o*ifMHGb_aX5cP+~u1lYy5sdh}hh_lx;_J z37?RnOz>8`2Oq_AWO;BK4!BeA>A}ewI7{~A)8sF*q`UU^X}tfp^jUj6^+bFUDfPWg63BE5nNvPqZ>{=wI5>Eu=9S4}SH zZnhs1kKc)Ng~t;M;p|yEzngpGzBt~uNAO;77;;pk_cZ;6`ETm~kOg=u^Sk}8wqn$y zbh5;?(f*F({int!td*4>#{aUbkV!Yw`R9A3*mb?ezB>%8ZPxqi zT6OL&&+Uk^nsfevaya_dUamUdGNC+e*4j0n?u}5sPT#nF@0aswoS|8KgG1kFeeXAC zk?z^Im&+%)M}4F9z2BTg)%tz2Tv^d9szJ?MxW*>g2KDI!;W=0&S|W_b}B(IUGmXL4VQzIoL1 zpG>-1S3`b}{gm_QMgaB6%rMNanv%yIz8M1!t?pFzCn=id6PV7g(PF67@}13He7lM7 z(mT7J^!!Z@kDjV*8lq9tPe|}`A1?N~7t)BzeY{QN67-#twPW+=>2q4Hbj`|a`GoST z8bZiG*AjL?sealk?RxSb3!XJx@aRBgbY~-9sIF(93axS!?m@;(yuZ#VCEyItzNu5Uh1xDgZw`Zwr0)h!FtcZr>JBX-RZWXg}%lRPgdG`7?+j)7_5# z$J_BVKEb2-4mO8I_Zg4<-U^Q|9%5>%kAhBpgBcanpN6;VQG6m^*tHg7+Un5IT*tC2 zc^p)UkNKwwtDqtXZNI&SWmiF66dY_E5(hDV?xnEWGr%$O?WJe8-fKf zf_fcIsw7L>*nK|zpkAGSZw9}nvYxIIuivkoPn&zAx{UXshHC-gZ{yUd0aVUxKeef@ z*O}G*&-oMX5<(_X4M9xilb}T~Q8hoGhJ>OHDpMwFxf|=)4K6^xlW&7J64UAWnZK47 zu?|kXoV5)ZX|d*HBw$m8T<89%{|gLQCLBg)Nxs|vC|=V?qCQP`fhDhanNjO=G*&y)W4W>Glk}L1 zGcGeSH|dlRmYrj6zud_6*>SJR?Z7?Fu*}HhQI~73M<(-V@l&LibvZWl4MkEj;BMl} zj9#B3a9(~?IxCcG7Iw(nvkWY(sha;AqjToxGNXUdI7L(8a_2|YV+o7pWePXi)u*Pu zo2t}U92c%OPK{Bi9b1lLHuaXSo3ocWwWjvcya9T-vFo!_<{( zm3hmI+|+TfPQaPv#;i{gt8_7rT3RpU4JkX-HZ4{fF}rc=tG45)R8)kA5zgli9G;c= zHyP|XA9gcN=uwJPD7V*7w)ZK#DY<~E*YHy_c5N*p2g#RiTbhP>{d+5T3w`LaAA)aN zBGu2f|JsTtiM`Oh@i_S!Ux!bF{L`-SY?-#l@jD*xb@&eHm06FHWjh#7u66eh`F`$G zlYOI(U>HKBrPw`=-X*V?9GY<=SlWz{wJ z+13ubN2S7)6}|3QS8eZgf1h`^k{+tgwe;&C zx%>}9-_vKn8y%SV5c5AW9jALUJzIFO}I{|I~ntY3DXR;gPL zjp2-WWvgp1CBMdh@7i+iWXv_;)<>prBA+=uREB=G0;0q*=p^&c@$Y*0Pgxlm>bY}P z38}1&4vh<>_Tt>ghi~Fzm*&|u*4Bn{!iZ+%2upqXO)ax)9Nq)!_G)tliJR;5Z4S2W9Zz#2a>*}Qq=@)hSo7H)m2*~eEt+kQ>e43eP>FVMh{ z)$MPU@4#JlBihg<*TGTw*Z2*@%5Ub^Tk#3;M0(|KZu~y9ZgAGM9wiOpDUnx=+P?mu zt;FjdC%lJ$*3iMGyd7&B9)oW|#m{3c?J=kZqb`84`q#l2)E`lyz*xDx*9~KdsW$ z7Ce|stM>yoV|(KCwqid>pRPTDx6{Uu{OB43_@Y5W&7#+CoPi$8x_i2&{m{?{hqy?2 zD~~nLq4~;HEXS{qBM;(-o#uYB<@kCnZRAzZ1lkB1PTiG#+1-|qzLhv*dkwtP1!?XC z5Upc3%FgKG)m=~?nyXwvZ7mGV(v%40&QwoWj+8+&LM#CEBC>iU_!3wlr@C5Lu1 zG*dY{vvxtMImghh>{%+u_3P%Y;IO*GhP(+^%CZ+~_cmiKl9WRj+I4z#8Y6e#^Y;CQ z%cBWXF;Hq1MBdfUY_u9IpZz3ug8fglpzK0ho=ac+v}wXX&VC%W65}!t5PqKK+l+mq3qk>O1k?I>4}AyUVKA z%yBJNj0TGa@7}bW9GQ4z(lQJ4lV8i+%ylP?@+@8fmGl{4)3e~R;Y02;<56?|I!;gPI8JaF9~YR( zj{a+m0}NlKU2yVm!)Lk|e+dtC>( ze(pKX-YWKSA$;a}OxC}hd!A2*=Gi1;DA zN@F;C2S&&fpvt;yxbn^0@f5gqkoLzNck=vjw)!6DgstQ!>b;a0a~MwwM{1hhjy1Cn zPJKVYvTF|-9}daP`)MqAK)Hw9=a;_d7vi7#qi@GkKn!?UpM&;zH74KRJOJ5fUW&h2&$o9=oGc1=;*i z5E8QHhs@4C;1^Kn^+bJ~aGTQc0_wIRVWH|ppwxaI&|K|qjG((0!4JF;4`c4{W1R=F zP9Q64y&12-H8?vRDuj=3V`to#SK<<9Lsct2WqF>YZSifa9_0@67nsDjZ{E|qcs?+= zkHeuOkm9}j^jP}H*<8$4`!$}v_4G$9#j6{4TIudZ2Tq0 z=Y;e(u>o*{J(0Pq3VTk7RX>Yo!8hqrJhKyjy-J^oBY~}OcJ@b(yJS&Y)*b|8(XiMv zvcwvMzR}BKtrdSPFDwZdY7_#B!^cleDt@Z z{p@Z0e=&~cJly!d@fv^6g(Rb1Z2U*uwa4!Elb&S%=1|h4g!xo5uzx6p9n!Cx zPl!Qs>j2PJhYwD}FzAv=;;oGJB%Qrw6JAbEo%;rPkJChMv-&5o55~2W?87?in9pNH zKgDTo26v%DBXS*_V*S8oH@(N_NTu>l45$HK0=MfrK|%TZpb$7HIuGN6gWw}$|Br-| z;1*cXZR6=v$yabj_>LY9g?$ohhoAovpTM!0=Xm3PHMjt_raIwZZ;%T^2Ac9hYc60K zK?`L?#z0w^f+}>O^@>ilLZD+=iq{tW63*}}J7k^g{IBsDkb^60U*NW$LH42H=nQm5 zHAhnh4>f1JqF3HQjMM%hVs6byTnFgYI`46;i8_Sim{Du9C0EbcKTADt(|^-H{*Y%t zUB>7yT#wFQP0s*(!j|IX;SZ7xU;=U$>i9Kqgui9!0CU9__@3lgmz_GahH_QS9WT3< z8tys9ijNe3z{B8iyCGev{mbhpW2{(mNF}(ReGT^L=JANGP>n!$G5hcP#zPvTAwai% z>+?Oj_#ME@JQlBzuF^PxIoMc_Mjy6;G!o^gU=uc2%ZzxUh0fUdB_}b)V;lT0CW+&h`8_!dyeW{a21K z*YMx(*nRukKx98;>BH9PDL$bb^{(&fJ<+bcNNo;4R1Dt`{Xn~U8sjhu*p1}_hBwC% zf22AayvEPb{bT+Jj{u{Z@$KAkjX2q#TR81e@gbq}!C#;~-2z<0mx?_yK>VG)-&-N4 z(88MZq3WHT*b`ab+*O@Bu9suUYgX!Hl0fxp(24QC?Z#|)1)a?!T^S0#nN~`~rH#?j zZ{edzEhrHRK{MrK<>Tp>h5wZF%X&Sm!o6841o)4v7XT?j3QpHGt}Mc+tta6GZxpd{_=LaYXD2smMfa0Ata z__S>qan}QUrt}&&F@O`;ir52Kz&-QwhSevI20P?;w@gN(v^FI@PMEG!k|)23O+flt zjDGiIFOt;lz0~?0ySO)^-^rS>we>p2m*?#4^}K%Rz#UPG#IkP%S0&qWh>Pi@@eN>8 z)bt7W@ojERnk@eo_wnsp^%&$2!)tUybos6NHQZEj2W45+qm;Rj>a@o>tHU1r+8lN( zaOR^EionAIO^deNH`E$YJt7%1`>}!t@%{Q|E7p$GAs@0{(|0jzzqHDuY=tJ;mR9h- z{7nBVMWE`L1xsqLS+TS@WH7M=c$Pruzxku`{?aql}t(Jl516#iPLy#>XJr zS6Wr~Xo|eRqp;k@$LrS6$~D0werfE8=p5Ndhau6(H@_CIsgL~bJ!~`rrz=_3#oP7G zgW$n!&&h?vH=YHDMJ|>8zmFmxiArYv|3l0t8_W73lBteK@GP8dJKo2}{U!ck+jDBU z6SZwg*7-Q*d>5lj0`X3ka9aV1+DDJHP$cH(SPP$bMfO+$9J`F})i$G~tb7e<#g>=I z0c;LzSJtfEFy8(6%e!epskuAs1G@2IFKe~<*zs5K4puq7Eu=2H$lepd1Iq$SP~TUE zca9s&6Jssh9H5R!LQOYf-1bim&l|B0X4Cp>I%RegETrX~CU4i%5%{0jLfjXwuh%BV4Z54i}KG6I?|DIyjN;_>ZQgD=E2H6Z*vWAI4Af4921V zb;2ZQupj=$q7rKKDp6j+Ze*X;G3PO;eI9gksRgHU^yY{)M(O))$k#BkwT`5~n%Idw zcKu|#9eDwzKW;!<-D11GTffIhXv_6F^7)rNlO+oc}+PcbH#S&q|QooIn>rX?Qc@4{24dF__;8OG2*8Zv9?AM19K z?|6XPE5Q%MNy!83yUgpcpk~#Haq?JyT_drl+d;RgvXFz%?-}$tJCIEw8@L{`+gk5BKkP_- zx1Y0ru^?w>KirW(*9kN{polk|EMT-XGLxnE;adck$*TVX?;Yh+6t3lJK#PtBMh--Y6_B^I=8KHIOrN%+w1WjPP_tG$+e_N({OXN&7( zkZe#ZyY=~;y=_i>2z?B^@7rIw^3unUUmNf2*%I~MctcvVfc^9osTR9OYJe=kf*=8Zf$~ltFeRZwDIga(d zrci_`^v3qz*hX6ymxu-#07aSZ8$^-8+_YOb!-RL~;O z>ztLs{eh)krucy4r~NZ3OBSuc(L;GUYq&r?){!{(p_v`!>Yr14Rb;{=EkCR92swFl zVh52i_%N`!)N2h>`H#Wq8g6LUz)s`b2tDF%{FLu{^~&+V-V-e#+oy~r8%=9#j^(a& zTHVlS+{5)<>~*K&cZ|0c>(!k_kKYYf(GBi!?x&EIDV|2o%*Riw+iD#n0m8a=ZRH2M+);`a?0_YUZfSvDe z`ZtmSUIunISE2g*d`q>z)BNpvdD~w@YpFH-2f<^rKSow0GGDqpek$(SE9tw0Og$EM z4_K>;LZF3yn==N*#XpMA)#udSiat%8lT~+zxqNQ=8joUy{p<8COde;=6qd{%QLiVE1+#+j`&TePW&l&WV*HD-B?*u^J_vxokBP_V*HBbv~vbHIr7r*Y1AWh z+xDe&M#`{bTaJ!+;I+VF@h&;HEVr`_E+5d#jekivuK0z#Xzqd{{>dpwXZaX#+-;$B2&p0iuLb&K!z%alpSN!5D`~b9Q@W;%+%<@he3|vllDdru65AM(|jX z#rh=1E>{iJD0aRKHh%Vg#o6Q0q)uQ+4kIy5BtknTNwl%15UrIH>i6N5Ses9@xtlUx z?weMx1m@CrD@y=)_LcAkac9X^yw9gKzv>%@YUuuP_;h@n&%-PId2p3-q^)@8)AYVp z#HZsUb?LX`x@_$~E_Yh*Oj5F}1oXL)Nd{%~dz9(5hb=~42Jx+<_x=icuk)Ga%2nge z1Fv`8HI6b(%e8Wzw}M8j5i*w2K}I!zt>(-)2HQ~9mEKw z2diFL8S%@$j-!y(_d>JEtJ{h!bfQ>?8~+;ruw|UFM7}z=ZX+GV$<4JC_l9qn|EpSn zOzQecvfLdfL7rd>^9(0juDzMD-nV+Rtui;GYxT&ZRj&sN2fRZ6%~q5>VI?%N7SUG6 zF{Qf?8QNamb*x`zF52q}DB8c5HLlV!GLDHxT}zSalQV=%Vi{-62@Z>;1HYBUOq>ge zNp=bP01+-^CH@C#2y~OV7LpZ@E>PoXdGU}kP5#@|lARZxPrxhRjk(Hff_{svrkhf? z|15bpxak%R|8A@~|7>+e`0KIAJi~HQ6g9{9^C(_z8QrhPC}Sdw)cmghmPgx~Y|ie7 z)^bg{JhEjy%}235az@_9Z`A|f72ly#29!Q$=SqnNzNl)4mc$EL{`v8aSG}nfDQ0C-ci2=Fy~2H>BV>t&dT>ckAFm9 zSu>WMD!az!f%|eI-xF-Qp2+hw&sH1}e&`6n zQS#_vS(25A45l_mez~pq3{RsfsAOp)1DdvR*Qlyj*^Lnx*BIj}7H%i6Z|H#9y2k0c zl?AVFuBafJ4Bi88>mIfA88ROC0ujZMU(HypIs)H2+>~DJuyY|;TuDpyx zBlr3DN|}{c&uv<8Te6RIvjk)FF5D1{wA~w&u4zsGcR4~+#|Ni{;f!7C`1upTw_j>g zvAu|;OGd`MnEz(%5_tq=soNcPZT`vGoX(E7?u+#U)gisXO#JFElXn4~1-@zS+k}@r zIL+*a9Bl+Il({MEP#z3xjLb#`401&oY<)9y^^LMMfJ}mWK~rRCGAbk1YiCcy;;A#u zb%pHO@jKar=;66i$#qIVfRU+&#Rh3=AoVGrjl%ERorC0Gb-pW*hIeB zeOi?0b1bP*I<&fCVE-U}!C^lOO<}XNwcTXjul%55(Pvivy%(SRv6v^ ze%0T{yz;NwB7?56Xsv(1{r~^&&b7yi;|Rn26atF0kp*HGB|yGJ3oHvV2FxY!!4i@o zP9$UJVk~1RKR)&JdtR!$r)SQbvp88tM>e}>XR5oa>t5B>*FqX!jsIS4Y+XgM_zQdV zbx<@?N045YouYG0*5pvpjfLo#Ei#7qG@SMR!k@O#eRlFlis7ePhW8P$5uJ-0{uvmO zwQC7q!&j7<7#R1~I6u?l#M~rW(fiz5WPc5biUllxPW_h6W!C*Q-HOVsuliL!j(q}G zZN)v5h;+vO^?XLFW|fXl|K&oi{%ut2vGX+*@H6$;l71Q4d^VJm@Cde&mnz38vJ%YETh*E-_hx}JX1WiSd^E6okuT!Zsa1q@Q!QZc_ikGmw!k(C{%O|2I$4MN6y}PS=X^K# z$r;PH;d#>&E=M)du_172~{@5cLo!A(y z2@dUZ8a-#W_|z@2c&(00di;^TRzw6@Apbx>+1T+Bl1?o6-8TXR1~q4@pfkcH{arTDNJHNi%c0Vf`vv&ArVc-Dj0x5stxDRDLNt zTr6QA>C1bY|Bhok?5Lttqg^0gPj>R>vXgUhi?w@&pOEJ*iP?T0@TZ^5gG1xwPwb;k zsSI6)W{~zngTp~@;)6evo>u>j)9Fqx=RQ{2z-S z-_Nw=VoAko;T5Cx>Q7QQQPbAb813z{R22N}9y5_7@+Zi&-fzAX->&tTi8#qer8Wo8 zu(lQjADJiKYi)sFlLyUhj>P@Zmc(N@)rqyx*AG(djJ%UY=W{qZv(_tg_9wDM zB`w4@zFnX1#Dj+Ch*L7&vK4Y;?><;MbL&b+PAAmVoIhtpDcs}g($5Mn9hZvzYnz`B zgD3dtIp<^a8}yui<6cJp#+guJOgzWW&tqrwj`NRno}rgD5k2HFers}AYj5)==W9jF zj-I|O_;gnRXdSNu?Y3-#Zq3=7VAsl*pH=3(sH^_Zvl1}VVw9B8ZQDcL1l90b&>h0ik9Lo|_}qR9u1|6>k*K;1H;)#|RlDzUp6He<(*YfS`G;%R2P`BwzxFqqiqz9DgL*|U^ zVD=h4r=6DUU#22aL25&&=K06>y}Rft=LU%znm@rNcV|eysTLqyYAO^TH!?PGzRf$X~knm8~qU6 z=RU6RCsIR{WBK9z6LrZ}<{9r|pUZF_#Z>bDurIboT90tqT5mZtA>a{7iYn7t+bn1# zX!Bj{M6Yk))|BpW4Ejbb$H=-W1Q{dVGSx8K&+Hnq@Oq5D5t}?^Q4bf$jfAA7fZGFRKDfhkn!qh zhfYs_GoQ$_pLD_iFIWIz2-`y5O-s-1yzKMlEYi~LPdz)L=K7;iRL8NamWMxuY<;oW z`88B+ovEmdaHOaNC#a7DJGDkMHQUBg722oNN$L00mZ#^E<0fa-^Gp7g49UIXDQFdX zkk{3&eEW_$hFI?xaX5cp%U6E~l}WnF#+5#U=4uXWtKTF=MU%46WnWk}pdaz@Ecs+J zOL~1>Mnm#Qha&$hO>^BXmG`vEIg-2woc(up1|EA5+$diad9Cv>=;>TB@>jnTW$mG1 zXG`{?G1TJ#e9Voh8U@Aq#H+5IOp9H53;ziT3-2p-CrzI-!(!mX#z{G;d#^l=X8R)e z-|x_cM)x66%W@~6A%($!@9{Z^706$zM-+9@QJM}DlFH=m=OqC15{7Du5 zv7={APo{>Nj#4_O)Mx*}VcV5q9^dTo!&u+ngTjpA)K`K9j#a0`8n6ubhPCUiF=RV? zTl;D79&i8hHbQz3me`hB;skpjy$!*h-HVlsPh}X!AHo8co}$*Bj1FspN?74k-guXj zBIYW;Z-iXhuj9Z}yEqF`Qx>x7e(Wi=viNqgoToFM-=XdP0#*Oi%ReZZL!*=1sk1C# zqWuOAT$6WkW)N(Hb7Z=y?|smlEI(;1Vo};laVB-sNPoYne2;gP86c+>rH0>;xyVt; zJm|TWu!1@A`_P~|r$LO0nL-ySaTT=`&8QX>d4)`a9tBtOF?S~I4Bv|&Yo}d{AtMoF zqvGKr#RLy4aU{?D-o4FxI*$+U+hfvMSaj>Vz{hqfa2~FvKIN;h6f$mm6j;9<-=vR^ zguc1?*8VN@!hcx}w=V?h$dC^EyuW?b&NsHJ&U$tHhqKH890(dCofX>p^*oPx`vWg~ zoqT^#I0n9v&a3~b5$2`c^?W4vhjyo~ecJ*@*sUMk#sEYV8 zM4d2p-h00LQ{VZiomUS9c%0cj`M4imDHe=;*<}cVNL9Zo#d2S{D_iHYkb2*C80u%z z!=WCMlfI9OWd)T`&4B!+o`@Sf*Td#^httqh+)V=Y@F1}&kCGpp%HW+ z2f74{5518m!Ka-%T;oZfId+77rgv2fojE3Kb_(kkjoHG_ywcJN-0`3vqaB5n$giwd zFgJqxv#Zt?T){(}>hcP)I~n0oz{P{d9>7keqNSbf2$tb;R*bGag5^8$(BNR?%^r|- z%DlbV2Y%3Pdo^TY4`}x3{XV+)#a*UpEILCnH~PuMV`cTH zh3lD9JKHv6&K)+}@B`g|q5H++c~PV)JTqQ(3r%$Vy2y0>T<>xIOs`e!ZfJQ7f&Kx@ z5b#O_fjy}sxcwSED|OB1RPGo$@{Myyc%%uvonODJNzZh#saJSbdIsKzqU%m7@}zUr z`lt3>nfz!yKdQ+d)ekN~Z%3Mk+wbq~utn$v%P!F0Lb7yvdxd2*F$mu-Fp|?PJh~3Q z2=8D7k3J+nmh)0*tli+!A%!~|M{voWBOCCZw!JpAxkMClhS?)` zWL=F`+4g7>q-D!{1cg1IP8&Pdv!R_xH^%)tpU7g86OF#MXggd3`6* zc{r~_wqtQ~ml{x$(hNx+THe&--E&lO2{!BGgs<*!8N+7PL?p59cY$4>+pv7#R!|}p zp8IyLK;oe5>Gb+^gH_#s!qyc)r)xE`KlRL{qWhSvD z-bQ~@SLV(6`^vPo)1vFZ5{v#Zi%HU*4wVGg`1aU8UB`T?V{gY+cgtwiFh;K84<;d1Dpm$(CJHl z8~z6?qR!wSW$F!xH)H9gxxw#R`v(6cSH}E7|nWva5&{Rp@Mr~B5!&7T9alOI=66nze zw~7OS*X1tXjODJ!>z=+-aL2sC3^@^5PRi7966t;60##-HwO|ZANDPsj1~QfkfOqkG zTQ5uYM5h7CO{*&5c031Xej86yE2H=**%Uc2ySb(o;t+|Ns z{7FN6BBIOUj$h)Wp(+EYv?9tP9%xS=&P(#c$ej>Pxm+-)GdSjB`kW9;BK8L#!8wZT z5`|*dL>s)%v*+`p)hUJve;h{C;Ba%Z;Q5>}vocQA*Hj((g@5|E!U5t!-MOqNProOZ z%dU}M)T*h^5jIr4{+pr`@e6q7ZFsoc3H&PTK*fXL0`;3C{>LpyoJ;?Ae3T0wy{YcW zbS}jHc@%FjB3%-Q2;=4HMo)j^=iFNiOP?qd5emK2XL+;&swV0BgI$LMwEzGB literal 0 HcmV?d00001 diff --git a/od-win32/resources/resource.h b/od-win32/resources/resource.h index e7416216..93f4ed6f 100644 --- a/od-win32/resources/resource.h +++ b/od-win32/resources/resource.h @@ -27,6 +27,7 @@ #define IDS_CHIPSET2 20 #define IDS_GAMEPORTS 21 #define IDS_EXPANSION 22 +#define IDS_STRING23 23 #define IDS_EXTTEXT 100 #define IDS_EXTACTUAL 101 #define IDS_SOUND 102 @@ -367,9 +368,11 @@ #define IDD_LIST 386 #define IDS_MISCLISTITEMS1 386 #define IDS_MISCLISTITEMS2 387 +#define IDD_CDDRIVE 387 #define IDS_MISCLISTITEMS3 388 #define IDS_WHEELMOUSE 389 #define IDS_JOYMODE_WHEELMOUSE 389 +#define IDS_NUMSG_KS68030PLUS 390 #define IDS_QS_MODELS 1000 #define IDS_QS_MODEL_A500 1001 #define IDS_QS_MODEL_A500P 1002 @@ -575,6 +578,7 @@ #define IDC_PORT0 1342 #define IDC_NEW_FSARCH 1342 #define IDC_PORT1 1343 +#define IDC_NEW_CD 1344 #define IDC_PATH_NAME 1362 #define IDC_SELECTOR 1363 #define IDC_VOLUME_NAME 1364 @@ -1166,7 +1170,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 #define _APS_3D_CONTROLS 1 -#define _APS_NEXT_RESOURCE_VALUE 387 +#define _APS_NEXT_RESOURCE_VALUE 388 #define _APS_NEXT_COMMAND_VALUE 40050 #define _APS_NEXT_CONTROL_VALUE 1815 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/od-win32/resources/winuae.rc b/od-win32/resources/winuae.rc index 5c6e0ba5..056a6f7e 100644 --- a/od-win32/resources/winuae.rc +++ b/od-win32/resources/winuae.rc @@ -321,6 +321,7 @@ BEGIN COMBOBOX IDC_CD_TYPE,282,279,71,50,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "Eject",IDC_CD_EJECT,360,278,30,15 COMBOBOX IDC_CD_TEXT,5,297,386,75,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add SCSI/IDE CD Drive",IDC_NEW_CD,135,176,126,15 END IDD_SOUND DIALOGEX 0, 0, 396, 288 @@ -1107,6 +1108,17 @@ BEGIN CONTROL "",IDC_LISTDIALOG_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,7,302,171 END +IDD_CDDRIVE DIALOGEX 0, 0, 396, 109 +STYLE DS_LOCALEDIT | DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_CENTER | DS_CENTERMOUSE | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "CD Settings" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + RTEXT "HD Controller:",IDC_STATIC,12,90,65,10,SS_CENTERIMAGE + COMBOBOX IDC_HDF_CONTROLLER,91,89,61,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "Add CD Drive",IDOK,236,87,73,14 + PUSHBUTTON "Cancel",IDCANCEL,316,87,73,14 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -1262,12 +1274,10 @@ GUIDELINES DESIGNINFO BEGIN IDD_KICKSTART, DIALOG BEGIN - BOTTOMMARGIN, 214 END IDD_DISPLAY, DIALOG BEGIN - BOTTOMMARGIN, 270 END IDD_MEMORY, DIALOG @@ -1296,7 +1306,6 @@ BEGIN IDD_IOPORTS, DIALOG BEGIN - BOTTOMMARGIN, 293 END IDD_GAMEPORTS, DIALOG @@ -1345,12 +1354,10 @@ BEGIN IDD_FILTER, DIALOG BEGIN - BOTTOMMARGIN, 286 END IDD_HARDDRIVE, DIALOG BEGIN - BOTTOMMARGIN, 108 END IDD_MISC2, DIALOG @@ -1399,7 +1406,6 @@ BEGIN IDD_INPUTMAP, DIALOG BEGIN - TOPMARGIN, 1 END IDD_INFOBOX, DIALOG @@ -1408,10 +1414,10 @@ BEGIN IDD_LIST, DIALOG BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 309 - TOPMARGIN, 7 - BOTTOMMARGIN, 199 + END + + IDD_CDDRIVE, DIALOG + BEGIN END END #endif // APSTUDIO_INVOKED @@ -1803,6 +1809,7 @@ BEGIN IDS_MISCLISTITEMS2 "Always on top\nDisable screensaver\nSynchronize clock\nFaster RTG\nClipboard sharing\nAllow native code\n" IDS_MISCLISTITEMS3 "Native on-screen display\nRTG on-screen display\nCreate winuaelog.txt log\nLog illegal memory accesses\nBlank unused displays\nStart mouse uncaptured\nStart minimized\nMinimize when focus is lost\n" IDS_JOYMODE_WHEELMOUSE "Wheel Mouse" + IDS_NUMSG_KS68030PLUS "The selected system ROM requires a 68030 or higher CPU." END #endif // English resources diff --git a/od-win32/win32.h b/od-win32/win32.h index 734fab4b..b962759d 100644 --- a/od-win32/win32.h +++ b/od-win32/win32.h @@ -19,11 +19,11 @@ #define LANG_DLL 1 #if WINUAEPUBLICBETA -#define WINUAEBETA _T("1") +#define WINUAEBETA _T("2") #else #define WINUAEBETA _T("") #endif -#define WINUAEDATE MAKEBD(2013, 1, 12) +#define WINUAEDATE MAKEBD(2013, 1, 20) #define WINUAEEXTRA _T("") //#define WINUAEEXTRA _T("AmiKit Preview") #define WINUAEREV _T("") diff --git a/od-win32/win32gui.cpp b/od-win32/win32gui.cpp index 30ea5373..f654266b 100644 --- a/od-win32/win32gui.cpp +++ b/od-win32/win32gui.cpp @@ -1968,6 +1968,8 @@ static UINT_PTR CALLBACK ofnhook (HWND hDlg, UINT message, WPARAM wParam, LPARAM static void eject_cd (void) { workprefs.cdslots[0].name[0] = 0; + if (full_property_sheet) + workprefs.cdslots[0].type = SCSI_UNIT_DEFAULT; quickstart_cddrive[0] = 0; workprefs.cdslots[0].inuse = false; if (full_property_sheet) { @@ -3878,19 +3880,23 @@ void InitializeListView (HWND hDlg) } else if (lv_type == LV_HARDDISK) { #ifdef FILESYS + listview_column_width[1] = 60; for(i = 0; i < workprefs.mountitems; i++) { struct uaedev_config_data *uci = &workprefs.mountconfig[i]; struct uaedev_config_info *ci = &uci->ci; int nosize = 0, type; struct mountedinfo mi; - TCHAR *rootdir = ci->rootdir; + TCHAR *rootdir; type = get_filesys_unitconfig (&workprefs, i, &mi); if (type < 0) { - type = uci->ishdf ? FILESYS_HARDFILE : FILESYS_VIRTUAL; + type = ci->type == UAEDEV_HDF || ci->type == UAEDEV_CD ? FILESYS_HARDFILE : FILESYS_VIRTUAL; nosize = 1; } + if (mi.size < 0) + nosize = 1; + rootdir = mi.rootdir; if (nosize) _tcscpy (size_str, _T("n/a")); @@ -3949,7 +3955,7 @@ void InitializeListView (HWND hDlg) WIN32GUI_LoadUIString (ci->readonly ? IDS_NO : IDS_YES, readwrite_str, sizeof (readwrite_str) / sizeof (TCHAR)); lvstruct.mask = LVIF_TEXT | LVIF_PARAM; - lvstruct.pszText = mi.ismedia == false ? _T("E") : (nosize ? _T("X") : (mi.ismounted ? _T("*") : _T(" "))); + lvstruct.pszText = mi.ismedia == false ? _T("E") : (nosize && mi.size >= 0 ? _T("X") : (mi.ismounted ? _T("*") : _T(" "))); if (ci->controller && mi.ismedia) lvstruct.pszText = _T(" "); lvstruct.lParam = 0; @@ -3958,7 +3964,7 @@ void InitializeListView (HWND hDlg) result = ListView_InsertItem (list, &lvstruct); if (result != -1) { - listview_column_width[0] = 15; + listview_column_width[0] = 20; ListView_SetItemText(list, result, 1, devname_str); width = ListView_GetStringWidth(list, devname_str) + 10; @@ -4009,8 +4015,10 @@ void InitializeListView (HWND hDlg) } } // Adjust our column widths so that we can see the contents... - for(i = 0; i < listview_num_columns; i++) - ListView_SetColumnWidth (list, i, listview_column_width[i]); + for(i = 0; i < listview_num_columns; i++) { + if (ListView_GetColumnWidth (list, i) < listview_column_width[i]) + ListView_SetColumnWidth (list, i, listview_column_width[i]); + } // Redraw the items in the list... items = ListView_GetItemCount (list); ListView_RedrawItems (list, 0, items); @@ -5422,6 +5430,8 @@ static INT_PTR CALLBACK QuickstartDlgProc (HWND hDlg, UINT msg, WPARAM wParam, L val = SendDlgItemMessage (hDlg, IDC_CD0Q_TYPE, CB_GETCURSEL, 0, 0); if (val != CB_ERR) { quickstart_cdtype = val; + if (full_property_sheet) + workprefs.cdslots[0].type = SCSI_UNIT_DEFAULT; if (quickstart_cdtype >= 2) { int len = sizeof quickstart_cddrive / sizeof (TCHAR); quickstart_cdtype = 2; @@ -9141,6 +9151,10 @@ static INT_PTR CALLBACK SoundDlgProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM #ifdef FILESYS +struct cddlg_vals +{ + struct uaedev_config_info ci; +}; struct fsvdlg_vals { struct uaedev_config_info ci; @@ -9156,6 +9170,7 @@ struct hfdlg_vals bool rdb; }; +static struct cddlg_vals current_cddlg; static struct fsvdlg_vals current_fsvdlg; static struct hfdlg_vals current_hfdlg; static int archivehd; @@ -9164,15 +9179,16 @@ static void default_fsvdlg (struct fsvdlg_vals *f) { memset (f, 0, sizeof (struct fsvdlg_vals)); f->ci.autoboot = true; + f->ci.type = UAEDEV_DIR; } static void default_hfdlg (struct hfdlg_vals *f, bool rdb) { memset (f, 0, sizeof (struct hfdlg_vals)); uci_set_defaults (&f->ci, rdb); f->original = true; + f->ci.type = UAEDEV_HDF; } - static void volumeselectfile (HWND hDlg) { TCHAR directory_path[MAX_DPATH]; @@ -9549,6 +9565,45 @@ static void hardfilecreatehdf (HWND hDlg, TCHAR *newpath) sethardfile (hDlg); } +static INT_PTR CALLBACK CDDriveSettingsProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static int recursive = 0; + int posn; + + switch (msg) { + + case WM_INITDIALOG: + recursive++; + inithdcontroller (hDlg); + SendDlgItemMessage (hDlg, IDC_HDF_CONTROLLER, CB_SETCURSEL, current_cddlg.ci.controller, 0); + recursive--; + customDlgType = IDD_CDDRIVE; + customDlg = hDlg; + return TRUE; + case WM_COMMAND: + if (recursive) + break; + recursive++; + switch (LOWORD (wParam)) + { + case IDOK: + EndDialog (hDlg, 1); + break; + case IDCANCEL: + EndDialog (hDlg, 0); + break; + case IDC_HDF_CONTROLLER: + posn = SendDlgItemMessage (hDlg, IDC_HDF_CONTROLLER, CB_GETCURSEL, 0, 0); + if (posn != CB_ERR) + current_cddlg.ci.controller = posn; + break; + } + recursive--; + break; + } + return FALSE; +} + static INT_PTR CALLBACK HardfileSettingsProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { static int recursive = 0; @@ -9887,7 +9942,7 @@ static void new_filesys (HWND hDlg, int entry) int bp = tweakbootpri (current_fsvdlg.ci.bootpri, current_fsvdlg.ci.autoboot, current_fsvdlg.ci.donotmount); memcpy (&ci, ¤t_fsvdlg.ci, sizeof (struct uaedev_config_info)); ci.bootpri = bp; - uci = add_filesys_config (&workprefs, entry, &ci, false); + uci = add_filesys_config (&workprefs, entry, &ci); if (uci) { if (uci->ci.rootdir[0]) filesys_media_change (uci->ci.rootdir, 1, uci); @@ -9896,6 +9951,17 @@ static void new_filesys (HWND hDlg, int entry) } } +static void new_cddrive (HWND hDlg, int entry) +{ + struct uaedev_config_info ci = { 0 }; + ci.cd_emu_unit = 0; + ci.controller = current_cddlg.ci.controller; + ci.type = UAEDEV_CD; + ci.readonly = true; + ci.blocksize = 2048; + add_filesys_config (&workprefs, entry, &ci); +} + static void new_hardfile (HWND hDlg, int entry) { struct uaedev_config_data *uci; @@ -9903,7 +9969,7 @@ static void new_hardfile (HWND hDlg, int entry) int bp = tweakbootpri (current_hfdlg.ci.bootpri, current_hfdlg.ci.autoboot, current_hfdlg.ci.donotmount); memcpy (&ci, ¤t_hfdlg.ci, sizeof (struct uaedev_config_info)); ci.bootpri = bp; - uci = add_filesys_config (&workprefs, entry, &ci, true); + uci = add_filesys_config (&workprefs, entry, &ci); if (uci) { struct hardfiledata *hfd = get_hardfile_data (uci->configoffset); hardfile_media_change (hfd, &ci, true, false); @@ -9914,7 +9980,7 @@ static void new_harddrive (HWND hDlg, int entry) { struct uaedev_config_data *uci; - uci = add_filesys_config (&workprefs, entry, ¤t_hfdlg.ci, true); + uci = add_filesys_config (&workprefs, entry, ¤t_hfdlg.ci); if (uci) { struct hardfiledata *hfd = get_hardfile_data (uci->configoffset); hardfile_media_change (hfd, ¤t_hfdlg.ci, true, false); @@ -9950,9 +10016,15 @@ static void harddisk_edit (HWND hDlg) type = get_filesys_unitconfig (&workprefs, entry, &mi); if (type < 0) - type = uci->ishdf ? FILESYS_HARDFILE : FILESYS_VIRTUAL; + type = uci->ci.type == UAEDEV_HDF ? FILESYS_HARDFILE : FILESYS_VIRTUAL; - if(type == FILESYS_HARDFILE || type == FILESYS_HARDFILE_RDB) + if (uci->ci.type == UAEDEV_CD) { + memcpy (¤t_cddlg.ci, uci, sizeof (struct uaedev_config_info)); + if (CustomDialogBox (IDD_CDDRIVE, hDlg, CDDriveSettingsProc)) { + new_cddrive (hDlg, entry); + } + } + else if(type == FILESYS_HARDFILE || type == FILESYS_HARDFILE_RDB) { current_hfdlg.forcedcylinders = uci->ci.highcyl; memcpy (¤t_hfdlg.ci, uci, sizeof (struct uaedev_config_info)); @@ -10018,6 +10090,11 @@ static int harddiskdlg_button (HWND hDlg, WPARAM wParam) new_hardfile (hDlg, -1); return 1; + case IDC_NEW_CD: + if (CustomDialogBox (IDD_CDDRIVE, hDlg, CDDriveSettingsProc)) + new_cddrive (hDlg, -1); + return 1; + case IDC_NEW_HD: memset (¤t_hfdlg, 0, sizeof (current_hfdlg)); if (hdf_init_target () == 0) { @@ -10180,13 +10257,19 @@ static INT_PTR CALLBACK HarddiskDlgProc (HWND hDlg, UINT msg, WPARAM wParam, LPA getfloppyname (hDlg, 0, 1, IDC_CD_TEXT); quickstart_cdtype = 1; workprefs.cdslots[0].inuse = true; + if (full_property_sheet) + workprefs.cdslots[0].type = SCSI_UNIT_DEFAULT; addcdtype (hDlg, IDC_CD_TYPE); addfloppyhistory_2 (hDlg, 0, IDC_CD_TEXT, HISTORY_CD); + InitializeListView (hDlg); + hilitehd (hDlg); break; case IDC_CD_TYPE: int val = SendDlgItemMessage (hDlg, IDC_CD_TYPE, CB_GETCURSEL, 0, 0); if (val != CB_ERR) { quickstart_cdtype = val; + if (full_property_sheet) + workprefs.cdslots[0].type = SCSI_UNIT_DEFAULT; if (quickstart_cdtype >= 2) { int len = sizeof quickstart_cddrive / sizeof (TCHAR); quickstart_cdtype = 2; @@ -10208,6 +10291,8 @@ static INT_PTR CALLBACK HarddiskDlgProc (HWND hDlg, UINT msg, WPARAM wParam, LPA } addcdtype (hDlg, IDC_CD_TYPE); addfloppyhistory_2 (hDlg, 0, IDC_CD_TEXT, HISTORY_CD); + InitializeListView (hDlg); + hilitehd (hDlg); } break; } @@ -14999,7 +15084,6 @@ static void centerWindow (HWND hDlg) y = mdc->rect.top; } } - SetForegroundWindow (hDlg); pt1.x = x + 100; pt1.y = y + (GetSystemMetrics (SM_CYMENU) + GetSystemMetrics (SM_CYBORDER)) / 2; pt2.x = x + gui_width - 100; @@ -15200,7 +15284,7 @@ int dragdrop (HWND hDlg, HDROP hd, struct uae_prefs *prefs, int currentpage) } else if (currentpage == HARDDISK_ID) { default_fsvdlg (¤t_fsvdlg); _tcscpy (current_fsvdlg.ci.rootdir, file); - add_filesys_config (&workprefs, -1, ¤t_fsvdlg.ci, false); + add_filesys_config (&workprefs, -1, ¤t_fsvdlg.ci); } else { drv = floppyslot_addfile (prefs, file, drv, firstdrv, i); if (drv < 0) @@ -15226,18 +15310,18 @@ int dragdrop (HWND hDlg, HDROP hd, struct uae_prefs *prefs, int currentpage) } else { default_fsvdlg (¤t_fsvdlg); _tcscpy (current_fsvdlg.ci.rootdir, file); - add_filesys_config (&workprefs, -1, ¤t_fsvdlg.ci, false); + add_filesys_config (&workprefs, -1, ¤t_fsvdlg.ci); } } else { uci_set_defaults (¤t_hfdlg.ci, false); current_hfdlg.forcedcylinders = 0; updatehdfinfo (NULL, true, true); - add_filesys_config (&workprefs, -1, ¤t_hfdlg.ci, true); + add_filesys_config (&workprefs, -1, ¤t_hfdlg.ci); } break; case ZFILE_HDFRDB: default_hfdlg (¤t_hfdlg, true); - add_filesys_config (&workprefs, -1, ¤t_hfdlg.ci, true); + add_filesys_config (&workprefs, -1, ¤t_hfdlg.ci); break; case ZFILE_NVR: _tcscpy (prefs->flashfile, file); @@ -15269,7 +15353,7 @@ int dragdrop (HWND hDlg, HDROP hd, struct uae_prefs *prefs, int currentpage) default_fsvdlg (¤t_fsvdlg); _tcscpy (current_fsvdlg.ci.rootdir, file); _tcscpy (current_fsvdlg.ci.volname, filepart); - add_filesys_config (&workprefs, -1, ¤t_fsvdlg.ci, false); + add_filesys_config (&workprefs, -1, ¤t_fsvdlg.ci); if (!full_property_sheet) do_filesys_insert (file); } else { @@ -16331,6 +16415,7 @@ static int transla[] = { NUMSG_NOCAPS, IDS_NUMSG_NOCAPS, NUMSG_KICKREP, IDS_NUMSG_KICKREP, NUMSG_KICKREPNO, IDS_NUMSG_KICKREPNO, + NUMSG_KS68030PLUS, IDS_NUMSG_KS68030PLUS, -1 }; diff --git a/od-win32/winuaechangelog.txt b/od-win32/winuaechangelog.txt index 50616346..46e17d2f 100644 --- a/od-win32/winuaechangelog.txt +++ b/od-win32/winuaechangelog.txt @@ -1,6 +1,31 @@ - restore only single input target to default. +Beta 2: + +- 030/040/060 MMU TAS and CAS/CAS2 Read-Modify-Write cycle special handling emulated, RMW read access + part is internally considered a write access and it will cause access fault if address is write protected. +- 040 MMU MOVEM special handling (SSR CM-bit) emulated. +- Some 040/060 MMU emulation bug fixes. (It still isn't fully compatible) +- A3000 2.04 ROM requires 68030 or higher, it is not 68030-only. (Only 1.4b is 68030-only) +- Bogus filesystem.resource entry was added if RDB HDF had more than one partition with custom filesystem (b1) +- Fixed stuck IDE emulation interrupt that hangs the emulated system, happened randomly during A4000 IDE detection. (Old bug) +- Geometry file FORCELOAD parameter added, forceload=1: do not re-use already loaded filesystem(s). +- A590/A2091/A3000 SCSI CDROM emulation added, uses SCSI emulation originally made for uaescsi.device. +- Gayle/A4000 ATAPI CDROM emulation added (uses uaescsi.device SCSI emulation, ATAPI = IDE CROM SCSI command support) +- CD image mounter SCSI emulation inquiry command's vendor/product/revision data was blank. +- Ultra-minimal "Add CD Drive" GUI added. Only IDE or SCSI and only one CD entry can be added. Use hardddrives panel bottom + CD drive/image selection select menu to select mounted CD. uaescsi.device still works as previously but note that + same CD drive/image slot (internally there is more but they are not available in GUI yet) is shared = it may not be good + idea to enable both uaescsi.device and hardware IDE/SCSI CD emulation at the same time. +- Added more emulated SCSI CD commands, READ TRACK INFORMATION, READ DISC INFORMATION and PREVENT/ALLOW MEDIUM REMOVAL + (which does nothing). READ TOC allocation length check fixed. + +Note: Curently SCSI and IDE CD is single threaded = whole emulation pauses while waiting for CD command to finish = better +not to use real CD drives. Only uaescsi.device runs in background. + +Beta 1: + Big MMU emulation update: - Previous (Next emulator) 68030 MMU added (+missing bus error exception stuff implemented by me), diff --git a/rommgr.cpp b/rommgr.cpp index 242345de..c14cb382 100644 --- a/rommgr.cpp +++ b/rommgr.cpp @@ -139,7 +139,7 @@ static struct romdata roms[] = { 0x64466c2a, 0xF72D8914,0x8DAC39C6,0x96E30B10,0x859EBC85,0x9226637B }, { _T("KS ROM v2.05 (A600HD)"), 2, 5, 37, 350, _T("A600HD\0A600\0"), 524288, 10, 0, 0, ROMTYPE_KICK, 0, 0, _T("391304-02"), 0x43b0df7b, 0x02843C42,0x53BBD29A,0xBA535B0A,0xA3BD9A85,0x034ECDE4 }, - { _T("KS ROM v2.04 (A3000)"), 2, 4, 37, 175, _T("A3000\0"), 524288, 71, 3, 0, ROMTYPE_KICK, 0, 0, NULL, + { _T("KS ROM v2.04 (A3000)"), 2, 4, 37, 175, _T("A3000\0"), 524288, 71, 8, 0, ROMTYPE_KICK, 0, 0, NULL, 0x234a7233, 0xd82ebb59,0xafc53540,0xddf2d718,0x7ecf239b,0x7ea91590 }, ALTROMPN(71, 1, 1, 262144, ROMTYPE_EVEN, _T("390629-03"), 0xa245dbdf,0x83bab8e9,0x5d378b55,0xb0c6ae65,0x61385a96,0xf638598f) ALTROMPN(71, 1, 2, 262144, ROMTYPE_ODD , _T("390630-03"), 0x7db1332b,0x48f14b31,0x279da675,0x7848df6f,0xeb531881,0x8f8f576c) diff --git a/scsi.cpp b/scsi.cpp index cf512ddd..0237e27a 100644 --- a/scsi.cpp +++ b/scsi.cpp @@ -16,8 +16,8 @@ #include "blkdev.h" static int outcmd[] = { 0x0a, 0x2a, 0x2f, 0xaa, -1 }; -static int incmd[] = { 0x03, 0x08, 0x12, 0x1a, 0x25, 0x28, 0x37, 0x42, 0x43, 0xa8, -1 }; -static int nonecmd[] = { 0x00, 0x35, -1 }; +static int incmd[] = { 0x03, 0x08, 0x12, 0x1a, 0x25, 0x28, 0x37, 0x42, 0x43, 0xa8, 0x51, 0x52, -1 }; +static int nonecmd[] = { 0x00, 0x1b, 0x1e, 0x35, -1 }; static int scsi_data_dir(struct scsi_data *sd) { @@ -40,8 +40,8 @@ static int scsi_data_dir(struct scsi_data *sd) return 0; } } - write_log (_T("SCSI command %02X, no direction specified (IN?)!\n"), sd->cmd[0]); - return -2; + write_log (_T("SCSI command %02X, no direction specified!\n"), sd->cmd[0]); + return 0; } void scsi_emulate_analyze (struct scsi_data *sd) @@ -67,6 +67,9 @@ void scsi_emulate_analyze (struct scsi_data *sd) case 0x25: case 0x28: case 0x35: + case 0x51: + case 0x52: + case 0x43: cmd_len = 10; break; case 0xa8: @@ -85,19 +88,35 @@ void scsi_emulate_cmd(struct scsi_data *sd) { sd->status = 0; //write_log (_T("CMD=%02x\n"), sd->cmd[0]); - if (sd->cmd[0] == 0x03) { /* REQUEST SENSE */ - int len = sd->cmd[4]; - memset (sd->buffer, 0, len); - memcpy (sd->buffer, sd->sense, sd->sense_len > len ? len : sd->sense_len); - sd->data_len = len; - sd->status = 0; + if (sd->cd_emu_unit >= 0) { + if (sd->cmd[0] == 0x03) { /* REQUEST SENSE */ + int len = sd->cmd[4]; + memset (sd->buffer, 0, len); + memcpy (sd->buffer, sd->sense, sd->sense_len > len ? len : sd->sense_len); + sd->data_len = len; + } else { + sd->status = scsi_cd_emulate(sd->cd_emu_unit, sd->cmd, sd->cmd_len, sd->buffer, &sd->data_len, sd->reply, &sd->reply_len, sd->sense, &sd->sense_len); + if (sd->status == 0) { + if (sd->reply_len > 0) { + memset(sd->buffer, 0, 256); + memcpy(sd->buffer, sd->reply, sd->reply_len); + } + } + } } else if (sd->nativescsiunit < 0) { - sd->status = scsi_emulate(&sd->hfd->hfd, sd->hfd, - sd->cmd, sd->cmd_len, sd->buffer, &sd->data_len, sd->reply, &sd->reply_len, sd->sense, &sd->sense_len); - if (sd->status == 0) { - if (sd->reply_len > 0) { - memset(sd->buffer, 0, 256); - memcpy(sd->buffer, sd->reply, sd->reply_len); + if (sd->cmd[0] == 0x03) { /* REQUEST SENSE */ + int len = sd->cmd[4]; + memset (sd->buffer, 0, len); + memcpy (sd->buffer, sd->sense, sd->sense_len > len ? len : sd->sense_len); + sd->data_len = len; + } else { + sd->status = scsi_hd_emulate(&sd->hfd->hfd, sd->hfd, + sd->cmd, sd->cmd_len, sd->buffer, &sd->data_len, sd->reply, &sd->reply_len, sd->sense, &sd->sense_len); + if (sd->status == 0) { + if (sd->reply_len > 0) { + memset(sd->buffer, 0, 256); + memcpy(sd->buffer, sd->reply, sd->reply_len); + } } } } else { @@ -125,12 +144,27 @@ void scsi_emulate_cmd(struct scsi_data *sd) sd->offset = 0; } -struct scsi_data *scsi_alloc(int id, struct hd_hardfiledata *hfd) +struct scsi_data *scsi_alloc_hd(int id, struct hd_hardfiledata *hfd) { struct scsi_data *sd = xcalloc (struct scsi_data, 1); sd->hfd = hfd; sd->id = id; sd->nativescsiunit = -1; + sd->cd_emu_unit = -1; + return sd; +} + +struct scsi_data *scsi_alloc_cd(int id, int unitnum) +{ + struct scsi_data *sd; + if (!sys_command_open (unitnum)) { + write_log (_T("SCSI: CD EMU scsi unit %d failed to open\n"), unitnum); + return NULL; + } + sd = xcalloc (struct scsi_data, 1); + sd->id = id; + sd->cd_emu_unit = unitnum; + sd->nativescsiunit = -1; return sd; } @@ -144,6 +178,7 @@ struct scsi_data *scsi_alloc_native(int id, int nativeunit) sd = xcalloc (struct scsi_data, 1); sd->id = id; sd->nativescsiunit = nativeunit; + sd->cd_emu_unit = -1; return sd; } @@ -158,6 +193,11 @@ void scsi_free(struct scsi_data *sd) return; if (sd->nativescsiunit >= 0) { sys_command_close (sd->nativescsiunit); + sd->nativescsiunit = -1; + } + if (sd->cd_emu_unit >= 0) { + sys_command_close (sd->cd_emu_unit); + sd->cd_emu_unit = -1; } xfree(sd); } -- 2.47.3