diff --git a/Altair8800/altair8800_defs.h b/Altair8800/altair8800_defs.h new file mode 100644 index 00000000..932fa543 --- /dev/null +++ b/Altair8800/altair8800_defs.h @@ -0,0 +1,45 @@ +/* altair8800_defs.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef _ALTAIR8800_DEFS_H +#define _ALTAIR8800_DEFS_H + +#include "sim_defs.h" + +#define KB 1024 /* kilo byte */ +#define KBLOG2 10 /* log2 of KB */ + +#define PLURAL(x) (x), (x) == 1 ? "" : "s" + +/* Keyboard Constants */ +#define KBD_BS 0x08 +#define KBD_DEL 0x7f + +#endif diff --git a/Altair8800/altair8800_dsk.c b/Altair8800/altair8800_dsk.c new file mode 100644 index 00000000..3ad89e0a --- /dev/null +++ b/Altair8800/altair8800_dsk.c @@ -0,0 +1,435 @@ +/* altair8800_dsk.c: Soft Sector Disk Library + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 13-Nov-2025 Initial version + +*/ + +#include "sim_defs.h" +#include "altair8800_dsk.h" + +static void calc_offset(DSK_INFO *d); + +/* + * INTERLEAVED disk images are structured as follows: + * + * +------------------+ + * | TRACK 0 / HEAD 0 | + * +------------------+ + * | TRACK 0 / HEAD 1 | + * +------------------+ + * | TRACK 1 / HEAD 0 | + * +------------------+ + * | TRACK 1 / HEAD 1 | + * +------------------+ + * | TRACK n / HEAD 0 | + * +------------------+ + * | TRACK n / HEAD 1 | + * +------------------+ + * + * NON-INTERLEAVED disk images are structured as follows: + * + * +------------------+ + * | TRACK 0 / HEAD 0 | + * +------------------+ + * | TRACK 1 / HEAD 0 | + * +------------------+ + * | TRACK n / HEAD 0 | + * +------------------+ + * | TRACK 0 / HEAD 1 | + * +------------------+ + * | TRACK 1 / HEAD 1 | + * +------------------+ + * | TRACK n / HEAD 1 | + * +------------------+ + * + */ + +t_stat dsk_init(DSK_INFO *d, UNIT *unit, int tracks, int heads, int interleaved) +{ + if (d == NULL) { + return SCPE_ARG; + } + if (tracks < 1 || tracks > DSK_MAX_TRACKS) { + return SCPE_ARG; + } + if (heads < 1 || heads > DSK_MAX_HEADS) { + return SCPE_ARG; + } + + d->unit = unit; + d->fmt.tracks = tracks; + d->fmt.heads = heads; + d->fmt.interleaved = interleaved; + + return SCPE_OK; +} + +t_stat dsk_init_format(DSK_INFO *d, int strack, int etrack, int shead, int ehead, int den, int secs, int secsize, int stsec) +{ + int tr, hd; + + if (d == NULL) { + return SCPE_ARG; + } + if (strack < 0 || strack > d->fmt.tracks - 1) { + return SCPE_ARG; + } + if (shead < 0 || shead > d->fmt.heads - 1) { + return SCPE_ARG; + } + if (strack > etrack || shead > ehead) { + return SCPE_ARG; + } + + if (d->fmt.tracks < (etrack - strack + 1)) { + d->fmt.tracks = (etrack - strack + 1); + } + + if (d->fmt.heads < (ehead - shead + 1)) { + d->fmt.heads = ehead - shead + 1; + } + + if (d->fmt.interleaved && d->fmt.heads > 1) { + for (tr = strack; tr <= etrack; tr++) { + for (hd = shead; hd <= ehead ; hd++) { + d->fmt.track[tr][hd].density = den; + d->fmt.track[tr][hd].sectors = secs; + d->fmt.track[tr][hd].sectorsize = secsize; + d->fmt.track[tr][hd].startsector = stsec; + } + } + } + else { + for (hd = shead; hd <= ehead; hd++) { + for (tr = strack; tr <= etrack; tr++) { + d->fmt.track[tr][hd].density = den; + d->fmt.track[tr][hd].sectors = secs; + d->fmt.track[tr][hd].sectorsize = secsize; + d->fmt.track[tr][hd].startsector = stsec; + } + } + } + + calc_offset(d); + + return SCPE_OK; +} + +static void calc_offset(DSK_INFO *d) { + int t, h, offset = 0; + + if (d->fmt.interleaved && d->fmt.heads > 1) { + for (t = 0; t < d->fmt.tracks; t++) { + for (h = 0; h < d->fmt.heads; h++) { + sim_debug(d->dbg_verbose, d->unit->dptr, "T:%02d H:%d O:%d\n", t, h, offset); + d->fmt.track[t][h].offset = offset; + + /* Set offset to start of next track */ + offset += d->fmt.track[t][h].sectors * d->fmt.track[t][h].sectorsize; + } + } + } + else { + for (h = 0; h < d->fmt.heads; h++) { + for (t = 0; t < d->fmt.tracks; t++) { + sim_debug(d->dbg_verbose, d->unit->dptr, "T:%02d H:%d O:%d\n", t, h, offset); + d->fmt.track[t][h].offset = offset; + + /* Set offset to start of next track */ + offset += d->fmt.track[t][h].sectors * d->fmt.track[t][h].sectorsize; + } + } + } +} + +t_stat dsk_validate(DSK_INFO *d, int track, int head, int sector) +{ + if (track < 0 || track > d->fmt.tracks - 1) { + sim_printf("DSK: ** Invalid track number %d\n", track); + return SCPE_IOERR; + } + if (head < 0 || head > (d->fmt.heads - 1)) { + sim_printf("DSK: ** Invalid head number %d\n", head); + return SCPE_IOERR; + } + if (sector < d->fmt.track[track][head].startsector || sector > (d->fmt.track[track][head].sectors - ((d->fmt.track[track][head].startsector) ? 0 : 1))) { + sim_printf("DSK: ** Invalid sector number. track/head %d/%d has %d sectors. %d requested.\n", track, head, d->fmt.track[track][head].sectors, sector); + return SCPE_IOERR; + } + + return SCPE_OK; +} + +int32 dsk_size(DSK_INFO *d) +{ + if (d != NULL && d->unit != NULL && d->unit->fileref != NULL) { + return sim_fsize(d->unit->fileref); + } + + return 0; +} + +int32 dsk_tracks(DSK_INFO *d) +{ + if (d != NULL) { + return d->fmt.tracks; + } + + return 0; +} + +int32 dsk_track_size(DSK_INFO *d, int32 track, int32 head) +{ + if (d != NULL) { + return d->fmt.track[track][head].sectors * d->fmt.track[track][head].sectorsize; + } + + return 0; +} + +int32 dsk_sectors(DSK_INFO *d, int32 track, int32 head) +{ + if (d != NULL) { + return d->fmt.track[track][head].sectors; + } + + return 0; +} + +int32 dsk_sector_size(DSK_INFO *d, int32 track, int32 head) +{ + if (d != NULL) { + return d->fmt.track[track][head].sectorsize; + } + + return 0; +} + +int32 dsk_start_sector(DSK_INFO *d, int32 track, int32 head) +{ + if (d != NULL) { + return d->fmt.track[track][head].startsector; + } + + return 0; +} + +int32 dsk_sector_offset(DSK_INFO *d, int32 track, int32 head, int32 sector) +{ + if (d != NULL) { + return d->fmt.track[track][head].offset + (dsk_sector_size(d, track, head) * (sector - dsk_start_sector(d, track, head))); + } + + return 0; +} + +t_stat dsk_read_sector(DSK_INFO *d, int32 track, int32 head, int32 sector, uint8 *buf, int32 *bytesread) +{ + int32 b, ssize; + t_stat r = SCPE_OK; + + if (d == NULL || d->unit == NULL || d->unit->fileref == NULL) { + return SCPE_ARG; + } + + if ((r = dsk_validate(d, track, head, sector)) != 0) { + return SCPE_ARG; + } + + ssize = dsk_sector_size(d, track, head); + + fseek(d->unit->fileref, dsk_sector_offset(d, track, head, sector), SEEK_SET); + + if ((b = fread(buf, 1, ssize, d->unit->fileref)) != ssize) { + r = SCPE_IOERR; + } + + if (bytesread != NULL) { + *bytesread = b; + sim_debug(d->dbg_verbose, d->unit->dptr, "DSK RD SEC: T:%d H:%d S:%d SS:%d READ:%d\n", track, head, sector, ssize, *bytesread); + } + + +// dsk_dump_buf(buf, ssize); + + return r; +} + +t_stat dsk_write_sector(DSK_INFO *d, int32 track, int32 head, int32 sector, const uint8 *buf, int32 *byteswritten) +{ + int b, ssize, offset; + int r = SCPE_OK; + + if (d == NULL || d->unit == NULL || d->unit->fileref == NULL) { + return SCPE_ARG; + } + + if ((r = dsk_validate(d, track, head, sector)) != 0) { + return r; + } + + ssize = dsk_sector_size(d, track, head); + offset = dsk_sector_offset(d, track,head, sector); + + fseek(d->unit->fileref, offset, SEEK_SET); + + b = fwrite(buf, 1, ssize, d->unit->fileref); + + if (byteswritten != NULL) { + *byteswritten = b; + sim_debug(d->dbg_verbose, d->unit->dptr, "DSK WR SEC: T:%d H:%d S:%d SS:%d O:%d WRITTEN:%d\n", track, head, sector, ssize, offset, *byteswritten); + } + + +// dsk_dump_buf(buf, ssize); + + return r; +} + +t_stat dsk_read_track(DSK_INFO *d, int32 track, int32 head, uint8 *buf) +{ + if (d == NULL || d->unit == NULL || d->unit->dptr == NULL) { + return SCPE_ARG; + } + + sim_debug(d->dbg_verbose, d->unit->dptr, "DSK RD TRK: T:%d H:%d\n", track, head); + + return SCPE_OK; +} + +t_stat dsk_write_track(DSK_INFO *d, int32 track, int32 head, uint8 fill) +{ + int s, ssize, start; + unsigned char *b; + + if (d == NULL) { + return SCPE_ARG; + } + + ssize = dsk_sector_size(d, track, head); + start = dsk_start_sector(d, track, head); + + if ((b = malloc(ssize)) == NULL) { + return 0; + } + + memset(b, fill, ssize); + + sim_debug(d->dbg_verbose, d->unit->dptr, "DSK WR TRK: T:%d H:%d SS:%d F:%02X\n", track, head, ssize, fill); + + for (s = 0; s < dsk_sectors(d, track, head); s++) { + dsk_write_sector(d, track, head, s + start, b, NULL); + } + + free(b); + + return 0; +} + +t_stat dsk_format(DSK_INFO *d, uint8 fill) +{ + int t, h; + + if (d == NULL) { + return SCPE_ARG; + } + + for (t = 0; t < d->fmt.tracks; t++) { + for (h = 0; h < d->fmt.heads; h++) { + dsk_write_track(d, t, h, fill); + } + } + + return SCPE_OK; +} + +void dsk_dump_buf(const uint8 *b, int32 size) +{ + int i; + + if (b == NULL) { + return; + } + + for (i = 0; i < size; i++) { + if ((i & 0x0f) == 0x00) { + sim_printf("%04X: ", i); + } + sim_printf("%02X%c", b[i], ((i & 0x0f) == 0x0f) ? '\n' : ' '); + } +} + +void dsk_show(DSK_INFO *d) +{ + int t, h; + + if (d != NULL) { + sim_printf("\n"); + sim_printf("fmt.tracks = %d\n", d->fmt.tracks); + sim_printf("fmt.heads = %d\n", d->fmt.heads); + + for (t = 0; t < d->fmt.tracks; t++) { + for (h = 0; h < d->fmt.heads; h++) { + sim_printf("T:%02d H:%d D:%s SECS:%02d SECSIZE:%04d OFFSET:%05X\n", t, h, + d->fmt.track[t][h].density == DSK_DENSITY_SD ? "SD" : "DD", + d->fmt.track[t][h].sectors, + d->fmt.track[t][h].sectorsize, + d->fmt.track[t][h].offset); + } + } + } +} + +void dsk_set_verbose_flag(DSK_INFO *d, uint32 flag) +{ + if (d != NULL) { + d->dbg_verbose = flag; + } +} + +t_stat dsk_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + uint32 i; + + for (i=0; i < dptr->numunits; i++) { + if ((dptr->units[i].flags & UNIT_ATTABLE) && + !(dptr->units[i].flags & UNIT_DIS)) { + fprintf (st, " sim> ATTACH {switches} %s diskfile\n", sim_uname(&dptr->units[i])); + } + } + + fprintf (st, "\n%s attach command switches\n", dptr->name); + fprintf (st, " -E Must Exist (if not specified an attempt to create the indicated\n"); + fprintf (st, " disk container will be attempted).\n"); + fprintf (st, " -N New file. Existing file is overwritten.\n"); + fprintf (st, " -R Attach Read Only.\n"); + + fprintf (st, "\n\n"); + + return SCPE_OK; +} + diff --git a/Altair8800/altair8800_dsk.h b/Altair8800/altair8800_dsk.h new file mode 100644 index 00000000..e4bb2de7 --- /dev/null +++ b/Altair8800/altair8800_dsk.h @@ -0,0 +1,85 @@ +/* altair8800_dsk.h: Soft Sector Disk Library + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 13-Nov-2025 Initial version + +*/ + +#ifndef _ALTAIR8800_DSK_H +#define _ALTAIR8800_DSK_H + +#include "sim_defs.h" + +#define DSK_MAX_TRACKS 80 +#define DSK_MAX_HEADS 2 + +#define DSK_DENSITY_SD 0x01 +#define DSK_DENSITY_DD 0x02 + +typedef struct { + int32 density; + int32 sectors; + int32 sectorsize; + int32 startsector; + int32 offset; +} DSK_TRACK; + +typedef struct { + int32 tracks; + int32 heads; + int32 interleaved; + DSK_TRACK track[DSK_MAX_TRACKS][DSK_MAX_HEADS]; +} DSK_FORMAT; + +typedef struct { + UNIT *unit; + DSK_FORMAT fmt; + uint32 dbg_verbose; +} DSK_INFO; + +extern t_stat dsk_init(DSK_INFO *d, UNIT *unit, int32 tracks, int32 heads, int32 interleaved); +extern t_stat dsk_init_format(DSK_INFO *d, int32 strack, int32 etrack, int32 shead, int32 ehead, + int32 den, int32 secs, int32 secsize, int32 stsec); +extern void dsk_set_verbose_flag(DSK_INFO *d, uint32 flag); +extern int32 dsk_size(DSK_INFO *d); +extern int32 dsk_tracks(DSK_INFO *d); +extern int32 dsk_track_size(DSK_INFO *d, int32 track, int32 head); +extern int32 dsk_sectors(DSK_INFO *d, int32 track, int32 head); +extern int32 dsk_sector_size(DSK_INFO *d, int32 track, int32 head); +extern int32 dsk_sector_offset(DSK_INFO *d, int32 track, int32 head, int32 sector); +extern int32 dsk_start_sector(DSK_INFO *d, int32 track, int32 head); +extern t_stat dsk_validate(DSK_INFO *d, int track, int head, int sector); +extern t_stat dsk_write_sector(DSK_INFO *d, int32 track, int32 head, int32 sector, const uint8 *buf, int32 *byteswritten); +extern t_stat dsk_read_sector(DSK_INFO *d, int32 track, int32 head, int32 sector, uint8 *buf, int32 *bytesread); +extern t_stat dsk_read_track(DSK_INFO *d, int32 track, int32 head, uint8 *buf); +extern t_stat dsk_write_track(DSK_INFO *d, int32 track, int32 head, uint8 fill); +extern t_stat dsk_format(DSK_INFO *d, uint8 fill); +extern void dsk_dump_buf(const uint8 *b, int32 size); +extern void dsk_show(DSK_INFO *d); +extern t_stat dsk_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); + +#endif + diff --git a/Altair8800/altair8800_sys.c b/Altair8800/altair8800_sys.c new file mode 100644 index 00000000..373f1bca --- /dev/null +++ b/Altair8800/altair8800_sys.c @@ -0,0 +1,167 @@ +/* altair8800_sys.c: MITS Altair 8800 SIMH System Interface + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + ---------------------------------------------------------- + + This module of the simulator contains the glue between the + Altair8800 simulator and SIMH. + + To add a device, these modules must be modified: + + altair8800_sys.h add external DEVICE declaration + altair8800_sys.c add DEVICE to sim_devices + + ---------------------------------------------------------- + + History: + 11/07/25 Initial version + +*/ + +#include "sim_defs.h" +#include "altair8800_sys.h" + +#if 0 +static t_bool fprint_stopped(FILE *st, t_stat reason); +#endif + +/* SCP data structures + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words needed for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "Altair 8800 (BUS)"; + +int32 sim_emax = SIM_EMAX; + +DEVICE *sim_devices[] = { + &bus_dev, + &cpu_dev, + &ssw_dev, + &simh_dev, + &ram_dev, + &bram_dev, + &rom_dev, + &po_dev, + &mdsk_dev, + &m2sio0_dev, + &m2sio1_dev, + &sio_dev, + &sbc200_dev, + &tarbell_dev, + &vfii_dev, + NULL +}; + +char memoryAccessMessage[256]; +char instructionMessage[256]; + +const char *sim_stop_messages[SCPE_BASE] = { + "Unknown error", /* 0 is reserved/unknown */ + "Breakpoint", + memoryAccessMessage, + instructionMessage, + "Invalid Opcode", + "HALT instruction" +}; + + +/* find_unit_index find index of a unit + + Inputs: + uptr = pointer to unit + Outputs: + result = index of device +*/ +int32 sys_find_unit_index(UNIT* uptr) +{ + DEVICE *dptr = find_dev_from_unit(uptr); + + if (dptr == NULL) { + return -1; + } + + return (uptr - dptr->units); +} + +#if 0 + +// Need to figure out how to initize in altair8800_sys.c +// +static t_bool fprint_stopped(FILE *st, t_stat reason) +{ + fprintf(st, "Hey, it stopped!\n"); + + return FALSE; +} + +#endif + +char *sys_strupr(const char *str) +{ + static char s[128]; + int i; + + for (i = 0; i < sizeof(s) && str[i] != '\0'; i++) { + s[i] = sim_toupper(str[i]); + } + + s[i] = '\0'; + + return s; +} + + +uint8 sys_floorlog2(unsigned int n) +{ + /* Compute log2(n) */ + uint8 r = 0; + if (n >= 1<<16) { + n >>=16; + r += 16; + } + if (n >= 1<< 8) { + n >>= 8; + r += 8; + } + if (n >= 1<< 4) { + n >>= 4; + r += 4; + } + if (n >= 1<< 2) { + n >>= 2; + r += 2; + } + if (n >= 1<< 1) { + r += 1; + } + return ((n == 0) ? (0xFF) : r); /* 0xFF is error return value */ +} + diff --git a/Altair8800/altair8800_sys.h b/Altair8800/altair8800_sys.h new file mode 100644 index 00000000..34a1b025 --- /dev/null +++ b/Altair8800/altair8800_sys.h @@ -0,0 +1,69 @@ +/* altair8800_sys.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef _ALTAIR8800_SYS_H +#define _ALTAIR8800_SYS_H + +#include "sim_defs.h" + +#define SIM_EMAX 6 + +extern DEVICE bus_dev; +extern DEVICE cpu_dev; +extern DEVICE ssw_dev; +extern DEVICE simh_dev; +extern DEVICE z80_dev; +extern DEVICE ram_dev; +extern DEVICE bram_dev; +extern DEVICE rom_dev; +extern DEVICE po_dev; +extern DEVICE mdsk_dev; +extern DEVICE m2sio0_dev; +extern DEVICE m2sio1_dev; +extern DEVICE sio_dev; +extern DEVICE sbc200_dev; +extern DEVICE tarbell_dev; +extern DEVICE vfii_dev; + +extern char memoryAccessMessage[256]; +extern char instructionMessage[256]; + +extern int32 sys_find_unit_index(UNIT* uptr); + +extern void sys_set_cpu_instr(t_stat (*routine)(void)); +extern void sys_set_cpu_pc(REG *reg); +extern void sys_set_cpu_pc_value(t_value (*routine)(void)); +extern void sys_set_cpu_parse_sym(t_stat (*routine)(CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)); +extern void sys_set_cpu_dasm(int32 (*routine)(char *S, const uint32 *val, const int32 addr)); +extern void sys_set_cpu_is_subroutine_call(t_bool (*routine)(t_addr **ret_addrs)); +extern char *sys_strupr(const char *str); +extern uint8 sys_floorlog2(unsigned int n); + +#endif diff --git a/Altair8800/mits_2sio.c b/Altair8800/mits_2sio.c new file mode 100644 index 00000000..592f8ecc --- /dev/null +++ b/Altair8800/mits_2sio.c @@ -0,0 +1,931 @@ +/* mits_2sio.c: MITS Altair 8800 88-2SIO + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 07-Nov-2025 Initial version + + ================================================================== + + The 88-2 Serial Input/Output Board (88-2SIO) is designed around an + Asynchronous Communications Interface Adapter (ACIA). + + The card had up to two physical I/O ports which could be connected + to any serial I/O device that would connect to a current loop, + RS232, or TTY interface. Available baud rates were jumper + selectable for each port from 110 to 9600. + + All I/O is via programmed I/O. Each each port has a status port + and a data port. A write to the status port can select some + options for the device (0x03 will reset the port). A read of the + status port gets the port status: + + +---+---+---+---+---+---+---+---+ + | R P V F C D O I | + +---+---+---+---+---+---+---+---+ + + I - A 1 in this bit position means a character has been received + on the data port and is ready to be read. + O - A 1 in this bit means the port is ready to receive a character + on the data port and transmit it out over the serial line. + D - A 1 in this bit means Data Carrier Detect is present. + C - A 1 in this bit means Clear to Send is present. + F - A 1 in this bit means a Framing Error has occurred. + V - A 1 in this bit means an Overrun has occurred. + P - A 1 in this bit means a Parity Error has occurred. + R - A 1 in this bit means an Interrupt has occurred. + + A read to the data port gets the buffered character, a write + to the data port writes the character to the device. + + The following are excerpts from Computer Notes, Volume 2, Issue 8, + Jan-Feb '77: + + GLITCHES + Q&A from the Repair Department + By Bruce Fowler + + We get many calls on how to interface terminals to the 2SIO. The + problem is that the Asynchronous Communications Interface Adapter's + (ACIA) handshaking signals make interfacing with the 2SIO a + somewhat complicated matter. An explanation of the signals and + their function should make the job easier. The three handshaking + signals--Data Carrier Detect (DCD), Request to Send (RTS) and + Clear to Send (CTS)--permit limited control of a modem or + peripheral. RTS is an output signal, and DCD and CTS are input + signals. + + Data will only leave the ACIA when CTS is active. + + The ACIA will receive data only when DCD is active. DCD is normally + used with modems. As long as DCD is inactive, the ACIA's receiver + section is inhibited and no data can be received by the ACIA. + + Information from the two input signals, CTS and DCD, is present in + the ACIA status register. Bit 2 represents /DCD, and bit 3 + represents /CTS. When bit 2 is high, DCD is inactive. When bit 3 is high, + CTS is inactive. When bit 2 goes low, valid data is sent to the ACIA. + When bit 3 goes low, data can be transmitted. + + / = Active Low + +*/ + +#include "sim_defs.h" +#include "sim_tmxr.h" +#include "altair8800_defs.h" +#include "s100_bus.h" +#include "mits_2sio.h" + +#define M2SIO_NAME "MITS 88-2SIO SERIAL ADAPTER" +#define M2SIO0_SNAME "M2SIO0" +#define M2SIO1_SNAME "M2SIO1" + +#define M2SIO_PORTS 2 + +#define M2SIO_WAIT 250 /* Service Wait Interval */ + +#define M2SIO0_IOBASE 0x10 +#define M2SIO0_IOSIZE 2 +#define M2SIO1_IOBASE 0x12 +#define M2SIO1_IOSIZE 2 + +#define M2SIO_RDRF 0x01 /* Receive Data Register Full */ +#define M2SIO_TDRE 0x02 /* Transmit Data Register Empty */ +#define M2SIO_DCD 0x04 /* Data Carrier Detect */ +#define M2SIO_CTS 0x08 /* Clear to Send */ +#define M2SIO_FE 0x10 /* Framing Error */ +#define M2SIO_OVRN 0x20 /* Overrun */ +#define M2SIO_PE 0x40 /* Parity Error */ +#define M2SIO_IRQ 0x80 /* Interrupt Request */ +#define M2SIO_RESET 0x03 /* Reset */ +#define M2SIO_CLK1 0x00 /* Divide Clock by 1 */ +#define M2SIO_CLK16 0x01 /* Divide Clock by 16 */ +#define M2SIO_CLK64 0x02 /* Divide Clock by 64 */ +#define M2SIO_72E 0x00 /* 7-2-E */ +#define M2SIO_72O 0x04 /* 7-2-O */ +#define M2SIO_71E 0x08 /* 7-1-E */ +#define M2SIO_71O 0x0C /* 7-1-O */ +#define M2SIO_82N 0x10 /* 8-2-N */ +#define M2SIO_81N 0x14 /* 8-1-N */ +#define M2SIO_81E 0x18 /* 8-1-E */ +#define M2SIO_81O 0x1C /* 8-1-O */ +#define M2SIO_FMTMSK 0x1c /* Length, Parity, Stop Mask */ +#define M2SIO_RTSLTID 0x00 /* RTS Low, Xmit Int Disabled */ +#define M2SIO_RTSLTIE 0x20 /* RTS Low, Xmit Int Enabled */ +#define M2SIO_RTSHTID 0x40 /* RTS High, Xmit Int Disabled */ +#define M2SIO_RTSHTBR 0x60 /* RTS High, Xmit Break */ +#define M2SIO_RTSMSK 0x60 /* RTS Bit Mask */ +#define M2SIO_RIE 0x80 /* Receive Int Enabled */ + +#define M2SIO_BAUD 9600 /* Default baud rate */ + +static const char* m2sio_description(DEVICE *dptr); +static t_stat m2sio_svc(UNIT *uptr); +static t_stat m2sio_reset(DEVICE *dptr, int32 (*routine)(const int32, const int32, const int32)); +static t_stat m2sio0_reset(DEVICE *dptr); +static t_stat m2sio1_reset(DEVICE *dptr); +static t_stat m2sio_attach(UNIT *uptr, const char *cptr); +static t_stat m2sio_detach(UNIT *uptr); +static t_stat m2sio_set_console(UNIT *uptr, int32 value, const char *cptr, void *desc); +static t_stat m2sio_set_baud(UNIT *uptr, int32 value, const char *cptr, void *desc); +static t_stat m2sio_show_baud(FILE *st, UNIT *uptr, int32 value, const void *desc); +static t_stat m2sio_config_line(UNIT *uptr); +static t_stat m2sio_config_rts(DEVICE *dptr, char rts); +static int32 m2sio0_io(int32 addr, int32 io, int32 data); +static int32 m2sio1_io(int32 addr, int32 io, int32 data); +static int32 m2sio_io(DEVICE *dptr, int32 addr, int32 io, int32 data); +static int32 m2sio_stat(DEVICE *dptr, int32 io, int32 data); +static int32 m2sio_data(DEVICE *dptr, int32 io, int32 data); +static void m2sio_int(UNIT *uptr); +static int32 m2sio_map_kbdchar(UNIT *uptr, int32 ch); +static t_stat m2sio_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); + +static M2SIO_REG m2sio0_reg; +static M2SIO_REG m2sio1_reg; + +/* Debug Flags */ +#define STATUS_MSG (1 << 0) +#define IRQ_MSG (1 << 1) +#define VERBOSE_MSG (1 << 2) + +/* Debug Table */ +static DEBTAB m2sio_dt[] = { + { "STATUS", STATUS_MSG, "Status messages" }, + { "IRQ", IRQ_MSG, "Interrupt messages" }, + { "VERBOSE", VERBOSE_MSG, "Verbose messages" }, + { NULL, 0 } +}; + +/* Terminal multiplexer library descriptors */ + +static TMLN m2sio0_tmln[] = { /* line descriptors */ + { 0 } +}; + +static TMLN m2sio1_tmln[] = { /* line descriptors */ + { 0 } +}; + +static TMXR m2sio0_tmxr = { /* multiplexer descriptor */ + 1, /* number of terminal lines */ + 0, /* listening port (reserved) */ + 0, /* master socket (reserved) */ + m2sio0_tmln, /* line descriptor array */ + NULL, /* line connection order */ + NULL /* multiplexer device (derived internally) */ +}; + +static TMXR m2sio1_tmxr = { /* multiplexer descriptor */ + 1, /* number of terminal lines */ + 0, /* listening port (reserved) */ + 0, /* master socket (reserved) */ + m2sio1_tmln, /* line descriptor array */ + NULL, /* line connection order */ + NULL /* multiplexer device (derived internally) */ +}; + + +static MTAB m2sio_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", + &set_iobase, &show_iobase, NULL, "Sets MITS 2SIO base I/O address" }, + + { UNIT_M2SIO_MAP, 0, "NOMAP", "NOMAP", NULL, NULL, NULL, + "Do not map any character" }, /* disable character mapping */ + { UNIT_M2SIO_MAP, UNIT_M2SIO_MAP, "MAP", "MAP", NULL, NULL, NULL, + "Enable mapping of characters" }, /* enable all character mapping */ + { UNIT_M2SIO_UPPER, 0, "NOUPPER", "NOUPPER", NULL, NULL, NULL, + "Console input remains unchanged" }, /* do not change case of input characters */ + { UNIT_M2SIO_UPPER, UNIT_M2SIO_UPPER, "UPPER", "UPPER", NULL, NULL, NULL, + "Convert console input to upper case" }, /* change input characters to upper case */ + { UNIT_M2SIO_BS, 0, "BS", "BS", NULL, NULL, NULL, + "Map delete to backspace" }, /* map delete to backspace */ + { UNIT_M2SIO_BS, UNIT_M2SIO_BS, "DEL", "DEL", NULL, NULL, NULL, + "Map backspace to delete" }, /* map backspace to delete */ + { UNIT_M2SIO_DTR, UNIT_M2SIO_DTR, "DTR", "DTR", NULL, NULL, NULL, + "DTR follows RTS" }, + { UNIT_M2SIO_DTR, 0, "NODTR", "NODTR", NULL, NULL, NULL, + "DTR does not follow RTS (default)" }, + { UNIT_M2SIO_DCD, UNIT_M2SIO_DCD, "DCD", "DCD", NULL, NULL, NULL, + "Force DCD active low" }, + { UNIT_M2SIO_DCD, 0, "NODCD", "NODCD", NULL, NULL, NULL, + "DCD follows status line (default)" }, + { UNIT_M2SIO_CTS, UNIT_M2SIO_CTS, "CTS", "CTS", NULL, NULL, NULL, + "Force CTS active low" }, + { UNIT_M2SIO_CTS, 0, "NOCTS", "NOCTS", NULL, NULL, NULL, + "CTS follows status line (default)" }, + + { MTAB_XTD | MTAB_VUN, UNIT_M2SIO_CONSOLE, NULL, "CONSOLE", &m2sio_set_console, NULL, NULL, "Set as CONSOLE" }, + { MTAB_XTD | MTAB_VUN, 0, NULL, "NOCONSOLE", &m2sio_set_console, NULL, NULL, "Remove as CONSOLE" }, + + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "BAUD", "BAUD", &m2sio_set_baud, &m2sio_show_baud, + NULL, "Set baud rate (default=9600)" }, + { 0 } +}; + +static RES m2sio0_res = { M2SIO0_IOBASE, M2SIO0_IOSIZE, 0, 0, &m2sio0_tmxr }; +static RES m2sio1_res = { M2SIO1_IOBASE, M2SIO1_IOSIZE, 0, 0, &m2sio1_tmxr }; + +static UNIT unit0[] = { + { UDATA (&m2sio_svc, UNIT_ATTABLE | UNIT_M2SIO_MAP | UNIT_M2SIO_CONSOLE | UNIT_M2SIO_DCD | UNIT_M2SIO_CTS , 0), M2SIO_WAIT }, +}; + +static UNIT unit1[] = { + { UDATA (&m2sio_svc, UNIT_ATTABLE | UNIT_M2SIO_DCD | UNIT_M2SIO_CTS, 0), M2SIO_WAIT }, +}; + +static REG reg0[] = { + { HRDATAD (M2STA0, m2sio0_reg.stb, 8, "2SIO port 0 status register"), }, + { HRDATAD (M2CTL0, m2sio0_reg.ctb, 8, "2SIO port 0 control register"), }, + { HRDATAD (M2RXD0, m2sio0_reg.rxb, 8, "2SIO port 0 rx data buffer"), }, + { HRDATAD (M2TXD0, m2sio0_reg.txb, 8, "2SIO port 0 tx data buffer"), }, + { FLDATAD (M2TXP0, m2sio0_reg.txp, 0, "2SIO port 0 tx data pending"), }, + { FLDATAD (M2CON0, m2sio0_reg.conn, 0, "2SIO port 0 connection status"), }, + { FLDATAD (M2RIE0, m2sio0_reg.rie, 0, "2SIO port 0 receive interrupt enable"), }, + { FLDATAD (M2TIE0, m2sio0_reg.tie, 0, "2SIO port 0 transmit interrupt enable"), }, + { FLDATAD (M2RTS0, m2sio0_reg.rts, 0, "2SIO port 0 RTS status (active low)"), }, + { FLDATAD (M2RDRF0, m2sio0_reg.stb, 0, "2SIO port 0 RDRF status"), }, + { FLDATAD (M2TDRE0, m2sio0_reg.stb, 1, "2SIO port 0 TDRE status"), }, + { FLDATAD (M2DCD0, m2sio0_reg.stb, 2, "2SIO port 0 DCD status (active low)"), }, + { FLDATAD (M2CTS0, m2sio0_reg.stb, 3, "2SIO port 0 CTS status (active low)"), }, + { FLDATAD (M2OVRN0, m2sio0_reg.stb, 4, "2SIO port 0 OVRN status"), }, + { FLDATAD (DCDL0, m2sio0_reg.dcdl, 0, "2SIO port 0 DCD latch"), }, + { DRDATAD (M2WAIT0, unit0[0].wait, 32, "2SIO port 0 wait cycles"), }, + { FLDATAD (M2INTEN0, m2sio0_reg.intenable, 1, "2SIO port 0 Global vectored interrupt enable"), }, + { DRDATAD (M2VEC0, m2sio0_reg.intvector, 8, "2SIO port 0 interrupt vector"), }, + { HRDATAD (M2DBVAL0, m2sio0_reg.databus, 8, "2SIO port 0 data bus value"), }, + { NULL } +}; +static REG reg1[] = { + { HRDATAD (M2STA1, m2sio1_reg.stb, 8, "2SIO port 1 status buffer"), }, + { HRDATAD (M2CTL1, m2sio1_reg.ctb, 8, "2SIO port 1 control register"), }, + { HRDATAD (M2RXD1, m2sio1_reg.rxb, 8, "2SIO port 1 rx data buffer"), }, + { HRDATAD (M2TXD1, m2sio1_reg.txb, 8, "2SIO port 1 tx data buffer"), }, + { FLDATAD (M2TXP1, m2sio1_reg.txp, 0, "2SIO port 1 tx data pending"), }, + { FLDATAD (M2CON1, m2sio1_reg.conn, 0, "2SIO port 1 connection status"), }, + { FLDATAD (M2RIE1, m2sio1_reg.rie, 0, "2SIO port 1 receive interrupt enable"), }, + { FLDATAD (M2TIE1, m2sio1_reg.tie, 0, "2SIO port 1 transmit interrupt enable"), }, + { FLDATAD (M2RTS1, m2sio1_reg.rts, 0, "2SIO port 1 RTS status (active low)"), }, + { FLDATAD (M2RDRF1, m2sio1_reg.stb, 0, "2SIO port 1 RDRF status"), }, + { FLDATAD (M2TDRE1, m2sio1_reg.stb, 1, "2SIO port 1 TDRE status"), }, + { FLDATAD (M2DCD1, m2sio1_reg.stb, 2, "2SIO port 1 DCD status (active low)"), }, + { FLDATAD (M2CTS1, m2sio1_reg.stb, 3, "2SIO port 1 CTS status (active low)"), }, + { FLDATAD (M2OVRN1, m2sio1_reg.stb, 4, "2SIO port 1 OVRN status"), }, + { FLDATAD (DCDL1, m2sio1_reg.dcdl, 0, "2SIO port 1 DCD latch"), }, + { DRDATAD (M2WAIT1, unit1[0].wait, 32, "2SIO port 1 wait cycles"), }, + { FLDATAD (M2INTEN1, m2sio1_reg.intenable, 1, "2SIO port 1 Global vectored interrupt enable"), }, + { DRDATAD (M2VEC1, m2sio1_reg.intvector, 8, "2SIO port 1 interrupt vector"), }, + { HRDATAD (M2DBVAL1, m2sio1_reg.databus, 8, "2SIO port 1 data bus value"), }, + { NULL } +}; + +DEVICE m2sio0_dev = { + M2SIO0_SNAME, /* name */ + unit0, /* unit */ + reg0, /* registers */ + m2sio_mod, /* modifiers */ + 1, /* # units */ + ADDRRADIX, /* address radix */ + ADDRWIDTH, /* address width */ + 1, /* address increment */ + DATARADIX, /* data radix */ + DATAWIDTH, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &m2sio0_reset, /* reset routine */ + NULL, /* boot routine */ + &m2sio_attach, /* attach routine */ + &m2sio_detach, /* detach routine */ + &m2sio0_res, /* context */ + (DEV_DISABLE | DEV_DEBUG | DEV_MUX), /* flags */ + 0, /* debug control */ + m2sio_dt, /* debug flags */ + NULL, /* mem size routine */ + NULL, /* logical name */ + &m2sio_show_help, /* help */ + NULL, /* attach help */ + NULL, /* context for help */ + &m2sio_description /* description */ +}; + +DEVICE m2sio1_dev = { + M2SIO1_SNAME, /* name */ + unit1, /* unit */ + reg1, /* registers */ + m2sio_mod, /* modifiers */ + 1, /* # units */ + 10, /* address radix */ + 31, /* address width */ + 1, /* address increment */ + 8, /* data radix */ + 8, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &m2sio1_reset, /* reset routine */ + NULL, /* boot routine */ + &m2sio_attach, /* attach routine */ + &m2sio_detach, /* detach routine */ + &m2sio1_res, /* context */ + (DEV_DISABLE | DEV_DEBUG | DEV_MUX), /* flags */ + 0, /* debug control */ + m2sio_dt, /* debug flags */ + NULL, /* mem size routine */ + NULL, /* logical name */ + &m2sio_show_help, /* help */ + NULL, /* attach help */ + NULL, /* context for help */ + &m2sio_description /* description */ +}; + +static const char* m2sio_description(DEVICE *dptr) +{ + return M2SIO_NAME; +} + +static t_stat m2sio0_reset(DEVICE *dptr) +{ + dptr->units->up8 = &m2sio0_reg; + + return(m2sio_reset(dptr, &m2sio0_io)); +} + +static t_stat m2sio1_reset(DEVICE *dptr) +{ + dptr->units->up8 = &m2sio1_reg; + + return(m2sio_reset(dptr, &m2sio1_io)); +} + +static t_stat m2sio_reset(DEVICE *dptr, int32 (*routine)(const int32, const int32, const int32)) +{ + RES *res; + M2SIO_REG *reg; + + if ((res = (RES *) dptr->ctxt) == NULL) { + return SCPE_IERR; + } + if ((reg = (M2SIO_REG *) dptr->units->up8) == NULL) { + return SCPE_IERR; + } + + /* Connect/Disconnect I/O Ports at base address */ + if (dptr->flags & DEV_DIS) { /* Device is disabled */ + s100_bus_remio(res->io_base, res->io_size, routine); + s100_bus_noconsole(&dptr->units[0]); + + return SCPE_OK; + } + + /* Device is enabled */ + s100_bus_addio(res->io_base, res->io_size, routine, dptr->name); + + /* Set as CONSOLE unit */ + if (dptr->units[0].flags & UNIT_M2SIO_CONSOLE) { + s100_bus_console(&dptr->units[0]); + } + + /* Set DEVICE for this UNIT */ + dptr->units[0].dptr = dptr; + dptr->units[0].wait = M2SIO_WAIT; + + /* Enable TMXR modem control passthrough */ + tmxr_set_modem_control_passthru(res->tmxr); + + /* Reset status registers */ + reg->stb = M2SIO_CTS | M2SIO_DCD; + reg->txp = FALSE; + reg->dcdl = FALSE; + + if (dptr->units[0].flags & UNIT_ATT) { + m2sio_config_rts(dptr, 1); /* disable RTS */ + } + + /* Start service routine */ + sim_activate(&dptr->units[0], dptr->units[0].wait); + + sim_debug(STATUS_MSG, dptr, "reset adapter.\n"); + + return SCPE_OK; +} + + +static t_stat m2sio_svc(UNIT *uptr) +{ + DEVICE *dptr; + RES *res; + M2SIO_REG *reg; + int32 c,s,stb; + t_stat r; + + if ((dptr = find_dev_from_unit(uptr)) == NULL) + return SCPE_IERR; + + if ((res = (RES *) dptr->ctxt) == NULL) { + return SCPE_IERR; + } + if ((reg = (M2SIO_REG *) uptr->up8) == NULL) { + return SCPE_IERR; + } + + /* Check for new incoming connection */ + if (uptr->flags & UNIT_ATT) { + if (tmxr_poll_conn(res->tmxr) >= 0) { /* poll connection */ + + reg->conn = TRUE; /* set connected */ + + sim_debug(STATUS_MSG, uptr->dptr, "new connection.\n"); + } + } + + /* Update incoming modem status bits */ + if (uptr->flags & UNIT_ATT) { + tmxr_set_get_modem_bits(res->tmxr->ldsc, 0, 0, &s); + stb = reg->stb; + reg->stb &= ~M2SIO_CTS; + reg->stb |= ((s & TMXR_MDM_CTS) || (uptr->flags & UNIT_M2SIO_CTS)) ? 0 : M2SIO_CTS; /* Active Low */ + if ((stb ^ reg->stb) & M2SIO_CTS) { + sim_debug(STATUS_MSG, uptr->dptr, "CTS state changed to %s.\n", (reg->stb & M2SIO_CTS) ? "LOW" : "HIGH"); + } + + if (!reg->dcdl) { + reg->stb &= ~M2SIO_DCD; + reg->stb |= ((s & TMXR_MDM_DCD) || (uptr->flags & UNIT_M2SIO_DCD)) ? 0 : M2SIO_DCD; /* Active Low */ + if ((stb ^ reg->stb) & M2SIO_DCD) { + if ((reg->stb & M2SIO_DCD) == M2SIO_DCD) { + reg->dcdl = TRUE; + if (reg->rie) { + m2sio_int(uptr); + } + } + sim_debug(STATUS_MSG, uptr->dptr, "DCD state changed to %s.\n", (reg->stb & M2SIO_DCD) ? "LOW" : "HIGH"); + } + } + + /* Enable receiver if DCD is active low */ + res->tmxr->ldsc->rcve = !(reg->stb & M2SIO_DCD); + } + + /* TX data */ + if (reg->txp) { + if (uptr->flags & UNIT_ATT) { + if (!(reg->stb & M2SIO_CTS)) { /* Active low */ + r = tmxr_putc_ln(res->tmxr->ldsc, reg->txb); + reg->txp = FALSE; /* Reset TX Pending */ + } else { + r = SCPE_STALL; + } + } else { + r = sim_putchar(reg->txb); + reg->txp = FALSE; /* Reset TX Pending */ + } + + if (r == SCPE_LOST) { + reg->conn = FALSE; /* Connection was lost */ + sim_debug(STATUS_MSG, uptr->dptr, "lost connection.\n"); + } + + /* If TX buffer now empty, send interrupt */ + if ((!reg->txp) && (reg->tie)) { + m2sio_int(uptr); + } + + } + + /* Update TDRE if not set and no character pending */ + if (!reg->txp && !(reg->stb & M2SIO_TDRE)) { + if (uptr->flags & UNIT_ATT) { + tmxr_poll_tx(res->tmxr); + reg->stb |= (tmxr_txdone_ln(res->tmxr->ldsc) && reg->conn) ? M2SIO_TDRE : 0; + } else { + reg->stb |= M2SIO_TDRE; + } + } + + /* Check for Data if RX buffer empty */ + if (!(reg->stb & M2SIO_RDRF)) { + if (uptr->flags & UNIT_ATT) { + tmxr_poll_rx(res->tmxr); + + c = tmxr_getc_ln(res->tmxr->ldsc); + } else { + c = s100_bus_poll_kbd(uptr); + } + + if (c & (TMXR_VALID | SCPE_KFLAG)) { + reg->rxb = m2sio_map_kbdchar(uptr, c); + reg->stb |= M2SIO_RDRF; + reg->stb &= ~(M2SIO_FE | M2SIO_OVRN | M2SIO_PE); + if (reg->rie) { + m2sio_int(uptr); + } + } + } + + sim_activate_abs(uptr, uptr->wait); + + return SCPE_OK; +} + + +/* Attach routine */ +static t_stat m2sio_attach(UNIT *uptr, const char *cptr) +{ + DEVICE *dptr; + RES *res; + M2SIO_REG *reg; + t_stat r; + + if ((dptr = find_dev_from_unit(uptr)) == NULL) + return SCPE_IERR; + + if ((res = (RES *) dptr->ctxt) == NULL) { + return SCPE_IERR; + } + if ((reg = (M2SIO_REG *) uptr->up8) == NULL) { + return SCPE_IERR; + } + + sim_debug(VERBOSE_MSG, uptr->dptr, "attach (%s).\n", cptr); + + if ((r = tmxr_attach(res->tmxr, uptr, cptr)) == SCPE_OK) { + + if (res->tmxr->ldsc->serport) { + r = m2sio_config_rts(uptr->dptr, reg->rts); /* update RTS */ + } + + res->tmxr->ldsc->rcve = 1; + } + + return r; +} + + +/* Detach routine */ +static t_stat m2sio_detach(UNIT *uptr) +{ + DEVICE *dptr; + RES *res; + + if ((dptr = find_dev_from_unit(uptr)) == NULL) + return SCPE_IERR; + + if ((res = (RES *) dptr->ctxt) == NULL) { + return SCPE_IERR; + } + + sim_debug(VERBOSE_MSG, uptr->dptr, "detach.\n"); + + if (uptr->flags & UNIT_ATT) { + sim_cancel(uptr); + + return (tmxr_detach(res->tmxr, uptr)); + } + + return SCPE_UNATT; +} + +static t_stat m2sio_set_console(UNIT *uptr, int32 value, const char *cptr, void *desc) +{ + if (value == UNIT_M2SIO_CONSOLE) { + s100_bus_console(uptr); + } + else { + s100_bus_noconsole(uptr); + } + + return SCPE_OK; +} + +static t_stat m2sio_set_baud(UNIT *uptr, int32 value, const char *cptr, void *desc) +{ + M2SIO_REG *reg; + int32 baud; + t_stat r = SCPE_ARG; + + if ((reg = (M2SIO_REG *) uptr->up8) == NULL) { + return SCPE_IERR; + } + + if (!(uptr->flags & UNIT_ATT)) { + return SCPE_UNATT; + } + + if (cptr != NULL) { + if (sscanf(cptr, "%d", &baud)) { + switch (baud) { + case 110: + case 150: + case 300: + case 1200: + case 1800: + case 2400: + case 4800: + case 9600: + case 19200: + reg->baud = baud; + r = m2sio_config_line(uptr); + + return r; + + default: + break; + } + } + } + + return r; +} + +static t_stat m2sio_show_baud(FILE *st, UNIT *uptr, int32 value, const void *desc) +{ + M2SIO_REG *reg; + + if ((reg = (M2SIO_REG *) uptr->up8) == NULL) { + return SCPE_IERR; + } + + if (uptr->flags & UNIT_ATT) { + fprintf(st, "Baud rate: %d", reg->baud); + } + + return SCPE_OK; +} + +static t_stat m2sio_config_line(UNIT *uptr) +{ + DEVICE *dptr; + RES *res; + M2SIO_REG *reg; + char config[20]; + const char *fmt; + t_stat r = SCPE_IERR; + + if ((dptr = find_dev_from_unit(uptr)) == NULL) + return SCPE_IERR; + + if ((res = (RES *) dptr->ctxt) == NULL) { + return SCPE_IERR; + } + if ((reg = (M2SIO_REG *) uptr->up8) == NULL) { + return SCPE_IERR; + } + + if (reg != NULL) { + switch (reg->ctb & M2SIO_FMTMSK) { + case M2SIO_72E: + fmt = "7E2"; + break; + case M2SIO_72O: + fmt = "7O2"; + break; + case M2SIO_71E: + fmt = "7E1"; + break; + case M2SIO_71O: + fmt = "7O1"; + break; + case M2SIO_82N: + fmt = "8N2"; + break; + case M2SIO_81E: + fmt = "8E1"; + break; + case M2SIO_81O: + fmt = "8O1"; + break; + case M2SIO_81N: + default: + fmt = "8N1"; + break; + } + + sprintf(config, "%d-%s", reg->baud, fmt); + + r = tmxr_set_config_line(res->tmxr->ldsc, config); + + sim_debug(STATUS_MSG, uptr->dptr, "port configuration set to '%s'.\n", config); + } + + return r; +} + +/* +** RTS is active low +** 0 = RTS active +** 1 = RTS inactive +*/ +static t_stat m2sio_config_rts(DEVICE *dptr, char rts) +{ + RES *res; + M2SIO_REG *reg; + t_stat r = SCPE_OK; + int32 s; + + if ((res = (RES *) dptr->ctxt) == NULL) { + return SCPE_IERR; + } + if ((reg = (M2SIO_REG *) dptr->units->up8) == NULL) { + return SCPE_IERR; + } + + if (dptr->units[0].flags & UNIT_ATT) { + /* RTS Control */ + s = TMXR_MDM_RTS; + if (dptr->units[0].flags & UNIT_M2SIO_DTR) { + s |= TMXR_MDM_DTR; + } + + if (!rts) { + r = tmxr_set_get_modem_bits(res->tmxr->ldsc, s, 0, NULL); + if (reg->rts) { + sim_debug(STATUS_MSG, dptr, "RTS state changed to HIGH.\n"); + } + } else { + r = tmxr_set_get_modem_bits(res->tmxr->ldsc, 0, s, NULL); + if (!reg->rts) { + sim_debug(STATUS_MSG, dptr, "RTS state changed to LOW.\n"); + } + } + } + + reg->rts = rts; /* Active low */ + + return r; +} + +static int32 m2sio0_io(int32 addr, int32 io, int32 data) +{ + return(m2sio_io(&m2sio0_dev, addr, io, data)); +} + +static int32 m2sio1_io(int32 addr, int32 io, int32 data) +{ + return(m2sio_io(&m2sio1_dev, addr, io, data)); +} + +static int32 m2sio_io(DEVICE *dptr, int32 addr, int32 io, int32 data) +{ + int32 r; + + if (addr & 0x01) { + r = m2sio_data(dptr, io, data); + } else { + r = m2sio_stat(dptr, io, data); + } + + return(r); +} + +static int32 m2sio_stat(DEVICE *dptr, int32 io, int32 data) +{ + M2SIO_REG *reg; + int32 r; + + if ((reg = (M2SIO_REG *) dptr->units->up8) == NULL) { + return SCPE_IERR; + } + + if (io == S100_IO_READ) { + r = reg->stb; + } else { + reg->ctb = data & 0xff; /* save control byte */ + + /* Master Reset */ + if ((data & M2SIO_RESET) == M2SIO_RESET) { + sim_debug(STATUS_MSG, dptr, "MC6850 master reset.\n"); + reg->stb &= (M2SIO_CTS | M2SIO_DCD); /* Reset status register */ + reg->rxb = 0x00; + reg->txp = FALSE; + reg->tie = FALSE; + reg->rie = FALSE; + reg->dcdl = FALSE; + m2sio_config_rts(dptr, 1); /* disable RTS */ + } else { + /* Interrupt Enable */ + reg->rie = (data & M2SIO_RIE) == M2SIO_RIE; /* Receive interrupt enable */ + reg->tie = (data & M2SIO_RTSMSK) == M2SIO_RTSLTIE; /* Transmit interrupt enable */ + switch (data & M2SIO_RTSMSK) { + case M2SIO_RTSLTIE: + case M2SIO_RTSLTID: + m2sio_config_rts(dptr, 0); /* enable RTS */ + break; + + case M2SIO_RTSHTID: + case M2SIO_RTSHTBR: + m2sio_config_rts(dptr, 1); /* disable RTS */ + break; + + default: + break; + } + + /* Set data bits, parity and stop bits format */ + m2sio_config_line(&dptr->units[0]); + } + + r = 0x00; + } + + return(r); +} + +static int32 m2sio_data(DEVICE *dptr, int32 io, int32 data) +{ + M2SIO_REG *reg; + int32 r; + + if ((reg = (M2SIO_REG *) dptr->units->up8) == NULL) { + return SCPE_IERR; + } + + if (io == S100_IO_READ) { + r = reg->rxb; + reg->stb &= ~(M2SIO_RDRF | M2SIO_FE | M2SIO_OVRN | M2SIO_PE | M2SIO_IRQ); + reg->dcdl = FALSE; + } else { + reg->txb = data; + reg->stb &= ~(M2SIO_TDRE | M2SIO_IRQ); + reg->txp = TRUE; + r = 0x00; + } + + return r; +} + +static void m2sio_int(UNIT *uptr) +{ + M2SIO_REG *reg; + + if ((reg = (M2SIO_REG *) uptr->up8) == NULL) { + return; + } + + if (reg->intenable) { + s100_bus_int((1 << reg->intvector), reg->databus); /* Generate interrupt on the bus */ + reg->stb |= M2SIO_IRQ; + + sim_debug(IRQ_MSG, uptr->dptr, "%s: IRQ Vector=%d Status=%02X\n", sim_uname(uptr), reg->intvector, reg->stb); + } +} + +static int32 m2sio_map_kbdchar(UNIT *uptr, int32 ch) +{ + ch &= 0xff; + + if (uptr->flags & UNIT_M2SIO_MAP) { + if (uptr->flags & UNIT_M2SIO_BS) { + if (ch == KBD_BS) { + return KBD_DEL; + } + } + else if (ch == KBD_DEL) { + return KBD_BS; + } + + if (uptr->flags & UNIT_M2SIO_UPPER) { + return toupper(ch); + } + } + + return ch; +} + +static t_stat m2sio_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "\nAltair 8800 88-2SIO (%s)\n", dptr->name); + + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + fprint_reg_help (st, dptr); + fprintf(st, "\n\n"); + tmxr_attach_help(st, dptr, uptr, flag, cptr); + + fprintf(st, "----- NOTES -----\n\n"); + fprintf(st, "Only one device may poll the host keyboard for CONSOLE input.\n"); + fprintf(st, "Use SET %s CONSOLE to select this UNIT as the CONSOLE device.\n", sim_dname(dptr)); + fprintf(st, "\nUse SHOW BUS CONSOLE to display the current CONSOLE device.\n\n"); + + return SCPE_OK; +} + diff --git a/Altair8800/mits_2sio.h b/Altair8800/mits_2sio.h new file mode 100644 index 00000000..7be304c9 --- /dev/null +++ b/Altair8800/mits_2sio.h @@ -0,0 +1,67 @@ +/* mits_2sio.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef _MITS_2SIO_H +#define _MITS_2SIO_H + +#define UNIT_V_M2SIO_CONSOLE (UNIT_V_UF + 0) /* Port checks console for input */ +#define UNIT_M2SIO_CONSOLE (1 << UNIT_V_M2SIO_CONSOLE) +#define UNIT_V_M2SIO_MAP (UNIT_V_UF + 1) /* map keyboard characters */ +#define UNIT_M2SIO_MAP (1 << UNIT_V_M2SIO_MAP) +#define UNIT_V_M2SIO_BS (UNIT_V_UF + 2) /* map delete to backspace */ +#define UNIT_M2SIO_BS (1 << UNIT_V_M2SIO_BS) +#define UNIT_V_M2SIO_UPPER (UNIT_V_UF + 3) /* map to upper case */ +#define UNIT_M2SIO_UPPER (1 << UNIT_V_M2SIO_UPPER) +#define UNIT_V_M2SIO_DTR (UNIT_V_UF + 4) /* DTR follows RTS */ +#define UNIT_M2SIO_DTR (1 << UNIT_V_M2SIO_DTR) +#define UNIT_V_M2SIO_DCD (UNIT_V_UF + 5) /* Force DCD active low */ +#define UNIT_M2SIO_DCD (1 << UNIT_V_M2SIO_DCD) +#define UNIT_V_M2SIO_CTS (UNIT_V_UF + 6) /* Force CTS active low */ +#define UNIT_M2SIO_CTS (1 << UNIT_V_M2SIO_CTS) + +typedef struct { + int32 port; /* Port 0 or 1 */ + t_bool conn; /* Connected Status */ + int32 baud; /* Baud rate */ + int32 rts; /* RTS Status */ + int32 rxb; /* Receive Buffer */ + int32 txb; /* Transmit Buffer */ + t_bool txp; /* Transmit Pending */ + int32 stb; /* Status Buffer */ + int32 ctb; /* Control Buffer */ + t_bool rie; /* Rx Int Enable */ + t_bool tie; /* Tx Int Enable */ + t_bool dcdl; /* DCD latch */ + uint8 intenable; /* Interrupt Enable */ + uint8 intvector; /* Interrupt Vector */ + uint8 databus; /* Data Bus Value */ +} M2SIO_REG; + +#endif diff --git a/Altair8800/mits_dsk.c b/Altair8800/mits_dsk.c new file mode 100644 index 00000000..cb07b689 --- /dev/null +++ b/Altair8800/mits_dsk.c @@ -0,0 +1,631 @@ +/* mits_dsk.c: MITS Altair 88-DCDD Simulator + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + Based on work by Charles E Owen (c) 1997 + Based on work by Peter Schorn (c) 2002-2023 + Minidisk support added by Mike Douglas + + History: + 07-Nov-2025 Initial version + + ================================================================== + + The 88-DCDD is a 8-inch floppy controller which can control up + to 16 daisy-chained Pertec FD-400 hard-sectored floppy drives. + Each diskette has physically 77 tracks of 32 137-byte sectors + each. + + The controller is interfaced to the CPU by use of 3 I/O addresses, + standardly, these are device numbers 10, 11, and 12 (octal). + + Address Mode Function + ------- ---- -------- + 10 Out Selects and enables Controller and Drive + 10 In Indicates status of Drive and Controller + 11 Out Controls Disk Function + 11 In Indicates current sector position of disk + 12 Out Write data + 12 In Read data + + Drive Select Out (Device 10 OUT): + + +---+---+---+---+---+---+---+---+ + | C | X | X | X | Device | + +---+---+---+---+---+---+---+---+ + + C = If this bit is 1, the disk controller selected by 'device' is + cleared. If the bit is zero, 'device' is selected as the + device being controlled by subsequent I/O operations. + X = not used + Device = value zero thru 15, selects drive to be controlled. + + Drive Status In (Device 10 IN): + + +---+---+---+---+---+---+---+---+ + | R | Z | I | X | X | H | M | W | + +---+---+---+---+---+---+---+---+ + + W - When 0, write circuit ready to write another byte. + M - When 0, head movement is allowed + H - When 0, indicates head is loaded for read/write + X - not used (will be 0) + I - When 0, indicates interrupts enabled (not used by this simulator) + Z - When 0, indicates head is on track 0 + R - When 0, indicates that read circuit has new byte to read + + Drive Control (Device 11 OUT): + + +---+---+---+---+---+---+---+---+ + | W | C | D | E | U | H | O | I | + +---+---+---+---+---+---+---+---+ + + I - When 1, steps head IN one track + O - When 1, steps head OUT one track + H - When 1, loads head to drive surface + U - When 1, unloads head + E - Enables interrupts (ignored by this simulator) + D - Disables interrupts (ignored by this simulator) + C - When 1 lowers head current (ignored by this simulator) + W - When 1, starts Write Enable sequence: W bit on device 10 + (see above) will go 1 and data will be read from port 12 + until 137 bytes have been read by the controller from + that port. The W bit will go off then, and the sector data + will be written to disk. Before you do this, you must have + stepped the track to the desired number, and waited until + the right sector number is presented on device 11 IN, then + set this bit. + + Sector Position (Device 11 IN): + + As the sectors pass by the read head, they are counted and the + number of the current one is available in this register. + + +---+---+---+---+---+---+---+---+ + | X | X | Sector Number | T | + +---+---+---+---+---+---+---+---+ + + X = Not used + Sector number = binary of the sector number currently under the + head, 0-31. + T = Sector True, is a 0 when the sector is positioned to read or + write. + +*/ + +#include "sim_defs.h" +#include "altair8800_sys.h" +#include "altair8800_dsk.h" +#include "s100_bus.h" +#include "mits_dsk.h" + +static int32 poc = TRUE; /* Power On Clear */ + +/* Debug flags */ +#define IN_MSG (1 << 0) +#define OUT_MSG (1 << 1) +#define READ_MSG (1 << 2) +#define WRITE_MSG (1 << 3) +#define SECTOR_STUCK_MSG (1 << 4) +#define TRACK_STUCK_MSG (1 << 5) +#define VERBOSE_MSG (1 << 6) + +static int32 mdsk10(const int32 port, const int32 io, const int32 data); +static int32 mdsk11(const int32 port, const int32 io, const int32 data); +static int32 mdsk12(const int32 port, const int32 io, const int32 data); + +static t_stat mdsk_boot(int32 unitno, DEVICE *dptr); +static t_stat mdsk_reset(DEVICE *dptr); +static t_stat mdsk_attach(UNIT *uptr, CONST char *cptr); +static t_stat mdsk_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +static const char* mdsk_description(DEVICE *dptr); + +/* global data on status */ + +/* currently selected drive (values are 0 .. NUM_OF_DSK) + current_disk < NUM_OF_DSK implies that the corresponding disk is attached to a file */ +static int32 current_disk = NUM_OF_DSK; + +static int32 current_track [NUM_OF_DSK]; +static int32 current_sector [NUM_OF_DSK]; +static int32 current_byte [NUM_OF_DSK]; +static int32 current_flag [NUM_OF_DSK]; +static int32 sectors_per_track [NUM_OF_DSK]; +static int32 current_imageSize [NUM_OF_DSK]; +static int32 tracks [NUM_OF_DSK]; +static int32 in9_count = 0; +static int32 in9_message = FALSE; +static int32 dirty = FALSE; /* TRUE when buffer has unwritten data in it */ +static int32 warnLevelDSK = 3; +static int32 warnLock [NUM_OF_DSK]; +static int32 warnAttached [NUM_OF_DSK]; +static int32 warnDSK10 = 0; +static int32 warnDSK11 = 0; +static int32 warnDSK12 = 0; +static int8 dskbuf[DSK_SECTSIZE]; /* data Buffer */ +static int32 sector_true = 0; /* sector true flag for sector register read */ + +/* 88DSK Standard I/O Data Structures */ + +static UNIT mdsk_unit[NUM_OF_DSK] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) } +}; + +static REG mdsk_reg[] = { + { FLDATAD (POC, poc, 0x01, "Power on Clear flag"), }, + { DRDATAD (DISK, current_disk, 4, + "Selected disk register"), }, + { BRDATAD (CURTRACK, current_track, 10, 32, NUM_OF_DSK, + "Selected track register array"), REG_CIRC + REG_RO }, + { BRDATAD (CURSECTOR, current_sector, 10, 32, NUM_OF_DSK, + "Selected sector register array"), REG_CIRC + REG_RO }, + { BRDATAD (CURBYTE, current_byte, 10, 32, NUM_OF_DSK, + "Current byte register array"), REG_CIRC + REG_RO }, + { BRDATAD (CURFLAG, current_flag, 10, 32, NUM_OF_DSK, + "Current flag register array"), REG_CIRC + REG_RO }, + { BRDATAD (TRACKS, tracks, 10, 32, NUM_OF_DSK, + "Number of tracks register array"), REG_CIRC }, + { BRDATAD (SECTPERTRACK, sectors_per_track, 10, 32, NUM_OF_DSK, + "Number of sectors per track register array"), REG_CIRC }, + { BRDATAD (IMAGESIZE, current_imageSize, 10, 32, NUM_OF_DSK, + "Size of disk image array"), REG_CIRC + REG_RO }, + { DRDATAD (IN9COUNT, in9_count, 4, + "Count of IN(9) register"), REG_RO }, + { DRDATAD (IN9MESSAGE, in9_message, 4, + "BOOL for IN(9) message register"), REG_RO }, + { DRDATAD (DIRTY, dirty, 4, + "BOOL for write needed register"), REG_RO }, + { DRDATAD (DSKWL, warnLevelDSK, 32, + "Warn level register") }, + { BRDATAD (WARNLOCK, warnLock, 10, 32, NUM_OF_DSK, + "Count of write to locked register array"), REG_CIRC + REG_RO }, + { BRDATAD (WARNATTACHED, warnAttached, 10, 32, NUM_OF_DSK, + "Count for selection of unattached disk register array"), REG_CIRC + REG_RO }, + { DRDATAD (WARNDSK10, warnDSK10, 4, + "Count of IN(8) on unattached disk register"), REG_RO }, + { DRDATAD (WARNDSK11, warnDSK11, 4, + "Count of IN/OUT(9) on unattached disk register"), REG_RO }, + { DRDATAD (WARNDSK12, warnDSK12, 4, + "Count of IN/OUT(10) on unattached disk register"), REG_RO }, + { BRDATAD (DISKBUFFER, dskbuf, 10, 8, DSK_SECTSIZE, + "Disk data buffer array"), REG_CIRC + REG_RO }, + { NULL } +}; + +#define DSK_NAME "MITS 88-DCDD Floppy Disk Controller" +#define DEV_NAME "DSK" + +static const char* mdsk_description(DEVICE *dptr) { + return DSK_NAME; +} + +static MTAB mdsk_mod[] = { + { UNIT_DSK_WLK, 0, "WRTENB", "WRTENB", NULL, NULL, NULL, + "Enables " DSK_NAME "n for writing" }, + { UNIT_DSK_WLK, UNIT_DSK_WLK, "WRTLCK", "WRTLCK", NULL, NULL, NULL, + "Locks " DSK_NAME "n for writing" }, + { 0 } +}; + +/* Debug Flags */ +static DEBTAB mdsk_dt[] = { + { "IN", IN_MSG, "IN operations" }, + { "OUT", OUT_MSG, "OUT operations" }, + { "READ", READ_MSG, "Read operations" }, + { "WRITE", WRITE_MSG, "Write operations" }, + { "SECTOR_STUCK", SECTOR_STUCK_MSG, "Sector stuck" }, + { "TRACK_STUCK", TRACK_STUCK_MSG, "Track stuck" }, + { "VERBOSE", VERBOSE_MSG, "Verbose messages" }, + { NULL, 0 } +}; + +DEVICE mdsk_dev = { + DEV_NAME, mdsk_unit, mdsk_reg, mdsk_mod, + NUM_OF_DSK, ADDRRADIX, ADDRWIDTH, 1, DATARADIX, DATAWIDTH, + NULL, NULL, &mdsk_reset, + &mdsk_boot, &mdsk_attach, NULL, + NULL, (DEV_DISABLE | DEV_DEBUG), 0, + mdsk_dt, NULL, NULL, &mdsk_show_help, &dsk_attach_help, NULL, + &mdsk_description +}; + +static const char* selectInOut(const int32 io) { + return io == 0 ? "IN" : "OUT"; +} + +/* service routines to handle simulator functions */ +/* reset routine */ + +static t_stat mdsk_reset(DEVICE *dptr) +{ + int32 i; + + if (dptr->flags & DEV_DIS) { + s100_bus_remio(0x08, 1, &mdsk10); + s100_bus_remio(0x09, 1, &mdsk11); + s100_bus_remio(0x0A, 1, &mdsk12); + + poc = TRUE; + } + else { + if (poc) { + s100_bus_addio(0x08, 1, &mdsk10, dptr->name); + s100_bus_addio(0x09, 1, &mdsk11, dptr->name); + s100_bus_addio(0x0A, 1, &mdsk12, dptr->name); + + for (i = 0; i < NUM_OF_DSK; i++) { + current_imageSize[i] = 0; + sectors_per_track[i] = DSK_SECT; + tracks[i] = MAX_TRACKS; + } + } + } + + for (i = 0; i < NUM_OF_DSK; i++) { + warnLock[i] = 0; + warnAttached[i] = 0; + current_track[i] = 0; + current_sector[i] = 0; + current_byte[i] = 0; + current_flag[i] = 0; + } + + warnDSK10 = 0; + warnDSK11 = 0; + warnDSK12 = 0; + current_disk = NUM_OF_DSK; + in9_count = 0; + in9_message = FALSE; + + return SCPE_OK; +} +/* mdsk_attach - determine type of drive attached based on disk image size */ + +static t_stat mdsk_attach(UNIT *uptr, CONST char *cptr) +{ + int32 thisUnitIndex; + int32 imageSize; + t_stat r; + + sim_switches |= SWMASK ('E'); /* File must exist */ + + r = attach_unit(uptr, cptr); /* attach unit */ + + if (r != SCPE_OK) { /* error? */ + return r; + } + + ASSURE(uptr != NULL); + thisUnitIndex = sys_find_unit_index(uptr); + ASSURE((0 <= thisUnitIndex) && (thisUnitIndex < NUM_OF_DSK)); + + /* If the file size is close to the mini-disk image size, set the number of + tracks to 16, otherwise, 32 sectors per track. */ + + imageSize = sim_fsize(uptr -> fileref); + current_imageSize[thisUnitIndex] = imageSize; + sectors_per_track[thisUnitIndex] = (((MINI_DISK_SIZE - MINI_DISK_DELTA < imageSize) && + (imageSize < MINI_DISK_SIZE + MINI_DISK_DELTA)) ? + MINI_DISK_SECT : DSK_SECT); + return SCPE_OK; +} + +static t_stat mdsk_boot(int32 unitno, DEVICE *dptr) +{ + *((int32 *) sim_PC->loc) = 0xff00; + return SCPE_OK; +} + +static int32 dskseek(const UNIT *xptr) +{ + return sim_fseek(xptr -> fileref, DSK_SECTSIZE * sectors_per_track[current_disk] * current_track[current_disk] + + DSK_SECTSIZE * current_sector[current_disk], SEEK_SET); +} + +/* precondition: current_disk < NUM_OF_DSK */ +static void writebuf(void) +{ + int32 i, rtn; + UNIT *uptr; + i = current_byte[current_disk]; /* null-fill rest of sector if any */ + while (i < DSK_SECTSIZE) + dskbuf[i++] = 0; + uptr = mdsk_dev.units + current_disk; + if (((uptr -> flags) & UNIT_DSK_WLK) == 0) { /* write enabled */ + sim_debug(WRITE_MSG, &mdsk_dev, + "DSK%i: " ADDRESS_FORMAT " OUT 0x0a (WRITE) D%d T%d S%d\n", + current_disk, s100_bus_get_addr(), current_disk, + current_track[current_disk], current_sector[current_disk]); + if (dskseek(uptr)) { + sim_debug(VERBOSE_MSG, &mdsk_dev, + "DSK%i: " ADDRESS_FORMAT " fseek failed D%d T%d S%d\n", + current_disk, s100_bus_get_addr(), current_disk, + current_track[current_disk], current_sector[current_disk]); + } + rtn = sim_fwrite(dskbuf, 1, DSK_SECTSIZE, uptr -> fileref); + if (rtn != DSK_SECTSIZE) { + sim_debug(VERBOSE_MSG, &mdsk_dev, + "DSK%i: " ADDRESS_FORMAT " sim_fwrite failed T%d S%d Return=%d\n", + current_disk, s100_bus_get_addr(), current_track[current_disk], + current_sector[current_disk], rtn); + } + } else if ( (mdsk_dev.dctrl & VERBOSE_MSG) && (warnLock[current_disk] < warnLevelDSK) ) { + /* write locked - print warning message if required */ + warnLock[current_disk]++; + sim_debug(VERBOSE_MSG, &mdsk_dev, + "DSK%i: " ADDRESS_FORMAT " Attempt to write to locked DSK%d - ignored.\n", + current_disk, s100_bus_get_addr(), current_disk); + } + current_flag[current_disk] &= 0xfe; /* ENWD off */ + current_byte[current_disk] = 0xff; + dirty = FALSE; +} + +/* I/O instruction handlers, called from the CPU module when an + IN or OUT instruction is issued. + + Each function is passed an 'io' flag, where 0 means a read from + the port, and 1 means a write to the port. On input, the actual + input is passed as the return value, on output, 'data' is written + to the device. +*/ + +/* Disk Controller Status/Select */ + +/* IMPORTANT: The status flags read by port 8 IN instruction are + INVERTED, that is, 0 is true and 1 is false. To handle this, the + simulator keeps it's own status flags as 0=false, 1=true; and + returns the COMPLEMENT of the status flags when read. This makes + setting/testing of the flag bits more logical, yet meets the + simulation requirement that they are reversed in hardware. +*/ + +static int32 mdsk10(const int32 port, const int32 io, const int32 data) +{ + int32 current_disk_flags; + in9_count = 0; + if (io == 0) { /* IN: return flags */ + if (current_disk >= NUM_OF_DSK) { + if ((mdsk_dev.dctrl & VERBOSE_MSG) && (warnDSK10 < warnLevelDSK)) { + warnDSK10++; + sim_debug(VERBOSE_MSG, &mdsk_dev, + "DSK%i: " ADDRESS_FORMAT + " Attempt of IN 0x08 on unattached disk - ignored.\n", + current_disk, s100_bus_get_addr()); + } + return 0xff; /* no drive selected - can do nothing */ + } + return (~current_flag[current_disk]) & 0xff; /* return the COMPLEMENT! */ + } + + /* OUT: Controller set/reset/enable/disable */ + if (dirty) /* implies that current_disk < NUM_OF_DSK */ + writebuf(); + sim_debug(OUT_MSG, &mdsk_dev, "DSK%i: " ADDRESS_FORMAT " OUT 0x08: %x\n", current_disk, s100_bus_get_addr(), data); + current_disk = data & NUM_OF_DSK_MASK; /* 0 <= current_disk < NUM_OF_DSK */ + current_disk_flags = (mdsk_dev.units + current_disk) -> flags; + if ((current_disk_flags & UNIT_ATT) == 0) { /* nothing attached? */ + if ( (mdsk_dev.dctrl & VERBOSE_MSG) && (warnAttached[current_disk] < warnLevelDSK) ) { + warnAttached[current_disk]++; + sim_debug(VERBOSE_MSG, &mdsk_dev, + "DSK%i: " ADDRESS_FORMAT + " Attempt to select unattached DSK%d - ignored.\n", + current_disk, s100_bus_get_addr(), current_disk); + } + current_disk = NUM_OF_DSK; + } else { + current_sector[current_disk] = 0xff; /* reset internal counters */ + current_byte[current_disk] = 0xff; + if (data & 0x80) /* disable drive? */ + current_flag[current_disk] = 0; /* yes, clear all flags */ + else { /* enable drive */ + current_flag[current_disk] = 0x1a; /* move head true */ + if (current_track[current_disk] == 0) /* track 0? */ + current_flag[current_disk] |= 0x40; /* yes, set track 0 true as well */ + if (sectors_per_track[current_disk] == MINI_DISK_SECT) /* drive enable loads head for Minidisk */ + current_flag[current_disk] |= 0x84; + } + } + return 0; /* ignored since OUT */ +} + +/* Disk Drive Status/Functions */ + +static int32 mdsk11(const int32 port, const int32 io, const int32 data) +{ + if (current_disk >= NUM_OF_DSK) { + if ((mdsk_dev.dctrl & VERBOSE_MSG) && (warnDSK11 < warnLevelDSK)) { + warnDSK11++; + sim_debug(VERBOSE_MSG, &mdsk_dev, + "DSK%i: " ADDRESS_FORMAT + " Attempt of %s 0x09 on unattached disk - ignored.\n", + current_disk, s100_bus_get_addr(), selectInOut(io)); + } + return 0xff; /* no drive selected - can do nothing */ + } + + /* now current_disk < NUM_OF_DSK */ + if (io == 0) { /* read sector position */ + in9_count++; + if ((mdsk_dev.dctrl & SECTOR_STUCK_MSG) && (in9_count > 2 * DSK_SECT) && (!in9_message)) { + in9_message = TRUE; + sim_debug(SECTOR_STUCK_MSG, &mdsk_dev, + "DSK%i: " ADDRESS_FORMAT " Looping on sector find.\n", + current_disk, s100_bus_get_addr()); + } + sim_debug(IN_MSG, &mdsk_dev, "DSK%i: " ADDRESS_FORMAT " IN 0x09\n", current_disk, s100_bus_get_addr()); + if (dirty) /* implies that current_disk < NUM_OF_DSK */ + writebuf(); + if (current_flag[current_disk] & 0x04) { /* head loaded? */ + sector_true ^= 1; /* return sector true every other entry */ + if (sector_true == 0) { /* true when zero */ + current_sector[current_disk]++; + if (current_sector[current_disk] >= sectors_per_track[current_disk]) + current_sector[current_disk] = 0; + current_byte[current_disk] = 0xff; + } + return (((current_sector[current_disk] << 1) & 0x3e) /* return sector number and...) */ + | 0xc0 | sector_true); /* sector true, and set 'unused' bits */ + } else + return 0xff; /* head not loaded - return 0xff */ + } + + in9_count = 0; + /* drive functions */ + + sim_debug(OUT_MSG, &mdsk_dev, "DSK%i: " ADDRESS_FORMAT " OUT 0x09: %x\n", current_disk, s100_bus_get_addr(), data); + if (data & 0x01) { /* step head in */ + if (current_track[current_disk] == (tracks[current_disk] - 1)) { + sim_debug(TRACK_STUCK_MSG, &mdsk_dev, + "DSK%i: " ADDRESS_FORMAT " Unnecessary step in.\n", + current_disk, s100_bus_get_addr()); + } + current_track[current_disk]++; + current_flag[current_disk] &= 0xbf; /* mwd 1/29/13: track zero now false */ + if (current_track[current_disk] > (tracks[current_disk] - 1)) + current_track[current_disk] = (tracks[current_disk] - 1); + if (dirty) /* implies that current_disk < NUM_OF_DSK */ + writebuf(); + current_sector[current_disk] = 0xff; + current_byte[current_disk] = 0xff; + } + + if (data & 0x02) { /* step head out */ + if (current_track[current_disk] == 0) { + sim_debug(TRACK_STUCK_MSG, &mdsk_dev, + "DSK%i: " ADDRESS_FORMAT " Unnecessary step out.\n", + current_disk, s100_bus_get_addr()); + } + current_track[current_disk]--; + if (current_track[current_disk] < 0) { + current_track[current_disk] = 0; + current_flag[current_disk] |= 0x40; /* track 0 if there */ + } + if (dirty) /* implies that current_disk < NUM_OF_DSK */ + writebuf(); + current_sector[current_disk] = 0xff; + current_byte[current_disk] = 0xff; + } + + if (dirty) /* implies that current_disk < NUM_OF_DSK */ + writebuf(); + + if (data & 0x04) { /* head load */ + current_flag[current_disk] |= 0x04; /* turn on head loaded bit */ + current_flag[current_disk] |= 0x80; /* turn on 'read data available' */ + } + + if ((data & 0x08) && (sectors_per_track[current_disk] != MINI_DISK_SECT)) { /* head unload */ + current_flag[current_disk] &= 0xfb; /* turn off 'head loaded' bit */ + current_flag[current_disk] &= 0x7f; /* turn off 'read data available' */ + current_sector[current_disk] = 0xff; + current_byte[current_disk] = 0xff; + } + + /* interrupts & head current are ignored */ + + if (data & 0x80) { /* write sequence start */ + current_byte[current_disk] = 0; + current_flag[current_disk] |= 0x01; /* enter new write data on */ + } + return 0; /* ignored since OUT */ +} + +/* Disk Data In/Out */ + +static int32 mdsk12(const int32 port, const int32 io, const int32 data) +{ + int32 i, rtn; + UNIT *uptr; + + if (current_disk >= NUM_OF_DSK) { + if ((mdsk_dev.dctrl & VERBOSE_MSG) && (warnDSK12 < warnLevelDSK)) { + warnDSK12++; + sim_debug(VERBOSE_MSG, &mdsk_dev, + "DSK%i: " ADDRESS_FORMAT + " Attempt of %s 0x0a on unattached disk - ignored.\n", + current_disk, s100_bus_get_addr(), selectInOut(io)); + } + return 0; + } + + /* now current_disk < NUM_OF_DSK */ + in9_count = 0; + uptr = mdsk_dev.units + current_disk; + if (io == 0) { + if (current_byte[current_disk] >= DSK_SECTSIZE) { + /* physically read the sector */ + sim_debug(READ_MSG, &mdsk_dev, + "DSK%i: " ADDRESS_FORMAT " IN 0x0a (READ) D%d T%d S%d\n", + current_disk, s100_bus_get_addr(), current_disk, + current_track[current_disk], current_sector[current_disk]); + for (i = 0; i < DSK_SECTSIZE; i++) + dskbuf[i] = 0; + if (dskseek(uptr)) { + if ((mdsk_dev.dctrl & VERBOSE_MSG) && (warnDSK12 < warnLevelDSK)) { + warnDSK12++; + sim_debug(VERBOSE_MSG, &mdsk_dev, + "DSK%i: " ADDRESS_FORMAT " fseek error D%d T%d S%d\n", + current_disk, s100_bus_get_addr(), current_disk, + current_track[current_disk], current_sector[current_disk]); + } + } + rtn = sim_fread(dskbuf, 1, DSK_SECTSIZE, uptr -> fileref); + if (rtn != DSK_SECTSIZE) { + if ((mdsk_dev.dctrl & VERBOSE_MSG) && (warnDSK12 < warnLevelDSK)) { + warnDSK12++; + sim_debug(VERBOSE_MSG, &mdsk_dev, + "DSK%i: " ADDRESS_FORMAT " sim_fread error D%d T%d S%d\n", + current_disk, s100_bus_get_addr(), current_disk, + current_track[current_disk], current_sector[current_disk]); + } + } + current_byte[current_disk] = 0; + } + return dskbuf[current_byte[current_disk]++] & 0xff; + } else { + if (current_byte[current_disk] >= DSK_SECTSIZE) + writebuf(); /* from above we have that current_disk < NUM_OF_DSK */ + else { + dirty = TRUE; /* this guarantees for the next call to writebuf that current_disk < NUM_OF_DSK */ + dskbuf[current_byte[current_disk]++] = data & 0xff; + } + return 0; /* ignored since OUT */ + } +} + +static t_stat mdsk_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "\nAltair 8800 88-DCDD (%s)\n", sim_dname(dptr)); + + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + fprint_reg_help (st, dptr); + + return SCPE_OK; +} + diff --git a/Altair8800/mits_dsk.h b/Altair8800/mits_dsk.h new file mode 100644 index 00000000..de843e5e --- /dev/null +++ b/Altair8800/mits_dsk.h @@ -0,0 +1,57 @@ +/* mits_dsk.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef _MITS_DSK_H +#define _MITS_DSK_H + +#define UNIT_V_DSK_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_DSK_WLK (1 << UNIT_V_DSK_WLK) + +#define NUM_OF_DSK 4 /* NUM_OF_DSK must be power of two */ +#define NUM_OF_DSK_MASK (NUM_OF_DSK - 1) + +#define DSK_SECTSIZE 137 /* size of sector */ +#define DSK_SECT 32 /* sectors per track */ +#define MAX_TRACKS 2048 /* number of tracks, + original Altair has 77 tracks only */ +#define DSK_TRACSIZE (DSK_SECTSIZE * DSK_SECT) +#define MAX_DSK_SIZE (DSK_TRACSIZE * MAX_TRACKS) +#define BOOTROM_SIZE_DSK 256 /* size of boot rom */ + +#define MINI_DISK_SECT 16 /* mini disk sectors per track */ +#define MINI_DISK_TRACKS 35 /* number of tracks on mini disk */ +#define MINI_DISK_SIZE (MINI_DISK_TRACKS * MINI_DISK_SECT * DSK_SECTSIZE) +#define MINI_DISK_DELTA 4096 /* threshold for detecting mini disks */ + +#define ALTAIR_DISK_SIZE 337664 /* size of regular Altair disks */ +#define ALTAIR_DISK_DELTA 256 /* threshold for detecting regular Altair disks */ + +#endif + diff --git a/Altair8800/s100_bram.c b/Altair8800/s100_bram.c new file mode 100644 index 00000000..2388b1de --- /dev/null +++ b/Altair8800/s100_bram.c @@ -0,0 +1,484 @@ +/* s100_bram.c: MITS Altair 8800 Banked RAM + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 07-Nov-2025 Initial version + +*/ + +#include "s100_bus.h" +#include "s100_bram.h" + +static t_stat bram_reset (DEVICE *dptr); +static t_stat bram_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +static t_stat bram_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +static int32 bram_io (const int32 addr, const int32 rw, const int32 data); +static int32 bram_memio (const int32 addr, const int32 rw, const int32 data); +static t_stat bram_set_banks (int32 banks); +static t_stat bram_clear_command (UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat bram_enable_command (UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat bram_randomize_command (UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat bram_banks_command (UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static void bram_addio (int32 type); +static void bram_remio (int32 type); +static t_stat bram_set_type (int32 type); +static t_stat bram_type_command (UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static void bram_clear (void); +static void bram_randomize (void); +static t_stat bram_show_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +static const char* bram_description (DEVICE *dptr); + +static void PutBYTE(register uint32 Addr, const register uint32 Value); +static uint32 GetBYTE(register uint32 Addr); + +static int32 poc = TRUE; /* Power On Clear */ + +static int32 *M = NULL; /* RAM */ +static int32 bram_banks = 0; +static int32 bram_bank = 0; +static int32 bram_type = BRAM_TYPE_NONE; + +static BRAM B[BRAM_TYPE_MAX + 1] = { + { 0x00, 0, 0, "NONE" }, + { 0xff, 1, 8, "ERAM" }, + { 0x40, 1, 8, "VRAM" }, + { 0x40, 1, 7, "CRAM" }, + { 0xc0, 1, MAXBANK, "HRAM" }, + { 0x40, 1, MAXBANK, "B810" }, +}; + +#define DEV_NAME "BRAM" + +static const char* bram_description(DEVICE *dptr) { + return "Banked Random Access Memory"; +} + +static UNIT bram_unit = { + UDATA (NULL, UNIT_FIX | UNIT_BINK, MAXBANKSIZE) +}; + +static REG bram_reg[] = { + { FLDATAD (POC, poc, 0x01, "Power on Clear flag"), }, + { HRDATAD (BANK, bram_bank, MAXBANKS2LOG, "Selected bank"), }, + { DRDATAD (BANKS, bram_banks, 8, "Number of banks"), }, + { DRDATAD (TYPE, bram_type, 8, "RAM type"), }, + { NULL } +}; + +static MTAB bram_mod[] = { + { UNIT_BRAM_VERBOSE, UNIT_BRAM_VERBOSE, "VERBOSE", "VERBOSE", NULL, NULL, + NULL, "Enable verbose messages" }, + { UNIT_BRAM_VERBOSE, 0, "QUIET", "QUIET", NULL, NULL, + NULL, "Disable verbose messages" }, + + { MTAB_XTD | MTAB_VDV, BRAM_TYPE_B810 , NULL, "B810", &bram_type_command, + NULL, NULL, "Sets the RAM type to Digital Design B810" }, + { MTAB_XTD | MTAB_VDV, BRAM_TYPE_CRAM , NULL, "CRAM", &bram_type_command, + NULL, NULL, "Sets the RAM type to Cromemco" }, + { MTAB_XTD | MTAB_VDV, BRAM_TYPE_ERAM , NULL, "ERAM", &bram_type_command, + NULL, NULL, "Sets the RAM type to SD Systems ExpandoRAM" }, + { MTAB_XTD | MTAB_VDV, BRAM_TYPE_HRAM , NULL, "HRAM", &bram_type_command, + NULL, NULL, "Sets the RAM type to NorthStar" }, + { MTAB_XTD | MTAB_VDV, BRAM_TYPE_VRAM , NULL, "VRAM", &bram_type_command, + NULL, NULL, "Sets the RAM type to Vector" }, + { MTAB_XTD | MTAB_VDV, BRAM_TYPE_NONE , NULL, "NONE", &bram_type_command, + NULL, NULL, "Sets the RAM type to NONE" }, + + { MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, NULL, "BANKS={1-16}", &bram_banks_command, + NULL, NULL, "Sets the RAM size" }, + { MTAB_XTD | MTAB_VDV | MTAB_VALR, 1, NULL, "ADDPAGE={PAGE | START-END | ALL}", &bram_enable_command, + NULL, NULL, "Enable RAM page(s)" }, + { MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, NULL, "REMPAGE={PAGE | START-END | ALL}", &bram_enable_command, + NULL, NULL, "Disable RAM page(s)" }, + { MTAB_VDV, 0, NULL, "CLEAR", &bram_clear_command, + NULL, NULL, "Sets RAM to 0x00" }, + { MTAB_VDV, 0, NULL, "RANDOMIZE", &bram_randomize_command, + NULL, NULL, "Sets RAM to random values" }, + { 0 } +}; + +/* Debug Flags */ +static DEBTAB bram_dt[] = { + { NULL, 0 } +}; + +DEVICE bram_dev = { + DEV_NAME, /* name */ + &bram_unit, /* units */ + bram_reg, /* registers */ + bram_mod, /* modifiers */ + 1, /* # units */ + ADDRRADIX, /* address radix */ + ADDRWIDTH, /* address width */ + 1, /* addr increment */ + DATARADIX, /* data radix */ + DATAWIDTH, /* data width */ + &bram_ex, /* examine routine */ + &bram_dep, /* deposit routine */ + &bram_reset, /* reset routine */ + NULL, /* boot routine */ + NULL, /* attach routine */ + NULL, /* detach routine */ + NULL, /* context */ + (DEV_DISABLE | DEV_DIS), /* flags */ + 0, /* debug control */ + bram_dt, /* debug flags */ + NULL, /* mem size routine */ + NULL, /* logical name */ + &bram_show_help, /* help */ + NULL, /* attach help */ + NULL, /* context available to help routines */ + &bram_description /* device description */ +}; + +static t_stat bram_reset(DEVICE *dptr) +{ + if (dptr->flags & DEV_DIS) { /* Disable Device */ + bram_set_type(BRAM_TYPE_NONE); + + poc = TRUE; + } + else { + if (poc) { + poc = FALSE; + } + else { + bram_bank = 0; + } + } + + return SCPE_OK; +} + +/* memory examine */ +static t_stat bram_ex(t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ + *vptr = GetBYTE(addr & ADDRMASK) & DATAMASK; + + return SCPE_OK; +} + +/* memory deposit */ +static t_stat bram_dep(t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ + PutBYTE(addr & ADDRMASK, val & DATAMASK); + + return SCPE_OK; +} + +static int32 bram_io(const int32 addr, const int32 rw, const int32 data) +{ + if (rw == S100_IO_WRITE) { + + switch (bram_type) { + case BRAM_TYPE_HRAM: + if (data >= 0 && data < B[bram_type].banks) { + bram_bank = data; + } else { + sim_printf("Invalid bank select 0x%02x for %s\n", data, B[bram_type].name); + } + break; + + case BRAM_TYPE_B810: + if (data >= 0 && data < B[bram_type].banks) { + bram_bank = data; + } else { + sim_printf("Invalid bank select 0x%02x for %s\n", data, B[bram_type].name); + } + break; + + case BRAM_TYPE_ERAM: + if (data >= 0 && data < B[bram_type].banks) { + bram_bank = data; + if (bram_unit.flags & UNIT_BRAM_VERBOSE) { + sim_printf("%s selecting bank %d\n", B[bram_type].name, data); + } + } else { + sim_printf("Invalid bank select 0x%02x for %s\n", data, B[bram_type].name); + } + break; + + case BRAM_TYPE_VRAM: + switch(data & 0xFF) { + case 0x01: + case 0x41: // OASIS uses this for some reason? */ + bram_bank = 0; + break; + case 0x02: + case 0x42: // OASIS uses this for some reason? */ + bram_bank = 1; + break; + case 0x04: + bram_bank = 2; + break; + case 0x08: + bram_bank = 3; + break; + case 0x10: + bram_bank = 4; + break; + case 0x20: + bram_bank = 5; + break; + case 0x40: + bram_bank = 6; + break; + case 0x80: + bram_bank = 7; + break; + default: + sim_printf("Invalid bank select 0x%02x for %s\n", data, B[bram_type].name); + break; + } + break; + + case BRAM_TYPE_CRAM: + switch(data & 0x7F) { + case 0x01: + bram_bank = 0; + break; + case 0x02: + bram_bank = 1; + break; + case 0x04: + bram_bank = 2; + break; + case 0x08: + bram_bank = 3; + break; + case 0x10: + bram_bank = 4; + break; + case 0x20: + bram_bank = 5; + break; + case 0x40: + bram_bank = 6; + break; + default: + sim_printf("Invalid bank select 0x%02x for %s\n", data, B[bram_type].name); + break; + } + break; + + default: + break; + } + } + + return DATAMASK; +} + +static int32 bram_memio(const int32 addr, const int32 rw, const int32 data) +{ + if (rw == S100_IO_READ) { + return GetBYTE(addr); + } + + PutBYTE(addr, data); + + return DATAMASK; +} + +static uint32 GetBYTE(register uint32 Addr) +{ + t_addr bankAddr; + + if (M != NULL) { + Addr &= ADDRMASK; + + bankAddr = Addr + (bram_bank * MAXBANKSIZE); + + return M[bankAddr] & DATAMASK; + } + + return DATAMASK; +} + +static void PutBYTE(register uint32 Addr, const register uint32 Value) +{ + t_addr bankAddr; + + if (M != NULL) { + Addr &= ADDRMASK; + + bankAddr = Addr + (bram_bank * MAXBANKSIZE); + + M[bankAddr] = Value & DATAMASK; + } +} + +static void bram_addio(int32 type) +{ + if (type > BRAM_TYPE_NONE && type <= BRAM_TYPE_MAX) { + if (B[type].size) { + s100_bus_addio_out(B[type].baseport, B[type].size, &bram_io, B[type].name); + } + } +} + +static void bram_remio(int32 type) +{ + if (type > BRAM_TYPE_NONE && type <= BRAM_TYPE_MAX) { + s100_bus_remio_out(B[type].baseport, B[type].size, &bram_io); + } +} + +static t_stat bram_set_type(int32 type) +{ + if (bram_type == type) { /* No change */ + return SCPE_OK; + } + + bram_remio(bram_type); /* Changing type - remove previous IO */ + + bram_type = type; + bram_bank = 0; + + bram_set_banks(B[bram_type].banks); + bram_addio(bram_type); + + return SCPE_OK; +} + +static t_stat bram_type_command(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + return bram_set_type(value); +} + +static t_stat bram_set_banks(int32 banks) { + if (banks > 0 && banks <= MAXBANK) { + M = realloc(M, banks * MAXBANKSIZE); + } + else if (M != NULL) { + free(M); + + M = NULL; + + s100_bus_remmem(0x0000, MAXBANKSIZE, &bram_memio); /* Remove enabled pages */ + } + + bram_banks = banks; + + return SCPE_OK; +} + +static t_stat bram_banks_command(UNIT *uptr, int32 value, CONST char *cptr, void *desc) { + int32 result, banks; + + if (cptr == NULL) { + sim_printf("Banks must be provided as SET %s BANKS=1-%d\n", DEV_NAME, MAXBANK); + return SCPE_ARG | SCPE_NOMESSAGE; + } + + result = sscanf(cptr, "%i", &banks); + + if (result == 1 && banks && banks <= MAXBANK) { + return bram_set_banks(banks); + } + + return SCPE_ARG | SCPE_NOMESSAGE; +} + +static t_stat bram_enable_command(UNIT *uptr, int32 value, CONST char *cptr, void *desc) { + int32 size; + t_addr start, end; + + if (cptr == NULL) { + sim_printf("Memory page(s) must be provided as SET %s [ADD|REM]PAGE=E0-EF\n", DEV_NAME); + return SCPE_ARG | SCPE_NOMESSAGE; + } + + if (get_range(NULL, cptr, &start, &end, 16, PAGEMASK, 0) == NULL) { + return SCPE_ARG; + } + + if (start < MAXPAGE) { + start = start << LOG2PAGESIZE; + } + if (end < MAXPAGE) { + end = end << LOG2PAGESIZE; + } + + start &= 0xff00; + end &= 0xff00; + + size = end - start + PAGESIZE; + + if (value) { + s100_bus_addmem(start, size, &bram_memio, DEV_NAME); /* Add pages */ + } + else { + s100_bus_remmem(start, size, &bram_memio); /* Remove pages */ + } + + return SCPE_OK; +} + +static t_stat bram_clear_command(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + bram_clear(); + + return SCPE_OK; +} + +static t_stat bram_randomize_command(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + bram_randomize(); + + return SCPE_OK; +} + +static void bram_clear() +{ + int32 i; + + for (i = 0; i < MAXBANKSIZE; i++) { + M[i] = 0; + } +} + +static void bram_randomize() +{ + int32 i; + + for (i = 0; i < bram_banks * MAXBANKSIZE; i++) { + if (M != NULL) { + M[i] = sim_rand() & DATAMASK; + } + } +} + +static t_stat bram_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "\nAltair 8800 Banked RAM (%s)\n", dptr->name); + + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + fprint_reg_help (st, dptr); + + return SCPE_OK; +} + diff --git a/Altair8800/s100_bram.h b/Altair8800/s100_bram.h new file mode 100644 index 00000000..f25d15a9 --- /dev/null +++ b/Altair8800/s100_bram.h @@ -0,0 +1,57 @@ +/* s100_bram.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef _S100_BRAM_H +#define _S100_BRAM_H + +#include "sim_defs.h" + +#define UNIT_BRAM_V_VERBOSE (UNIT_V_UF+0) /* Enable verbose messagesto */ +#define UNIT_BRAM_VERBOSE (1 << UNIT_BRAM_V_VERBOSE) + +/* Supported Memory Boards */ + +#define BRAM_TYPE_NONE 0 /* No type selected */ +#define BRAM_TYPE_ERAM 1 /* SD Systems ExpandoRAM */ +#define BRAM_TYPE_VRAM 2 /* Vector Graphic RAM card */ +#define BRAM_TYPE_CRAM 3 /* Cromemco RAM card */ +#define BRAM_TYPE_HRAM 4 /* North Start Horizon RAM card */ +#define BRAM_TYPE_B810 5 /* AB Digital Design B810 RAM card */ +#define BRAM_TYPE_MAX BRAM_TYPE_B810 /* Maximum type */ + +typedef struct { + int32 baseport; /* Base IO address */ + int32 size; /* Number of addresses */ + int32 banks; /* Number of banks */ + char *name; /* Short name */ +} BRAM; + +#endif + diff --git a/Altair8800/s100_bus.c b/Altair8800/s100_bus.c new file mode 100644 index 00000000..cdbcd1a3 --- /dev/null +++ b/Altair8800/s100_bus.c @@ -0,0 +1,988 @@ +/* s100_bus.c - S100 Bus Simulator + + Copyright (c) 2025, Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + +*/ + +#include "sim_defs.h" +#include "altair8800_sys.h" +#include "s100_z80.h" +#include "s100_bus.h" + +static t_stat bus_reset (DEVICE *dptr); +static t_stat bus_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +static t_stat bus_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +static t_stat bus_cmd_memory (int32 flag, CONST char *cptr); +static t_stat bus_show_config (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat bus_show_console (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat bus_show_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +static t_stat bus_hexload_command (int32 flag, CONST char *cptr); +static t_stat bus_hexsave_command (int32 flag, CONST char *cptr); +static t_stat hexload (const char *filename, t_addr bias); +static t_stat hexsave (FILE *outFile, t_addr start, t_addr end); + +static ChipType chiptype = CHIP_TYPE_Z80; + +static MDEV mdev_table[MAXPAGE]; /* Active memory table */ +static MDEV mdev_dflt; /* Default memory table */ + +static uint32 bus_addr = 0x0000; + +static int32 poc = TRUE; /* Power On Clear */ + +/* Interrupts */ +uint32 nmiInterrupt = 0x00; /* NMI */ +uint32 vectorInterrupt = 0x00; /* Vector Interrupt bits */ +uint8 dataBus[MAX_INT_VECTORS]; /* Data bus value */ + +/* This is the I/O configuration table. There are 255 possible + device addresses, if a device is plugged to a port it's routine + address is here, 'nulldev' means no device is available +*/ +IDEV idev_in[MAXPAGE]; +IDEV idev_out[MAXPAGE]; + +int32 nulldev(CONST int32 addr, CONST int32 io, CONST int32 data) { return 0xff; } + +/* Which UNIT is the CONSOLE */ +UNIT *bus_console = NULL; + +static CONST char* bus_description(DEVICE *dptr) { + return "S100 Bus"; +} + +static UNIT bus_unit = { + UDATA (NULL, 0, 0) +}; + +static REG bus_reg[] = { + { HRDATAD (WRU, sim_int_char, 8, "Interrupt character pseudo register"), }, + { FLDATAD (POC, poc, 0x01, "Power on Clear flag"), }, + { HRDATAD(VECINT,vectorInterrupt, 8, "Vector Interrupt pseudo register"), }, + { BRDATAD (DATABUS, dataBus, 16, 8, MAX_INT_VECTORS, "Data bus pseudo register"), REG_RO + REG_CIRC }, + { HRDATAD(NMI, nmiInterrupt, 1, "NMI Interrupt pseudo register"), }, + { NULL } +}; + +static MTAB bus_mod[] = { + { UNIT_BUS_VERBOSE, UNIT_BUS_VERBOSE, "VERBOSE", "VERBOSE", NULL, NULL, + NULL, "Enable verbose messages" }, + { UNIT_BUS_VERBOSE, 0, "QUIET", "QUIET", NULL, NULL, + NULL, "Disable verbose messages" }, + + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "CONFIG", NULL, NULL, &bus_show_config, NULL, "Show BUS configuration" }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "CONSOLE", NULL, NULL, &bus_show_console, NULL, "Show CONSOLE unit" }, + + { 0 } +}; + +static DEBTAB bus_dt[] = { + { NULL, 0 } +}; + +DEVICE bus_dev = { + "BUS", &bus_unit, bus_reg, bus_mod, + 1, ADDRRADIX, ADDRWIDTH, 1, DATARADIX, DATAWIDTH, + &bus_ex, &bus_dep, &bus_reset, + NULL, NULL, NULL, + NULL, 0, 0, + bus_dt, NULL, NULL, &bus_show_help, NULL, NULL, &bus_description +}; + +/* Simulator-specific commands */ +static CTAB bus_cmd_tbl[] = { + { "REG", &z80_cmd_reg, 0, "REG Display registers\n" }, + { "MEM", &bus_cmd_memory, 0, "MEM
Dump a block of memory\n" }, + { "HEXLOAD", &bus_hexload_command, 0, "HEXLOAD [fname] Load Intel hex file\n" }, + { "HEXSAVE", &bus_hexsave_command, 0, "HEXSAVE [fname] [start-end] Save Intel hex file\n" }, + { NULL, NULL, 0, NULL } +}; + +/* bus reset */ +static t_stat bus_reset(DEVICE *dptr) { + int i; + + if (poc) { + sim_vm_cmd = bus_cmd_tbl; + + /* Clear MEM and IO table */ + for (i = 0; i < MAXPAGE; i++) { + mdev_table[i].routine = &nulldev; + mdev_table[i].name = "nulldev"; + + mdev_dflt.routine = &nulldev; + mdev_dflt.name = "nulldev"; + + idev_in[i].routine = &nulldev; + idev_in[i].name = "nulldev"; + idev_out[i].routine = &nulldev; + idev_out[i].name = "nulldev"; + } + + poc = FALSE; + } + + return SCPE_OK; +} + +/* memory examine */ +static t_stat bus_ex(t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ + *vptr = s100_bus_memr(addr & ADDRMASK); + + return SCPE_OK; +} + +/* memory deposit */ +static t_stat bus_dep(t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ + s100_bus_memw(addr & ADDRMASK, val); + + return SCPE_OK; +} + +static t_stat bus_show_config(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + CONST char *last = NULL; + int i, spage, epage; + + /* show memory */ + fprintf(st, "\nMEMORY:\n"); + + for (i = 0; i < MAXPAGE; i++) { + if (mdev_table[i].name != last) { + if (last != NULL) { + fprintf(st, "%04X-%04X: %s\n", spage << LOG2PAGESIZE, (epage << LOG2PAGESIZE) | 0xff, mdev_table[epage].routine != &nulldev ? sys_strupr(last) : ""); + } + + last = mdev_table[i].name; + spage = i; + } + + epage = i; + } + + fprintf(st, "%04X-%04X: %s\n", spage << LOG2PAGESIZE, (epage << LOG2PAGESIZE) | 0xff, mdev_table[epage].routine != &nulldev ? sys_strupr(last) : ""); + + fprintf(st, "\nDefault Memory Device: %s\n", sys_strupr(mdev_dflt.name)); + + /* show which ports are assigned */ + fprintf(st, "\nIO:\n"); + fprintf(st, "PORT %-8.8s %-8.8s\n", "IN", "OUT"); + for (i = 0; i < MAXPAGE; i++) { + if (idev_in[i].routine != &nulldev || idev_out[i].routine != &nulldev) { + fprintf(st, "%02X: ", i); + fprintf(st, "%-8.8s ", sys_strupr(idev_in[i].name)); /* strupr must be output before called again */ + fprintf(st, "%-8.8s\n", sys_strupr(idev_out[i].name)); + } + } + + fprintf(st, "\n"); + + bus_show_console(st, NULL, 0, NULL); + + return SCPE_OK; +} + +static t_stat bus_show_console(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + /* show current CONSOLE unit */ + fprintf(st, "CONSOLE Unit: %s\n", (bus_console == NULL) ? "NONE" : sim_uname(bus_console)); + + return SCPE_OK; +} + +void s100_bus_get_idev(int32 port, IDEV *idev_in, IDEV *idev_out) +{ + if (idev_in != NULL) { + idev_in->routine = idev_in[port & 0xff].routine; + idev_in->name = idev_in[port & 0xff].name; + } + if (idev_out != NULL) { + idev_out->routine = idev_out[port & 0xff].routine; + idev_out->name = idev_out[port & 0xff].name; + } +} + +t_stat s100_bus_addio(int32 port, int32 size, int32 (*routine)(CONST int32, CONST int32, CONST int32), CONST char *name) +{ + s100_bus_addio_in(port, size, routine, name); + s100_bus_addio_out(port, size, routine, name); + + return SCPE_OK; +} + +t_stat s100_bus_addio_in(int32 port, int32 size, int32 (*routine)(CONST int32, CONST int32, CONST int32), CONST char *name) +{ + int i; + + for (i = port; i < port + size; i++) { + if (bus_unit.flags & UNIT_BUS_VERBOSE) { + sim_printf("Mapping IO %04x IN, handler=%s\n", i, name); + } + + idev_in[i & 0xff].routine = routine; + idev_in[i & 0xff].name = name; + } + + return SCPE_OK; +} + +t_stat s100_bus_addio_out(int32 port, int32 size, int32 (*routine)(CONST int32, CONST int32, CONST int32), CONST char *name) +{ + int i; + + for (i = port; i < port + size; i++) { + if (bus_unit.flags & UNIT_BUS_VERBOSE) { + sim_printf("Mapping IO %04x OUT, handler=%s\n", i, name); + } + + idev_out[i & 0xff].routine = routine; + idev_out[i & 0xff].name = name; + } + + return SCPE_OK; +} + + +t_stat s100_bus_remio(int32 port, int32 size, int32 (*routine)(CONST int32, CONST int32, CONST int32)) +{ + s100_bus_remio_in(port, size, routine); + s100_bus_remio_out(port, size, routine); + + return SCPE_OK; +} + +t_stat s100_bus_remio_in(int32 port, int32 size, int32 (*routine)(CONST int32, CONST int32, CONST int32)) +{ + int i; + + for (i = port; i < port + size; i++) { + if (idev_in[i & 0xff].routine == routine) { + if (bus_unit.flags & UNIT_BUS_VERBOSE) { + sim_printf("Unmapping IO %04x IN, handler=%s\n", i, idev_in[i & 0xff].name); + } + + idev_in[i & 0xff].routine = &nulldev; + idev_in[i & 0xff].name = "nulldev"; + } + } + + return SCPE_OK; +} + +t_stat s100_bus_remio_out(int32 port, int32 size, int32 (*routine)(CONST int32, CONST int32, CONST int32)) +{ + int i; + + for (i = port; i < port + size; i++) { + if (idev_out[i & 0xff].routine == routine) { + if (bus_unit.flags & UNIT_BUS_VERBOSE) { + sim_printf("Unmapping IO %04x OUT, handler=%s\n", i, idev_out[i & 0xff].name); + } + + idev_out[i & 0xff].routine = &nulldev; + idev_out[i & 0xff].name = "nulldev"; + } + } + + return SCPE_OK; +} + +void s100_bus_get_mdev(int32 addr, MDEV *mdev) +{ + int32 page; + + page = (addr & ADDRMASK) >> LOG2PAGESIZE; + + if (mdev != NULL) { + mdev->routine = mdev_table[page].routine; + mdev->name = mdev_table[page].name; + } +} + +t_stat s100_bus_addmem(int32 baseaddr, uint32 size, + int32 (*routine)(CONST int32 addr, CONST int32 rw, CONST int32 data), CONST char *name) +{ + int32 page; + uint32 i; + + page = (baseaddr & ADDRMASK) >> LOG2PAGESIZE; + + if (size < PAGESIZE) { + size = PAGESIZE; + } + + if (bus_unit.flags & UNIT_BUS_VERBOSE) { + sim_printf("addmem: baseaddr=%04X page=%02X size=%04X LOG2SIZE=%04X name=%s\n", baseaddr, page, size, size >> LOG2PAGESIZE, name); + } + + for (i = 0; i < (size >> LOG2PAGESIZE); i++) { + mdev_table[page + i].routine = routine; + mdev_table[page + i].name = name; + } + + return SCPE_OK; +} + +t_stat s100_bus_setmem_dflt(int32 (*routine)(CONST int32 addr, CONST int32 rw, CONST int32 data), CONST char *name) +{ + mdev_dflt.routine = routine; + mdev_dflt.name = name; + + return SCPE_OK; +} + +t_stat s100_bus_remmem(int32 baseaddr, uint32 size, + int32 (*routine)(CONST int32 addr, CONST int32 rw, CONST int32 data)) +{ + int32 page; + uint32 i; + + page = (baseaddr & ADDRMASK) >> LOG2PAGESIZE; + + for (i = 0; i < (size >> LOG2PAGESIZE); i++) { + if (mdev_table[page + i].routine == routine) { + mdev_table[page + i].routine = mdev_dflt.routine; + mdev_table[page + i].name = mdev_dflt.name; + } + } + + return SCPE_OK; +} + +t_stat s100_bus_remmem_dflt(int32 (*routine)(CONST int32 addr, CONST int32 rw, CONST int32 data)) +{ + if (mdev_dflt.routine == routine) { + mdev_dflt.routine = &nulldev; + mdev_dflt.name = "nulldev"; + } + + return SCPE_OK; +} + +int32 s100_bus_in(int32 port) +{ + return idev_in[port].routine(port, S100_IO_READ, 0); +} + +void s100_bus_out(int32 port, int32 data) +{ + idev_out[port].routine(port, S100_IO_WRITE, data); +} + +int32 s100_bus_memr(t_addr addr) +{ + int32 page; + + page = (addr & ADDRMASK) >> LOG2PAGESIZE; + + return mdev_table[page].routine(addr, S100_IO_READ, 0); +} + +void s100_bus_memw(t_addr addr, int32 data) +{ + int32 page; + + page = (addr & ADDRMASK) >> LOG2PAGESIZE; + + mdev_table[page].routine(addr, S100_IO_WRITE, data); +} + +ChipType s100_bus_set_chiptype(ChipType new) +{ + chiptype = new; + + return chiptype; +} + +ChipType s100_bus_get_chiptype(void) +{ + return chiptype; +} + +uint32 s100_bus_set_addr(uint32 new) +{ + bus_addr = new; + + return bus_addr; +} + +uint32 s100_bus_get_addr(void) +{ + return bus_addr; +} + +uint32 s100_bus_int(int32 vector, int32 data) +{ + vectorInterrupt |= vector; + dataBus[vector] = data; + + return vectorInterrupt; +} + +uint32 s100_bus_get_int(void) +{ + return vectorInterrupt; +} + +uint32 s100_bus_get_int_data(int32 vector) +{ + return dataBus[vector]; +} + +uint32 s100_bus_clr_int(int32 vector) +{ + vectorInterrupt &= ~(1 << vector); + + return vectorInterrupt; +} + +void s100_bus_nmi() +{ + nmiInterrupt = TRUE; +} + +int32 s100_bus_get_nmi() +{ + return nmiInterrupt; +} + +void s100_bus_clr_nmi() +{ + nmiInterrupt = FALSE; +} + +static t_stat bus_cmd_memory(int32 flag, CONST char *cptr) +{ + char abuf[16]; + t_addr lo, hi, last; + t_value byte; + static t_addr disp_addr = 0; + + if (get_range(NULL, cptr, &lo, &hi, 16, ADDRMASK, 0) == NULL) { + lo = hi = disp_addr; + } + else { + disp_addr = lo & ~(0x0f); + } + + if (hi == lo) { + hi = (lo & ~(0x0f)) + 0xff; + } + + last = hi | 0x00000f; + + while (disp_addr <= last && disp_addr <= ADDRMASK) { + + if (!(disp_addr & 0x0f)) { + if (ADDRMASK+1 <= 0x10000) { + sim_printf("%04X ", disp_addr); + } + else { + sim_printf("%02X:%04X ", disp_addr >> 16, disp_addr & 0xffff); + } + } + + if (disp_addr < lo || disp_addr > hi) { + sim_printf(" "); + abuf[disp_addr & 0x0f] = ' '; + } + else { + byte = s100_bus_memr(disp_addr); + sim_printf("%02X ", byte); + abuf[disp_addr & 0x0f] = sim_isprint(byte) ? byte : '.'; + } + + if ((disp_addr & 0x000f) == 0x000f) { + sim_printf("%16.16s\n", abuf); + } + + disp_addr++; + } + + if (disp_addr > ADDRMASK) { + disp_addr = 0; + } + + return SCPE_OK | SCPE_NOMESSAGE; +} + +/* This is the binary loader. The input file is considered to be a string of + literal bytes with no special format. The load starts at the current value + of the PC if no start address is given. If the input string ends with ROM + (not case sensitive) the memory area is made read only. + ALTAIRROM/NOALTAIRROM settings are ignored. +*/ + +t_stat sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag) +{ + int32 i; + uint32 addr, cnt = 0, org; + t_addr j, lo, hi; + CONST char *result; + char gbuf[CBUFSIZE]; + + if (flag) { /* dump ram to file */ + result = get_range(NULL, cptr, &lo, &hi, 16, ADDRMASK, 0); + + if (result == NULL) { + return SCPE_ARG; + } + + for (j = lo; j <= hi; j++) { + if (putc(s100_bus_memr(j & ADDRMASK), fileref) == EOF) { + return SCPE_IOERR; + } + } + sim_printf("%d byte%s dumped [%x - %x] to %s.\n", PLURAL(hi + 1 - lo), lo, hi, fnam); + + return SCPE_OK; + } + + if (*cptr == 0) { + addr = s100_bus_get_addr(); + } + + else { + get_glyph(cptr, gbuf, 0); + + addr = strtotv(cptr, &result, 16) & ADDRMASK; + + if (cptr == result) { + return SCPE_ARG; + } + + while (isspace(*result)) { + result++; + } + } + + /* addr is start address to load to, makeROM == TRUE iff memory should become ROM */ + org = addr; + + while ((addr < MAXBANKSIZE) && ((i = getc(fileref)) != EOF)) { + s100_bus_memw(addr & ADDRMASK, i); + addr++; + cnt++; + } + + sim_printf("%d (%04X) byte%s [%d page%s] loaded at %04X.\n", cnt, PLURAL(cnt), PLURAL((cnt + 0xff) >> 8), org); + + return SCPE_OK; +} + +static t_stat bus_hexload_command(int32 flag, CONST char *cptr) +{ + char filename[4*CBUFSIZE]; + t_addr lo = 0, hi = 0; + + GET_SWITCHES(cptr); /* get switches */ + + if (*cptr == 0) { /* must be more */ + return SCPE_2FARG; + } + + cptr = get_glyph_quoted(cptr, filename, 0); /* get filename */ + sim_trim_endspc(filename); + + if (*cptr != 0) { /* bias available */ + get_range(NULL, cptr, &lo, &hi, ADDRRADIX, 0, 0); + } + + lo &= ADDRMASK; + + hexload(filename, lo); + + return SCPE_OK; +} + +static t_stat bus_hexsave_command(int32 flag, CONST char *cptr) +{ + char filename[4*CBUFSIZE]; + FILE *sfile; + t_addr lo = 0, hi = 0; + + GET_SWITCHES(cptr); /* get switches */ + + if (*cptr == 0) { /* must be more */ + return SCPE_2FARG; + } + + cptr = get_glyph_quoted(cptr, filename, 0); /* get filename */ + sim_trim_endspc(filename); + + if (*cptr == 0) { /* must be more */ + return SCPE_2FARG; + } + + get_range(NULL, cptr, &lo, &hi, ADDRRADIX, 0, 0); + + lo &= ADDRMASK; + hi &= ADDRMASK; + + if (hi < lo) { /* bad addresses */ + return SCPE_ARG; + } + + if ((sfile = sim_fopen(filename, "w")) == NULL) { /* try existing file */ + return SCPE_OPENERR; + } + + hexsave(sfile, lo, hi); + + sim_printf("Output file: %s\n", filename); + + fclose (sfile); + + return SCPE_OK; +} + +/* hexload will load an Intel hex file into RAM. + Based on HEX2BIN by Mike Douglas + https://deramp.com/downloads/misc_software/hex-binary utilities for the PC/ +*/ + +#define INBUF_LEN 600 + +static t_stat hexload(const char *filename, t_addr bias) +{ + FILE *sFile; + char inBuf[INBUF_LEN]; + char dataStr[INBUF_LEN]; + int sRecords = 0; + int byteCount, dataAddr, recType, dataByte, checkSum; + int lowAddr = ADDRMASK; + int highAddr = 0; + char *bufPtr; + + if ((sFile = sim_fopen(filename, "r")) == NULL) { /* try existing file */ + return SCPE_OPENERR; + } + + /* Read the hex or s-record file and put data into a memory image array */ + while (fgets(inBuf, INBUF_LEN, sFile)) { + inBuf[strcspn(inBuf, "\n")] = '\0'; /* end string at new line if present */ + + if (sRecords) { + sscanf(inBuf, "S%1X%2x%4x%s", &recType, &byteCount, &dataAddr, dataStr); + checkSum = byteCount + (dataAddr >> 8) + (dataAddr & 0xff) + 1; + byteCount -= 3; /* make byteCount = data bytes only */ + recType--; /* make S1 match .hex record type 0 */ + } + else { + sscanf(inBuf, ":%2x%4x%2x%s", &byteCount, &dataAddr, &recType, dataStr); + checkSum = byteCount + (dataAddr >> 8) + (dataAddr & 0xff) + recType; + } + + bufPtr = dataStr; + + if ((recType == 0) && (byteCount > 0) && (dataAddr+byteCount <= MAXADDR)) { + if (dataAddr+byteCount > highAddr) { + highAddr = dataAddr + byteCount; + } + + if (dataAddr < lowAddr) { + lowAddr = dataAddr; + } + + do { + sscanf(bufPtr, "%2x", &dataByte); + bufPtr += 2; + s100_bus_memw((dataAddr + bias) & ADDRMASK, dataByte); /* Write to memory */ + dataAddr++; + checkSum += dataByte; + } while (--byteCount != 0); + + sscanf(bufPtr, "%2x", &dataByte); /* checksum byte */ + + if (0 != ((checkSum+dataByte) & 0xff)) { + fprintf(stderr,"Checksum error\n %s\n", inBuf); + fclose (sFile); + + return SCPE_IERR; + } + } + } + + /* Display results */ + if (bias) { + sim_printf("%s: %04X (%04X+%04X)-%04X (%04X+%04X)\n", filename, + (lowAddr + bias) & ADDRMASK, lowAddr, bias, + (highAddr + bias - 1) & ADDRMASK, highAddr, bias); + } + else { + sim_printf("%s %04X-%04X\n", filename, lowAddr, highAddr-1); + } + + fclose (sFile); + + return SCPE_OK; +} + +/* hexsave will load an Intel hex file into RAM. + Based on HEX2BIN by Mike Douglas + https://deramp.com/downloads/misc_software/hex-binary utilities for the PC/ +*/ + +#define LINE_LEN 32 + +static t_stat hexsave(FILE *outFile, t_addr start, t_addr end) +{ + uint8 inBuf[INBUF_LEN]; + int sRecords = 0; + int checkSum; + uint32 i; + t_addr dataAddr; + uint32 byteCount; + + dataAddr = start; + + do { + for (byteCount = 0; byteCount < LINE_LEN && dataAddr + byteCount <= end; byteCount++) { + inBuf[byteCount] = s100_bus_memr(dataAddr + byteCount); + } + + + if (byteCount > 0) { + if (sRecords) { + fprintf(outFile, "S1%02X%04X", byteCount+3, dataAddr); + checkSum = byteCount + (dataAddr >> 8) + (dataAddr & 0xff) + 4; + } + else { + fprintf(outFile, ":%02X%04X00", byteCount, dataAddr); + checkSum = byteCount + (dataAddr >> 8) + (dataAddr & 0xff); + } + + for (i=0; ictxt; + + if (res == NULL) + return SCPE_IERR; + + newba = get_uint (cptr, 16, 0xFFFF, &r); + + if (r != SCPE_OK) + return r; + + if ((newba > 0xFFFF) || (newba % res->mem_size)) + return SCPE_ARG; + + if (dptr->flags & DEV_DIS) { + sim_printf("device not enabled yet.\n"); + res->mem_base = newba & ~(res->mem_size-1); + } else { + dptr->flags |= DEV_DIS; + dptr->reset(dptr); + res->mem_base = newba & ~(res->mem_size-1); + dptr->flags &= ~DEV_DIS; + dptr->reset(dptr); + } + + return SCPE_OK; +} + +/* Show Base Address routine */ +t_stat show_membase(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + DEVICE *dptr; + RES *res; + + if (uptr == NULL) + return SCPE_IERR; + + if ((dptr = find_dev_from_unit(uptr)) == NULL) + return SCPE_IERR; + + res = (RES *) dptr->ctxt; + + if (res == NULL) + return SCPE_IERR; + + fprintf(st, "MEM=0x%04X-0x%04X", res->mem_base, res->mem_base + res->mem_size-1); + + return SCPE_OK; +} + +/* Set Memory Base Address routine */ +t_stat set_iobase(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + DEVICE *dptr; + RES *res; + uint32 newba; + t_stat r; + + if (cptr == NULL) + return SCPE_ARG; + + if (uptr == NULL) + return SCPE_IERR; + + if ((dptr = find_dev_from_unit(uptr)) == NULL) + return SCPE_IERR; + + res = (RES *) dptr->ctxt; + + if (res == NULL) + return SCPE_IERR; + + newba = get_uint (cptr, 16, 0xFF, &r); + + if (r != SCPE_OK) + return r; + + if ((newba > 0xFF) || (newba % res->io_size)) + return SCPE_ARG; + + if (dptr->flags & DEV_DIS) { + sim_printf("device not enabled yet.\n"); + res->io_base = newba & ~(res->io_size-1); + } else { + dptr->flags |= DEV_DIS; + dptr->reset(dptr); + res->io_base = newba & ~(res->io_size-1); + dptr->flags &= ~DEV_DIS; + dptr->reset(dptr); + } + + return SCPE_OK; +} + +/* Show I/O Base Address routine */ +t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + DEVICE *dptr; + RES *res; + + if (uptr == NULL) + return SCPE_IERR; + + if ((dptr = find_dev_from_unit(uptr)) == NULL) + return SCPE_IERR; + + res = (RES *) dptr->ctxt; + + if (res == NULL) + return SCPE_IERR; + + fprintf(st, "I/O=0x%02X-0x%02X", res->io_base, res->io_base + res->io_size-1); + + return SCPE_OK; +} + +/* Set new CONSOLE unit */ +t_stat s100_bus_console(UNIT *uptr) +{ + bus_console = uptr; + + return SCPE_ARG; +} + +/* Set new CONSOLE unit */ +UNIT *s100_bus_get_console() +{ + return bus_console; +} + +t_stat s100_bus_noconsole(UNIT *uptr) +{ + if (bus_console == uptr) { + bus_console = NULL; + + return SCPE_OK; + } + + return SCPE_ARG; +} + +t_stat s100_bus_poll_kbd(UNIT *uptr) +{ + if (bus_console == uptr) { + return sim_poll_kbd(); + } + + return SCPE_OK; +} + +static t_stat bus_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "\nAltair 8800 Bus (%s)\n", dptr->name); + + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + fprint_reg_help (st, dptr); + + return SCPE_OK; +} + diff --git a/Altair8800/s100_bus.h b/Altair8800/s100_bus.h new file mode 100644 index 00000000..5165a975 --- /dev/null +++ b/Altair8800/s100_bus.h @@ -0,0 +1,156 @@ +/* s100_bus.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef _SIM_BUS_H +#define _SIM_BUS_H + +#include "sim_defs.h" +#include "sim_tmxr.h" + +#define UNIT_BUS_V_VERBOSE (UNIT_V_UF+0) /* warn if ROM is written to */ +#define UNIT_BUS_VERBOSE (1 << UNIT_BUS_V_VERBOSE) + +/* S100 Bus Architecture */ + +#define ADDRWIDTH 16 +#define DATAWIDTH 8 + +#define ADDRRADIX 16 +#define DATARADIX 16 + +#define MAXADDR (1 << ADDRWIDTH) +#define MAXDATA (1 << DATAWIDTH) +#define ADDRMASK (MAXADDR - 1) +#define DATAMASK (MAXDATA - 1) + +#define LOG2PAGESIZE 8 +#define PAGESIZE (1 << LOG2PAGESIZE) + +#define MAXMEMORY MAXADDR +#define MAXBANKSIZE MAXADDR +#define MAXPAGE (MAXADDR >> LOG2PAGESIZE) +#define PAGEMASK (MAXPAGE - 1) + +#define MAXBANK 16 +#define MAXBANKS2LOG 5 + +#define ADDRESS_FORMAT "[0x%08x]" + +/* This is the I/O configuration table. There are 255 possible +device addresses, if a device is plugged to a port it's routine +address is here, 'nulldev' means no device is available +*/ + +#define S100_IO_READ 0 +#define S100_IO_WRITE 1 + +/* Interrupt Vectors */ +#define MAX_INT_VECTORS 32 /* maximum number of interrupt vectors */ + +extern uint32 nmiInterrupt; /* NMI */ +extern uint32 vectorInterrupt; /* Vector Interrupt bits */ +extern uint8 dataBus[MAX_INT_VECTORS]; /* Data bus value */ + +/* + * Generic device resource information. Pointed to by DEVICE *up7 + */ +typedef struct { + uint32 io_base; /* I/O Base Address */ + uint32 io_size; /* I/O Address Space requirement */ + uint32 mem_base; /* Memory Base Address */ + uint32 mem_size; /* Memory Address space requirement */ + TMXR *tmxr; /* TMXR pointer */ +} RES; + +/* data structure for IN/OUT instructions */ +typedef struct idev { + int32 (*routine)(CONST int32 addr, CONST int32 rw, CONST int32 data); + CONST char *name; +} IDEV; + +typedef struct { /* Structure to describe memory device address space */ + int32 (*routine)(CONST int32 addr, CONST int32 rw, CONST int32 data); + CONST char *name; /* name of handler routine */ +} MDEV; + +extern t_stat s100_bus_addio(int32 port, int32 size, int32 (*routine)(CONST int32, CONST int32, CONST int32), CONST char* name); +extern t_stat s100_bus_addio_in(int32 port, int32 size, int32 (*routine)(CONST int32, CONST int32, CONST int32), CONST char* name); +extern t_stat s100_bus_addio_out(int32 port, int32 size, int32 (*routine)(CONST int32, CONST int32, CONST int32), CONST char* name); +extern t_stat s100_bus_remio(int32 port, int32 size, int32 (*routine)(CONST int32, CONST int32, CONST int32)); +extern t_stat s100_bus_remio_in(int32 port, int32 size, int32 (*routine)(CONST int32, CONST int32, CONST int32)); +extern t_stat s100_bus_remio_out(int32 port, int32 size, int32 (*routine)(CONST int32, CONST int32, CONST int32)); +extern t_stat s100_bus_addmem(int32 baseaddr, uint32 size, + int32 (*routine)(CONST int32 addr, CONST int32 rw, CONST int32 data), CONST char *name); +extern t_stat s100_bus_remmem(int32 baseaddr, uint32 size, + int32 (*routine)(CONST int32 addr, CONST int32 rw, CONST int32 data)); +extern t_stat s100_bus_setmem_dflt(int32 (*routine)(CONST int32 addr, CONST int32 rw, CONST int32 data), CONST char *name); +extern t_stat s100_bus_remmem_dflt(int32 (*routine)(CONST int32 addr, CONST int32 rw, CONST int32 data)); + +extern void s100_bus_get_idev(int32 port, IDEV *idev_in, IDEV *idev_out); +extern void s100_bus_get_mdev(int32 addr, MDEV *mdev); +extern int32 nulldev(CONST int32 addr, CONST int32 io, CONST int32 data); + +extern uint32 s100_bus_set_addr(uint32 pc); +extern uint32 s100_bus_get_addr(void); + +extern t_stat s100_bus_console(UNIT *uptr); +extern UNIT *s100_bus_get_console(void); +extern t_stat s100_bus_noconsole(UNIT *uptr); +extern t_stat s100_bus_poll_kbd(UNIT *uptr); + +extern int32 s100_bus_in(int32 port); +extern void s100_bus_out(int32 port, int32 data); +extern int32 s100_bus_memr(t_addr addr); +extern void s100_bus_memw(t_addr addr, int32 data); +extern uint32 s100_bus_int(int32 vector, int32 data); +extern uint32 s100_bus_get_int(void); +extern uint32 s100_bus_get_int_data(int32 vector); +extern uint32 s100_bus_clr_int(int32 vector); +extern void s100_bus_nmi(void); +extern int32 s100_bus_get_nmi(void); +extern void s100_bus_clr_nmi(void); + +#define S100_BUS_MEMR 0x01 +#define S100_BUS_MEMW 0x02 +#define S100_BUS_IN 0x04 +#define S100_BUS_OUT 0x08 + +#define RESOURCE_TYPE_MEMORY (S100_BUS_MEMR | S100_BUS_MEMW) +#define RESOURCE_TYPE_IO (S100_BUS_IN | S100_BUS_OUT) + +#define sim_map_resource(a,b,c,d,e,f) s100_map_resource(a,b,c,d,e,f) + +extern t_stat set_iobase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +extern t_stat set_membase(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +extern void cpu_raise_interrupt(uint32 irq); + +#endif diff --git a/Altair8800/s100_cpu.c b/Altair8800/s100_cpu.c new file mode 100644 index 00000000..58f1706c --- /dev/null +++ b/Altair8800/s100_cpu.c @@ -0,0 +1,251 @@ +/* s100_cpu.c: MITS Altair CPU Management + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 07-Nov-2025 Initial version + +*/ + +#include "sim_defs.h" +#include "altair8800_sys.h" +#include "s100_bus.h" +#include "s100_z80.h" +#include "s100_cpu.h" + +REG *sim_PC; + +static int32 poc = TRUE; /* Power On Clear */ + +static ChipType cpu_type = CHIP_TYPE_8080; + +static char *cpu_chipname[] = { + "Intel 8080", + "Zilog Z80" +}; + +static t_stat (*cpu_instr)(void) = NULL; +static t_stat (*cpu_parse_sym)(CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) = NULL; +static int32 (*cpu_dasm)(char *S, const uint32 *val, const int32 addr) = NULL; + +static t_stat cpu_reset(DEVICE *dptr); +static void cpu_set_instr(t_stat (*routine)(void)); +static void cpu_set_pc(REG *reg); +static void cpu_set_pc_value(t_value (*routine)(void)); +static void cpu_set_parse_sym(t_stat (*routine)(CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)); +static void cpu_set_dasm(int32 (*routine)(char *S, const uint32 *val, const int32 addr)); +static void cpu_set_is_subroutine_call(t_bool (*routine)(t_addr **ret_addrs)); + +static const char* cpu_description(DEVICE *dptr) { + return "Central Processing Unit"; +} + +static CPU cpu[] = { + { &z80_dev, &z80_pc_reg, &z80_chiptype, &z80_instr, &z80_pc_value, + &z80_parse_sym, &z80_dasm, &z80_is_pc_a_subroutine_call, &z80_show_help }, // 8080 + + { &z80_dev, &z80_pc_reg, &z80_chiptype, &z80_instr, &z80_pc_value, + &z80_parse_sym, &z80_dasm, &z80_is_pc_a_subroutine_call, &z80_show_help }, // Z80 + + { NULL, NULL, NULL, NULL, NULL, NULL, NULL } +}; + +static UNIT cpu_unit = { + UDATA (NULL, 0, 0) +}; + +static REG cpu_reg[] = { + { NULL } +}; + +static MTAB cpu_mod[] = { + { 0 } +}; + +/* Debug Flags */ +static DEBTAB cpu_dt[] = { + { NULL, 0 } +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, ADDRRADIX, ADDRWIDTH, 1, DATARADIX, DATAWIDTH, + NULL, NULL, &cpu_reset, + NULL, NULL, NULL, + NULL, (DEV_DISABLE | DEV_DEBUG), 0, + cpu_dt, NULL, NULL, NULL, NULL, NULL, &cpu_description +}; + +static t_stat cpu_reset(DEVICE *dptr) { + if (dptr->flags & DEV_DIS) { /* Disable Device */ + poc = TRUE; + } + else { + cpu_set_instr(cpu[cpu_type].instr); + cpu_set_pc(*cpu[cpu_type].pc_reg); + cpu_set_pc_value(cpu[cpu_type].pc_val); + cpu_set_parse_sym(cpu[cpu_type].parse_sym); + cpu_set_dasm(cpu[cpu_type].dasm); + cpu_set_is_subroutine_call(cpu[cpu_type].isc); + + dptr->units = cpu[cpu_type].dev->units; + dptr->registers = cpu[cpu_type].dev->registers; + dptr->modifiers = cpu[cpu_type].dev->modifiers; + dptr->help = cpu[cpu_type].dev->help; + dptr->help_ctx = cpu[cpu_type].dev->help_ctx; + dptr->description = cpu[cpu_type].dev->description; + + if (poc) { + poc = FALSE; + } + } + + /* Reset selected CPU */ + if (cpu[cpu_type].dev != NULL) { + cpu[cpu_type].dev->reset(cpu[0].dev); + } + + return SCPE_OK; +} + +void cpu_set_chiptype(ChipType new_type) +{ + ChipType old_type = cpu_type; + + if (cpu_type == new_type) { + return; + } + + switch (new_type) { + case CHIP_TYPE_8080: + case CHIP_TYPE_Z80: + cpu_type = new_type; + break; + + default: + break; + } + + if (cpu_dev.units[0].flags & UNIT_CPU_VERBOSE) { + sim_printf("CPU changed from %s to %s\n", cpu_get_chipname(old_type), cpu_get_chipname(new_type)); + } + + /* Install new CPU device */ + if (cpu[cpu_type].chiptype != NULL) { + *cpu[cpu_type].chiptype = cpu_type; + } + + cpu_reset(&cpu_dev); +} + +char * cpu_get_chipname(ChipType type) +{ + return cpu_chipname[type]; +} + +t_stat sim_instr() +{ + t_stat reason = SCPE_NXDEV; + + if (cpu_instr != NULL) { + reason = (*cpu_instr)(); + } + + return reason; +} + +static void cpu_set_instr(t_stat (*routine)(void)) +{ + cpu_instr = routine; +} + +static void cpu_set_pc(REG *reg) +{ + sim_PC = reg; +} + +static void cpu_set_pc_value(t_value (*routine)(void)) +{ + sim_vm_pc_value = routine; +} + +static void cpu_set_parse_sym(t_stat (*routine)(CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)) +{ + cpu_parse_sym = routine; +} + +static void cpu_set_dasm(int32 (*routine)(char *S, const uint32 *val, const int32 addr)) +{ + cpu_dasm = routine; +} + +static void cpu_set_is_subroutine_call(t_bool (*routine)(t_addr **ret_addrs)) +{ + sim_vm_is_subroutine_call = routine; +} + +t_stat fprint_sym(FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) +{ + char disasm_result[128]; + int32 ch = val[0] & 0x7f; + long r = 1; + if (sw & (SWMASK('A') | SWMASK('C'))) { + fprintf(of, ((0x20 <= ch) && (ch < 0x7f)) ? "'%c'" : "%02x", ch); + return SCPE_OK; + } + if (!(sw & SWMASK('M'))) { + return SCPE_ARG; + } + + if (cpu_dasm != NULL) { + r = cpu_dasm(disasm_result, val, addr); + + fprintf(of, "%s", disasm_result); + } + + return 1 - r; +} + +t_stat parse_sym(CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ + while (isspace(*cptr)) { + cptr++; /* absorb spaces */ + } + + if ((sw & (SWMASK('A') | SWMASK('C'))) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) { + return SCPE_ARG; /* must have one char */ + } + + val[0] = (uint32) cptr[0]; + return SCPE_OK; + } + + if (cpu_parse_sym != NULL) { + return cpu_parse_sym(cptr, addr, uptr, val, sw); + } + + return SCPE_OK; +} + diff --git a/Altair8800/s100_cpu.h b/Altair8800/s100_cpu.h new file mode 100644 index 00000000..a526474e --- /dev/null +++ b/Altair8800/s100_cpu.h @@ -0,0 +1,62 @@ +/* s100_cpu.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef _S100_CPU_H +#define _S100_CPU_H + +#include "sim_defs.h" + +#define UNIT_CPU_V_VERBOSE (UNIT_V_UF+0) /* Enable verbose messagesto */ +#define UNIT_CPU_VERBOSE (1 << UNIT_CPU_V_VERBOSE) + +/* CPU chip types */ +typedef enum { + CHIP_TYPE_8080 = 0, + CHIP_TYPE_Z80, + NUM_CHIP_TYPE, /* must be last */ +} ChipType; + +typedef struct { + DEVICE *dev; + REG **pc_reg; + ChipType *chiptype; + t_stat (*instr)(void); + t_value (*pc_val)(void); + t_stat (*parse_sym)(CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw); + int32 (*dasm)(char *S, const uint32 *val, const int32 addr); + t_bool (*isc)(t_addr **ret_addrs); + t_stat (*help)(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +} CPU; + +extern void cpu_set_chiptype(ChipType type); +extern char * cpu_get_chipname(ChipType type); + +#endif + diff --git a/Altair8800/s100_po.c b/Altair8800/s100_po.c new file mode 100644 index 00000000..7f7a0363 --- /dev/null +++ b/Altair8800/s100_po.c @@ -0,0 +1,122 @@ +/* s100_po.c: MITS Altair 8800 Programmed Output + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 07-Nov-2025 Initial version + +*/ + +#include "sim_defs.h" +#include "s100_bus.h" +#include "s100_po.h" + +#define DEVICE_NAME "PO" + +static int32 poc = TRUE; /* Power On Clear */ + +static int32 PO = 0; /* programmed output register */ + +static t_stat po_reset (DEVICE *dptr); +static int32 po_io (const int32 addr, const int32 rw, const int32 data); + +static t_stat po_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); + +static const char* po_description(DEVICE *dptr) { + return "Front Panel"; +} + +static UNIT po_unit = { + UDATA (NULL, UNIT_PO_VERBOSE, 0) +}; + +static REG po_reg[] = { + { HRDATAD (PO, PO, 8, "Programmed Output") }, + { NULL } +}; + +static MTAB po_mod[] = { + { UNIT_PO_VERBOSE, UNIT_PO_VERBOSE, "VERBOSE", "VERBOSE", NULL, NULL, + NULL, "Enable verbose messages" }, + { UNIT_PO_VERBOSE, 0, "QUIET", "QUIET", NULL, NULL, + NULL, "Disable verbose messages" }, + { 0 } +}; + +/* Debug Flags */ +static DEBTAB po_dt[] = { + { NULL, 0 } +}; + +DEVICE po_dev = { + DEVICE_NAME, &po_unit, po_reg, po_mod, + 1, ADDRRADIX, ADDRWIDTH, 1, DATARADIX, DATAWIDTH, + NULL, NULL, &po_reset, + NULL, NULL, NULL, + NULL, (DEV_DISABLE | DEV_DIS), 0, + po_dt, NULL, NULL, &po_show_help, NULL, NULL, &po_description +}; + +static t_stat po_reset(DEVICE *dptr) { + if (dptr->flags & DEV_DIS) { /* Disable Device */ + s100_bus_remio_out(0xff, 1, &po_io); + + poc = TRUE; + } + else { + if (poc) { + s100_bus_addio_out(0xff, 1, &po_io, DEVICE_NAME); + + poc = FALSE; + } + } + + return SCPE_OK; +} + +static int32 po_io(const int32 addr, const int32 rw, const int32 data) +{ + if (rw == S100_IO_WRITE) { + + PO = data & DATAMASK; + + if (po_unit.flags & UNIT_PO_VERBOSE) { + sim_printf("\n[PO %02X]\n", ~data & DATAMASK); /* IMSAI FP is Inverted */ + } + } + + return 0x0ff; +} + +static t_stat po_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "\nProgrammed Output (%s)\n", dptr->name); + + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + fprint_reg_help (st, dptr); + + return SCPE_OK; +} + diff --git a/Altair8800/s100_po.h b/Altair8800/s100_po.h new file mode 100644 index 00000000..4d64be58 --- /dev/null +++ b/Altair8800/s100_po.h @@ -0,0 +1,37 @@ +/* s100_po.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef S100_PO_H_ +#define S100_PO_H_ + +#define UNIT_PO_V_VERBOSE (UNIT_V_UF+0) +#define UNIT_PO_VERBOSE (1 << UNIT_PO_V_VERBOSE) + +#endif diff --git a/Altair8800/s100_ram.c b/Altair8800/s100_ram.c new file mode 100644 index 00000000..0246f86b --- /dev/null +++ b/Altair8800/s100_ram.c @@ -0,0 +1,325 @@ +/* s100_ram.c: MITS Altair 8800 RAM + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 07-Nov-2025 Initial version + +*/ + +#include "altair8800_defs.h" +#include "s100_bus.h" +#include "s100_ram.h" + +static t_stat ram_reset (DEVICE *dptr); +static t_stat ram_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +static t_stat ram_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +static int32 ram_memio (const int32 addr, const int32 rw, const int32 data); +static t_stat ram_default_ena (UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat ram_default_dis (UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat ram_set_memsize (int32 value); +static t_stat ram_clear_command (UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat ram_enable_command (UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat ram_randomize_command (UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat ram_size_command (UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static void ram_clear (void); +static void ram_randomize (void); +static t_stat ram_show_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +static const char* ram_description (DEVICE *dptr); + +static void PutBYTE(register uint32 Addr, const register uint32 Value); +static uint32 GetBYTE(register uint32 Addr); + +static int32 poc = TRUE; /* Power On Clear */ + +static int32 M[MAXBANKSIZE]; /* RAM */ +static int32 P[MAXBANKSIZE >> LOG2PAGESIZE]; /* Active pages */ +static int32 memsize = MAXBANKSIZE; + +static const char* ram_description(DEVICE *dptr) { + return "Random Access Memory"; +} + +static UNIT ram_unit = { + UDATA (NULL, UNIT_FIX | UNIT_BINK | UNIT_RAM_DEFAULT, MAXBANKSIZE) +}; + +static REG ram_reg[] = { + { FLDATAD (POC, poc, 0x01, "Power on Clear flag"), }, + { NULL } +}; + +static MTAB ram_mod[] = { + { UNIT_RAM_VERBOSE, UNIT_RAM_VERBOSE, "VERBOSE", "VERBOSE", NULL, NULL, + NULL, "Enable verbose messages" }, + { UNIT_RAM_VERBOSE, 0, "QUIET", "QUIET", NULL, NULL, + NULL, "Disable verbose messages" }, + { UNIT_RAM_DEFAULT, UNIT_RAM_DEFAULT, "DEFAULT", "DEFAULT", &ram_default_ena, NULL, + NULL, "Enable RAM as default memory" }, + { UNIT_RAM_DEFAULT, 0, "NODEFAULT", "NODEFAULT", &ram_default_dis, NULL, + NULL, "Disable RAM as default memory" }, + + { MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, NULL, "SIZE={1-64}", &ram_size_command, + NULL, NULL, "Sets the RAM size" }, + { MTAB_XTD | MTAB_VDV | MTAB_VALR, 1, NULL, "ADDRAM={PAGE | START-END | ALL}", &ram_enable_command, + NULL, NULL, "Enable RAM page(s)" }, + { MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, NULL, "REMRAM={PAGE | START-END | ALL}", &ram_enable_command, + NULL, NULL, "Disable RAM page(s)" }, + { MTAB_VDV, 0, NULL, "CLEAR", &ram_clear_command, + NULL, NULL, "Sets RAM to 0x00" }, + { MTAB_VDV, 0, NULL, "RANDOM", &ram_randomize_command, + NULL, NULL, "Sets RAM to random values" }, + { 0 } +}; + +/* Debug Flags */ +static DEBTAB ram_dt[] = { + { NULL, 0 } +}; + +DEVICE ram_dev = { + "RAM", /* name */ + &ram_unit, /* units */ + ram_reg, /* registers */ + ram_mod, /* modifiers */ + 1, /* # units */ + ADDRRADIX, /* address radix */ + ADDRWIDTH, /* address width */ + 1, /* addr increment */ + DATARADIX, /* data radix */ + DATAWIDTH, /* data width */ + &ram_ex, /* examine routine */ + &ram_dep, /* deposit routine */ + &ram_reset, /* reset routine */ + NULL, /* boot routine */ + NULL, /* attach routine */ + NULL, /* detach routine */ + NULL, /* context */ + (DEV_DISABLE), /* flags */ + 0, /* debug control */ + ram_dt, /* debug flags */ + NULL, /* mem size routine */ + NULL, /* logical name */ + &ram_show_help, /* help */ + NULL, /* attach help */ + NULL, /* context available to help routines */ + &ram_description /* device description */ +}; + +static t_stat ram_reset(DEVICE *dptr) +{ + if (dptr->flags & DEV_DIS) { /* Disable Device */ + s100_bus_remmem(0x0000, MAXBANKSIZE, &ram_memio); + ram_default_dis(NULL, 0, NULL, NULL); + + poc = TRUE; + } + else { + if (poc) { + ram_set_memsize(memsize); + + if (ram_unit.flags & UNIT_RAM_DEFAULT) { + ram_default_ena(NULL, 0, NULL, NULL); + } + + poc = FALSE; + } + } + + return SCPE_OK; +} + +/* memory examine */ +static t_stat ram_ex(t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { + *vptr = GetBYTE(addr & ADDRMASK) & DATAMASK; + + return SCPE_OK; +} + +/* memory deposit */ +static t_stat ram_dep(t_value val, t_addr addr, UNIT *uptr, int32 sw) { + PutBYTE(addr & ADDRMASK, val & DATAMASK); + + return SCPE_OK; +} + +static int32 ram_memio(const int32 addr, const int32 rw, const int32 data) +{ + if (rw == S100_IO_READ) { + return GetBYTE(addr); + } + + PutBYTE(addr, data); + + return 0x0ff; +} + +static void PutBYTE(register uint32 Addr, const register uint32 Value) +{ + M[Addr & ADDRMASK] = Value & DATAMASK; +} + +static uint32 GetBYTE(register uint32 Addr) +{ + return M[Addr & ADDRMASK] & DATAMASK; /* RAM */ +} + +static t_stat ram_default_ena(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + s100_bus_setmem_dflt(&ram_memio, "RAM"); /* Set RAM as default memory device */ + + return SCPE_OK; +} + +static t_stat ram_default_dis(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + s100_bus_remmem_dflt(&ram_memio); /* Remove RAM as default memory device */ + + return SCPE_OK; +} + +/* set memory to 'size' kilo byte */ +static t_stat ram_set_memsize(int32 size) { + int32 page; + + size <<= KBLOG2; + + if (size < KB) { + memsize = KB; + } + else if (size > MAXBANKSIZE) { + memsize = MAXBANKSIZE; + } + else { + memsize = size; + } + + s100_bus_remmem(0x0000, MAXBANKSIZE, &ram_memio); /* Remove all pages */ + s100_bus_addmem(0x0000, memsize, &ram_memio, "RAM"); /* Add memsize pages */ + + /* Keep track of active pages for SHOW */ + for (page = 0; page < (MAXBANKSIZE >> LOG2PAGESIZE); page++) { + P[page] = (page << LOG2PAGESIZE) <= memsize; + } + + ram_unit.capac = memsize; + + return SCPE_OK; +} + +static void ram_clear() +{ + uint32 i; + + for (i = 0; i < MAXBANKSIZE; i++) { + M[i] = 0; + } +} + +static void ram_randomize() +{ + uint32 i; + + for (i = 0; i < MAXBANKSIZE; i++) { + M[i] = sim_rand() & DATAMASK; + } +} + +static t_stat ram_size_command(UNIT *uptr, int32 value, CONST char *cptr, void *desc) { + int32 size, result; + + if (cptr == NULL) { + sim_printf("Memory size must be provided as SET RAM SIZE=1-64\n"); + return SCPE_ARG | SCPE_NOMESSAGE; + } + + result = sscanf(cptr, "%i", &size); + + if (result == 1) { + return ram_set_memsize(size); /* Set size in KB */ + } + + return SCPE_ARG | SCPE_NOMESSAGE; +} + +static t_stat ram_enable_command(UNIT *uptr, int32 value, CONST char *cptr, void *desc) { + int32 size; + t_addr start, end; + + if (cptr == NULL) { + sim_printf("Memory page(s) must be provided as SET RAM ENABLE=E0-EF\n"); + return SCPE_ARG | SCPE_NOMESSAGE; + } + + if (get_range(NULL, cptr, &start, &end, 16, PAGEMASK, 0) == NULL) { + return SCPE_ARG; + } + + if (start < MAXPAGE) { + start = start << LOG2PAGESIZE; + } + if (end < MAXPAGE) { + end = end << LOG2PAGESIZE; + } + + start &= 0xff00; + end &= 0xff00; + + size = end - start + PAGESIZE; + + if (value) { + s100_bus_addmem(start, size, &ram_memio, "RAM"); /* Add pages */ + } + else { + s100_bus_remmem(start, size, &ram_memio); /* Remove pages */ + } + + return SCPE_OK; +} + +static t_stat ram_clear_command(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + ram_clear(); + + return SCPE_OK; +} + +static t_stat ram_randomize_command(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + ram_randomize(); + + return SCPE_OK; +} + +static t_stat ram_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "\nAltair 8800 RAM (%s)\n", dptr->name); + + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + fprint_reg_help (st, dptr); + + return SCPE_OK; +} + diff --git a/Altair8800/s100_ram.h b/Altair8800/s100_ram.h new file mode 100644 index 00000000..861c0c65 --- /dev/null +++ b/Altair8800/s100_ram.h @@ -0,0 +1,42 @@ +/* s100_ram.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef _S100_RAM_H +#define _S100_RAM_H + +#include "sim_defs.h" + +#define UNIT_RAM_V_VERBOSE (UNIT_V_UF+0) /* Enable verbose messagesto */ +#define UNIT_RAM_VERBOSE (1 << UNIT_RAM_V_VERBOSE) +#define UNIT_RAM_V_DEFAULT (UNIT_V_UF+1) /* Make RAM default */ +#define UNIT_RAM_DEFAULT (1 << UNIT_RAM_V_DEFAULT) + +#endif + diff --git a/Altair8800/s100_rom.c b/Altair8800/s100_rom.c new file mode 100644 index 00000000..31d5f369 --- /dev/null +++ b/Altair8800/s100_rom.c @@ -0,0 +1,252 @@ +/* s100_rom.c: MITS Altair 8800 ROM + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 07-Nov-2025 Initial version + +*/ + +#include "sim_defs.h" +#include "s100_bus.h" +#include "s100_ram.h" +#include "s100_rom.h" +#include "s100_roms.h" + +static t_stat rom_reset (DEVICE *dptr); +static int32 rom_memio (const int32 addr, const int32 rw, const int32 data); + +static int32 poc = TRUE; /* Power On Clear */ + +static const char* rom_description (DEVICE *dptr); + +static uint32 GetBYTE(register uint32 Addr); + +static t_stat rom_enadis(int32 value, int32 ena); +static t_stat rom_ena(UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat rom_dis_dbl(UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat rom_dis_hdsk(UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat rom_dis_altmon(UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat rom_dis_turmon(UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat rom_show_list(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat rom_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); + +static int32 M[MAXBANKSIZE]; + +static ROM rom_table[] = { + { UNIT_ROM_ALTMON, rom_altmon, ROM_ALTMON_BASEADDR, ROM_ALTMON_SIZE, ROM_ALTMON_NAME, ROM_ALTMON_DESC }, + { UNIT_ROM_DBL, rom_mits_dbl, ROM_MITS_DBL_BASEADDR, ROM_MITS_DBL_SIZE, ROM_MITS_DBL_NAME, ROM_MITS_DBL_DESC }, + { UNIT_ROM_HDSK, rom_mits_hdsk, ROM_MITS_HDSK_BASEADDR, ROM_MITS_HDSK_SIZE, ROM_MITS_HDSK_NAME, ROM_MITS_HDSK_DESC }, + { UNIT_ROM_TURMON, rom_mits_turmon, ROM_MITS_TURMON_BASEADDR, ROM_MITS_TURMON_SIZE, ROM_MITS_TURMON_NAME, ROM_MITS_TURMON_DESC }, + { UNIT_ROM_AZ80DBL, rom_az80_dbl, ROM_AZ80_DBL_BASEADDR, ROM_AZ80_DBL_SIZE, ROM_AZ80_DBL_NAME, ROM_AZ80_DBL_DESC }, + + { 0, NULL, 0x0000, 0, "", "" } +}; + +static const char* rom_description(DEVICE *dptr) { + return "Read Only Memory"; +} + +static UNIT rom_unit = { + UDATA (NULL, UNIT_FIX | UNIT_BINK | UNIT_ROM_DBL, MAXBANKSIZE) +}; + +static REG rom_reg[] = { + { FLDATAD (POC, poc, 0x01, "Power on Clear flag"), }, + { NULL } +}; + +static MTAB rom_mod[] = { + { UNIT_ROM_VERBOSE, UNIT_ROM_VERBOSE, "VERBOSE", "VERBOSE", NULL, NULL, + NULL, "Enable verbose messages" }, + { UNIT_ROM_VERBOSE, 0, "QUIET", "QUIET", NULL, NULL, + NULL, "Disable verbose messages" }, + + { UNIT_ROM_DBL, UNIT_ROM_DBL, ROM_MITS_DBL_NAME, ROM_MITS_DBL_NAME, &rom_ena, NULL, + NULL, "Enable " ROM_MITS_DBL_DESC }, + { UNIT_ROM_DBL, 0, "NO" ROM_MITS_DBL_NAME, "NO" ROM_MITS_DBL_NAME, &rom_dis_dbl, NULL, + NULL, "Disable " ROM_MITS_DBL_DESC }, + + { UNIT_ROM_AZ80DBL, UNIT_ROM_AZ80DBL, ROM_AZ80_DBL_NAME, ROM_AZ80_DBL_NAME, &rom_ena, NULL, + NULL, "Enable " ROM_AZ80_DBL_DESC }, + { UNIT_ROM_AZ80DBL, 0, "NO" ROM_AZ80_DBL_NAME, "NO" ROM_AZ80_DBL_NAME, &rom_dis_dbl, NULL, + NULL, "Disable " ROM_AZ80_DBL_DESC }, + + { UNIT_ROM_HDSK, UNIT_ROM_HDSK, ROM_MITS_HDSK_NAME, ROM_MITS_HDSK_NAME, &rom_ena, NULL, + NULL, "Enable " ROM_MITS_HDSK_DESC }, + { UNIT_ROM_HDSK, 0, "NO" ROM_MITS_HDSK_NAME, "NO" ROM_MITS_HDSK_NAME, &rom_dis_hdsk, NULL, + NULL, "Disable " ROM_MITS_HDSK_DESC }, + + { UNIT_ROM_ALTMON, UNIT_ROM_ALTMON, ROM_ALTMON_NAME, ROM_ALTMON_NAME, &rom_ena, NULL, + NULL, "Enable " ROM_ALTMON_DESC }, + { UNIT_ROM_ALTMON, 0, "NO" ROM_ALTMON_NAME, "NO" ROM_ALTMON_NAME, &rom_dis_altmon, NULL, + NULL, "Disable " ROM_ALTMON_DESC }, + + { UNIT_ROM_TURMON, UNIT_ROM_TURMON, ROM_MITS_TURMON_NAME, ROM_MITS_TURMON_NAME, &rom_ena, NULL, + NULL, "Enable " ROM_MITS_TURMON_DESC }, + { UNIT_ROM_TURMON, 0, "NO" ROM_MITS_TURMON_NAME, "NO" ROM_MITS_TURMON_NAME, &rom_dis_turmon, NULL, + NULL, "Disable " ROM_MITS_TURMON_DESC }, + + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "LIST", NULL, NULL, &rom_show_list, NULL, "Show available ROMs" }, + + { 0 } +}; + +/* Debug Flags */ +static DEBTAB rom_dt[] = { + { NULL, 0 } +}; + +DEVICE rom_dev = { + "ROM", &rom_unit, rom_reg, rom_mod, + 1, ADDRRADIX, ADDRWIDTH, 1, DATARADIX, DATAWIDTH, + NULL, NULL, &rom_reset, + NULL, NULL, NULL, + NULL, (DEV_DISABLE), 0, + rom_dt, NULL, NULL, + &rom_show_help, NULL, NULL, + &rom_description +}; + +static t_stat rom_reset(DEVICE *dptr) { + if (dptr->flags & DEV_DIS) { /* Disable Device */ + rom_enadis(rom_unit.flags, FALSE); + + poc = TRUE; + } + else { + if (poc) { + rom_enadis(rom_unit.flags, TRUE); + + poc = FALSE; + } + } + + return SCPE_OK; +} + +static int32 rom_memio(const int32 addr, const int32 rw, const int32 data) +{ + if (rw == S100_IO_READ) { + return GetBYTE(addr); + } + + return 0x0ff; +} + +uint32 GetBYTE(register uint32 Addr) +{ + return M[Addr & ADDRMASK]; /* ROM */ +} + +static t_stat rom_enadis(int32 value, int32 ena) +{ + ROM *r = rom_table; + int i; + + while (r->flag != 0) { + if (value & r->flag) { + if (ena) { + for (i = 0; i < r->size; i++) { + M[r->baseaddr + i] = r->rom[i]; + } + + s100_bus_addmem(r->baseaddr, r->size, &rom_memio, r->name); + + if (rom_unit.flags & UNIT_ROM_VERBOSE) { + sim_printf("Installed ROM %s @ %04X\n", r->name, r->baseaddr); + } + } + else { + s100_bus_remmem(r->baseaddr, r->size, &rom_memio); + + if (rom_unit.flags & UNIT_ROM_VERBOSE) { + sim_printf("Removed ROM %s @ %04X\n", r->name, r->baseaddr); + } + } + } + + r++; + } + + return SCPE_OK; +} + +static t_stat rom_ena(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + return rom_enadis(value, TRUE); +} + +static t_stat rom_dis_dbl(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + return rom_enadis(UNIT_ROM_DBL, FALSE); +} + +static t_stat rom_dis_hdsk(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + return rom_enadis(UNIT_ROM_HDSK, FALSE); +} + +static t_stat rom_dis_turmon(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + return rom_enadis(UNIT_ROM_TURMON, FALSE); +} + +static t_stat rom_dis_altmon(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + return rom_enadis(UNIT_ROM_ALTMON, FALSE); +} + +static t_stat rom_show_list(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + ROM *r = rom_table; + + fprintf(st, "\n"); + + while (r->rom != NULL) { + fprintf(st, "%c %-8.8s: %-25.25s @ %04X-%04X\n", + rom_unit.flags & r->flag ? '*' : ' ', r->name, r->desc, r->baseaddr, r->baseaddr + r->size - 1); + r++; + } + + fprintf(st, "\n* = enabled\n"); + + return SCPE_OK; +} + +static t_stat rom_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "\nAltair 8800 ROM (%s)\n", dptr->name); + + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + fprint_reg_help (st, dptr); + + fprintf(st, "\nVarious ROMs are available through the ROM device. A list of ROMs is available using\n"); + fprintf(st, "the SHOW ROM LIST command. To enable a ROM, enter SET ROM . To disable a ROM,\n"); + fprintf(st, "enter SET ROM NO. Enabled ROMs can be seen with the SHOW BUS CONFIG command.\n\n"); + + return SCPE_OK; +} + diff --git a/Altair8800/s100_rom.h b/Altair8800/s100_rom.h new file mode 100644 index 00000000..6a7981a2 --- /dev/null +++ b/Altair8800/s100_rom.h @@ -0,0 +1,59 @@ +/* s100_rom.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef _S100_ROM_H +#define _S100_ROM_H + +#include "sim_defs.h" + +#define UNIT_ROM_V_VERBOSE (UNIT_V_UF+0) /* warn if ROM is written to */ +#define UNIT_ROM_VERBOSE (1 << UNIT_ROM_V_VERBOSE) +#define UNIT_ROM_V_DBL (UNIT_V_UF+1) /* Enable/Disable Disk Boot Loader */ +#define UNIT_ROM_DBL (1 << UNIT_ROM_V_DBL ) +#define UNIT_ROM_V_HDSK (UNIT_V_UF+2) /* Enable/Disable Hard Disk Boot Loader */ +#define UNIT_ROM_HDSK (1 << UNIT_ROM_V_HDSK ) +#define UNIT_ROM_V_ALTMON (UNIT_V_UF+3) /* Enable/Disable Altmon */ +#define UNIT_ROM_ALTMON (1 << UNIT_ROM_V_ALTMON ) +#define UNIT_ROM_V_TURMON (UNIT_V_UF+4) /* Enable/Disable Turnkey Monitor */ +#define UNIT_ROM_TURMON (1 << UNIT_ROM_V_TURMON ) +#define UNIT_ROM_V_AZ80DBL (UNIT_V_UF+5) /* Enable/Disable AltairZ80 Disk Boot Loader */ +#define UNIT_ROM_AZ80DBL (1 << UNIT_ROM_V_AZ80DBL) + +typedef struct { + uint32 flag; + int32 *rom; + int32 baseaddr; + int32 size; + char *name; + char *desc; +} ROM; + +#endif + diff --git a/Altair8800/s100_roms.h b/Altair8800/s100_roms.h new file mode 100644 index 00000000..0474ee20 --- /dev/null +++ b/Altair8800/s100_roms.h @@ -0,0 +1,373 @@ +/* s100_roms.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef _S100_ROMS_H +#define _S100_ROMS_H + +/* + * MITS Altair Disk Boot Loader Version 4.1 + * https://deramp.com/downloads/altair/software/roms/orginal_roms/DBL.ASM + */ +#define ROM_MITS_DBL_BASEADDR 0xff00 +#define ROM_MITS_DBL_SIZE 0x0100 +#define ROM_MITS_DBL_NAME "DBL" +#define ROM_MITS_DBL_DESC "MITS Disk Boot Loader 4.1" + +static int32 rom_mits_dbl[] = { + 0x21, 0x13, 0xff, 0x11, 0x00, 0x2c, 0x0e, 0xeb, + 0x7e, 0x12, 0x23, 0x13, 0x0d, 0xc2, 0x08, 0xff, + 0xc3, 0x00, 0x2c, 0xf3, 0xaf, 0xd3, 0x22, 0x2f, + 0xd3, 0x23, 0x3e, 0x2c, 0xd3, 0x22, 0x3e, 0x03, + 0xd3, 0x10, 0xdb, 0xff, 0xe6, 0x10, 0x0f, 0x0f, + 0xc6, 0x10, 0xd3, 0x10, 0x31, 0x79, 0x2d, 0xaf, + 0xd3, 0x08, 0xdb, 0x08, 0xe6, 0x08, 0xc2, 0x1c, + 0x2c, 0x3e, 0x04, 0xd3, 0x09, 0xc3, 0x38, 0x2c, + 0xdb, 0x08, 0xe6, 0x02, 0xc2, 0x2d, 0x2c, 0x3e, + 0x02, 0xd3, 0x09, 0xdb, 0x08, 0xe6, 0x40, 0xc2, + 0x2d, 0x2c, 0x11, 0x00, 0x00, 0x06, 0x00, 0x3e, + 0x10, 0xf5, 0xd5, 0xc5, 0xd5, 0x11, 0x86, 0x80, + 0x21, 0xeb, 0x2c, 0xdb, 0x09, 0x1f, 0xda, 0x50, + 0x2c, 0xe6, 0x1f, 0xb8, 0xc2, 0x50, 0x2c, 0xdb, + 0x08, 0xb7, 0xfa, 0x5c, 0x2c, 0xdb, 0x0a, 0x77, + 0x23, 0x1d, 0xca, 0x72, 0x2c, 0x1d, 0xdb, 0x0a, + 0x77, 0x23, 0xc2, 0x5c, 0x2c, 0xe1, 0x11, 0xee, + 0x2c, 0x01, 0x80, 0x00, 0x1a, 0x77, 0xbe, 0xc2, + 0xcb, 0x2c, 0x80, 0x47, 0x13, 0x23, 0x0d, 0xc2, + 0x79, 0x2c, 0x1a, 0xfe, 0xff, 0xc2, 0x90, 0x2c, + 0x13, 0x1a, 0xb8, 0xc1, 0xeb, 0xc2, 0xc2, 0x2c, + 0xf1, 0xf1, 0x2a, 0xec, 0x2c, 0xcd, 0xe5, 0x2c, + 0xd2, 0xbb, 0x2c, 0x04, 0x04, 0x78, 0xfe, 0x20, + 0xda, 0x44, 0x2c, 0x06, 0x01, 0xca, 0x44, 0x2c, + 0xdb, 0x08, 0xe6, 0x02, 0xc2, 0xad, 0x2c, 0x3e, + 0x01, 0xd3, 0x09, 0xc3, 0x42, 0x2c, 0x3e, 0x80, + 0xd3, 0x08, 0xc3, 0x00, 0x00, 0xd1, 0xf1, 0x3d, + 0xc2, 0x46, 0x2c, 0x3e, 0x43, 0x01, 0x3e, 0x4d, + 0xfb, 0x32, 0x00, 0x00, 0x22, 0x01, 0x00, 0x47, + 0x3e, 0x80, 0xd3, 0x08, 0x78, 0xd3, 0x01, 0xd3, + 0x11, 0xd3, 0x05, 0xd3, 0x23, 0xc3, 0xda, 0x2c, + 0x7a, 0xbc, 0xc0, 0x7b, 0xbd, 0xc9, 0x00, 0x00, +}; + +/* + * MITS Altair Turnkey Monitor + * https://deramp.com/downloads/altair/software/roms/orginal_roms/TURMON.ASM + */ +#define ROM_MITS_TURMON_BASEADDR 0xfd00 +#define ROM_MITS_TURMON_SIZE 0x0100 +#define ROM_MITS_TURMON_NAME "TURMON" +#define ROM_MITS_TURMON_DESC "MITS Turnkey Monitor" + +static int32 rom_mits_turmon[] = { + 0x3e, 0x03, 0xd3, 0x10, 0x3e, 0x11, 0xd3, 0x10, + 0x31, 0x00, 0xf8, 0xcd, 0x9d, 0xfd, 0x3e, 0x2e, + 0xcd, 0xf2, 0xfd, 0xcd, 0xe8, 0xfd, 0xfe, 0x4d, + 0xca, 0x29, 0xfd, 0xfe, 0x44, 0xcc, 0x4f, 0xfd, + 0xfe, 0x4a, 0xc2, 0x08, 0xfd, 0xcd, 0xa7, 0xfd, + 0xe9, 0xcd, 0xa7, 0xfd, 0x3e, 0x23, 0xcd, 0x9d, + 0xfd, 0x54, 0x5d, 0xcd, 0xc9, 0xfd, 0x1a, 0x67, + 0xcd, 0xcf, 0xfd, 0xcd, 0xa8, 0xfd, 0xeb, 0xda, + 0x2d, 0xfd, 0x77, 0xbe, 0xca, 0x2d, 0xfd, 0x3e, + 0x3f, 0xcd, 0xf2, 0xfd, 0xc3, 0x08, 0xfd, 0xcd, + 0xa7, 0xfd, 0xeb, 0xd4, 0xe3, 0xfd, 0xcd, 0xa7, + 0xfd, 0x3e, 0x0d, 0x06, 0x3c, 0xcd, 0xf2, 0xfd, + 0x05, 0xc2, 0x5d, 0xfd, 0xb8, 0x78, 0xc2, 0x5b, + 0xfd, 0x7d, 0x93, 0x6f, 0x7c, 0x9a, 0x67, 0x23, + 0x05, 0x7c, 0xb7, 0xc2, 0x77, 0xfd, 0x45, 0x3e, + 0x3c, 0xcd, 0xf2, 0xfd, 0x78, 0xcd, 0xf2, 0xfd, + 0x0e, 0x00, 0x7b, 0xcd, 0xf2, 0xfd, 0x7a, 0xcd, + 0xf2, 0xfd, 0x1a, 0xcd, 0xf2, 0xfd, 0x13, 0x2b, + 0x05, 0xc2, 0x8a, 0xfd, 0x79, 0xcd, 0xf2, 0xfd, + 0x7c, 0xb5, 0xc2, 0x70, 0xfd, 0x3e, 0x0d, 0xcd, + 0xf2, 0xfd, 0x3e, 0x0a, 0xc3, 0xf2, 0xfd, 0x06, + 0x06, 0x03, 0x21, 0x00, 0x00, 0xcd, 0xe8, 0xfd, + 0x4f, 0xfe, 0x20, 0x37, 0xc8, 0xe6, 0xb8, 0xee, + 0x30, 0xc2, 0x47, 0xfd, 0x79, 0xe6, 0x07, 0x29, + 0x29, 0x29, 0x85, 0x6f, 0x05, 0xc2, 0xad, 0xfd, + 0xc9, 0x06, 0x06, 0xaf, 0xc3, 0xd6, 0xfd, 0x06, + 0x03, 0xe6, 0x29, 0x17, 0x29, 0x17, 0x29, 0x17, + 0xe6, 0x07, 0xf6, 0x30, 0xcd, 0xf2, 0xfd, 0x05, + 0xc2, 0xd2, 0xfd, 0x3e, 0x20, 0xc3, 0xf2, 0xfd, + 0xdb, 0x10, 0x0f, 0xd2, 0xe8, 0xfd, 0xdb, 0x11, + 0xe6, 0x7f, 0xf5, 0x81, 0x4f, 0xdb, 0x10, 0x0f, + 0x0f, 0xd2, 0xf5, 0xfd, 0xf1, 0xd3, 0x11, 0xc9, +}; + +#define ROM_MITS_HDSK_BASEADDR 0xfc00 +#define ROM_MITS_HDSK_SIZE 0x0200 +#define ROM_MITS_HDSK_NAME "HDSK" +#define ROM_MITS_HDSK_DESC "MITS Hard Disk Boot ROM" + +static int32 rom_mits_hdsk[] = { + 0x31, 0x00, 0xc0, 0xcd, 0xa6, 0xfd, 0xcd, 0xbe, + 0xfd, 0xdb, 0xa0, 0x07, 0xda, 0x15, 0xfc, 0x11, + 0xe1, 0xfd, 0xcd, 0xb1, 0xfd, 0x21, 0x00, 0x00, + 0xcd, 0x5b, 0xfc, 0x06, 0x28, 0xdb, 0xa5, 0x05, + 0xc2, 0x1d, 0xfc, 0xdb, 0xa5, 0x6f, 0xdb, 0xa5, + 0x67, 0xdb, 0xa5, 0x5f, 0xdb, 0xa5, 0x57, 0xd5, + 0x06, 0xd4, 0xdb, 0xa5, 0x05, 0xc2, 0x32, 0xfc, + 0x11, 0xec, 0xfd, 0xcd, 0xb1, 0xfd, 0x11, 0x00, + 0x00, 0xcd, 0x5b, 0xfc, 0x06, 0x00, 0xdb, 0xa5, + 0x12, 0x13, 0x05, 0xc2, 0x46, 0xfc, 0x23, 0xe3, + 0x2b, 0x7c, 0xb5, 0xca, 0x5a, 0xfc, 0xe3, 0xc3, + 0x41, 0xfc, 0xe9, 0xe5, 0xd5, 0xc5, 0x06, 0x30, + 0x11, 0x10, 0x00, 0x29, 0x7a, 0x17, 0xb8, 0xda, + 0x6c, 0xfc, 0x90, 0x2c, 0x57, 0x1d, 0xc2, 0x63, + 0xfc, 0x47, 0x7d, 0xd3, 0xa7, 0xdb, 0xa3, 0x7c, + 0xf6, 0x00, 0xd3, 0xa3, 0xcd, 0xa9, 0xfc, 0x78, + 0xfe, 0x18, 0xda, 0x89, 0xfc, 0xd6, 0x18, 0xc6, + 0x20, 0xd3, 0xa7, 0xdb, 0xa3, 0x3e, 0x30, 0xd3, + 0xa3, 0xcd, 0xa9, 0xfc, 0xdb, 0xa3, 0xdb, 0xa5, + 0xaf, 0xd3, 0xa7, 0x3e, 0x50, 0xd3, 0xa3, 0xdb, + 0xa4, 0x07, 0xd2, 0x9f, 0xfc, 0xc1, 0xd1, 0xe1, + 0xc9, 0xdb, 0xa0, 0x07, 0xd2, 0xa9, 0xfc, 0xdb, + 0xa1, 0xe6, 0x7f, 0xc8, 0x11, 0xf5, 0xfd, 0xcd, + 0xb1, 0xfd, 0xcd, 0x8f, 0xfd, 0xcd, 0xbe, 0xfd, + 0xc3, 0x06, 0xfd, 0xaf, 0xd3, 0xa0, 0xd3, 0xa2, + 0xd3, 0xa4, 0xd3, 0xa6, 0xd3, 0xa1, 0xd3, 0xa5, + 0x2f, 0xd3, 0xa3, 0xd3, 0xa7, 0x3e, 0x2c, 0xd3, + 0xa0, 0xd3, 0xa4, 0xd3, 0xa6, 0x3e, 0x24, 0xd3, + 0xa2, 0xdb, 0xa1, 0xc9, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x00, 0xc0, 0xcd, 0xa6, 0xfd, 0xcd, 0xbe, + 0xfd, 0x3e, 0x2e, 0xcd, 0xd5, 0xfd, 0xcd, 0xc8, + 0xfd, 0xfe, 0x4c, 0xca, 0x06, 0xfc, 0xfe, 0x4d, + 0xca, 0x24, 0xfd, 0xfe, 0x4a, 0xc2, 0x06, 0xfd, + 0xcd, 0x54, 0xfd, 0xe9, 0xcd, 0xd3, 0xfd, 0xcd, + 0x54, 0xfd, 0x3e, 0x23, 0xcd, 0xbe, 0xfd, 0x7c, + 0xcd, 0x8f, 0xfd, 0x7d, 0xcd, 0x8f, 0xfd, 0xcd, + 0xd3, 0xfd, 0x7e, 0xcd, 0x8f, 0xfd, 0xcd, 0xd3, + 0xfd, 0xcd, 0x5d, 0xfd, 0xda, 0x2b, 0xfd, 0x77, + 0xbe, 0xca, 0x2b, 0xfd, 0x3e, 0x3f, 0xcd, 0xd5, + 0xfd, 0xc3, 0x06, 0xfd, 0xcd, 0x5d, 0xfd, 0x67, + 0xcd, 0x5d, 0xfd, 0x6f, 0xc9, 0xcd, 0xc8, 0xfd, + 0xfe, 0x20, 0x37, 0xc8, 0xcd, 0x74, 0xfd, 0x07, + 0x07, 0x07, 0x07, 0x4f, 0xcd, 0xc8, 0xfd, 0xcd, + 0x74, 0xfd, 0xb1, 0xc9, 0xfe, 0x30, 0xda, 0x4c, + 0xfd, 0xfe, 0x3a, 0xda, 0x8c, 0xfd, 0xe6, 0x5f, + 0xfe, 0x41, 0xda, 0x4c, 0xfd, 0xfe, 0x47, 0xd2, + 0x4c, 0xfd, 0xd6, 0x07, 0xe6, 0x0f, 0xc9, 0xf5, + 0x07, 0x07, 0x07, 0x07, 0xcd, 0x98, 0xfd, 0xf1, + 0xe6, 0x0f, 0xfe, 0x0a, 0xda, 0xa1, 0xfd, 0xc6, + 0x07, 0xc6, 0x30, 0xc3, 0xd5, 0xfd, 0x3e, 0x03, + 0xd3, 0x10, 0x3e, 0x11, 0xd3, 0x10, 0xc3, 0xc3, + 0xfc, 0xf5, 0x1a, 0xcd, 0xd5, 0xfd, 0x13, 0xe6, + 0x80, 0xca, 0xb2, 0xfd, 0xf1, 0xc9, 0x3e, 0x0d, + 0xcd, 0xd5, 0xfd, 0x3e, 0x0a, 0xc3, 0xd5, 0xfd, + 0xdb, 0x10, 0x0f, 0xd2, 0xc8, 0xfd, 0xdb, 0x11, + 0xe6, 0x7f, 0xda, 0x3e, 0x20, 0xf5, 0xdb, 0x10, + 0xe6, 0x02, 0xca, 0xd6, 0xfd, 0xf1, 0xd3, 0x11, + 0xc9, 0x52, 0x45, 0x53, 0x45, 0x54, 0x20, 0x43, + 0x54, 0x4c, 0x0d, 0x8a, 0x4c, 0x4f, 0x41, 0x44, + 0x49, 0x4e, 0x47, 0x0d, 0x8a, 0x4c, 0x4f, 0x41, + 0x44, 0x20, 0x45, 0x52, 0x52, 0x4f, 0x52, 0xba, +}; + +/* + * ALTMON 1.3 + * https://deramp.com/downloads/altair/software/roms/orginal_roms/DBL.ASM + */ +#define ROM_ALTMON_BASEADDR 0xf800 +#define ROM_ALTMON_SIZE 0x0400 +#define ROM_ALTMON_NAME "ALTMON" +#define ROM_ALTMON_DESC "Altmon 1.3" + +static int32 rom_altmon[ROM_ALTMON_SIZE] = { + 0x3e, 0x03, 0xd3, 0x10, 0xd3, 0x12, 0x3e, 0x11, + 0xd3, 0x10, 0xd3, 0x12, 0x31, 0x00, 0xc0, 0xcd, + 0xa5, 0xfb, 0x0d, 0x0a, 0x0a, 0x41, 0x4c, 0x54, + 0x4d, 0x4f, 0x4e, 0x20, 0x31, 0x2e, 0xb3, 0x31, + 0x00, 0xc0, 0x21, 0x1f, 0xf8, 0xe5, 0xcd, 0x63, + 0xfb, 0x3e, 0x2a, 0xcd, 0x48, 0xfb, 0xcd, 0xbc, + 0xfb, 0xe6, 0x5f, 0xfe, 0x42, 0xd8, 0xfe, 0x55, + 0xd0, 0x21, 0xc0, 0xf8, 0x87, 0x85, 0x6f, 0x5e, + 0x23, 0x56, 0xeb, 0xe9, 0x76, 0xf8, 0xcf, 0xf9, + 0xf1, 0xf8, 0x8f, 0xf9, 0xfb, 0xf9, 0x6a, 0xf8, + 0x76, 0xfa, 0x5c, 0xfa, 0x6a, 0xf8, 0x69, 0xf9, + 0x76, 0xfa, 0x84, 0xf9, 0xb6, 0xf9, 0x3f, 0xfa, + 0x32, 0xf9, 0x80, 0xf8, 0x00, 0xfc, 0xef, 0xf9, + 0x99, 0xf8, 0xcd, 0xa5, 0xfb, 0x47, 0x4f, 0x54, + 0xcf, 0xcd, 0x20, 0xfb, 0xeb, 0xe9, 0xcd, 0xa5, + 0xfb, 0x42, 0x4f, 0x4f, 0xd4, 0xc3, 0x00, 0xff, + 0xcd, 0xa5, 0xfb, 0x43, 0x53, 0x55, 0xcd, 0xcd, + 0x1d, 0xfb, 0x06, 0x00, 0x7e, 0x80, 0x47, 0xcd, + 0xea, 0xfb, 0xc2, 0x8c, 0xf8, 0x78, 0xc3, 0x79, + 0xfb, 0xcd, 0xa5, 0xfb, 0x54, 0x45, 0x53, 0xd4, + 0xcd, 0x1d, 0xfb, 0x01, 0x5a, 0x5a, 0xcd, 0xdf, + 0xf8, 0xc5, 0xe5, 0xd5, 0x7c, 0xfe, 0xbf, 0xca, + 0xb6, 0xf8, 0xcd, 0xdf, 0xf8, 0x70, 0xcd, 0xea, + 0xfb, 0xc2, 0xac, 0xf8, 0xd1, 0xe1, 0xc1, 0xe5, + 0xd5, 0x7c, 0xfe, 0xbf, 0xca, 0xcf, 0xf8, 0xcd, + 0xdf, 0xf8, 0x7e, 0xb8, 0xc4, 0x6d, 0xfb, 0xcd, + 0xea, 0xfb, 0xc2, 0xc1, 0xf8, 0xd1, 0xe1, 0x3e, + 0x2e, 0xcd, 0x48, 0xfb, 0xc3, 0xa6, 0xf8, 0xcd, + 0xc7, 0xfb, 0x78, 0xe6, 0xb4, 0xa7, 0xea, 0xea, + 0xf8, 0x37, 0x79, 0x17, 0x4f, 0x78, 0x17, 0x47, + 0xc9, 0xcd, 0xa5, 0xfb, 0x44, 0x55, 0x4d, 0xd0, + 0xcd, 0x1d, 0xfb, 0xe5, 0x0e, 0x10, 0xcd, 0x81, + 0xfb, 0x7e, 0xcd, 0x79, 0xfb, 0xcd, 0x46, 0xfb, + 0x23, 0x0d, 0xc2, 0x01, 0xf9, 0xcd, 0x46, 0xfb, + 0xe1, 0x0e, 0x10, 0x7e, 0xfe, 0x7f, 0xd2, 0x1e, + 0xf9, 0xfe, 0x20, 0xd2, 0x20, 0xf9, 0x3e, 0x2e, + 0xcd, 0x48, 0xfb, 0xcd, 0xea, 0xfb, 0x0d, 0xc2, + 0x13, 0xf9, 0xcd, 0xea, 0xfb, 0xc8, 0x2b, 0xc3, + 0xfb, 0xf8, 0xcd, 0xa5, 0xfb, 0x50, 0x47, 0xcd, + 0xcd, 0x20, 0xfb, 0xeb, 0xcd, 0x63, 0xfb, 0x7e, + 0xcd, 0x79, 0xfb, 0x3e, 0x2d, 0xcd, 0x48, 0xfb, + 0xcd, 0xb3, 0xfb, 0xfe, 0x20, 0xca, 0x65, 0xf9, + 0xfe, 0x0d, 0xc2, 0x5b, 0xf9, 0xcd, 0x63, 0xfb, + 0xc3, 0x48, 0xf9, 0xeb, 0x21, 0x00, 0x00, 0x0e, + 0x02, 0xcd, 0x28, 0xfb, 0x73, 0x23, 0xc3, 0x3f, + 0xf9, 0xcd, 0xa5, 0xfb, 0x46, 0x49, 0x4c, 0xcc, + 0xcd, 0x1d, 0xfb, 0xe5, 0x0e, 0x02, 0xcd, 0x22, + 0xfb, 0xeb, 0xe3, 0xc1, 0x71, 0xcd, 0xea, 0xfb, + 0xc8, 0xc3, 0x7c, 0xf9, 0xcd, 0xa5, 0xfb, 0x4d, + 0x4f, 0x56, 0xc5, 0xaf, 0xc3, 0x96, 0xf9, 0xcd, + 0xa5, 0xfb, 0x45, 0x58, 0x43, 0xc8, 0x47, 0xcd, + 0x1d, 0xfb, 0xe5, 0xcd, 0x20, 0xfb, 0xeb, 0xe3, + 0x4e, 0xe3, 0x78, 0xb7, 0xca, 0xab, 0xf9, 0x7e, + 0xe3, 0x77, 0xe3, 0x71, 0x23, 0xe3, 0xcd, 0xea, + 0xfb, 0xc2, 0xa0, 0xf9, 0xe1, 0xc9, 0xcd, 0xa5, + 0xfb, 0x52, 0x41, 0x4d, 0x54, 0x4f, 0xd0, 0x21, + 0xff, 0xff, 0x23, 0x7e, 0x47, 0x2f, 0x77, 0xbe, + 0x70, 0xca, 0xc2, 0xf9, 0xc3, 0x6d, 0xfb, 0xcd, + 0xa5, 0xfb, 0x43, 0x4f, 0x4d, 0xd0, 0xcd, 0x1d, + 0xfb, 0xe5, 0xcd, 0x20, 0xfb, 0xeb, 0x7e, 0x23, + 0xe3, 0xbe, 0x46, 0xc4, 0x6d, 0xfb, 0xcd, 0xea, + 0xfb, 0xe3, 0xc2, 0xde, 0xf9, 0xe1, 0xc9, 0xcd, + 0xa5, 0xfb, 0x46, 0x49, 0x4e, 0x44, 0xb1, 0xaf, + 0xc3, 0x03, 0xfa, 0xcd, 0xa5, 0xfb, 0x46, 0x49, + 0x4e, 0x44, 0xb2, 0xf5, 0xcd, 0x1d, 0xfb, 0xe5, + 0x0e, 0x02, 0xcd, 0x22, 0xfb, 0xeb, 0x45, 0xe1, + 0xf1, 0xb7, 0xf5, 0xca, 0x1f, 0xfa, 0xe5, 0x0e, + 0x02, 0xcd, 0x22, 0xfb, 0xeb, 0x4d, 0xe1, 0x7e, + 0xb8, 0xc2, 0x37, 0xfa, 0xf1, 0xb7, 0xf5, 0xca, + 0x31, 0xfa, 0x23, 0x7e, 0x2b, 0xb9, 0xc2, 0x37, + 0xfa, 0x23, 0x7e, 0x2b, 0xcd, 0x6d, 0xfb, 0xcd, + 0xea, 0xfb, 0xc2, 0x1f, 0xfa, 0xf1, 0xc9, 0xcd, + 0xa5, 0xfb, 0x4f, 0x55, 0xd4, 0x0e, 0x02, 0xcd, + 0x22, 0xfb, 0x0e, 0x02, 0xcd, 0x22, 0xfb, 0x55, + 0x21, 0xd0, 0xbf, 0x36, 0xc9, 0x2b, 0x72, 0x2b, + 0x36, 0xd3, 0x7b, 0xe9, 0xcd, 0xa5, 0xfb, 0x49, + 0xce, 0x0e, 0x02, 0xcd, 0x22, 0xfb, 0x21, 0xd0, + 0xbf, 0x36, 0xc9, 0x2b, 0x73, 0x2b, 0x36, 0xdb, + 0xcd, 0xce, 0xbf, 0xc3, 0x79, 0xfb, 0xcd, 0xa5, + 0xfb, 0x48, 0x45, 0x58, 0x4c, 0x4f, 0x41, 0xc4, + 0x0e, 0x01, 0xcd, 0x22, 0xfb, 0x21, 0xe0, 0xbf, + 0x73, 0xcd, 0x63, 0xfb, 0x0e, 0x00, 0xcd, 0xf3, + 0xfa, 0xd6, 0x3a, 0xc2, 0x8e, 0xfa, 0x57, 0xcd, + 0xd5, 0xfa, 0x7b, 0xb7, 0xca, 0xc3, 0xfa, 0x43, + 0x0c, 0xcd, 0xd5, 0xfa, 0x63, 0xcd, 0xd5, 0xfa, + 0x6b, 0x0d, 0xcd, 0xd5, 0xfa, 0xcd, 0xd5, 0xfa, + 0x73, 0x23, 0x05, 0xc2, 0xad, 0xfa, 0xcd, 0xd5, + 0xfa, 0xca, 0x89, 0xfa, 0xcd, 0xa5, 0xfb, 0x20, + 0x45, 0x52, 0xd2, 0xdb, 0x11, 0x11, 0xb1, 0x28, + 0xdb, 0x10, 0x0f, 0xda, 0xc3, 0xfa, 0x1b, 0x7a, + 0xb3, 0xc2, 0xc8, 0xfa, 0xc9, 0xcd, 0xf3, 0xfa, + 0xcd, 0xeb, 0xfa, 0x87, 0x87, 0x87, 0x87, 0x5f, + 0xcd, 0xf3, 0xfa, 0xcd, 0xeb, 0xfa, 0x83, 0x5f, + 0x82, 0x57, 0xc9, 0xd6, 0x30, 0xfe, 0x0a, 0xd8, + 0xd6, 0x07, 0xc9, 0xc5, 0x3a, 0xe0, 0xbf, 0xb7, + 0xc2, 0x04, 0xfb, 0xcd, 0xd6, 0xfb, 0xca, 0xfb, + 0xfa, 0xc3, 0x0f, 0xfb, 0xcd, 0xd6, 0xfb, 0xdb, + 0x12, 0x0f, 0xd2, 0x04, 0xfb, 0xdb, 0x13, 0x47, + 0x79, 0xb7, 0xca, 0x1a, 0xfb, 0x78, 0xc1, 0xc3, + 0x48, 0xfb, 0x78, 0xc1, 0xc9, 0xcd, 0x20, 0xfb, + 0x0e, 0x04, 0x21, 0x00, 0x00, 0xcd, 0xb3, 0xfb, + 0xfe, 0x30, 0xda, 0x1f, 0xf8, 0xfe, 0x3a, 0xd4, + 0x56, 0xfb, 0x29, 0x29, 0x29, 0x29, 0xd6, 0x30, + 0xfe, 0x0a, 0xda, 0x3f, 0xfb, 0xd6, 0x07, 0x85, + 0x6f, 0x0d, 0xc2, 0x25, 0xfb, 0xeb, 0x3e, 0x20, + 0xf5, 0xdb, 0x10, 0xe6, 0x02, 0xca, 0x49, 0xfb, + 0xf1, 0xe6, 0x7f, 0xd3, 0x11, 0xc9, 0xfe, 0x41, + 0xda, 0x1f, 0xf8, 0xe6, 0x5f, 0xfe, 0x47, 0xd2, + 0x1f, 0xf8, 0xc9, 0x3e, 0x0d, 0xcd, 0x48, 0xfb, + 0x3e, 0x0a, 0xc3, 0x48, 0xfb, 0xf5, 0xcd, 0x81, + 0xfb, 0x78, 0xcd, 0x79, 0xfb, 0xcd, 0x46, 0xfb, + 0xf1, 0xf5, 0xcd, 0x93, 0xfb, 0xf1, 0xc3, 0x97, + 0xfb, 0xcd, 0x63, 0xfb, 0xcd, 0xc7, 0xfb, 0x7c, + 0xcd, 0x79, 0xfb, 0x7d, 0xcd, 0x79, 0xfb, 0xcd, + 0x46, 0xfb, 0xc9, 0x1f, 0x1f, 0x1f, 0x1f, 0xe6, + 0x0f, 0xc6, 0x30, 0xfe, 0x3a, 0xda, 0x48, 0xfb, + 0xc6, 0x07, 0xc3, 0x48, 0xfb, 0xe1, 0x7e, 0xcd, + 0x48, 0xfb, 0xb6, 0x23, 0xf2, 0xa6, 0xfb, 0xcd, + 0x46, 0xfb, 0xe9, 0xcd, 0xbc, 0xfb, 0xfe, 0x1b, + 0xc8, 0xc3, 0x48, 0xfb, 0xdb, 0x10, 0x0f, 0xd2, + 0xbc, 0xfb, 0xdb, 0x11, 0xe6, 0x7f, 0xc9, 0xcd, + 0xd6, 0xfb, 0xfe, 0x20, 0xc0, 0xcd, 0xd6, 0xfb, + 0xfe, 0x20, 0xc2, 0xcd, 0xfb, 0xc9, 0xdb, 0x10, + 0xe6, 0x01, 0xc8, 0xdb, 0x11, 0xe6, 0x7f, 0xfe, + 0x03, 0xca, 0x1f, 0xf8, 0xfe, 0x1b, 0xca, 0x1f, + 0xf8, 0xc9, 0x7b, 0x95, 0xc2, 0xf1, 0xfb, 0x7a, + 0x9c, 0x23, 0xc0, 0x13, 0xc9, 0xff, 0xff, 0xff +}; + +#define ROM_AZ80_DBL_BASEADDR 0xff00 +#define ROM_AZ80_DBL_SIZE 0x0100 +#define ROM_AZ80_DBL_NAME "AZ80DBL" +#define ROM_AZ80_DBL_DESC "AltairZ80 Disk Boot Loader" + +int32 rom_az80_dbl[] = { + 0xf3, 0x06, 0x80, 0x3e, 0x0e, 0xd3, 0xfe, 0x05, /* ff00-ff07 */ + 0xc2, 0x05, 0xff, 0x3e, 0x16, 0xd3, 0xfe, 0x3e, /* ff08-ff0f */ + 0x12, 0xd3, 0xfe, 0xdb, 0xfe, 0xb7, 0xca, 0x20, /* ff10-ff17 */ + 0xff, 0x3e, 0x0c, 0xd3, 0xfe, 0xaf, 0xd3, 0xfe, /* ff18-ff1f */ + 0x21, 0x00, 0x5c, 0x11, 0x33, 0xff, 0x0e, 0x88, /* ff20-ff27 */ + 0x1a, 0x77, 0x13, 0x23, 0x0d, 0xc2, 0x28, 0xff, /* ff28-ff2f */ + 0xc3, 0x00, 0x5c, 0x31, 0x21, 0x5d, 0x3e, 0x00, /* ff30-ff37 */ + 0xd3, 0x08, 0x3e, 0x04, 0xd3, 0x09, 0xc3, 0x19, /* ff38-ff3f */ + 0x5c, 0xdb, 0x08, 0xe6, 0x02, 0xc2, 0x0e, 0x5c, /* ff40-ff47 */ + 0x3e, 0x02, 0xd3, 0x09, 0xdb, 0x08, 0xe6, 0x40, /* ff48-ff4f */ + 0xc2, 0x0e, 0x5c, 0x11, 0x00, 0x00, 0x06, 0x08, /* ff50-ff57 */ + 0xc5, 0xd5, 0x11, 0x86, 0x80, 0x21, 0x88, 0x5c, /* ff58-ff5f */ + 0xdb, 0x09, 0x1f, 0xda, 0x2d, 0x5c, 0xe6, 0x1f, /* ff60-ff67 */ + 0xb8, 0xc2, 0x2d, 0x5c, 0xdb, 0x08, 0xb7, 0xfa, /* ff68-ff6f */ + 0x39, 0x5c, 0xdb, 0x0a, 0x77, 0x23, 0x1d, 0xc2, /* ff70-ff77 */ + 0x39, 0x5c, 0xd1, 0x21, 0x8b, 0x5c, 0x06, 0x80, /* ff78-ff7f */ + 0x7e, 0x12, 0x23, 0x13, 0x05, 0xc2, 0x4d, 0x5c, /* ff80-ff87 */ + 0xc1, 0x21, 0x00, 0x5c, 0x7a, 0xbc, 0xc2, 0x60, /* ff88-ff8f */ + 0x5c, 0x7b, 0xbd, 0xd2, 0x80, 0x5c, 0x04, 0x04, /* ff90-ff97 */ + 0x78, 0xfe, 0x20, 0xda, 0x25, 0x5c, 0x06, 0x01, /* ff98-ff9f */ + 0xca, 0x25, 0x5c, 0xdb, 0x08, 0xe6, 0x02, 0xc2, /* ffa0-ffa7 */ + 0x70, 0x5c, 0x3e, 0x01, 0xd3, 0x09, 0x06, 0x00, /* ffa8-ffaf */ + 0xc3, 0x25, 0x5c, 0x3e, 0x80, 0xd3, 0x08, 0xfb, /* ffb0-ffb7 */ + 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffb8-ffbf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffc0-ffc7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffc8-ffcf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffd0-ffd7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffd8-ffdf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffe0-ffe7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffe8-ffef */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* fff0-fff7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* fff8-ffff */ +}; + +#endif diff --git a/Altair8800/s100_simh.c b/Altair8800/s100_simh.c new file mode 100644 index 00000000..2b6ce598 --- /dev/null +++ b/Altair8800/s100_simh.c @@ -0,0 +1,505 @@ +/* s100_simh.c: MITS Altair 8800 SIMH Pseudo Device + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + Based on work by Charles E Owen (c) 1997 + Based on work by Peter Schorn (c) 2002-2023 + + History: + 07-Nov-2025 Initial version + +*/ + +#include "sim_defs.h" +#include "s100_bus.h" +#include "s100_cpu.h" +#include "s100_simh.h" + +static t_stat simh_dev_reset(DEVICE *dptr); +static int32 simh_io_status(const int32 port, const int32 io, const int32 data); +static int32 simh_io_data(const int32 port, const int32 io, const int32 data); +static int32 simh_io_cmd(const int32 port, const int32 io, const int32 data); +static int32 simh_cmd_in(const int32 port); +static int32 simh_cmd_out(const int32 port, const int32 data); +static t_stat simh_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +static void createCPMCommandLine(void); +static void attachCPM(UNIT *uptr, int32 readOnly); +static void detachCPM(UNIT *uptr); + +static IDEV idev_stat[2] = { {NULL, NULL}, {NULL, NULL} }; /* Save IO devices on 0x12 and 0x13 */ +static IDEV idev_data[2] = { {NULL, NULL}, {NULL, NULL} }; /* Save IO devices on 0x12 and 0x13 */ + +/* Debug flags */ +#define IN_MSG (1 << 0) +#define OUT_MSG (1 << 1) +#define CMD_MSG (1 << 2) +#define VERBOSE_MSG (1 << 3) + +/* Debug Flags */ +static DEBTAB generic_dt[] = { + { "IN", IN_MSG, "IN messages" }, + { "OUT", OUT_MSG, "OUT messages" }, + { "CMD", CMD_MSG, "Commands" }, + { "VERBOSE", VERBOSE_MSG, "Verbose messages" }, + { NULL, 0 } +}; + +#define CPM_COMMAND_LINE_LENGTH 128 +#define CPM_FCB_ADDRESS 0x0080 /* Default FCB address for CP/M. */ + +#define SIMH_CAN_READ 0x01 /* bit 0 is set iff character available */ +#define SIMH_CAN_WRITE 0x02 /* bit 1 is set iff character can be sent */ +#define SIMH_RESET 0x03 /* Command to reset SIMH */ +#define CONTROLZ_CHAR 0x1a /* control Z character */ + +/* SIMH pseudo device status registers */ +/* miscellaneous */ +static int32 versionPos = 0; /* determines state for sending device identifier */ +static int32 lastCPMStatus = 0; /* result of last attachCPM command */ +static int32 lastCommand = 0; /* most recent command processed on port 0xfeh */ + +/* Set FCB Address (needed for MS-DOS READ and WRITE commands. */ +static int32 FCBAddress = CPM_FCB_ADDRESS; /* FCB Address */ + +static int32 warnLevelSIMH = 3; /* display at most 'warnLevelSIMH' times the same warning */ +static int32 warnUnattachedSIMH = 0; /* display a warning message if < warnLevel and SIMH set to + VERBOSE and output to SIMH without an attached file */ +static int32 warnSIMHEOF = 0; /* display a warning message if < warnLevel and SIMH set to + VERBOSE and attempt to read from SIMH past EOF */ + +/* Synthetic device SIMH for communication + between Altair and SIMH environment using port 0xfe */ +static UNIT simh_unit = { + UDATA (NULL, UNIT_ATTABLE | UNIT_ROABLE, 0) +}; + +static REG simh_reg[] = { + { DRDATAD (VPOS, versionPos, 8, + "Status register for sending version information"), REG_RO }, + { DRDATAD (LCPMS, lastCPMStatus, 8, + "Result of last attachCPM command"), REG_RO }, + { DRDATAD (LCMD, lastCommand, 8, + "Last command processed on SIMH port"), REG_RO }, + { HRDATAD (FCBA, FCBAddress, 16, + "Address of the FCB for file operations") }, + { NULL } +}; + +static MTAB simh_mod[] = { + { UNIT_SIMH_VERBOSE, UNIT_SIMH_VERBOSE, "VERBOSE", "VERBOSE", NULL, NULL, + NULL, "Enable verbose messages" }, + { UNIT_SIMH_VERBOSE, 0, "QUIET", "QUIET", NULL, NULL, + NULL, "Disable verbose messages" }, + { 0 } +}; + +const char* simh_description(DEVICE *dptr) { + return "SIMH Pseudo Device"; +} + +DEVICE simh_dev = { + "SIMH", &simh_unit, simh_reg, simh_mod, + 1, ADDRRADIX, ADDRWIDTH, 1, DATARADIX, DATAWIDTH, + NULL, NULL, &simh_dev_reset, + NULL, NULL, NULL, + NULL, (DEV_DISABLE | DEV_DEBUG), 0, + generic_dt, NULL, NULL, &simh_show_help, NULL, NULL, &simh_description +}; + +static char cpmCommandLine[CPM_COMMAND_LINE_LENGTH]; + +/* Z80 or 8080 programs communicate with the SIMH pseudo device via port 0xfe. + The following principles apply: + + 1) For commands that do not require parameters and do not return results + ld a, + out (0feh),a + Special case is the reset command which needs to be send 128 times to make + sure that the internal state is properly reset. + + 2) For commands that require parameters and do not return results + ld a, + out (0feh),a + ld a, + out (0feh),a + ld a, + out (0feh),a + ... + Note: The calling program must send all parameter bytes. Otherwise + the pseudo device is left in an undefined state. + + 3) For commands that do not require parameters and return results + ld a, + out (0feh),a + in a,(0feh) ; contains first byte of result + in a,(0feh) ; contains second byte of result + ... + Note: The calling program must request all bytes of the result. Otherwise + the pseudo device is left in an undefined state. + + 4) For commands that do require parameters and return results + ld a, + out (0feh),a + ld a, + out (0feh),a + ld a, + out (0feh),a + ... ; send all parameters + in a,(0feh) ; contains first byte of result + in a,(0feh) ; contains second byte of result + ... + +*/ + +/* simhPseudoDeviceCommands - do not change command numbers */ +#define resetPTRCmd 3 /* 3 reset the PTR device */ +#define attachPTRCmd 4 /* 4 attach the PTR device */ +#define detachPTRCmd 5 /* 5 detach the PTR device */ +#define getSIMHVersionCmd 6 /* 6 get the current version of the SIMH pseudo device */ +#define resetSIMHInterfaceCmd 14 /* 14 reset the SIMH pseudo device */ +#define attachPTPCmd 16 /* 16 attach PTP to the file with name at beginning of CP/M command line*/ +#define detachPTPCmd 17 /* 17 detach PTP */ +#define setZ80CPUCmd 19 /* 19 set the CPU to a Z80 */ +#define set8080CPUCmd 20 /* 20 set the CPU to an 8080 */ +#define getHostOSPathSeparatorCmd 28 /* 28 obtain the file path separator of the OS under which SIMH runs */ +#define kSimhPseudoDeviceCommands 35 /* Highest AltairZ80 SIMH command */ + +static const char *cmdNames[kSimhPseudoDeviceCommands] = { + "Undefined", + "Undefined", + "Undefined", + "resetPTR", + "attachPTR", + "detachPTR", + "getSIMHVersion", + "Undefined", + "Undefined", + "Undefined", + "Undefined", + "Undefined", + "Undefined", + "Undefined", + "resetSIMHInterface", + "Undefined", + "attachPTP", + "detachPTP", + "Undefined", + "setZ80CPU", + "set8080CPU", + "Undefined", + "Undefined", + "Undefined", + "Undefined", + "Undefined", + "Undefined", + "Undefined", + "getHostOSPathSeparator", + "Undefined", + "Undefined", + "Undefined", + "Undefined", + "Undefined", + "Undefined", +}; + +static char version[] = "SIMH005"; + +static t_stat simh_dev_reset(DEVICE *dptr) { + if (dptr->flags & DEV_DIS) { + s100_bus_remio(0xfe, 1, &simh_io_cmd); /* Command Port */ + } + else { + s100_bus_addio(0xfe, 1, &simh_io_cmd, "SIMH"); /* Command Port */ + } + + versionPos = 0; + lastCommand = 0; + lastCPMStatus = SCPE_OK; + FCBAddress = CPM_FCB_ADDRESS; + + if (simh_unit.flags & UNIT_ATT) { /* not attached */ + detachCPM(&simh_unit); + } + + return SCPE_OK; +} + +static void createCPMCommandLine(void) { + int32 i, len = (s100_bus_memr(FCBAddress) & 0x7f); /* 0x80 contains length of command line, discard first char */ + for (i = 0; i < len - 1; i++) { + cpmCommandLine[i] = (char) s100_bus_memr(FCBAddress + 0x02 + i); /* the first char, typically ' ', is discarded */ + } + cpmCommandLine[i] = 0; /* make C string */ +} + +/* The CP/M command line is used as the name of a file and UNIT* uptr is attached to it. */ +static void attachCPM(UNIT *uptr, int32 readOnly) +{ + createCPMCommandLine(); + + sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT + " CP/M command line='%s'.\n", s100_bus_get_addr(), cpmCommandLine); + + if (readOnly) { + sim_switches = SWMASK('R') | SWMASK('Q'); + } + else { + sim_switches = SWMASK('W') | SWMASK('N') | SWMASK('Q'); + } + + /* 'N' option makes sure that file is properly truncated if it had existed before */ + sim_quiet = sim_switches & SWMASK('Q'); /* -q means quiet */ + + lastCPMStatus = attach_unit(uptr, cpmCommandLine); + + if (lastCPMStatus != SCPE_OK) { + sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT + " Cannot open '%s' (%s).\n", s100_bus_get_addr(), cpmCommandLine, + sim_error_text(lastCPMStatus)); + } + + /* Save any devices attached to IO Port 0x12 and 0x13 */ + s100_bus_get_idev(0x12, &idev_stat[0], &idev_stat[1]); + s100_bus_get_idev(0x13, &idev_data[0], &idev_data[1]); + + s100_bus_addio(0x12, 1, &simh_io_status, "SIMHS"); /* Status Port */ + s100_bus_addio(0x13, 1, &simh_io_data, "SIMHD"); /* Data Port */ + + simh_unit.u3 = FALSE; /* reset EOF indicator */ +} + +static void detachCPM(UNIT *uptr) +{ + detach_unit(&simh_unit); + + if (idev_stat[0].routine != NULL) { + s100_bus_addio_in(0x12, 1, idev_stat[0].routine, idev_stat[0].name); /* Status IN Port */ + idev_stat[0].routine = NULL; + } + if (idev_stat[1].routine != NULL) { + s100_bus_addio_out(0x12, 1, idev_stat[1].routine, idev_stat[1].name); /* Status OUT Port */ + idev_stat[1].routine = NULL; + } + if (idev_data[0].routine != NULL) { + s100_bus_addio_in(0x13, 1, idev_data[0].routine, idev_data[0].name); /* Data IN Port */ + idev_data[0].routine = NULL; + } + if (idev_data[1].routine != NULL) { + s100_bus_addio_out(0x13, 1, idev_data[1].routine, idev_data[1].name); /* Data OUT Port */ + idev_data[1].routine = NULL; + } +} + +static int32 simh_cmd_in(const int32 port) +{ + int32 result = 0; + + switch(lastCommand) { + case attachPTRCmd: + case attachPTPCmd: + result = lastCPMStatus; + lastCommand = 0; + break; + + case getSIMHVersionCmd: + result = version[versionPos++]; + if (result == 0) + versionPos = lastCommand = 0; + break; + + case getHostOSPathSeparatorCmd: + result = sim_file_path_separator; + break; + + default: + sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT + " Undefined IN from SIMH pseudo device on port %03xh ignored.\n", + s100_bus_get_addr(), port); + result = lastCommand = 0; + } + + return result; +} + +static int32 simh_cmd_out(const int32 port, const int32 data) { + + switch(lastCommand) { + default: /* lastCommand not yet set */ + sim_debug(CMD_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT + " CMD(0x%02x) <- %i (0x%02x, '%s')\n", + s100_bus_get_addr(), port, data, data, + (0 <= data) && (data < kSimhPseudoDeviceCommands) ? + cmdNames[data] : "Unknown command"); + + lastCommand = data; + + switch(data) { + case getSIMHVersionCmd: + versionPos = 0; + break; + + case resetPTRCmd: /* reset ptr device */ + break; + + case attachPTRCmd: /* attach ptr to the file with name at beginning of CP/M command line */ + attachCPM(&simh_unit, TRUE); + break; + + case detachPTRCmd: /* detach ptr */ + detachCPM(&simh_unit); + break; + + case attachPTPCmd: /* attach ptp to the file with name at beginning of CP/M command line */ + attachCPM(&simh_unit, FALSE); + break; + + case detachPTPCmd: /* detach ptp */ + detachCPM(&simh_unit); + break; + + case resetSIMHInterfaceCmd: + lastCommand = 0; + FCBAddress = CPM_FCB_ADDRESS; + break; + + case setZ80CPUCmd: + cpu_set_chiptype(CHIP_TYPE_Z80); + break; + + case set8080CPUCmd: + cpu_set_chiptype(CHIP_TYPE_8080); + break; + + case getHostOSPathSeparatorCmd: + break; + + default: + sim_debug(CMD_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT + " Unknown command (%i) to SIMH pseudo device on port %03xh ignored.\n", + s100_bus_get_addr(), data, port); + } + } + + return 0xff; /* ignored, since OUT */ +} + +/* port 0xfc is a device for communication SIMH <--> Altair machine */ +static int32 simh_io_status(const int32 port, const int32 io, const int32 data) +{ + if (io == S100_IO_READ) { /* IN */ + if ((simh_unit.flags & UNIT_ATT) == 0) { /* SIMH is not attached */ + if ((simh_dev.dctrl & VERBOSE_MSG) && (warnUnattachedSIMH < warnLevelSIMH)) { + warnUnattachedSIMH++; +/*06*/ sim_debug(VERBOSE_MSG, &simh_dev, "PTR: " ADDRESS_FORMAT + " Attempt to test status of unattached SIMH[0x%02x]. 0x02 returned.\n", s100_bus_get_addr(), port); + } + return SIMH_CAN_WRITE; + } + /* if EOF then SIMH_CAN_WRITE else + (SIMH_CAN_WRITE and SIMH_CAN_READ) */ + return simh_unit.u3 ? SIMH_CAN_WRITE : (SIMH_CAN_READ | SIMH_CAN_WRITE); + } /* OUT follows */ + if (data == SIMH_RESET) { + simh_unit.u3 = FALSE; /* reset EOF indicator */ + sim_debug(CMD_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT + " Command OUT(0x%03x) = 0x%02x\n", s100_bus_get_addr(), port, data); + } + return 0x00; /* ignored since OUT */ +} + +/* port 0xfd is a device for communication SIMH <--> Altair machine */ +static int32 simh_io_data(const int32 port, const int32 io, const int32 data) +{ + int32 ch; + + if (io == S100_IO_READ) { /* IN */ + if (simh_unit.u3) { /* EOF reached, no more data available */ + if ((simh_dev.dctrl & VERBOSE_MSG) && (warnSIMHEOF < warnLevelSIMH)) { + warnSIMHEOF++; +/*07*/ sim_debug(VERBOSE_MSG, &simh_dev, "PTR: " ADDRESS_FORMAT + " SIMH[0x%02x] attempted to read past EOF. 0x00 returned.\n", s100_bus_get_addr(), port); + } + return 0x00; + } + if ((simh_unit.flags & UNIT_ATT) == 0) { /* not attached */ + if ((simh_dev.dctrl & VERBOSE_MSG) && (warnUnattachedSIMH < warnLevelSIMH)) { + warnUnattachedSIMH++; +/*08*/ sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT + " Attempt to read from unattached SIMH[0x%02x]. 0x00 returned.\n", s100_bus_get_addr(), port); + } + return 0x00; + } + if ((ch = getc(simh_unit.fileref)) == EOF) { /* end of file? */ + simh_unit.u3 = TRUE; /* remember EOF reached */ + sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT + " EOF on read\n", s100_bus_get_addr()); + return CONTROLZ_CHAR; /* ^Z denotes end of text file in CP/M */ + } + return ch & 0xff; + } /* OUT follows */ + if (simh_unit.flags & UNIT_ATT) /* unit must be attached */ + putc(data, simh_unit.fileref); + /* else ignore data */ + else if ((simh_dev.dctrl & VERBOSE_MSG) && (warnUnattachedSIMH < warnLevelSIMH)) { + warnUnattachedSIMH++; +/*09*/ sim_debug(VERBOSE_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT + " Attempt to output '0x%02x' to unattached SIMH[0x%02x] - ignored.\n", s100_bus_get_addr(), data, port); + } + return 0x00; /* ignored since OUT */ +} + +/* port 0xfe is a device for communication SIMH <--> Altair machine */ +static int32 simh_io_cmd(const int32 port, const int32 io, const int32 data) +{ + int32 result = 0; + + if (io == S100_IO_READ) { + result = simh_cmd_in(port); + sim_debug(IN_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT + " IN(0x%02x) -> %i (0x%02x, '%c')\n", s100_bus_get_addr(), + port, result, result, + (32 <= (result & 0xff)) && ((result & 0xff) <= 127) ? (result & 0xff) : '?'); + + } else { + sim_debug(OUT_MSG, &simh_dev, "SIMH: " ADDRESS_FORMAT + " OUT(0x%02x) <- %i (0x%02x, '%c')\n", s100_bus_get_addr(), + port, data, data, + (32 <= (data & 0xff)) && ((data & 0xff) <= 127) ? (data & 0xff) : '?'); + simh_cmd_out(port, data); + } + + return result; +} + +static t_stat simh_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "\nSIMH Pseudo Device (%s)\n", dptr->name); + + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + fprint_reg_help (st, dptr); + + return SCPE_OK; +} + diff --git a/Altair8800/s100_simh.h b/Altair8800/s100_simh.h new file mode 100644 index 00000000..53c8b396 --- /dev/null +++ b/Altair8800/s100_simh.h @@ -0,0 +1,37 @@ +/* s100_simh.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef _S100_SIMH_H +#define _S100_SIMH_H + +#define UNIT_V_SIMH_VERBOSE (UNIT_V_UF + 0) /* verbose mode, i.e. show error messages */ +#define UNIT_SIMH_VERBOSE (1 << UNIT_V_SIMH_VERBOSE) + +#endif diff --git a/Altair8800/s100_sio.c b/Altair8800/s100_sio.c new file mode 100644 index 00000000..a4985f2b --- /dev/null +++ b/Altair8800/s100_sio.c @@ -0,0 +1,432 @@ +/* s100_sio.c: MITS Altair 8800 Generic SIO + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 07-Nov-2025 Initial version + +*/ + +#include "sim_defs.h" +#include "s100_bus.h" +#include "s100_sio.h" + +static int32 poc = TRUE; /* Power On Clear */ + +#define SIO_TYPE_CUST 0 +#define SIO_TYPE_2502 1 +#define SIO_TYPE_2651 2 +#define SIO_TYPE_6850 3 +#define SIO_TYPE_8250 4 +#define SIO_TYPE_8251 5 +#define SIO_TYPE_NONE 0xff + +static SIO sio_types[] = { +/* TYPE NAME DESC BASE STAT DATA RDRE RDRF TDRE TDRF */ + { SIO_TYPE_CUST, "CUST", "CUSTOM", 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { SIO_TYPE_2502, "2502", "2502 UART", 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x08 }, + { SIO_TYPE_2651, "2651", "2651 UART", 0x00, 0x01, 0x00, 0xc0, 0xc2, 0xc1, 0xc0 }, + { SIO_TYPE_6850, "6850", "6850 ACIA", 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02 }, + { SIO_TYPE_8250, "8250", "8250 UART", 0x00, 0x05, 0x00, 0x00, 0x01, 0x60, 0x00 }, + { SIO_TYPE_8251, "8251", "8251 UART", 0x00, 0x01, 0x00, 0x80, 0x82, 0x85, 0x80 }, + { SIO_TYPE_NONE, "NONE", "NONE" , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } +}; + +static SIO_BOARD sio_boards[] = { + /* MITS 88-SIO */ + { SIO_TYPE_2502, "SIO", "MITS 88-SIO", 0x00 }, + + /* CompuPro System Support 1 */ + { SIO_TYPE_2651, "SS1", "CompuPro System Support 1", 0x5c }, + + /* No type selected */ + { SIO_TYPE_NONE, "NONE", "NONE", 0x00 } +}; + +static SIO sio; /* Active SIO configuration */ + +static int32 sio_type = SIO_TYPE_NONE; + +static int32 sio_rdr; /* Receive Data Register */ +static int32 sio_rdre; /* Receive Data Register Empty Flag */ +static int32 sio_tdre; /* Transmit Buffer Full Empty */ + +static t_stat sio_reset(DEVICE *dptr); +static int32 sio_io(const int32 addr, const int32 rw, const int32 data); +static int32 sio_io_in(const int32 addr); +static void sio_io_out(const int32 addr, int32 data); +static t_stat sio_set_board(UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat sio_set_type (UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat sio_set_val(UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat sio_set_console(UNIT *uptr, int32 value, const char *cptr, void *desc); +static t_stat sio_show_config(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat sio_show_list(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat sio_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); + +static const char* sio_description(DEVICE *dptr) { + return "Generic Serial IO"; +} + +static UNIT sio_unit = { + UDATA (NULL, 0, 0) +}; + +static REG sio_reg[] = { + { HRDATAD (TYPE, sio_type, 8, "SIO Board Type") }, + { HRDATAD (RDR, sio_rdr, 8, "Receive Data Register") }, + { HRDATAD (RDRE, sio_rdre, 1, "Receive Data Register Empty") }, + { HRDATAD (TDRE, sio_tdre, 1, "Transmit Data Register Empty") }, + { NULL } +}; + +static MTAB sio_mod[] = { + { UNIT_SIO_VERBOSE, UNIT_SIO_VERBOSE, "VERBOSE", "VERBOSE", NULL, NULL, + NULL, "Enable verbose messages" }, + { UNIT_SIO_VERBOSE, 0, "QUIET", "QUIET", NULL, NULL, + NULL, "Disable verbose messages" }, + + { MTAB_XTD | MTAB_VUN, UNIT_SIO_CONSOLE, NULL, "CONSOLE", &sio_set_console, NULL, NULL, "Set as CONSOLE" }, + { MTAB_XTD | MTAB_VUN, 0, NULL, "NOCONSOLE", &sio_set_console, NULL, NULL, "Remove as CONSOLE" }, + + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "CONFIG", NULL, NULL, &sio_show_config, NULL, "Show SIO configuration" }, + + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "LIST", NULL, NULL, &sio_show_list, NULL, "Show available types and boards" }, + + { MTAB_XTD | MTAB_VDV | MTAB_VALO, SIO_TYPE_2502, NULL, "2502={base}", &sio_set_type, NULL, NULL, "Configure SIO for 2502 at base" }, + { MTAB_XTD | MTAB_VDV | MTAB_VALO, SIO_TYPE_2651, NULL, "2651={base}", &sio_set_type, NULL, NULL, "Configure SIO for 2651 at base" }, + { MTAB_XTD | MTAB_VDV | MTAB_VALO, SIO_TYPE_6850, NULL, "6850={base}", &sio_set_type, NULL, NULL, "Configure SIO for 6850 at base" }, + { MTAB_XTD | MTAB_VDV | MTAB_VALO, SIO_TYPE_8250, NULL, "8250={base}", &sio_set_type, NULL, NULL, "Configure SIO for 8250 at base" }, + { MTAB_XTD | MTAB_VDV | MTAB_VALO, SIO_TYPE_8251, NULL, "8251={base}", &sio_set_type, NULL, NULL, "Configure SIO for 8251 at base" }, + { MTAB_XTD | MTAB_VDV , SIO_TYPE_NONE, NULL, "NONE", &sio_set_type, NULL, NULL, "No type selected" }, + + { MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, NULL, "BOARD={name}", &sio_set_board, NULL, NULL, "Configure SIO for name" }, + + { MTAB_XTD | MTAB_VDV | MTAB_VALR, 1, NULL, "IOBASE={base}", &sio_set_val, NULL, NULL, "Set BASE I/O Address" }, + { MTAB_XTD | MTAB_VDV | MTAB_VALR, 2, NULL, "STAT={offset}", &sio_set_val, NULL, NULL, "Set STAT I/O Offset" }, + { MTAB_XTD | MTAB_VDV | MTAB_VALR, 3, NULL, "DATA={offset}", &sio_set_val, NULL, NULL, "Set DATA I/O Offset" }, + { MTAB_XTD | MTAB_VDV | MTAB_VALR, 4, NULL, "RDRE={mask}", &sio_set_val, NULL, NULL, "Set RDRE Mask" }, + { MTAB_XTD | MTAB_VDV | MTAB_VALR, 5, NULL, "RDRF={mask}", &sio_set_val, NULL, NULL, "Set RDRF Mask" }, + { MTAB_XTD | MTAB_VDV | MTAB_VALR, 6, NULL, "TDRE={mask}", &sio_set_val, NULL, NULL, "Set TDRE Mask" }, + { MTAB_XTD | MTAB_VDV | MTAB_VALR, 7, NULL, "TDRF={mask}", &sio_set_val, NULL, NULL, "Set TDRF Mask" }, + + { 0 } +}; + +/* Debug Flags */ +#define STATUS_MSG (1 << 0) +#define IN_MSG (1 << 1) +#define OUT_MSG (1 << 2) + +/* Debug Flags */ +static DEBTAB sio_dt[] = { + { "STATUS", STATUS_MSG, "Status messages" }, + { "IN", IN_MSG, "IN operations" }, + { "OUT", OUT_MSG, "OUT operations" }, + { NULL, 0 } +}; + +#define SIO_SNAME "SIO" + +DEVICE sio_dev = { + SIO_SNAME, &sio_unit, sio_reg, sio_mod, + 1, ADDRRADIX, ADDRWIDTH, 1, DATARADIX, DATAWIDTH, + NULL, NULL, &sio_reset, + NULL, NULL, NULL, + NULL, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), 0, + sio_dt, NULL, NULL, &sio_show_help, NULL, NULL, &sio_description +}; + +static t_stat sio_reset(DEVICE *dptr) +{ + if (dptr->flags & DEV_DIS) { /* Disable Device */ + if (sio_type != SIO_TYPE_NONE) { + s100_bus_remio(sio.stat, 1, &sio_io); + s100_bus_remio(sio.data, 1, &sio_io); + + s100_bus_noconsole(&dptr->units[0]); + } + + poc = TRUE; + + return SCPE_OK; + } + + /* Device is enabled */ + if (poc) { + /* Set board type */ + sio_set_type(NULL, sio_type, NULL, NULL); + + poc = FALSE; + } + + /* Set as CONSOLE unit */ + if (dptr->units[0].flags & UNIT_SIO_CONSOLE) { + s100_bus_console(&dptr->units[0]); + } + + sio_rdre = TRUE; + sio_tdre = TRUE; + + sim_debug(STATUS_MSG, dptr, "reset adapter.\n"); + + return SCPE_OK; +} + +static int32 sio_io(const int32 addr, const int32 rw, const int32 data) +{ + int32 c; + + if (sio_rdre) { /* If the receive data register is empty and this */ + c = s100_bus_poll_kbd(&sio_unit); /* is the CONSOLE, check for keyboard input */ + + if (c & SCPE_KFLAG) { + sio_rdre = FALSE; + sio_rdr = c & DATAMASK; + } + } + + if (rw == S100_IO_READ) { + return sio_io_in(addr); + } + + sio_io_out(addr, data); + + return 0x0ff; +} + +static int32 sio_io_in(const int32 addr) +{ + sim_debug(IN_MSG, &sio_dev, ADDRESS_FORMAT " Port %02X.\n", s100_bus_get_addr(), addr & DATAMASK); + + if (addr == sio.base + sio.stat) { + return ((sio_rdre) ? sio.rdre : sio.rdrf) | ((sio_tdre) ? sio.tdre : sio.tdrf); + } + else if (addr == sio.base + sio.data) { + sio_rdre = TRUE; /* Clear RDF status bit */ + + return sio_rdr; /* return byte */ + } + + return 0xff; +} + +static void sio_io_out(const int32 addr, int32 data) +{ + sim_debug(OUT_MSG, &sio_dev, ADDRESS_FORMAT " Port %02X.\n", s100_bus_get_addr(), addr & DATAMASK); + + if (addr == sio.base + sio.data) { + sim_putchar(data & DATAMASK); + + sio_tdre = TRUE; /* Transmit buffer is always empty */ + } +} + +static t_stat sio_set_type(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + int32 result, base; + + if (value == sio_type) { + return SCPE_OK; + } + + if (sio_type != SIO_TYPE_NONE) { + s100_bus_remio(sio.base + sio.stat, 1, &sio_io); + s100_bus_remio(sio.base + sio.data, 1, &sio_io); + } + + sio_type = value; + + if (sio_type != SIO_TYPE_NONE) { + sio.type = sio_type; + sio.name = sio_types[sio_type].name; + sio.desc = sio_types[sio_type].desc; + sio.base = sio_types[sio_type].base; + sio.stat = sio_types[sio_type].stat; + sio.data = sio_types[sio_type].data; + sio.rdre = sio_types[sio_type].rdre; + sio.rdrf = sio_types[sio_type].rdrf; + sio.tdre = sio_types[sio_type].tdre; + sio.tdrf = sio_types[sio_type].tdrf; + + if (cptr != NULL) { + result = sscanf(cptr, "%x", &base); + + if (result == 1) { + sio.base = base & DATAMASK; + } + } + + s100_bus_addio(sio.base + sio.stat, 1, &sio_io, SIO_SNAME"S"); + s100_bus_addio(sio.base + sio.data, 1, &sio_io, SIO_SNAME"D"); + } + + return SCPE_OK; +} + +static t_stat sio_set_board(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + char cbuf[10]; + int i = 0; + + if (cptr == NULL) { + return SCPE_ARG; + } + + do { + if (sim_strcasecmp(cptr, sio_boards[i].name) == 0) { + sprintf(cbuf, "%04X", sio_boards[i].base); + return sio_set_type(uptr, sio_boards[i].type, cbuf, NULL); + } + } while (sio_boards[i++].type != SIO_TYPE_NONE); + + return SCPE_ARG; +} + +static t_stat sio_set_val(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + uint32 val; + + if (cptr == NULL || sscanf(cptr, "%02x", &val) == 0) { + return SCPE_ARG; + } + + val &= DATAMASK; + + switch (value) { + case 1: + sio.base = val; + break; + + case 2: + sio.stat = val; + break; + + case 3: + sio.data = val; + break; + + case 4: + sio.rdre = val; + break; + + case 5: + sio.rdrf = val; + break; + + case 6: + sio.tdre = val; + break; + + case 7: + sio.tdrf = val; + break; + + default: + return SCPE_ARG; + } + + sio.name = sio_types[SIO_TYPE_CUST].name; + sio.desc = sio_types[SIO_TYPE_CUST].desc; + sio.type = SIO_TYPE_CUST; + sio_type = SIO_TYPE_CUST; + + return SCPE_OK; +} + +static t_stat sio_set_console(UNIT *uptr, int32 value, const char *cptr, void *desc) +{ + if (value == UNIT_SIO_CONSOLE) { + s100_bus_console(uptr); + } + else { + s100_bus_noconsole(uptr); + } + + return SCPE_OK; +} + +static t_stat sio_show_config(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + if (sio_type != SIO_TYPE_NONE) { + sim_printf("SIO Base Address: %02X\n\n", sio.base); + sim_printf("SIO Status Register: %02X\n", sio.base + sio.stat); + sim_printf("SIO Data Register: %02X\n", sio.base + sio.data); + + sim_printf("SIO RDRE Mask: %02X\n", sio.rdre); + sim_printf("SIO RDRF Mask: %02X\n\n", sio.rdrf); + + sim_printf("SIO TDRE Mask: %02X\n", sio.tdre); + sim_printf("SIO TDRF Mask: %02X\n\n", sio.tdrf); + + sim_printf("%sCONSOLE\n", (uptr->flags & UNIT_SIO_CONSOLE) ? "" : "NO"); + } + else { + sim_printf("\n\tNot configured.\n"); + } + + return SCPE_OK; +} + +static t_stat sio_show_list(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + int i; + + sim_printf("\nAvailable types:\n"); + + i = 0; + + do { + if (sio_types[i].type != SIO_TYPE_CUST) { + sim_printf("%-8.8s %s\n", sio_types[i].name, sio_types[i].desc); + } + } while (sio_types[i++].type != SIO_TYPE_NONE); + + sim_printf("\nAvailable boards:\n"); + + i = 0; + + do { + sim_printf("%-8.8s %s\n", sio_boards[i].name, sio_boards[i].desc); + } while (sio_boards[i++].type != SIO_TYPE_NONE); + + return SCPE_OK; +} + +static t_stat sio_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "\nAltair 8800 Generic SIO Device (%s)\n", dptr->name); + + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + fprint_reg_help (st, dptr); + + fprintf(st, "\n"); + fprintf(st, "----- NOTES -----\n\n"); + fprintf(st, "Only one device may poll the host keyboard for CONSOLE input.\n"); + fprintf(st, "Use SET %s CONSOLE to select this UNIT as the CONSOLE device.\n", sim_dname(dptr)); + fprintf(st, "\nUse SHOW BUS CONSOLE to display the current CONSOLE device.\n\n"); + + return SCPE_OK; +} + diff --git a/Altair8800/s100_sio.h b/Altair8800/s100_sio.h new file mode 100644 index 00000000..cae135cf --- /dev/null +++ b/Altair8800/s100_sio.h @@ -0,0 +1,60 @@ +/* s100_sio.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef _S100_SIO_H +#define _S100_SIO_H + +#define UNIT_SIO_V_VERBOSE (UNIT_V_UF+0) +#define UNIT_SIO_VERBOSE (1 << UNIT_SIO_V_VERBOSE) +#define UNIT_SIO_V_CONSOLE (UNIT_V_UF+1) +#define UNIT_SIO_CONSOLE (1 << UNIT_SIO_V_CONSOLE) + +typedef struct { + uint8 type; /* Type Value */ + char *name; /* Name */ + char *desc; /* Description */ + int32 base; /* Base Port */ + int32 stat; /* Status Port Offset */ + int32 data; /* Data Port Offset */ + int32 rdre; /* Receive Data Register Empty Mask */ + int32 rdrf; /* Receive Data Register Full Mask */ + int32 tdre; /* Transmit Data Register Empty Mask */ + int32 tdrf; /* Transmit Data Register Full Mask */ +} SIO; + +typedef struct { + uint8 type; /* Board SIO Configuration Type */ + char *name; /* Board Name */ + char *desc; /* Board Description */ + int32 base; /* Board Base I/O Address */ +} SIO_BOARD; + +#endif + diff --git a/Altair8800/s100_ssw.c b/Altair8800/s100_ssw.c new file mode 100644 index 00000000..e8fae5cd --- /dev/null +++ b/Altair8800/s100_ssw.c @@ -0,0 +1,116 @@ +/* s100_ssw.c: MITS Altair 8800 Sense Switches + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 07-Nov-2025 Initial version + +*/ + +#include "s100_bus.h" +#include "s100_ssw.h" + +#define DEVICE_NAME "SSW" + +static int32 poc = TRUE; /* Power On Clear */ + +static int32 SSW = 0; /* sense switch register */ + +static t_stat ssw_reset (DEVICE *dptr); +static int32 ssw_io (const int32 addr, const int32 rw, const int32 data); + +static t_stat ssw_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); + +static const char* ssw_description(DEVICE *dptr) { + return "Front Panel Sense Switches"; +} + +static UNIT ssw_unit = { + UDATA (NULL, 0, 0) +}; + +static REG ssw_reg[] = { + { HRDATAD (SSWVAL, SSW, 8, "Front panel sense switches pseudo register") }, + { NULL } +}; + +static MTAB ssw_mod[] = { + { 0 } +}; + +/* Debug Flags */ +static DEBTAB ssw_dt[] = { + { NULL, 0 } +}; + +DEVICE ssw_dev = { + DEVICE_NAME, &ssw_unit, ssw_reg, ssw_mod, + 1, ADDRRADIX, ADDRWIDTH, 1, DATARADIX, DATAWIDTH, + NULL, NULL, &ssw_reset, + NULL, NULL, NULL, + NULL, (DEV_DISABLE), 0, + ssw_dt, NULL, NULL, + &ssw_show_help, NULL, NULL, + &ssw_description +}; + +static t_stat ssw_reset(DEVICE *dptr) { + if (dptr->flags & DEV_DIS) { /* Disable Device */ + s100_bus_remio_in(0xff, 1, &ssw_io); + + poc = TRUE; + } + else { + if (poc) { + s100_bus_addio_in(0xff, 1, &ssw_io, DEVICE_NAME); + + poc = FALSE; + } + } + + return SCPE_OK; +} + +static int32 ssw_io(const int32 addr, const int32 rw, const int32 data) +{ + if (rw == S100_IO_READ) { + return SSW; + } + + return 0x0ff; +} + +static t_stat ssw_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "\nAltair 8800 Front Panel Sense Switches (%s)\n", dptr->name); + + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + fprint_reg_help (st, dptr); + + fprintf (st, "\nUse DEP SSWVAL to set the value returned by an IN 0FFH instruction.\n\n"); + + return SCPE_OK; +} + diff --git a/Altair8800/s100_ssw.h b/Altair8800/s100_ssw.h new file mode 100644 index 00000000..039fcddd --- /dev/null +++ b/Altair8800/s100_ssw.h @@ -0,0 +1,37 @@ +/* s100_ssw.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef S100_SSW_H_ +#define S100_SSW_H_ + +#define UNIT_SSW_V_VERBOSE (UNIT_V_UF+0) +#define UNIT_SSW_VERBOSE (1 << UNIT_SSW_V_VERBOSE) + +#endif diff --git a/Altair8800/s100_z80.c b/Altair8800/s100_z80.c new file mode 100644 index 00000000..078f850c --- /dev/null +++ b/Altair8800/s100_z80.c @@ -0,0 +1,6549 @@ +/* s100_z80.c: MITS Altair 8080/Z80 CPU + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + Based on work by Charles E Owen (c) 1997 + Based on work by Peter Schorn (c) 2002-2024 + Code for Z80 CPU from Frank D. Cringle ((c) 1995 under GNU license) + Contributions from Thomas Eberhardt + + History: + 07-Nov-2025 Initial version + +*/ + +#include "sim_defs.h" +#include "altair8800_sys.h" +#include "s100_bus.h" +#include "s100_cpu.h" +#include "s100_z80.h" +#include "s100_ram.h" + +/* Debug flags */ +#define INT_MSG (1 << 2) + +ChipType z80_chiptype = CHIP_TYPE_8080; + +static int32 poc = TRUE; /* Power On Clear */ + +#define INST_MAX_BYTES 4 /* instruction max bytes */ + +#define IFF1 1 /* Interrupt flip-flop 1 */ +#define IFF2 2 /* Interrupt flip-flop 2 */ + +#define FLAG_C 1 +#define FLAG_N 2 +#define FLAG_P 4 +#define FLAG_H 16 +#define FLAG_Z 64 +#define FLAG_S 128 + +#define SETFLAG(f,c) AF = (c) ? AF | FLAG_ ## f : AF & ~FLAG_ ## f +#define TSTFLAG(f) ((AF & FLAG_ ## f) != 0) +#define TSTFLAG2(a, f) ((a & FLAG_ ## f) != 0) + +#define LOW_DIGIT(x) ((x) & 0xf) +#define HIGH_DIGIT(x) (((x) >> 4) & 0xf) +#define LOW_REGISTER(x) ((x) & 0xff) +#define HIGH_REGISTER(x) (((x) >> 8) & 0xff) + +#define SET_LOW_REGISTER(x, v) x = (((x) & 0xff00) | ((v) & 0xff)) +#define SET_HIGH_REGISTER(x, v) x = (((x) & 0xff) | (((v) & 0xff) << 8)) + +#define PARITY(x) parityTable[(x) & 0xff] + +/* SET_PV and SET_PV2 are used to provide correct PARITY flag semantics for the 8080 in cases + where the Z80 uses the overflow flag. +*/ +#define SET_PVS(s) ((z80_chiptype == CHIP_TYPE_Z80) ? (((cbits >> 6) ^ (cbits >> 5)) & 4) : (PARITY(s))) +#define SET_PV (SET_PVS(sum)) +#define SET_PV2(x) ((z80_chiptype == CHIP_TYPE_Z80) ? (((temp == (x)) << 2)) : (PARITY(temp))) + +/* CHECK_CPU_8080 must be invoked whenever a Z80 only instruction is executed + In case a Z80 instruction is executed on an 8080 there are two cases: + 1) Trapping is enabled: execution stops + 2) Trapping is not enabled: decoding continues with the next byte, i.e. interpret as NOP + Note: in some cases different instructions need to be chosen on an 8080. +*/ + +#define CHECK_CPU_8080 \ + if ((z80_chiptype == CHIP_TYPE_8080) && (z80_unit.flags & UNIT_Z80_OPSTOP)) { \ + reason = STOP_OPCODE; \ + goto end_decode; \ + } + +/* CHECK_CPU_Z80 must be invoked whenever a non Z80 instruction is executed */ +#define CHECK_CPU_Z80 \ + if (z80_unit.flags & UNIT_Z80_OPSTOP) { \ + reason = STOP_OPCODE; \ + goto end_decode; \ + } + +#define POP(x) { \ + register uint32 y = RAM_PP(SP); \ + x = y + (RAM_PP(SP) << 8); \ +} + +#define JPC(cond) { \ + tStates += 10; \ + if (cond) { \ + PC = GET_WORD(PC); \ + } else { \ + PC += 2; \ + } \ +} + +#define CALLC(cond) { \ + if (cond) { \ + register uint32 adrr = GET_WORD(PC); \ + CHECK_BREAK_WORD(SP - 2); \ + PUSH(PC + 2); \ + PC = adrr; \ + tStates += 17; \ + } else { \ + PC += 2; \ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 11 : 10); \ + } \ +} + +/* increase R by val */ +#define INCR(val) IR_S = (IR_S & ~0x7f) | ((IR_S + (val)) & 0x7f) + +/* function prototypes */ +static t_stat z80_set_chiptype (UNIT *uptr, int32 value, CONST char *cptr, void *desc); +static t_stat z80_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat z80_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat z80_show (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat chip_show (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat z80_reset(DEVICE *dptr); +static const char* z80_description(DEVICE *dptr); + +static int32 parse_X80(const char *cptr, const int32 addr, uint32 *val, const char *const Mnemonics[]); + +static void prepareMemoryAccessMessage(const t_addr loc); +static void prepareInstructionMessage(const t_addr loc, const uint32 op); + +/* Z80 data structures + z80_dev Z80 device descriptor + z80_unit Z80 unit descriptor + z80_reg Z80 register list + z80_mod Z80 modifiers list +*/ + +static UNIT z80_unit = { + UDATA (NULL, UNIT_Z80_STOPONHALT, 0) +}; + +static uint32 PCX = 0; /* external view of PC */ +static int32 AF_S; /* AF register */ +static int32 BC_S; /* BC register */ +static int32 DE_S; /* DE register */ +static int32 HL_S; /* HL register */ +static int32 IX_S; /* IX register */ +static int32 IY_S; /* IY register */ +static int32 PC_S = 0; /* 8080 / Z80 program counter */ +static int32 SP_S; /* SP register */ +static int32 AF1_S; /* alternate AF register */ +static int32 BC1_S; /* alternate BC register */ +static int32 DE1_S; /* alternate DE register */ +static int32 HL1_S; /* alternate HL register */ +static int32 IFF_S; /* Interrupt Flip Flop */ +static int32 IM_S; /* Interrupt Mode register */ +static int32 IR_S; /* Interrupt (upper) / Refresh (lower) register */ + +static uint32 executedTStates = 0; /* executed t-states */ + +#define HIST_MIN 16 +#define HIST_MAX 8192 + +typedef struct { + uint8 valid; + uint16 af; + uint16 bc; + uint16 de; + uint16 hl; + t_addr pc; + t_addr sp; + uint16 af1; + uint16 bc1; + uint16 de1; + uint16 hl1; + uint16 ix; + uint16 iy; + t_value op[INST_MAX_BYTES]; +} insthist_t; + +static uint32 hst_p = 0; /* history pointer */ +static uint32 hst_lnt = 0; /* history length */ +static insthist_t *hst = NULL; /* instruction history */ + +static REG z80_reg[] = { + // 8080 and Z80 registers + { HRDATAD (AF, AF_S, 16, "8080 / Z80 Accumulator Flag register") }, + { HRDATAD (BC, BC_S, 16, "8080 / Z80 BC register") }, + { HRDATAD (DE, DE_S, 16, "8080 / Z80 DE register") }, + { HRDATAD (HL, HL_S, 16, "8080 / Z80 HL register") }, + { HRDATAD (PC, PC_S, 16, "8080 / Z80 Program Counter register") }, + { HRDATAD (SP, SP_S, 16, "8080 / Z80 Stack Pointer register") }, + + // Z80 registers + { HRDATAD (IX, IX_S, 16, "Z80 IX register") }, + { HRDATAD (IY, IY_S, 16, "Z80 IY register") }, + { HRDATAD (AF1, AF1_S, 16, "Z80 Alternate Accumulator Flag register") }, + { HRDATAD (BC1, BC1_S, 16, "Z80 Alternate BC register") }, + { HRDATAD (DE1, DE1_S, 16, "Z80 Alternate DE register") }, + { HRDATAD (HL1, HL1_S, 16, "Z80 Alternate HL register") }, + { GRDATAD (IFF, IFF_S, 2, 2, 0, "Z80 Interrupt Flip Flop register") }, + { HRDATAD (IM, IM_S, 2, "Z80 Interrupt Mode register") }, + { HRDATAD (IR, IR_S, 16, "Z80 Interrupt (upper) / Refresh (lower) register") }, + + // Pseudo registers + { FLDATAD (POC, poc, 0x01, "Power on Clear flag"), }, + { FLDATAD (OPSTOP, z80_unit.flags, UNIT_Z80_V_OPSTOP, "Stop on invalid operation pseudo register"), REG_HRO }, + { DRDATAD (TSTATES, executedTStates, 32, "Executed t-states for 8080 / Z80 pseudo register"), REG_RO }, + { NULL } +}; + +REG *z80_pc_reg = &z80_reg[CPU_INDEX_8080]; + +static const char* z80_description(DEVICE *dptr) { + return "8080/Z80 CPU"; +} + +static MTAB z80_mod[] = { + { MTAB_XTD | MTAB_VDV, CHIP_TYPE_8080, NULL, "8080", &z80_set_chiptype, + NULL, NULL, "Chooses 8080 CPU"}, + { MTAB_XTD | MTAB_VDV, CHIP_TYPE_Z80, NULL, "Z80", &z80_set_chiptype, + NULL, NULL, "Chooses Z80 CPU" }, + { UNIT_Z80_OPSTOP, UNIT_Z80_OPSTOP, "ITRAP", "ITRAP", NULL, &chip_show, + NULL, "Stop on illegal instruction" }, + { UNIT_Z80_OPSTOP, 0, "NOITRAP", "NOITRAP", NULL, &chip_show, + NULL, "Do not stop on illegal instruction" }, + { UNIT_Z80_STOPONHALT, UNIT_Z80_STOPONHALT,"STOPONHALT", "STOPONHALT", NULL, + NULL, NULL, "Stop on halt instruction" }, + { UNIT_Z80_STOPONHALT, 0, "LOOPONHALT", "LOOPONHALT", NULL, + NULL, NULL, "Enter loop on halt instruction" }, + { UNIT_CPU_VERBOSE, UNIT_CPU_VERBOSE, "VERBOSE", "VERBOSE", NULL, &z80_show, + NULL, "Enable verbose messages" }, + { UNIT_CPU_VERBOSE, 0, "QUIET", "QUIET", NULL, NULL, + NULL, "Disable verbose messages" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_VALO|MTAB_SHP, 0, "HISTORY", "HISTORY", &z80_set_hist, &z80_show_hist, + NULL, "Instruction history buffer"}, + { 0 } +}; + +/* Debug Flags */ +static DEBTAB z80_dt[] = { + { "LOG_INT", INT_MSG, "Log interrupts" }, + { NULL, 0 } +}; + +DEVICE z80_dev = { + "Z80", &z80_unit, z80_reg, z80_mod, + 1, ADDRRADIX, ADDRWIDTH, 1, DATARADIX, DATAWIDTH, + NULL, NULL, &z80_reset, + NULL, NULL, NULL, + NULL, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), 0, + z80_dt, NULL, NULL, &z80_show_help, NULL, NULL, &z80_description +}; + +/* the following tables precompute some common subexpressions + parityTable[i] 0..255 (number of 1's in i is odd) ? 0 : 4 + incTable[i] 0..256! (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) + decTable[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | 2 + cbitsTable[i] 0..511 (i & 0x10) | ((i >> 8) & 1) + cbitsDup8Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | ((i & 0xff) << 8) | (i & 0xa8) | + (((i & 0xff) == 0) << 6) + cbitsDup16Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | (i & 0x28) + cbits2Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | 2 + rrcaTable[i] 0..255 ((i & 1) << 15) | ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1) + rraTable[i] 0..255 ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1) + addTable[i] 0..511 ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) + subTable[i] 0..255 ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | 2 + andTable[i] 0..255 (i << 8) | (i & 0xa8) | ((i == 0) << 6) | 0x10 | parityTable[i] + xororTable[i] 0..255 (i << 8) | (i & 0xa8) | ((i == 0) << 6) | parityTable[i] + rotateShiftTable[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i & 0xff] + incZ80Table[i] 0..256! (i & 0xa8) | (((i & 0xff) == 0) << 6) | + (((i & 0xf) == 0) << 4) | ((i == 0x80) << 2) + decZ80Table[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | + (((i & 0xf) == 0xf) << 4) | ((i == 0x7f) << 2) | 2 + cbitsZ80Table[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) + cbitsZ80DupTable[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | + ((i >> 8) & 1) | (i & 0xa8) + cbits2Z80Table[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 + cbits2Z80DupTable[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 | + (i & 0xa8) + negTable[i] 0..255 (((i & 0x0f) != 0) << 4) | ((i == 0x80) << 2) | 2 | (i != 0) + rrdrldTable[i] 0..255 (i << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i] + cpTable[i] 0..255 (i & 0x80) | (((i & 0xff) == 0) << 6) +*/ + +/* parityTable[i] = (number of 1's in i is odd) ? 0 : 4, i = 0..255 */ +static const uint8 parityTable[256] = { + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, +}; + +/* incTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4), i = 0..256 */ +static const uint8 incTable[257] = { + 80, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 80 +}; + +/* decTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | 2, i = 0..255 */ +static const uint8 decTable[256] = { + 66, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, +}; + +/* cbitsTable[i] = (i & 0x10) | ((i >> 8) & 1), i = 0..511 */ +static const uint8 cbitsTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +}; + +/* cbitsDup8Table[i] = (i & 0x10) | ((i >> 8) & 1) | ((i & 0xff) << 8) | (i & 0xa8) | + (((i & 0xff) == 0) << 6), i = 0..511 */ +static const uint16 cbitsDup8Table[512] = { + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1818,0x1918,0x1a18,0x1b18,0x1c18,0x1d18,0x1e18,0x1f18, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3030,0x3130,0x3230,0x3330,0x3430,0x3530,0x3630,0x3730, + 0x3838,0x3938,0x3a38,0x3b38,0x3c38,0x3d38,0x3e38,0x3f38, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5818,0x5918,0x5a18,0x5b18,0x5c18,0x5d18,0x5e18,0x5f18, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7030,0x7130,0x7230,0x7330,0x7430,0x7530,0x7630,0x7730, + 0x7838,0x7938,0x7a38,0x7b38,0x7c38,0x7d38,0x7e38,0x7f38, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9090,0x9190,0x9290,0x9390,0x9490,0x9590,0x9690,0x9790, + 0x9898,0x9998,0x9a98,0x9b98,0x9c98,0x9d98,0x9e98,0x9f98, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0b0,0xb1b0,0xb2b0,0xb3b0,0xb4b0,0xb5b0,0xb6b0,0xb7b0, + 0xb8b8,0xb9b8,0xbab8,0xbbb8,0xbcb8,0xbdb8,0xbeb8,0xbfb8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd090,0xd190,0xd290,0xd390,0xd490,0xd590,0xd690,0xd790, + 0xd898,0xd998,0xda98,0xdb98,0xdc98,0xdd98,0xde98,0xdf98, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0b0,0xf1b0,0xf2b0,0xf3b0,0xf4b0,0xf5b0,0xf6b0,0xf7b0, + 0xf8b8,0xf9b8,0xfab8,0xfbb8,0xfcb8,0xfdb8,0xfeb8,0xffb8, + 0x0041,0x0101,0x0201,0x0301,0x0401,0x0501,0x0601,0x0701, + 0x0809,0x0909,0x0a09,0x0b09,0x0c09,0x0d09,0x0e09,0x0f09, + 0x1011,0x1111,0x1211,0x1311,0x1411,0x1511,0x1611,0x1711, + 0x1819,0x1919,0x1a19,0x1b19,0x1c19,0x1d19,0x1e19,0x1f19, + 0x2021,0x2121,0x2221,0x2321,0x2421,0x2521,0x2621,0x2721, + 0x2829,0x2929,0x2a29,0x2b29,0x2c29,0x2d29,0x2e29,0x2f29, + 0x3031,0x3131,0x3231,0x3331,0x3431,0x3531,0x3631,0x3731, + 0x3839,0x3939,0x3a39,0x3b39,0x3c39,0x3d39,0x3e39,0x3f39, + 0x4001,0x4101,0x4201,0x4301,0x4401,0x4501,0x4601,0x4701, + 0x4809,0x4909,0x4a09,0x4b09,0x4c09,0x4d09,0x4e09,0x4f09, + 0x5011,0x5111,0x5211,0x5311,0x5411,0x5511,0x5611,0x5711, + 0x5819,0x5919,0x5a19,0x5b19,0x5c19,0x5d19,0x5e19,0x5f19, + 0x6021,0x6121,0x6221,0x6321,0x6421,0x6521,0x6621,0x6721, + 0x6829,0x6929,0x6a29,0x6b29,0x6c29,0x6d29,0x6e29,0x6f29, + 0x7031,0x7131,0x7231,0x7331,0x7431,0x7531,0x7631,0x7731, + 0x7839,0x7939,0x7a39,0x7b39,0x7c39,0x7d39,0x7e39,0x7f39, + 0x8081,0x8181,0x8281,0x8381,0x8481,0x8581,0x8681,0x8781, + 0x8889,0x8989,0x8a89,0x8b89,0x8c89,0x8d89,0x8e89,0x8f89, + 0x9091,0x9191,0x9291,0x9391,0x9491,0x9591,0x9691,0x9791, + 0x9899,0x9999,0x9a99,0x9b99,0x9c99,0x9d99,0x9e99,0x9f99, + 0xa0a1,0xa1a1,0xa2a1,0xa3a1,0xa4a1,0xa5a1,0xa6a1,0xa7a1, + 0xa8a9,0xa9a9,0xaaa9,0xaba9,0xaca9,0xada9,0xaea9,0xafa9, + 0xb0b1,0xb1b1,0xb2b1,0xb3b1,0xb4b1,0xb5b1,0xb6b1,0xb7b1, + 0xb8b9,0xb9b9,0xbab9,0xbbb9,0xbcb9,0xbdb9,0xbeb9,0xbfb9, + 0xc081,0xc181,0xc281,0xc381,0xc481,0xc581,0xc681,0xc781, + 0xc889,0xc989,0xca89,0xcb89,0xcc89,0xcd89,0xce89,0xcf89, + 0xd091,0xd191,0xd291,0xd391,0xd491,0xd591,0xd691,0xd791, + 0xd899,0xd999,0xda99,0xdb99,0xdc99,0xdd99,0xde99,0xdf99, + 0xe0a1,0xe1a1,0xe2a1,0xe3a1,0xe4a1,0xe5a1,0xe6a1,0xe7a1, + 0xe8a9,0xe9a9,0xeaa9,0xeba9,0xeca9,0xeda9,0xeea9,0xefa9, + 0xf0b1,0xf1b1,0xf2b1,0xf3b1,0xf4b1,0xf5b1,0xf6b1,0xf7b1, + 0xf8b9,0xf9b9,0xfab9,0xfbb9,0xfcb9,0xfdb9,0xfeb9,0xffb9, +}; + +/* cbitsDup16Table[i] = (i & 0x10) | ((i >> 8) & 1) | (i & 0x28), i = 0..511 */ +static const uint8 cbitsDup16Table[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, +}; + +/* cbits2Table[i] = (i & 0x10) | ((i >> 8) & 1) | 2, i = 0..511 */ +static const uint8 cbits2Table[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* rrcaTable[i] = ((i & 1) << 15) | ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1), i = 0..255 */ +static const uint16 rrcaTable[256] = { + 0x0000,0x8001,0x0100,0x8101,0x0200,0x8201,0x0300,0x8301, + 0x0400,0x8401,0x0500,0x8501,0x0600,0x8601,0x0700,0x8701, + 0x0808,0x8809,0x0908,0x8909,0x0a08,0x8a09,0x0b08,0x8b09, + 0x0c08,0x8c09,0x0d08,0x8d09,0x0e08,0x8e09,0x0f08,0x8f09, + 0x1000,0x9001,0x1100,0x9101,0x1200,0x9201,0x1300,0x9301, + 0x1400,0x9401,0x1500,0x9501,0x1600,0x9601,0x1700,0x9701, + 0x1808,0x9809,0x1908,0x9909,0x1a08,0x9a09,0x1b08,0x9b09, + 0x1c08,0x9c09,0x1d08,0x9d09,0x1e08,0x9e09,0x1f08,0x9f09, + 0x2020,0xa021,0x2120,0xa121,0x2220,0xa221,0x2320,0xa321, + 0x2420,0xa421,0x2520,0xa521,0x2620,0xa621,0x2720,0xa721, + 0x2828,0xa829,0x2928,0xa929,0x2a28,0xaa29,0x2b28,0xab29, + 0x2c28,0xac29,0x2d28,0xad29,0x2e28,0xae29,0x2f28,0xaf29, + 0x3020,0xb021,0x3120,0xb121,0x3220,0xb221,0x3320,0xb321, + 0x3420,0xb421,0x3520,0xb521,0x3620,0xb621,0x3720,0xb721, + 0x3828,0xb829,0x3928,0xb929,0x3a28,0xba29,0x3b28,0xbb29, + 0x3c28,0xbc29,0x3d28,0xbd29,0x3e28,0xbe29,0x3f28,0xbf29, + 0x4000,0xc001,0x4100,0xc101,0x4200,0xc201,0x4300,0xc301, + 0x4400,0xc401,0x4500,0xc501,0x4600,0xc601,0x4700,0xc701, + 0x4808,0xc809,0x4908,0xc909,0x4a08,0xca09,0x4b08,0xcb09, + 0x4c08,0xcc09,0x4d08,0xcd09,0x4e08,0xce09,0x4f08,0xcf09, + 0x5000,0xd001,0x5100,0xd101,0x5200,0xd201,0x5300,0xd301, + 0x5400,0xd401,0x5500,0xd501,0x5600,0xd601,0x5700,0xd701, + 0x5808,0xd809,0x5908,0xd909,0x5a08,0xda09,0x5b08,0xdb09, + 0x5c08,0xdc09,0x5d08,0xdd09,0x5e08,0xde09,0x5f08,0xdf09, + 0x6020,0xe021,0x6120,0xe121,0x6220,0xe221,0x6320,0xe321, + 0x6420,0xe421,0x6520,0xe521,0x6620,0xe621,0x6720,0xe721, + 0x6828,0xe829,0x6928,0xe929,0x6a28,0xea29,0x6b28,0xeb29, + 0x6c28,0xec29,0x6d28,0xed29,0x6e28,0xee29,0x6f28,0xef29, + 0x7020,0xf021,0x7120,0xf121,0x7220,0xf221,0x7320,0xf321, + 0x7420,0xf421,0x7520,0xf521,0x7620,0xf621,0x7720,0xf721, + 0x7828,0xf829,0x7928,0xf929,0x7a28,0xfa29,0x7b28,0xfb29, + 0x7c28,0xfc29,0x7d28,0xfd29,0x7e28,0xfe29,0x7f28,0xff29, +}; + +/* rraTable[i] = ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1), i = 0..255 */ +static const uint16 rraTable[256] = { + 0x0000,0x0001,0x0100,0x0101,0x0200,0x0201,0x0300,0x0301, + 0x0400,0x0401,0x0500,0x0501,0x0600,0x0601,0x0700,0x0701, + 0x0808,0x0809,0x0908,0x0909,0x0a08,0x0a09,0x0b08,0x0b09, + 0x0c08,0x0c09,0x0d08,0x0d09,0x0e08,0x0e09,0x0f08,0x0f09, + 0x1000,0x1001,0x1100,0x1101,0x1200,0x1201,0x1300,0x1301, + 0x1400,0x1401,0x1500,0x1501,0x1600,0x1601,0x1700,0x1701, + 0x1808,0x1809,0x1908,0x1909,0x1a08,0x1a09,0x1b08,0x1b09, + 0x1c08,0x1c09,0x1d08,0x1d09,0x1e08,0x1e09,0x1f08,0x1f09, + 0x2020,0x2021,0x2120,0x2121,0x2220,0x2221,0x2320,0x2321, + 0x2420,0x2421,0x2520,0x2521,0x2620,0x2621,0x2720,0x2721, + 0x2828,0x2829,0x2928,0x2929,0x2a28,0x2a29,0x2b28,0x2b29, + 0x2c28,0x2c29,0x2d28,0x2d29,0x2e28,0x2e29,0x2f28,0x2f29, + 0x3020,0x3021,0x3120,0x3121,0x3220,0x3221,0x3320,0x3321, + 0x3420,0x3421,0x3520,0x3521,0x3620,0x3621,0x3720,0x3721, + 0x3828,0x3829,0x3928,0x3929,0x3a28,0x3a29,0x3b28,0x3b29, + 0x3c28,0x3c29,0x3d28,0x3d29,0x3e28,0x3e29,0x3f28,0x3f29, + 0x4000,0x4001,0x4100,0x4101,0x4200,0x4201,0x4300,0x4301, + 0x4400,0x4401,0x4500,0x4501,0x4600,0x4601,0x4700,0x4701, + 0x4808,0x4809,0x4908,0x4909,0x4a08,0x4a09,0x4b08,0x4b09, + 0x4c08,0x4c09,0x4d08,0x4d09,0x4e08,0x4e09,0x4f08,0x4f09, + 0x5000,0x5001,0x5100,0x5101,0x5200,0x5201,0x5300,0x5301, + 0x5400,0x5401,0x5500,0x5501,0x5600,0x5601,0x5700,0x5701, + 0x5808,0x5809,0x5908,0x5909,0x5a08,0x5a09,0x5b08,0x5b09, + 0x5c08,0x5c09,0x5d08,0x5d09,0x5e08,0x5e09,0x5f08,0x5f09, + 0x6020,0x6021,0x6120,0x6121,0x6220,0x6221,0x6320,0x6321, + 0x6420,0x6421,0x6520,0x6521,0x6620,0x6621,0x6720,0x6721, + 0x6828,0x6829,0x6928,0x6929,0x6a28,0x6a29,0x6b28,0x6b29, + 0x6c28,0x6c29,0x6d28,0x6d29,0x6e28,0x6e29,0x6f28,0x6f29, + 0x7020,0x7021,0x7120,0x7121,0x7220,0x7221,0x7320,0x7321, + 0x7420,0x7421,0x7520,0x7521,0x7620,0x7621,0x7720,0x7721, + 0x7828,0x7829,0x7928,0x7929,0x7a28,0x7a29,0x7b28,0x7b29, + 0x7c28,0x7c29,0x7d28,0x7d29,0x7e28,0x7e29,0x7f28,0x7f29, +}; + +/* addTable[i] = ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6), i = 0..511 */ +static const uint16 addTable[512] = { + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1808,0x1908,0x1a08,0x1b08,0x1c08,0x1d08,0x1e08,0x1f08, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3020,0x3120,0x3220,0x3320,0x3420,0x3520,0x3620,0x3720, + 0x3828,0x3928,0x3a28,0x3b28,0x3c28,0x3d28,0x3e28,0x3f28, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5808,0x5908,0x5a08,0x5b08,0x5c08,0x5d08,0x5e08,0x5f08, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7020,0x7120,0x7220,0x7320,0x7420,0x7520,0x7620,0x7720, + 0x7828,0x7928,0x7a28,0x7b28,0x7c28,0x7d28,0x7e28,0x7f28, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9080,0x9180,0x9280,0x9380,0x9480,0x9580,0x9680,0x9780, + 0x9888,0x9988,0x9a88,0x9b88,0x9c88,0x9d88,0x9e88,0x9f88, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0a0,0xb1a0,0xb2a0,0xb3a0,0xb4a0,0xb5a0,0xb6a0,0xb7a0, + 0xb8a8,0xb9a8,0xbaa8,0xbba8,0xbca8,0xbda8,0xbea8,0xbfa8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd080,0xd180,0xd280,0xd380,0xd480,0xd580,0xd680,0xd780, + 0xd888,0xd988,0xda88,0xdb88,0xdc88,0xdd88,0xde88,0xdf88, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0a0,0xf1a0,0xf2a0,0xf3a0,0xf4a0,0xf5a0,0xf6a0,0xf7a0, + 0xf8a8,0xf9a8,0xfaa8,0xfba8,0xfca8,0xfda8,0xfea8,0xffa8, + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1808,0x1908,0x1a08,0x1b08,0x1c08,0x1d08,0x1e08,0x1f08, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3020,0x3120,0x3220,0x3320,0x3420,0x3520,0x3620,0x3720, + 0x3828,0x3928,0x3a28,0x3b28,0x3c28,0x3d28,0x3e28,0x3f28, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5808,0x5908,0x5a08,0x5b08,0x5c08,0x5d08,0x5e08,0x5f08, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7020,0x7120,0x7220,0x7320,0x7420,0x7520,0x7620,0x7720, + 0x7828,0x7928,0x7a28,0x7b28,0x7c28,0x7d28,0x7e28,0x7f28, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9080,0x9180,0x9280,0x9380,0x9480,0x9580,0x9680,0x9780, + 0x9888,0x9988,0x9a88,0x9b88,0x9c88,0x9d88,0x9e88,0x9f88, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0a0,0xb1a0,0xb2a0,0xb3a0,0xb4a0,0xb5a0,0xb6a0,0xb7a0, + 0xb8a8,0xb9a8,0xbaa8,0xbba8,0xbca8,0xbda8,0xbea8,0xbfa8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd080,0xd180,0xd280,0xd380,0xd480,0xd580,0xd680,0xd780, + 0xd888,0xd988,0xda88,0xdb88,0xdc88,0xdd88,0xde88,0xdf88, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0a0,0xf1a0,0xf2a0,0xf3a0,0xf4a0,0xf5a0,0xf6a0,0xf7a0, + 0xf8a8,0xf9a8,0xfaa8,0xfba8,0xfca8,0xfda8,0xfea8,0xffa8, +}; + +/* subTable[i] = ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | 2, i = 0..255 */ +static const uint16 subTable[256] = { + 0x0042,0x0102,0x0202,0x0302,0x0402,0x0502,0x0602,0x0702, + 0x080a,0x090a,0x0a0a,0x0b0a,0x0c0a,0x0d0a,0x0e0a,0x0f0a, + 0x1002,0x1102,0x1202,0x1302,0x1402,0x1502,0x1602,0x1702, + 0x180a,0x190a,0x1a0a,0x1b0a,0x1c0a,0x1d0a,0x1e0a,0x1f0a, + 0x2022,0x2122,0x2222,0x2322,0x2422,0x2522,0x2622,0x2722, + 0x282a,0x292a,0x2a2a,0x2b2a,0x2c2a,0x2d2a,0x2e2a,0x2f2a, + 0x3022,0x3122,0x3222,0x3322,0x3422,0x3522,0x3622,0x3722, + 0x382a,0x392a,0x3a2a,0x3b2a,0x3c2a,0x3d2a,0x3e2a,0x3f2a, + 0x4002,0x4102,0x4202,0x4302,0x4402,0x4502,0x4602,0x4702, + 0x480a,0x490a,0x4a0a,0x4b0a,0x4c0a,0x4d0a,0x4e0a,0x4f0a, + 0x5002,0x5102,0x5202,0x5302,0x5402,0x5502,0x5602,0x5702, + 0x580a,0x590a,0x5a0a,0x5b0a,0x5c0a,0x5d0a,0x5e0a,0x5f0a, + 0x6022,0x6122,0x6222,0x6322,0x6422,0x6522,0x6622,0x6722, + 0x682a,0x692a,0x6a2a,0x6b2a,0x6c2a,0x6d2a,0x6e2a,0x6f2a, + 0x7022,0x7122,0x7222,0x7322,0x7422,0x7522,0x7622,0x7722, + 0x782a,0x792a,0x7a2a,0x7b2a,0x7c2a,0x7d2a,0x7e2a,0x7f2a, + 0x8082,0x8182,0x8282,0x8382,0x8482,0x8582,0x8682,0x8782, + 0x888a,0x898a,0x8a8a,0x8b8a,0x8c8a,0x8d8a,0x8e8a,0x8f8a, + 0x9082,0x9182,0x9282,0x9382,0x9482,0x9582,0x9682,0x9782, + 0x988a,0x998a,0x9a8a,0x9b8a,0x9c8a,0x9d8a,0x9e8a,0x9f8a, + 0xa0a2,0xa1a2,0xa2a2,0xa3a2,0xa4a2,0xa5a2,0xa6a2,0xa7a2, + 0xa8aa,0xa9aa,0xaaaa,0xabaa,0xacaa,0xadaa,0xaeaa,0xafaa, + 0xb0a2,0xb1a2,0xb2a2,0xb3a2,0xb4a2,0xb5a2,0xb6a2,0xb7a2, + 0xb8aa,0xb9aa,0xbaaa,0xbbaa,0xbcaa,0xbdaa,0xbeaa,0xbfaa, + 0xc082,0xc182,0xc282,0xc382,0xc482,0xc582,0xc682,0xc782, + 0xc88a,0xc98a,0xca8a,0xcb8a,0xcc8a,0xcd8a,0xce8a,0xcf8a, + 0xd082,0xd182,0xd282,0xd382,0xd482,0xd582,0xd682,0xd782, + 0xd88a,0xd98a,0xda8a,0xdb8a,0xdc8a,0xdd8a,0xde8a,0xdf8a, + 0xe0a2,0xe1a2,0xe2a2,0xe3a2,0xe4a2,0xe5a2,0xe6a2,0xe7a2, + 0xe8aa,0xe9aa,0xeaaa,0xebaa,0xecaa,0xedaa,0xeeaa,0xefaa, + 0xf0a2,0xf1a2,0xf2a2,0xf3a2,0xf4a2,0xf5a2,0xf6a2,0xf7a2, + 0xf8aa,0xf9aa,0xfaaa,0xfbaa,0xfcaa,0xfdaa,0xfeaa,0xffaa, +}; + +/* andTable[i] = (i << 8) | (i & 0xa8) | ((i == 0) << 6) | 0x10 | parityTable[i], i = 0..255 */ +static const uint16 andTable[256] = { + 0x0054,0x0110,0x0210,0x0314,0x0410,0x0514,0x0614,0x0710, + 0x0818,0x091c,0x0a1c,0x0b18,0x0c1c,0x0d18,0x0e18,0x0f1c, + 0x1010,0x1114,0x1214,0x1310,0x1414,0x1510,0x1610,0x1714, + 0x181c,0x1918,0x1a18,0x1b1c,0x1c18,0x1d1c,0x1e1c,0x1f18, + 0x2030,0x2134,0x2234,0x2330,0x2434,0x2530,0x2630,0x2734, + 0x283c,0x2938,0x2a38,0x2b3c,0x2c38,0x2d3c,0x2e3c,0x2f38, + 0x3034,0x3130,0x3230,0x3334,0x3430,0x3534,0x3634,0x3730, + 0x3838,0x393c,0x3a3c,0x3b38,0x3c3c,0x3d38,0x3e38,0x3f3c, + 0x4010,0x4114,0x4214,0x4310,0x4414,0x4510,0x4610,0x4714, + 0x481c,0x4918,0x4a18,0x4b1c,0x4c18,0x4d1c,0x4e1c,0x4f18, + 0x5014,0x5110,0x5210,0x5314,0x5410,0x5514,0x5614,0x5710, + 0x5818,0x591c,0x5a1c,0x5b18,0x5c1c,0x5d18,0x5e18,0x5f1c, + 0x6034,0x6130,0x6230,0x6334,0x6430,0x6534,0x6634,0x6730, + 0x6838,0x693c,0x6a3c,0x6b38,0x6c3c,0x6d38,0x6e38,0x6f3c, + 0x7030,0x7134,0x7234,0x7330,0x7434,0x7530,0x7630,0x7734, + 0x783c,0x7938,0x7a38,0x7b3c,0x7c38,0x7d3c,0x7e3c,0x7f38, + 0x8090,0x8194,0x8294,0x8390,0x8494,0x8590,0x8690,0x8794, + 0x889c,0x8998,0x8a98,0x8b9c,0x8c98,0x8d9c,0x8e9c,0x8f98, + 0x9094,0x9190,0x9290,0x9394,0x9490,0x9594,0x9694,0x9790, + 0x9898,0x999c,0x9a9c,0x9b98,0x9c9c,0x9d98,0x9e98,0x9f9c, + 0xa0b4,0xa1b0,0xa2b0,0xa3b4,0xa4b0,0xa5b4,0xa6b4,0xa7b0, + 0xa8b8,0xa9bc,0xaabc,0xabb8,0xacbc,0xadb8,0xaeb8,0xafbc, + 0xb0b0,0xb1b4,0xb2b4,0xb3b0,0xb4b4,0xb5b0,0xb6b0,0xb7b4, + 0xb8bc,0xb9b8,0xbab8,0xbbbc,0xbcb8,0xbdbc,0xbebc,0xbfb8, + 0xc094,0xc190,0xc290,0xc394,0xc490,0xc594,0xc694,0xc790, + 0xc898,0xc99c,0xca9c,0xcb98,0xcc9c,0xcd98,0xce98,0xcf9c, + 0xd090,0xd194,0xd294,0xd390,0xd494,0xd590,0xd690,0xd794, + 0xd89c,0xd998,0xda98,0xdb9c,0xdc98,0xdd9c,0xde9c,0xdf98, + 0xe0b0,0xe1b4,0xe2b4,0xe3b0,0xe4b4,0xe5b0,0xe6b0,0xe7b4, + 0xe8bc,0xe9b8,0xeab8,0xebbc,0xecb8,0xedbc,0xeebc,0xefb8, + 0xf0b4,0xf1b0,0xf2b0,0xf3b4,0xf4b0,0xf5b4,0xf6b4,0xf7b0, + 0xf8b8,0xf9bc,0xfabc,0xfbb8,0xfcbc,0xfdb8,0xfeb8,0xffbc, +}; + +/* xororTable[i] = (i << 8) | (i & 0xa8) | ((i == 0) << 6) | parityTable[i], i = 0..255 */ +static const uint16 xororTable[256] = { + 0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700, + 0x0808,0x090c,0x0a0c,0x0b08,0x0c0c,0x0d08,0x0e08,0x0f0c, + 0x1000,0x1104,0x1204,0x1300,0x1404,0x1500,0x1600,0x1704, + 0x180c,0x1908,0x1a08,0x1b0c,0x1c08,0x1d0c,0x1e0c,0x1f08, + 0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724, + 0x282c,0x2928,0x2a28,0x2b2c,0x2c28,0x2d2c,0x2e2c,0x2f28, + 0x3024,0x3120,0x3220,0x3324,0x3420,0x3524,0x3624,0x3720, + 0x3828,0x392c,0x3a2c,0x3b28,0x3c2c,0x3d28,0x3e28,0x3f2c, + 0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,0x4600,0x4704, + 0x480c,0x4908,0x4a08,0x4b0c,0x4c08,0x4d0c,0x4e0c,0x4f08, + 0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700, + 0x5808,0x590c,0x5a0c,0x5b08,0x5c0c,0x5d08,0x5e08,0x5f0c, + 0x6024,0x6120,0x6220,0x6324,0x6420,0x6524,0x6624,0x6720, + 0x6828,0x692c,0x6a2c,0x6b28,0x6c2c,0x6d28,0x6e28,0x6f2c, + 0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724, + 0x782c,0x7928,0x7a28,0x7b2c,0x7c28,0x7d2c,0x7e2c,0x7f28, + 0x8080,0x8184,0x8284,0x8380,0x8484,0x8580,0x8680,0x8784, + 0x888c,0x8988,0x8a88,0x8b8c,0x8c88,0x8d8c,0x8e8c,0x8f88, + 0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,0x9684,0x9780, + 0x9888,0x998c,0x9a8c,0x9b88,0x9c8c,0x9d88,0x9e88,0x9f8c, + 0xa0a4,0xa1a0,0xa2a0,0xa3a4,0xa4a0,0xa5a4,0xa6a4,0xa7a0, + 0xa8a8,0xa9ac,0xaaac,0xaba8,0xacac,0xada8,0xaea8,0xafac, + 0xb0a0,0xb1a4,0xb2a4,0xb3a0,0xb4a4,0xb5a0,0xb6a0,0xb7a4, + 0xb8ac,0xb9a8,0xbaa8,0xbbac,0xbca8,0xbdac,0xbeac,0xbfa8, + 0xc084,0xc180,0xc280,0xc384,0xc480,0xc584,0xc684,0xc780, + 0xc888,0xc98c,0xca8c,0xcb88,0xcc8c,0xcd88,0xce88,0xcf8c, + 0xd080,0xd184,0xd284,0xd380,0xd484,0xd580,0xd680,0xd784, + 0xd88c,0xd988,0xda88,0xdb8c,0xdc88,0xdd8c,0xde8c,0xdf88, + 0xe0a0,0xe1a4,0xe2a4,0xe3a0,0xe4a4,0xe5a0,0xe6a0,0xe7a4, + 0xe8ac,0xe9a8,0xeaa8,0xebac,0xeca8,0xedac,0xeeac,0xefa8, + 0xf0a4,0xf1a0,0xf2a0,0xf3a4,0xf4a0,0xf5a4,0xf6a4,0xf7a0, + 0xf8a8,0xf9ac,0xfaac,0xfba8,0xfcac,0xfda8,0xfea8,0xffac, +}; + +/* rotateShiftTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i & 0xff], i = 0..255 */ +static const uint8 rotateShiftTable[256] = { + 68, 0, 0, 4, 0, 4, 4, 0, 8, 12, 12, 8, 12, 8, 8, 12, + 0, 4, 4, 0, 4, 0, 0, 4, 12, 8, 8, 12, 8, 12, 12, 8, + 32, 36, 36, 32, 36, 32, 32, 36, 44, 40, 40, 44, 40, 44, 44, 40, + 36, 32, 32, 36, 32, 36, 36, 32, 40, 44, 44, 40, 44, 40, 40, 44, + 0, 4, 4, 0, 4, 0, 0, 4, 12, 8, 8, 12, 8, 12, 12, 8, + 4, 0, 0, 4, 0, 4, 4, 0, 8, 12, 12, 8, 12, 8, 8, 12, + 36, 32, 32, 36, 32, 36, 36, 32, 40, 44, 44, 40, 44, 40, 40, 44, + 32, 36, 36, 32, 36, 32, 32, 36, 44, 40, 40, 44, 40, 44, 44, 40, + 128,132,132,128,132,128,128,132,140,136,136,140,136,140,140,136, + 132,128,128,132,128,132,132,128,136,140,140,136,140,136,136,140, + 164,160,160,164,160,164,164,160,168,172,172,168,172,168,168,172, + 160,164,164,160,164,160,160,164,172,168,168,172,168,172,172,168, + 132,128,128,132,128,132,132,128,136,140,140,136,140,136,136,140, + 128,132,132,128,132,128,128,132,140,136,136,140,136,140,140,136, + 160,164,164,160,164,160,160,164,172,168,168,172,168,172,172,168, + 164,160,160,164,160,164,164,160,168,172,172,168,172,168,168,172, +}; + +/* incZ80Table[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | + (((i & 0xf) == 0) << 4) | ((i == 0x80) << 2), i = 0..256 */ +static const uint8 incZ80Table[257] = { + 80, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 148,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 80, +}; + +/* decZ80Table[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | + (((i & 0xf) == 0xf) << 4) | ((i == 0x7f) << 2) | 2, i = 0..255 */ +static const uint8 decZ80Table[256] = { + 66, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 62, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, +}; + +/* cbitsZ80Table[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1), i = 0..511 */ +static const uint8 cbitsZ80Table[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +}; + +/* cbitsZ80DupTable[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | + ((i >> 8) & 1) | (i & 0xa8), i = 0..511 */ +static const uint8 cbitsZ80DupTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, + 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 48, 48, 48, 48, 48, 48, 48, 56, 56, 56, 56, 56, 56, 56, 56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, + 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 48, 48, 48, 48, 48, 48, 48, 56, 56, 56, 56, 56, 56, 56, 56, + 132,132,132,132,132,132,132,132,140,140,140,140,140,140,140,140, + 148,148,148,148,148,148,148,148,156,156,156,156,156,156,156,156, + 164,164,164,164,164,164,164,164,172,172,172,172,172,172,172,172, + 180,180,180,180,180,180,180,180,188,188,188,188,188,188,188,188, + 132,132,132,132,132,132,132,132,140,140,140,140,140,140,140,140, + 148,148,148,148,148,148,148,148,156,156,156,156,156,156,156,156, + 164,164,164,164,164,164,164,164,172,172,172,172,172,172,172,172, + 180,180,180,180,180,180,180,180,188,188,188,188,188,188,188,188, + 5, 5, 5, 5, 5, 5, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, + 21, 21, 21, 21, 21, 21, 21, 21, 29, 29, 29, 29, 29, 29, 29, 29, + 37, 37, 37, 37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 45, 45, 45, + 53, 53, 53, 53, 53, 53, 53, 53, 61, 61, 61, 61, 61, 61, 61, 61, + 5, 5, 5, 5, 5, 5, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, + 21, 21, 21, 21, 21, 21, 21, 21, 29, 29, 29, 29, 29, 29, 29, 29, + 37, 37, 37, 37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 45, 45, 45, + 53, 53, 53, 53, 53, 53, 53, 53, 61, 61, 61, 61, 61, 61, 61, 61, + 129,129,129,129,129,129,129,129,137,137,137,137,137,137,137,137, + 145,145,145,145,145,145,145,145,153,153,153,153,153,153,153,153, + 161,161,161,161,161,161,161,161,169,169,169,169,169,169,169,169, + 177,177,177,177,177,177,177,177,185,185,185,185,185,185,185,185, + 129,129,129,129,129,129,129,129,137,137,137,137,137,137,137,137, + 145,145,145,145,145,145,145,145,153,153,153,153,153,153,153,153, + 161,161,161,161,161,161,161,161,169,169,169,169,169,169,169,169, + 177,177,177,177,177,177,177,177,185,185,185,185,185,185,185,185, +}; + +/* cbits2Z80Table[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2, i = 0..511 */ +static const uint8 cbits2Z80Table[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* cbits2Z80DupTable[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 | + (i & 0xa8), i = 0..511 */ +static const uint8 cbits2Z80DupTable[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, + 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, 26, 26, 26, 26, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 42, + 50, 50, 50, 50, 50, 50, 50, 50, 58, 58, 58, 58, 58, 58, 58, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, + 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, 26, 26, 26, 26, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 42, + 50, 50, 50, 50, 50, 50, 50, 50, 58, 58, 58, 58, 58, 58, 58, 58, + 134,134,134,134,134,134,134,134,142,142,142,142,142,142,142,142, + 150,150,150,150,150,150,150,150,158,158,158,158,158,158,158,158, + 166,166,166,166,166,166,166,166,174,174,174,174,174,174,174,174, + 182,182,182,182,182,182,182,182,190,190,190,190,190,190,190,190, + 134,134,134,134,134,134,134,134,142,142,142,142,142,142,142,142, + 150,150,150,150,150,150,150,150,158,158,158,158,158,158,158,158, + 166,166,166,166,166,166,166,166,174,174,174,174,174,174,174,174, + 182,182,182,182,182,182,182,182,190,190,190,190,190,190,190,190, + 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, + 23, 23, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, + 39, 39, 39, 39, 39, 39, 39, 39, 47, 47, 47, 47, 47, 47, 47, 47, + 55, 55, 55, 55, 55, 55, 55, 55, 63, 63, 63, 63, 63, 63, 63, 63, + 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, + 23, 23, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, + 39, 39, 39, 39, 39, 39, 39, 39, 47, 47, 47, 47, 47, 47, 47, 47, + 55, 55, 55, 55, 55, 55, 55, 55, 63, 63, 63, 63, 63, 63, 63, 63, + 131,131,131,131,131,131,131,131,139,139,139,139,139,139,139,139, + 147,147,147,147,147,147,147,147,155,155,155,155,155,155,155,155, + 163,163,163,163,163,163,163,163,171,171,171,171,171,171,171,171, + 179,179,179,179,179,179,179,179,187,187,187,187,187,187,187,187, + 131,131,131,131,131,131,131,131,139,139,139,139,139,139,139,139, + 147,147,147,147,147,147,147,147,155,155,155,155,155,155,155,155, + 163,163,163,163,163,163,163,163,171,171,171,171,171,171,171,171, + 179,179,179,179,179,179,179,179,187,187,187,187,187,187,187,187, +}; + +/* negTable[i] = (((i & 0x0f) != 0) << 4) | ((i == 0x80) << 2) | 2 | (i != 0), i = 0..255 */ +static const uint8 negTable[256] = { + 2,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 7,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* rrdrldTable[i] = (i << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i], i = 0..255 */ +static const uint16 rrdrldTable[256] = { + 0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700, + 0x0808,0x090c,0x0a0c,0x0b08,0x0c0c,0x0d08,0x0e08,0x0f0c, + 0x1000,0x1104,0x1204,0x1300,0x1404,0x1500,0x1600,0x1704, + 0x180c,0x1908,0x1a08,0x1b0c,0x1c08,0x1d0c,0x1e0c,0x1f08, + 0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724, + 0x282c,0x2928,0x2a28,0x2b2c,0x2c28,0x2d2c,0x2e2c,0x2f28, + 0x3024,0x3120,0x3220,0x3324,0x3420,0x3524,0x3624,0x3720, + 0x3828,0x392c,0x3a2c,0x3b28,0x3c2c,0x3d28,0x3e28,0x3f2c, + 0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,0x4600,0x4704, + 0x480c,0x4908,0x4a08,0x4b0c,0x4c08,0x4d0c,0x4e0c,0x4f08, + 0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700, + 0x5808,0x590c,0x5a0c,0x5b08,0x5c0c,0x5d08,0x5e08,0x5f0c, + 0x6024,0x6120,0x6220,0x6324,0x6420,0x6524,0x6624,0x6720, + 0x6828,0x692c,0x6a2c,0x6b28,0x6c2c,0x6d28,0x6e28,0x6f2c, + 0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724, + 0x782c,0x7928,0x7a28,0x7b2c,0x7c28,0x7d2c,0x7e2c,0x7f28, + 0x8080,0x8184,0x8284,0x8380,0x8484,0x8580,0x8680,0x8784, + 0x888c,0x8988,0x8a88,0x8b8c,0x8c88,0x8d8c,0x8e8c,0x8f88, + 0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,0x9684,0x9780, + 0x9888,0x998c,0x9a8c,0x9b88,0x9c8c,0x9d88,0x9e88,0x9f8c, + 0xa0a4,0xa1a0,0xa2a0,0xa3a4,0xa4a0,0xa5a4,0xa6a4,0xa7a0, + 0xa8a8,0xa9ac,0xaaac,0xaba8,0xacac,0xada8,0xaea8,0xafac, + 0xb0a0,0xb1a4,0xb2a4,0xb3a0,0xb4a4,0xb5a0,0xb6a0,0xb7a4, + 0xb8ac,0xb9a8,0xbaa8,0xbbac,0xbca8,0xbdac,0xbeac,0xbfa8, + 0xc084,0xc180,0xc280,0xc384,0xc480,0xc584,0xc684,0xc780, + 0xc888,0xc98c,0xca8c,0xcb88,0xcc8c,0xcd88,0xce88,0xcf8c, + 0xd080,0xd184,0xd284,0xd380,0xd484,0xd580,0xd680,0xd784, + 0xd88c,0xd988,0xda88,0xdb8c,0xdc88,0xdd8c,0xde8c,0xdf88, + 0xe0a0,0xe1a4,0xe2a4,0xe3a0,0xe4a4,0xe5a0,0xe6a0,0xe7a4, + 0xe8ac,0xe9a8,0xeaa8,0xebac,0xeca8,0xedac,0xeeac,0xefa8, + 0xf0a4,0xf1a0,0xf2a0,0xf3a4,0xf4a0,0xf5a4,0xf6a4,0xf7a0, + 0xf8a8,0xf9ac,0xfaac,0xfba8,0xfcac,0xfda8,0xfea8,0xffac, +}; + +/* cpTable[i] = (i & 0x80) | (((i & 0xff) == 0) << 6), i = 0..255 */ +static const uint8 cpTable[256] = { + 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +}; + +/* remove comments to generate table contents and add a call to + altairz80_print_tables in the altairz80_init +static void altairz80_print_tables(void) { +*/ +/* parityTable */ +/* + uint32 i, v; + for (i = 0; i < 256; i++) { + v = ((i & 1) + ((i & 2) >> 1) + ((i & 4) >> 2) + ((i & 8) >> 3) + + ((i & 16) >> 4) + ((i & 32) >> 5) + ((i & 64) >> 6) + ((i & 128) >> 7)) % 2 ? 0 : 4; + sim_printf("%1d,", v); + if ( ((i+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* incTable */ +/* + uint32 temp, v; + for (temp = 0; temp <= 256; temp++) { + v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | (((temp & 0xf) == 0) << 4); + sim_printf("%3d,", v); + if ( ((temp+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* decTable */ +/* + uint32 temp, v; + for (temp = 0; temp < 256; temp++) { + v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | (((temp & 0xf) == 0xf) << 4) | 2; + sim_printf("%3d,", v); + if ( ((temp+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* cbitsTable */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (cbits & 0x10) | ((cbits >> 8) & 1); + sim_printf("%2d,", v); + if ( ((cbits+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* cbitsDup8Table */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (cbits & 0x10) | ((cbits >> 8) & 1) | ((cbits & 0xff) << 8) | (cbits & 0xa8) | (((cbits & 0xff) == 0) << 6); + sim_printf("0x%04x,", v); + if ( ((cbits+1) & 0x7) == 0) { + sim_printf("\n"); + } + } +*/ +/* cbitsDup16Table */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (cbits & 0x10) | ((cbits >> 8) & 1) | (cbits & 0x28); + sim_printf("%2d,", v); + if ( ((cbits+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* cbits2Table */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (cbits & 0x10) | ((cbits >> 8) & 1) | 2; + sim_printf("%2d,", v); + if ( ((cbits+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* rrcaTable */ +/* + uint32 temp, sum, v; + for (temp = 0; temp < 256; temp++) { + sum = temp >> 1; + v = ((temp & 1) << 15) | (sum << 8) | (sum & 0x28) | (temp & 1); + sim_printf("0x%04x,", v); + if ( ((temp+1) & 0x7) == 0) { + sim_printf("\n"); + } + } +*/ +/* rraTable */ +/* + uint32 temp, sum, v; + for (temp = 0; temp < 256; temp++) { + sum = temp >> 1; + v = (sum << 8) | (sum & 0x28) | (temp & 1); + sim_printf("0x%04x,", v); + if ( ((temp+1) & 0x7) == 0) { + sim_printf("\n"); + } + } +*/ +/* addTable */ +/* + uint32 sum, v; + for (sum = 0; sum < 512; sum++) { + v = ((sum & 0xff) << 8) | (sum & 0xa8) | (((sum & 0xff) == 0) << 6); + sim_printf("0x%04x,", v); + if ( ((sum+1) & 0x7) == 0) { + sim_printf("\n"); + } + } +*/ +/* subTable */ +/* + uint32 sum, v; + for (sum = 0; sum < 256; sum++) { + v = ((sum & 0xff) << 8) | (sum & 0xa8) | (((sum & 0xff) == 0) << 6) | 2; + sim_printf("0x%04x,", v); + if ( ((sum+1) & 0x7) == 0) { + sim_printf("\n"); + } + } +*/ +/* andTable */ +/* + uint32 sum, v; + for (sum = 0; sum < 256; sum++) { + v = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | 0x10 | parityTable[sum]; + sim_printf("0x%04x,", v); + if ( ((sum+1) & 0x7) == 0) { + sim_printf("\n"); + } + } +*/ +/* xororTable */ +/* + uint32 sum, v; + for (sum = 0; sum < 256; sum++) { + v = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | parityTable[sum]; + sim_printf("0x%04x,", v); + if ( ((sum+1) & 0x7) == 0) { + sim_printf("\n"); + } + } +*/ +/* rotateShiftTable */ +/* + uint32 temp, v; + for (temp = 0; temp < 256; temp++) { + v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | PARITY(temp); + sim_printf("%3d,", v); + if ( ((temp+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* incZ80Table */ +/* + uint32 temp, v; + for (temp = 0; temp < 256; temp++) { + v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | ((temp == 0x80) << 2); + sim_printf("%3d,", v); + if ( ((temp+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* decZ80Table */ +/* + uint32 temp, v; + for (temp = 0; temp < 256; temp++) { + v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | ((temp == 0x7f) << 2) | 2; + sim_printf("%3d,", v); + if ( ((temp+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* cbitsZ80Table */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (cbits & 0x10) | (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + sim_printf("%2d,", v); + if ( ((cbits+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* cbitsZ80DupTable */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (cbits & 0x10) | (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1) | (cbits & 0xa8); + sim_printf("%3d,", v); + if ( ((cbits+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* cbits2Z80Table */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (((cbits >> 6) ^ (cbits >> 5)) & 4) | (cbits & 0x10) | 2 | ((cbits >> 8) & 1); + sim_printf("%2d,", v); + if ( ((cbits+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* cbits2Z80DupTable */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (((cbits >> 6) ^ (cbits >> 5)) & 4) | (cbits & 0x10) | 2 | ((cbits >> 8) & 1) | + (cbits & 0xa8); + sim_printf("%3d,", v); + if ( ((cbits+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* negTable */ +/* + uint32 temp, v; + for (temp = 0; temp < 256; temp++) { + v = (((temp & 0x0f) != 0) << 4) | ((temp == 0x80) << 2) | 2 | (temp != 0); + sim_printf("%2d,", v); + if ( ((temp+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* rrdrldTable */ +/* + uint32 acu, v; + for (acu = 0; acu < 256; acu++) { + v = (acu << 8) | (acu & 0xa8) | (((acu & 0xff) == 0) << 6) | parityTable[acu]; + sim_printf("0x%04x,", v); + if ( ((acu+1) & 0x7) == 0) { + sim_printf("\n"); + } + } +*/ +/* cpTable */ +/* + uint32 sum, v; + for (sum = 0; sum < 256; sum++) { + v = (sum & 0x80) | (((sum & 0xff) == 0) << 6); + sim_printf("%3d,", v); + if ( ((sum+1) & 0xf) == 0) { + sim_printf("\n"); + } + } +*/ +/* remove comments to generate table contents +} +*/ + +#define RAM_PP(Addr) s100_bus_memr(Addr++) +#define RAM_MM(Addr) s100_bus_memr(Addr--) +#define GET_WORD(Addr) (s100_bus_memr(Addr) | (s100_bus_memr(Addr + 1) << 8)) +#define PUT_WORD(Addr, v) { s100_bus_memw(Addr, v); s100_bus_memw(Addr + 1, v >> 8); } +#define PUT_BYTE_PP(a,v) s100_bus_memw(a++, v) +#define PUT_BYTE_MM(a,v) s100_bus_memw(a--, v) +#define MM_PUT_BYTE(a,v) s100_bus_memw(--a, v) + +#define PUSH(x) { \ + MM_PUT_BYTE(SP, (x) >> 8); \ + MM_PUT_BYTE(SP, x); \ +} + +#define CHECK_BREAK_BYTE(a) \ + if (sim_brk_summ & SWMASK('M')) { \ + if (sim_brk_test((a) & 0xffff, SWMASK('M'))) { \ + reason = STOP_MEM; \ + prepareMemoryAccessMessage((a) & 0xffff); \ + goto end_decode; \ + } \ + } + +#define CHECK_BREAK_TWO_BYTES_EXTENDED(a1, a2, iCode) \ + if (sim_brk_summ & SWMASK('M')) { \ + if (sim_brk_test((a1) & 0xffff, SWMASK('M'))) { \ + reason = STOP_MEM; \ + prepareMemoryAccessMessage((a1) & 0xffff); \ + iCode; \ + goto end_decode; \ + } \ + if (sim_brk_test((a2) & 0xffff, SWMASK('M'))) { \ + reason = STOP_MEM; \ + prepareMemoryAccessMessage((a2) & 0xffff); \ + iCode; \ + goto end_decode; \ + } \ + } + +#define CHECK_BREAK_TWO_BYTES(a1, a2) CHECK_BREAK_TWO_BYTES_EXTENDED(a1, a2,;) + +#define CHECK_BREAK_WORD(a) CHECK_BREAK_TWO_BYTES(a, (a + 1)) + +#define HALTINSTRUCTION 0x76 + +/* Macros for the IN/OUT instructions INI/INIR/IND/INDR/OUTI/OTIR/OUTD/OTDR + + Pre condition + acu == value of transferred byte (IN or OUT) + Post condition + F is set correctly + + Use INOUTFLAGS_ZERO(x) for INIR/INDR/OTIR/OTDR where + x == (C + 1) & 0xff for INIR + x == L for OTIR and OTDR + x == (C - 1) & 0xff for INDR + Use INOUTFLAGS_NONZERO(x) for INI/IND/OUTI/OUTD where + x == (C + 1) & 0xff for INI + x == L for OUTI and OUTD + x == (C - 1) & 0xff for IND +*/ +#define INOUTFLAGS_ZERO(x) \ + AF = (AF & 0xff00) | (FLAG_Z) | /* SF, YF, XF, ZF */ \ + ((acu & 0x80) >> 6) | /* NF */ \ + ((acu + (x)) > 0xff ? (FLAG_C | FLAG_H) : 0) | /* CF, HF */ \ + parityTable[((acu + (x)) & 7)] /* PF */ + +#define INOUTFLAGS_NONZERO(x) \ + AF = (AF & 0xff00) | (HIGH_REGISTER(BC) & 0xa8) | \ + ((HIGH_REGISTER(BC) == 0) << 6) | /* SF, YF, XF, ZF */ \ + ((acu & 0x80) >> 6) | /* NF */ \ + ((acu + (x)) > 0xff ? (FLAG_C | FLAG_H) : 0) | /* CF, HF */ \ + parityTable[((acu + (x)) & 7) ^ HIGH_REGISTER(BC)] /* PF */ + +t_stat z80_instr(void) +{ + int i; + int32 reason = SCPE_OK; + register uint32 specialProcessing; + register uint32 AF; + register uint32 BC; + register uint32 DE; + register uint32 HL; + register uint32 PC; + register uint32 SP; + register uint32 IX; + register uint32 IY; + register uint32 temp = 0; + register uint32 acu = 0; + register uint32 sum; + register uint32 cbits; + register uint32 op; + register uint32 adr; + + register uint32 tStates; /* number of t-states executed in the current time-slice */ + int32 tStateModifier = FALSE; + + AF = AF_S; + BC = BC_S; + DE = DE_S; + HL = HL_S; + PC = PC_S & ADDRMASK; + SP = SP_S; + IX = IX_S; + IY = IY_S; + +// vectorInterrupt = s100_bus_get_int(); +// nmiInterrupt = s100_bus_get_nmi(); + + specialProcessing = nmiInterrupt | vectorInterrupt | sim_brk_summ; + + tStates = 0; + + /* main instruction fetch/decode loop */ + while (1) { /* loop until halted */ + + if (sim_interval <= 0) { /* check clock queue */ + if ((reason = sim_process_event())) { + break; + } + + specialProcessing = nmiInterrupt | vectorInterrupt | sim_brk_summ; + } + + if (specialProcessing) { /* quick check for special processing */ + + if (nmiInterrupt) { + s100_bus_clr_nmi(); + specialProcessing = sim_brk_summ; + IFF_S &= IFF2; /* Clear IFF1 */ + CHECK_BREAK_TWO_BYTES_EXTENDED(SP - 2, SP - 1, (nmiInterrupt = TRUE, IFF_S |= IFF1)); + if ((s100_bus_memr(PC) == HALTINSTRUCTION) && ((z80_unit.flags & UNIT_Z80_STOPONHALT) == 0)) { + PUSH(PC + 1); + } + else { + PUSH(PC); + } + tStates += 11; + PC = 0x0066; + } + + if ((IM_S == 1) && vectorInterrupt && (IFF_S & IFF1)) { /* Z80 Interrupt Mode 1 */ + uint32 tempVectorInterrupt = vectorInterrupt; + uint8 intVector = 0; + + while ((tempVectorInterrupt & 1) == 0) { + tempVectorInterrupt >>= 1; + intVector++; + } + + s100_bus_clr_int(intVector); + + specialProcessing = sim_brk_summ; + IFF_S = 0; /* disable interrupts */ + CHECK_BREAK_TWO_BYTES_EXTENDED(SP - 2, SP - 1, (vectorInterrupt |= (1 << intVector), IFF_S |= (IFF1 | IFF2))); + if ((s100_bus_memr(PC) == HALTINSTRUCTION) && ((z80_unit.flags & UNIT_Z80_STOPONHALT) == 0)) { + PUSH(PC + 1); + } + else { + PUSH(PC); + } + + PC = 0x0038; + + sim_debug(INT_MSG, &z80_dev, ADDRESS_FORMAT + " INT(mode=1 intVector=%d PC=%04X)\n", PCX, intVector, PC); + } else if ((IM_S == 2) && vectorInterrupt && (IFF_S & IFF1)) { + int32 vector; + uint32 tempVectorInterrupt = vectorInterrupt; + uint8 intVector = 0; + + while ((tempVectorInterrupt & 1) == 0) { + tempVectorInterrupt >>= 1; + intVector++; + } + + vectorInterrupt &= ~(1 << intVector); + + specialProcessing = sim_brk_summ; + IFF_S = 0; /* disable interrupts */ + CHECK_BREAK_TWO_BYTES_EXTENDED(SP - 2, SP - 1, (vectorInterrupt |= (1 << intVector), IFF_S |= (IFF1 | IFF2))); + if ((s100_bus_memr(PC) == HALTINSTRUCTION) && ((z80_unit.flags & UNIT_Z80_STOPONHALT) == 0)) { + PUSH(PC + 1); + } + else { + PUSH(PC); + } + + vector = (HIGH_REGISTER(IR_S) << 8) | s100_bus_get_int_data(intVector); + PC = ((s100_bus_memr(vector+1) << 8) | s100_bus_memr(vector)) & ADDRMASK; + + sim_debug(INT_MSG, &z80_dev, ADDRESS_FORMAT + " INT(mode=2 intVector=%d vector=%04X PC=%04X)\n", PCX, intVector, vector, PC); + } + + if (sim_brk_summ) { + if (sim_brk_test(PC, (2u << SIM_BKPT_V_SPC) | SWMASK('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + if (sim_brk_test(s100_bus_memr(PC), (1u << SIM_BKPT_V_SPC) | SWMASK('I'))) { /* instruction breakpoint? */ + reason = STOP_INSTR; /* stop simulation */ + prepareInstructionMessage(PC, s100_bus_memr(PC)); + break; + } + } + } + + PCX = PC; + s100_bus_set_addr(PCX); /* Put current address on the bus */ + + /* 8080 INT/Z80 Interrupt Mode 0 + Instruction to execute (ex. RST0-7) is on the data bus + NOTE: does not support multi-byte instructions such as CALL + */ + if ((IM_S == 0) && vectorInterrupt && (IFF_S & IFF1)) { /* 8080/Z80 Interrupt Mode 0 */ + uint32 tempVectorInterrupt = vectorInterrupt; + uint8 intVector = 0; + + while ((tempVectorInterrupt & 1) == 0) { + tempVectorInterrupt >>= 1; + intVector++; + } + + IFF_S = 0; /* disable interrupts */ + + s100_bus_clr_int(intVector); /* Clear interrupt */ + + op = s100_bus_get_int_data(intVector); + + sim_debug(INT_MSG, &z80_dev, ADDRESS_FORMAT + " INT(mode=0 vectorInterrupt=%X intVector=%d op=%02X)\n", PCX, vectorInterrupt, intVector, op); + } else { + INCR(1); + op = RAM_PP(PC); + } + + switch(op) { + + case 0x00: /* NOP */ + tStates += 4; /* NOP 4 */ + break; + + case 0x01: /* LD BC,nnnn */ + tStates += 10; /* LXI B,nnnn 10 */ + BC = GET_WORD(PC); + PC += 2; + break; + + case 0x02: /* LD (BC),A */ + tStates += 7; /* STAX B 7 */ + CHECK_BREAK_BYTE(BC) + s100_bus_memw(BC, HIGH_REGISTER(AF)); + break; + + case 0x03: /* INC BC */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 6); /* INX B 5 */ + ++BC; + break; + + case 0x04: /* INC B */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* INR B 5 */ + BC += 0x100; + temp = HIGH_REGISTER(BC); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x05: /* DEC B */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* DCR B 5 */ + BC -= 0x100; + temp = HIGH_REGISTER(BC); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x06: /* LD B,nn */ + tStates += 7; /* MVI B,nn 7 */ + SET_HIGH_REGISTER(BC, RAM_PP(PC)); + break; + + case 0x07: /* RLCA */ + tStates += 4; /* RLC 4 */ + temp = ((AF >> 7) & 0x0128) | ((AF << 1) & ~0x1ff) | + (AF & 0xc4) | ((AF >> 15) & 1); + if (z80_chiptype == CHIP_TYPE_8080) + temp = (temp & ~0x10) | (AF & 0x10); + AF = temp; + break; + + case 0x08: /* EX AF,AF' */ + tStates += 4; /* NOP 4 */ + CHECK_CPU_8080; + temp = AF; + AF = AF1_S; + AF1_S = temp; + break; + + case 0x09: /* ADD HL,BC */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 10 : 11); /* DAD B 10 */ + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL + BC; + temp = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ BC ^ sum) >> 8]; + if (z80_chiptype == CHIP_TYPE_8080) + temp = (temp & ~0x10) | (AF & 0x10); + AF = temp; + HL = sum; + break; + + case 0x0a: /* LD A,(BC) */ + tStates += 7; /* LDAX B 7 */ + CHECK_BREAK_BYTE(BC) + SET_HIGH_REGISTER(AF, s100_bus_memr(BC)); + break; + + case 0x0b: /* DEC BC */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 6); /* DCX B 5 */ + --BC; + break; + + case 0x0c: /* INC C */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* INR C 5 */ + temp = LOW_REGISTER(BC) + 1; + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x0d: /* DEC C */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* DCR C 5 */ + temp = LOW_REGISTER(BC) - 1; + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x0e: /* LD C,nn */ + tStates += 7; /* MVI C,nn 7 */ + SET_LOW_REGISTER(BC, RAM_PP(PC)); + break; + + case 0x0f: /* RRCA */ + tStates += 4; /* RRC 4 */ + temp = (AF & 0xc4) | rrcaTable[HIGH_REGISTER(AF)]; + if (z80_chiptype == CHIP_TYPE_8080) + temp = (temp & ~0x10) | (AF & 0x10); + AF = temp; + break; + + case 0x10: /* DJNZ dd */ + if (z80_chiptype == CHIP_TYPE_8080) + tStates += 4; /* NOP 4 */ + CHECK_CPU_8080; + if ((BC -= 0x100) & 0xff00) { + PC += (int8) s100_bus_memr(PC) + 1; + tStates += 13; + } else { + PC++; + tStates += 8; + } + break; + + case 0x11: /* LD DE,nnnn */ + tStates += 10; /* LXI D,nnnn 10 */ + DE = GET_WORD(PC); + PC += 2; + break; + + case 0x12: /* LD (DE),A */ + tStates += 7; /* STAX D 7 */ + CHECK_BREAK_BYTE(DE) + s100_bus_memw(DE, HIGH_REGISTER(AF)); + break; + + case 0x13: /* INC DE */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 6); /* INX D 5 */ + ++DE; + break; + + case 0x14: /* INC D */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* INR D 5 */ + DE += 0x100; + temp = HIGH_REGISTER(DE); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x15: /* DEC D */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* DCR D 5 */ + DE -= 0x100; + temp = HIGH_REGISTER(DE); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x16: /* LD D,nn */ + tStates += 7; /* MVI D,nn 7 */ + SET_HIGH_REGISTER(DE, RAM_PP(PC)); + break; + + case 0x17: /* RLA */ + tStates += 4; /* RAL 4 */ + temp = ((AF << 8) & 0x0100) | ((AF >> 7) & 0x28) | ((AF << 1) & ~0x01ff) | + (AF & 0xc4) | ((AF >> 15) & 1); + if (z80_chiptype == CHIP_TYPE_8080) + temp = (temp & ~0x10) | (AF & 0x10); + AF = temp; + break; + + case 0x18: /* JR dd */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 4 : 12); /* NOP 4 */ + CHECK_CPU_8080; + PC += (int8) s100_bus_memr(PC) + 1; + break; + + case 0x19: /* ADD HL,DE */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 10 : 11); /* DAD D 10 */ + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL + DE; + temp = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ DE ^ sum) >> 8]; + if (z80_chiptype == CHIP_TYPE_8080) + temp = (temp & ~0x10) | (AF & 0x10); + AF = temp; + HL = sum; + break; + + case 0x1a: /* LD A,(DE) */ + tStates += 7; /* LDAX D 7 */ + CHECK_BREAK_BYTE(DE) + SET_HIGH_REGISTER(AF, s100_bus_memr(DE)); + break; + + case 0x1b: /* DEC DE */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 6); /* DCX D 5 */ + --DE; + break; + + case 0x1c: /* INC E */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* INR E 5 */ + temp = LOW_REGISTER(DE) + 1; + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x1d: /* DEC E */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* DCR E 5 */ + temp = LOW_REGISTER(DE) - 1; + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x1e: /* LD E,nn */ + tStates += 7; /* MVI E 7 */ + SET_LOW_REGISTER(DE, RAM_PP(PC)); + break; + + case 0x1f: /* RRA */ + tStates += 4; /* RAR 4 */ + temp = ((AF & 1) << 15) | (AF & 0xc4) | rraTable[HIGH_REGISTER(AF)]; + if (z80_chiptype == CHIP_TYPE_8080) + temp = (temp & ~0x10) | (AF & 0x10); + AF = temp; + break; + + case 0x20: /* JR NZ,dd */ + if (z80_chiptype == CHIP_TYPE_8080) + tStates += 4; /* NOP 4 */ + CHECK_CPU_8080; + if (TSTFLAG(Z)) { + PC++; + tStates += 7; + } else { + PC += (int8) s100_bus_memr(PC) + 1; + tStates += 12; + } + break; + + case 0x21: /* LD HL,nnnn */ + tStates += 10; /* LXI H,nnnn 10 */ + HL = GET_WORD(PC); + PC += 2; + break; + + case 0x22: /* LD (nnnn),HL */ + tStates += 16; /* SHLD 16 */ + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + PUT_WORD(temp, HL); + PC += 2; + break; + + case 0x23: /* INC HL */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 6); /* INX H 5 */ + ++HL; + break; + + case 0x24: /* INC H */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* INR H 5 */ + HL += 0x100; + temp = HIGH_REGISTER(HL); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x25: /* DEC H */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* DCR H 5 */ + HL -= 0x100; + temp = HIGH_REGISTER(HL); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x26: /* LD H,nn */ + tStates += 7; /* MVI H,nn 7 */ + SET_HIGH_REGISTER(HL, RAM_PP(PC)); + break; + + case 0x27: /* DAA */ + tStates += 4; /* DAA 4 */ + acu = HIGH_REGISTER(AF); + temp = LOW_DIGIT(acu); + cbits = TSTFLAG(C); + if (z80_chiptype == CHIP_TYPE_Z80 && TSTFLAG(N)) { /* last operation was a subtract */ + int hd = cbits || acu > 0x99; + if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ + if (temp > 5) { + SETFLAG(H, 0); + } + acu -= 6; + acu &= 0xff; + } + if (hd) + acu -= 0x160; /* adjust high digit */ + } else { /* last operation was an add */ + if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ + SETFLAG(H, (temp > 9)); + acu += 6; + } + if (cbits || ((acu & 0x1f0) > 0x90)) + acu += 0x60; /* adjust high digit */ + } + AF = (AF & 0x12) | rrdrldTable[acu & 0xff] | ((acu >> 8) & 1) | cbits; + break; + + case 0x28: /* JR Z,dd */ + if (z80_chiptype == CHIP_TYPE_8080) + tStates += 4; /* NOP 4 */ + CHECK_CPU_8080; + if (TSTFLAG(Z)) { + PC += (int8) s100_bus_memr(PC) + 1; + tStates += 12; + } else { + PC++; + tStates += 7; + } + break; + + case 0x29: /* ADD HL,HL */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 10 : 11); /* DAD H 10 */ + HL &= ADDRMASK; + sum = HL + HL; + temp = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + if (z80_chiptype == CHIP_TYPE_8080) + temp = (temp & ~0x10) | (AF & 0x10); + AF = temp; + HL = sum; + break; + + case 0x2a: /* LD HL,(nnnn) */ + tStates += 16; /* LHLD nnnn 16 */ + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + HL = GET_WORD(temp); + PC += 2; + break; + + case 0x2b: /* DEC HL */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 6); /* DCX H 5 */ + --HL; + break; + + case 0x2c: /* INC L */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* INR L 5 */ + temp = LOW_REGISTER(HL) + 1; + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x2d: /* DEC L */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* DCR L 5 */ + temp = LOW_REGISTER(HL) - 1; + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x2e: /* LD L,nn */ + tStates += 7; /* MVI L,nn 7 */ + SET_LOW_REGISTER(HL, RAM_PP(PC)); + break; + + case 0x2f: /* CPL */ + tStates += 4; /* CMA 4 */ + temp = (~AF & ~0xff) | (AF & 0xc5) | ((~AF >> 8) & 0x28) | 0x12; + if (z80_chiptype == CHIP_TYPE_8080) + temp = (temp & ~0x10) | (AF & 0x10); + AF = temp; + break; + + case 0x30: /* JR NC,dd */ + if (z80_chiptype == CHIP_TYPE_8080) + tStates += 4; /* NOP 4 */ + CHECK_CPU_8080; + if (TSTFLAG(C)) { + PC++; + tStates += 7; + } else { + PC += (int8) s100_bus_memr(PC) + 1; + tStates += 12; + } + break; + + case 0x31: /* LD SP,nnnn */ + tStates += 10; /* LXI SP,nnnn 10 */ + SP = GET_WORD(PC); + PC += 2; + break; + + case 0x32: /* LD (nnnn),A */ + tStates += 13; /* STA nnnn 13 */ + temp = GET_WORD(PC); + CHECK_BREAK_BYTE(temp); + s100_bus_memw(temp, HIGH_REGISTER(AF)); + PC += 2; + break; + + case 0x33: /* INC SP */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 6); /* INX SP 5 */ + ++SP; + break; + + case 0x34: /* INC (HL) */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 10 : 11); /* INR M 10 */ + CHECK_BREAK_BYTE(HL); + temp = s100_bus_memr(HL) + 1; + s100_bus_memw(HL, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x35: /* DEC (HL) */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 10 : 11); /* DCR M 10 */ + CHECK_BREAK_BYTE(HL); + temp = s100_bus_memr(HL) - 1; + s100_bus_memw(HL, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x36: /* LD (HL),nn */ + tStates += 10; /* MVI M 10 */ + CHECK_BREAK_BYTE(HL); + s100_bus_memw(HL, RAM_PP(PC)); + break; + + case 0x37: /* SCF */ + tStates += 4; /* STC 4 */ + temp = (AF & ~0x3b) | ((AF >> 8) & 0x28) | 1; + if (z80_chiptype == CHIP_TYPE_8080) + temp = (temp & ~0x10) | (AF & 0x10); + AF = temp; + break; + + case 0x38: /* JR C,dd */ + if (z80_chiptype == CHIP_TYPE_8080) + tStates += 4; /* NOP 4 */ + CHECK_CPU_8080; + if (TSTFLAG(C)) { + PC += (int8) s100_bus_memr(PC) + 1; + tStates += 12; + } else { + PC++; + tStates += 7; + } + break; + + case 0x39: /* ADD HL,SP */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 10 : 11); /* DAD SP 10 */ + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL + SP; + temp = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ SP ^ sum) >> 8]; + if (z80_chiptype == CHIP_TYPE_8080) + temp = (temp & ~0x10) | (AF & 0x10); + AF = temp; + HL = sum; + break; + + case 0x3a: /* LD A,(nnnn) */ + tStates += 13; /* LDA nnnn 13 */ + temp = GET_WORD(PC); + CHECK_BREAK_BYTE(temp); + SET_HIGH_REGISTER(AF, s100_bus_memr(temp)); + PC += 2; + break; + + case 0x3b: /* DEC SP */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 6); /* DCX SP 5 */ + --SP; + break; + + case 0x3c: /* INC A */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* INR A 5 */ + AF += 0x100; + temp = HIGH_REGISTER(AF); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x3d: /* DEC A */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* DCR A 5 */ + AF -= 0x100; + temp = HIGH_REGISTER(AF); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x3e: /* LD A,nn */ + tStates += 7; /* MVI A,nn 7 */ + SET_HIGH_REGISTER(AF, RAM_PP(PC)); + break; + + case 0x3f: /* CCF */ + tStates += 4; /* CMC 4 */ + temp = (AF & ~0x3b) | ((AF >> 8) & 0x28) | ((AF & 1) << 4) | (~AF & 1); + if (z80_chiptype == CHIP_TYPE_8080) + temp = (temp & ~0x10) | (AF & 0x10); + AF = temp; + break; + + case 0x40: /* LD B,B */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV B,B 5 */ + break; + + case 0x41: /* LD B,C */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV B,C 5 */ + BC = (BC & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x42: /* LD B,D */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV B,D 5 */ + BC = (BC & 0xff) | (DE & ~0xff); + break; + + case 0x43: /* LD B,E */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV B,E 5 */ + BC = (BC & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x44: /* LD B,H */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV B,H 5 */ + BC = (BC & 0xff) | (HL & ~0xff); + break; + + case 0x45: /* LD B,L */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV B,L 5 */ + BC = (BC & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x46: /* LD B,(HL) */ + tStates += 7; /* MOV B,M 7 */ + CHECK_BREAK_BYTE(HL); + SET_HIGH_REGISTER(BC, s100_bus_memr(HL)); + break; + + case 0x47: /* LD B,A */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV B,A 5 */ + BC = (BC & 0xff) | (AF & ~0xff); + break; + + case 0x48: /* LD C,B */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV C,B 5 */ + BC = (BC & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x49: /* LD C,C */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV C,C 5 */ + break; + + case 0x4a: /* LD C,D */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV C,D 5 */ + BC = (BC & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x4b: /* LD C,E */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV C,E 5 */ + BC = (BC & ~0xff) | (DE & 0xff); + break; + + case 0x4c: /* LD C,H */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV C,H 5 */ + BC = (BC & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x4d: /* LD C,L */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV C,L 5 */ + BC = (BC & ~0xff) | (HL & 0xff); + break; + + case 0x4e: /* LD C,(HL) */ + tStates += 7; /* MOV C,M 7 */ + CHECK_BREAK_BYTE(HL); + SET_LOW_REGISTER(BC, s100_bus_memr(HL)); + break; + + case 0x4f: /* LD C,A */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV C,A 5 */ + BC = (BC & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x50: /* LD D,B */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV D,B 5 */ + DE = (DE & 0xff) | (BC & ~0xff); + break; + + case 0x51: /* LD D,C */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV D,C 5 */ + DE = (DE & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x52: /* LD D,D */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV D,D 5 */ + break; + + case 0x53: /* LD D,E */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV D,E 5 */ + DE = (DE & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x54: /* LD D,H */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV D,H 5 */ + DE = (DE & 0xff) | (HL & ~0xff); + break; + + case 0x55: /* LD D,L */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV D,L 5 */ + DE = (DE & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x56: /* LD D,(HL) */ + tStates += 7; /* MOV D,M 7 */ + CHECK_BREAK_BYTE(HL); + SET_HIGH_REGISTER(DE, s100_bus_memr(HL)); + break; + + case 0x57: /* LD D,A */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV D,A 5 */ + DE = (DE & 0xff) | (AF & ~0xff); + break; + + case 0x58: /* LD E,B */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV E,B 5 */ + DE = (DE & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x59: /* LD E,C */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV E,C 5 */ + DE = (DE & ~0xff) | (BC & 0xff); + break; + + case 0x5a: /* LD E,D */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV E,D 5 */ + DE = (DE & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x5b: /* LD E,E */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV E,E 5 */ + break; + + case 0x5c: /* LD E,H */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV E,H 5 */ + DE = (DE & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x5d: /* LD E,L */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV E,L 5 */ + DE = (DE & ~0xff) | (HL & 0xff); + break; + + case 0x5e: /* LD E,(HL) */ + tStates += 7; /* MOV E,M 7 */ + CHECK_BREAK_BYTE(HL); + SET_LOW_REGISTER(DE, s100_bus_memr(HL)); + break; + + case 0x5f: /* LD E,A */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV E,A 5 */ + DE = (DE & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x60: /* LD H,B */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV H,B 5 */ + HL = (HL & 0xff) | (BC & ~0xff); + break; + + case 0x61: /* LD H,C */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV H,C 5 */ + HL = (HL & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x62: /* LD H,D */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV H,D 5 */ + HL = (HL & 0xff) | (DE & ~0xff); + break; + + case 0x63: /* LD H,E */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV H,E 5 */ + HL = (HL & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x64: /* LD H,H */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV H,H 5 */ + break; + + case 0x65: /* LD H,L */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV H,L 5 */ + HL = (HL & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x66: /* LD H,(HL) */ + tStates += 7; /* MOV H,M 7 */ + CHECK_BREAK_BYTE(HL); + SET_HIGH_REGISTER(HL, s100_bus_memr(HL)); + break; + + case 0x67: /* LD H,A */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV H,A 5 */ + HL = (HL & 0xff) | (AF & ~0xff); + break; + + case 0x68: /* LD L,B */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV L,B 5 */ + HL = (HL & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x69: /* LD L,C */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV L,C 5 */ + HL = (HL & ~0xff) | (BC & 0xff); + break; + + case 0x6a: /* LD L,D */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV L,D 5 */ + HL = (HL & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x6b: /* LD L,E */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV L,E 5 */ + HL = (HL & ~0xff) | (DE & 0xff); + break; + + case 0x6c: /* LD L,H */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV L,H 5 */ + HL = (HL & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x6d: /* LD L,L */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV L,L 5 */ + break; + + case 0x6e: /* LD L,(HL) */ + tStates += 7; /* MOV L,M 7 */ + CHECK_BREAK_BYTE(HL); + SET_LOW_REGISTER(HL, s100_bus_memr(HL)); + break; + + case 0x6f: /* LD L,A */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV L,A 5 */ + HL = (HL & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x70: /* LD (HL),B */ + tStates += 7; /* MOV M,B 7 */ + CHECK_BREAK_BYTE(HL); + s100_bus_memw(HL, HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (HL),C */ + tStates += 7; /* MOV M,C 7 */ + CHECK_BREAK_BYTE(HL); + s100_bus_memw(HL, LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (HL),D */ + tStates += 7; /* MOV M,D 7 */ + CHECK_BREAK_BYTE(HL); + s100_bus_memw(HL, HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (HL),E */ + tStates += 7; /* MOV M,E 7 */ + CHECK_BREAK_BYTE(HL); + s100_bus_memw(HL, LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (HL),H */ + tStates += 7; /* MOV M,H 7 */ + CHECK_BREAK_BYTE(HL); + s100_bus_memw(HL, HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (HL),L */ + tStates += 7; /* MOV M,L 7 */ + CHECK_BREAK_BYTE(HL); + s100_bus_memw(HL, LOW_REGISTER(HL)); + break; + + case HALTINSTRUCTION: /* HALT */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 7 : 4); /* HLT 7 */ + PC--; + if (z80_unit.flags & UNIT_Z80_STOPONHALT) { + reason = STOP_HALT; + goto end_decode; + } + sim_interval = 0; + break; + + case 0x77: /* LD (HL),A */ + tStates += 7; /* MOV M,A 7 */ + CHECK_BREAK_BYTE(HL); + s100_bus_memw(HL, HIGH_REGISTER(AF)); + break; + + case 0x78: /* LD A,B */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV A,B 5 */ + AF = (AF & 0xff) | (BC & ~0xff); + break; + + case 0x79: /* LD A,C */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV A,C 5 */ + AF = (AF & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x7a: /* LD A,D */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV A,D 5 */ + AF = (AF & 0xff) | (DE & ~0xff); + break; + + case 0x7b: /* LD A,E */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV A,E 5 */ + AF = (AF & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x7c: /* LD A,H */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV A,H 5 */ + AF = (AF & 0xff) | (HL & ~0xff); + break; + + case 0x7d: /* LD A,L */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV A,L 5 */ + AF = (AF & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x7e: /* LD A,(HL) */ + tStates += 7; /* MOV A,M 7 */ + CHECK_BREAK_BYTE(HL); + SET_HIGH_REGISTER(AF, s100_bus_memr(HL)); + break; + + case 0x7f: /* LD A,A */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* MOV A,A 5 */ + break; + + case 0x80: /* ADD A,B */ + tStates += 4; /* ADD B 4 */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x81: /* ADD A,C */ + tStates += 4; /* ADD C 4 */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x82: /* ADD A,D */ + tStates += 4; /* ADD D 4 */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x83: /* ADD A,E */ + tStates += 4; /* ADD E 4 */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x84: /* ADD A,H */ + tStates += 4; /* ADD H 4 */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x85: /* ADD A,L */ + tStates += 4; /* ADD L 4 */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x86: /* ADD A,(HL) */ + tStates += 7; /* ADD M 7 */ + CHECK_BREAK_BYTE(HL); + temp = s100_bus_memr(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x87: /* ADD A,A */ + tStates += 4; /* ADD A 4 */ + cbits = 2 * HIGH_REGISTER(AF); + AF = cbitsDup8Table[cbits] | (SET_PVS(cbits)); + break; + + case 0x88: /* ADC A,B */ + tStates += 4; /* ADC B 4 */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x89: /* ADC A,C */ + tStates += 4; /* ADC C 4 */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8a: /* ADC A,D */ + tStates += 4; /* ADC D 4 */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8b: /* ADC A,E */ + tStates += 4; /* ADC E 4 */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8c: /* ADC A,H */ + tStates += 4; /* ADC H 4 */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8d: /* ADC A,L */ + tStates += 4; /* ADC L 4 */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8e: /* ADC A,(HL) */ + tStates += 7; /* ADC M 7 */ + CHECK_BREAK_BYTE(HL); + temp = s100_bus_memr(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8f: /* ADC A,A */ + tStates += 4; /* ADC A 4 */ + cbits = 2 * HIGH_REGISTER(AF) + TSTFLAG(C); + AF = cbitsDup8Table[cbits] | (SET_PVS(cbits)); + break; + + case 0x90: /* SUB B */ + tStates += 4; /* SUB B 4 */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x91: /* SUB C */ + tStates += 4; /* SUB C 4 */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x92: /* SUB D */ + tStates += 4; /* SUB D 4 */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x93: /* SUB E */ + tStates += 4; /* SUB E 4 */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x94: /* SUB H */ + tStates += 4; /* SUB H 4 */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x95: /* SUB L */ + tStates += 4; /* SUB L 4 */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x96: /* SUB (HL) */ + tStates += 7; /* SUB M 7 */ + CHECK_BREAK_BYTE(HL); + temp = s100_bus_memr(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x97: /* SUB A */ + tStates += 4; /* SUB A 4 */ + AF = (z80_chiptype == CHIP_TYPE_Z80) ? 0x42 : 0x56; + break; + + case 0x98: /* SBC A,B */ + tStates += 4; /* SBB B 4 */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x99: /* SBC A,C */ + tStates += 4; /* SBB C 4 */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x9a: /* SBC A,D */ + tStates += 4; /* SBB D 4 */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x9b: /* SBC A,E */ + tStates += 4; /* SBB E 4 */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x9c: /* SBC A,H */ + tStates += 4; /* SBB H 4 */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x9d: /* SBC A,L */ + tStates += 4; /* SBB L 4 */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x9e: /* SBC A,(HL) */ + tStates += 7; /* SBB M 7 */ + CHECK_BREAK_BYTE(HL); + temp = s100_bus_memr(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0x9f: /* SBC A,A */ + tStates += 4; /* SBB A 4 */ + cbits = -TSTFLAG(C); + AF = subTable[cbits & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PVS(cbits)); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0xa0: /* AND B */ + tStates += 4; /* ANA B 4 */ + acu = andTable[((AF & BC) >> 8) & 0xff]; + if (z80_chiptype == CHIP_TYPE_8080 && ((AF | BC) & 0x0800) == 0) + acu &= ~0x10; + AF = acu; + break; + + case 0xa1: /* AND C */ + tStates += 4; /* ANA C 4 */ + acu = andTable[((AF >> 8) & BC) & 0xff]; + if (z80_chiptype == CHIP_TYPE_8080 && (((AF >> 8) | BC) & 0x08) == 0) + acu &= ~0x10; + AF = acu; + break; + + case 0xa2: /* AND D */ + tStates += 4; /* ANA D 4 */ + acu = andTable[((AF & DE) >> 8) & 0xff]; + if (z80_chiptype == CHIP_TYPE_8080 && ((AF | DE) & 0x0800) == 0) + acu &= ~0x10; + AF = acu; + break; + + case 0xa3: /* AND E */ + tStates += 4; /* ANA E 4 */ + acu = andTable[((AF >> 8) & DE) & 0xff]; + if (z80_chiptype == CHIP_TYPE_8080 && (((AF >> 8) | DE) & 0x08) == 0) + acu &= ~0x10; + AF = acu; + break; + + case 0xa4: /* AND H */ + tStates += 4; /* ANA H 4 */ + acu = andTable[((AF & HL) >> 8) & 0xff]; + if (z80_chiptype == CHIP_TYPE_8080 && ((AF | HL) & 0x0800) == 0) + acu &= ~0x10; + AF = acu; + break; + + case 0xa5: /* AND L */ + tStates += 4; /* ANA L 4 */ + acu = andTable[((AF >> 8) & HL) & 0xff]; + if (z80_chiptype == CHIP_TYPE_8080 && (((AF >> 8) | HL) & 0x08) == 0) + acu &= ~0x10; + AF = acu; + break; + + case 0xa6: /* AND (HL) */ + tStates += 7; /* ANA M 7 */ + CHECK_BREAK_BYTE(HL); + temp = s100_bus_memr(HL); + acu = andTable[((AF >> 8) & temp) & 0xff]; + if (z80_chiptype == CHIP_TYPE_8080 && (((AF >> 8) | temp) & 0x08) == 0) + acu &= ~0x10; + AF = acu; + break; + + case 0xa7: /* AND A */ + tStates += 4; /* ANA A 4 */ + acu = andTable[(AF >> 8) & 0xff]; + if (z80_chiptype == CHIP_TYPE_8080 && (AF & 0x0800) == 0) + acu &= ~0x10; + AF = acu; + break; + + case 0xa8: /* XOR B */ + tStates += 4; /* XRA B 4 */ + AF = xororTable[((AF ^ BC) >> 8) & 0xff]; + break; + + case 0xa9: /* XOR C */ + tStates += 4; /* XRA C 4 */ + AF = xororTable[((AF >> 8) ^ BC) & 0xff]; + break; + + case 0xaa: /* XOR D */ + tStates += 4; /* XRA D 4 */ + AF = xororTable[((AF ^ DE) >> 8) & 0xff]; + break; + + case 0xab: /* XOR E */ + tStates += 4; /* XRA E 4 */ + AF = xororTable[((AF >> 8) ^ DE) & 0xff]; + break; + + case 0xac: /* XOR H */ + tStates += 4; /* XRA H 4 */ + AF = xororTable[((AF ^ HL) >> 8) & 0xff]; + break; + + case 0xad: /* XOR L */ + tStates += 4; /* XRA L 4 */ + AF = xororTable[((AF >> 8) ^ HL) & 0xff]; + break; + + case 0xae: /* XOR (HL) */ + tStates += 7; /* XRA M 7 */ + CHECK_BREAK_BYTE(HL); + AF = xororTable[((AF >> 8) ^ s100_bus_memr(HL)) & 0xff]; + break; + + case 0xaf: /* XOR A */ + tStates += 4; /* XRA A 4 */ + AF = 0x44; + break; + + case 0xb0: /* OR B */ + tStates += 4; /* ORA B 4 */ + AF = xororTable[((AF | BC) >> 8) & 0xff]; + break; + + case 0xb1: /* OR C */ + tStates += 4; /* ORA C 4 */ + AF = xororTable[((AF >> 8) | BC) & 0xff]; + break; + + case 0xb2: /* OR D */ + tStates += 4; /* ORA D 4 */ + AF = xororTable[((AF | DE) >> 8) & 0xff]; + break; + + case 0xb3: /* OR E */ + tStates += 4; /* ORA E 4 */ + AF = xororTable[((AF >> 8) | DE) & 0xff]; + break; + + case 0xb4: /* OR H */ + tStates += 4; /* ORA H 4 */ + AF = xororTable[((AF | HL) >> 8) & 0xff]; + break; + + case 0xb5: /* OR L */ + tStates += 4; /* ORA L 4 */ + AF = xororTable[((AF >> 8) | HL) & 0xff]; + break; + + case 0xb6: /* OR (HL) */ + tStates += 7; /* ORA M 7 */ + CHECK_BREAK_BYTE(HL); + AF = xororTable[((AF >> 8) | s100_bus_memr(HL)) & 0xff]; + break; + + case 0xb7: /* OR A */ + tStates += 4; /* ORA A 4 */ + AF = xororTable[(AF >> 8) & 0xff]; + break; + + case 0xb8: /* CP B */ + tStates += 4; /* CMP B 4 */ + temp = HIGH_REGISTER(BC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0xb9: /* CP C */ + tStates += 4; /* CMP C 4 */ + temp = LOW_REGISTER(BC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0xba: /* CP D */ + tStates += 4; /* CMP D 4 */ + temp = HIGH_REGISTER(DE); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0xbb: /* CP E */ + tStates += 4; /* CMP E 4 */ + temp = LOW_REGISTER(DE); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0xbc: /* CP H */ + tStates += 4; /* CMP H 4 */ + temp = HIGH_REGISTER(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0xbd: /* CP L */ + tStates += 4; /* CMP L 4 */ + temp = LOW_REGISTER(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0xbe: /* CP (HL) */ + tStates += 7; /* CMP M 7 */ + CHECK_BREAK_BYTE(HL); + temp = s100_bus_memr(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0xbf: /* CP A */ + tStates += 4; /* CMP A 4 */ + SET_LOW_REGISTER(AF, (HIGH_REGISTER(AF) & 0x28) | (z80_chiptype == CHIP_TYPE_Z80 ? 0x42 : 0x56)); + break; + + case 0xc0: /* RET NZ */ + if (TSTFLAG(Z)) { + tStates += 5; /* RNZ 5 */ + } else { + CHECK_BREAK_WORD(SP); + POP(PC); + tStates += 11; /* RNZ 11 */ + } + break; + + case 0xc1: /* POP BC */ + tStates += 10; /* POP B 10 */ + CHECK_BREAK_WORD(SP); + POP(BC); + break; + + case 0xc2: /* JP NZ,nnnn */ + JPC(!TSTFLAG(Z)); /* also updates tStates, Z80 and 8080 are equal */ + break; + + case 0xc3: /* JP nnnn */ + JPC(1); /* also updates tStates, Z80 and 8080 are equal */ + break; + + case 0xc4: /* CALL NZ,nnnn */ + CALLC(!TSTFLAG(Z)); /* also updates tStates */ + break; + + case 0xc5: /* PUSH BC */ + tStates += 11; /* PUSH B 11 */ + CHECK_BREAK_WORD(SP - 2); + PUSH(BC); + break; + + case 0xc6: /* ADD A,nn */ + tStates += 7; /* ADI nn 7 */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0xc7: /* RST 0 */ + tStates += 11; /* RST 0 11 */ + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PC = 0; + break; + + case 0xc8: /* RET Z */ + if (TSTFLAG(Z)) { + CHECK_BREAK_WORD(SP); + POP(PC); + tStates += 11; /* RZ 11 */ + } else { + tStates += 5; /* RZ 5 */ + } + break; + + case 0xc9: /* RET */ + tStates += 10; /* RET 10 */ + CHECK_BREAK_WORD(SP); + POP(PC); + break; + + case 0xca: /* JP Z,nnnn */ + JPC(TSTFLAG(Z)); /* also updates tStates */ + break; + + case 0xcb: /* CB prefix */ + if (z80_chiptype == CHIP_TYPE_8080) { + if (z80_unit.flags & UNIT_Z80_OPSTOP) { + reason = STOP_OPCODE; + goto end_decode; + } else { + JPC(1); + break; + } + } + + INCR(1); + adr = HL; + switch ((op = s100_bus_memr(PC)) & 7) { + + case 0: + tStateModifier = FALSE; + ++PC; + acu = HIGH_REGISTER(BC); + tStates += 8; + break; + + case 1: + tStateModifier = FALSE; + ++PC; + acu = LOW_REGISTER(BC); + tStates += 8; + break; + + case 2: + tStateModifier = FALSE; + ++PC; + acu = HIGH_REGISTER(DE); + tStates += 8; + break; + + case 3: + tStateModifier = FALSE; + ++PC; + acu = LOW_REGISTER(DE); + tStates += 8; + break; + + case 4: + tStateModifier = FALSE; + ++PC; + acu = HIGH_REGISTER(HL); + tStates += 8; + break; + + case 5: + tStateModifier = FALSE; + ++PC; + acu = LOW_REGISTER(HL); + tStates += 8; + break; + + case 6: + CHECK_BREAK_BYTE(adr); + ++PC; + acu = s100_bus_memr(adr); + tStateModifier = TRUE; + tStates += 15; + break; + + case 7: + tStateModifier = FALSE; + ++PC; + acu = HIGH_REGISTER(AF); + tStates += 8; + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + switch (op & 0x38) { + + case 0x00: /* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg1; + + case 0x08: /* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg1; + + case 0x10: /* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg1; + + case 0x18: /* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg1; + + case 0x20: /* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg1; + + case 0x28: /* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg1; + + case 0x30: /* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg1; + + case 0x38: /* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg1: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + /* !!cbits == 0 if cbits == 0 !!cbits == 1 if cbits > 0 */ + } + break; + + case 0x40: /* BIT */ + if (tStateModifier) + tStates -= 3; + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else + AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) + AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + s100_bus_memw(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xcc: /* CALL Z,nnnn */ + CALLC(TSTFLAG(Z)); /* also updates tStates */ + break; + + case 0xcd: /* CALL nnnn */ + CALLC(1); /* also updates tStates */ + break; + + case 0xce: /* ADC A,nn */ + tStates += 7; /* ACI nn 7 */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0xcf: /* RST 8 */ + tStates += 11; /* RST 1 */ + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PC = 8; + break; + + case 0xd0: /* RET NC */ + if (TSTFLAG(C)) { + tStates += 5; /* RNC 5 */ + } else { + CHECK_BREAK_WORD(SP); + POP(PC); + tStates += 11; /* RNC 11 */ + } + break; + + case 0xd1: /* POP DE */ + tStates += 10; /* POP D 10 */ + CHECK_BREAK_WORD(SP); + POP(DE); + break; + + case 0xd2: /* JP NC,nnnn */ + JPC(!TSTFLAG(C)); /* also updates tStates */ + break; + + case 0xd3: /* OUT (nn),A */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 10 :11); /* OUT nn 10 */ + s100_bus_out(RAM_PP(PC), HIGH_REGISTER(AF)); + break; + + case 0xd4: /* CALL NC,nnnn */ + CALLC(!TSTFLAG(C)); /* also updates tStates */ + break; + + case 0xd5: /* PUSH DE */ + tStates += 11; /* PUSH D 11 */ + CHECK_BREAK_WORD(SP - 2); + PUSH(DE); + break; + + case 0xd6: /* SUB nn */ + tStates += 7; /* SUI nn 7 */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0xd7: /* RST 10H */ + tStates += 11; /* RST 2 11 */ + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PC = 0x10; + break; + + case 0xd8: /* RET C */ + if (TSTFLAG(C)) { + CHECK_BREAK_WORD(SP); + POP(PC); + tStates += 11; /* RC 11 */ + } else { + tStates += 5; /* RC 5 */ + } + break; + + case 0xd9: /* EXX */ + if (z80_chiptype == CHIP_TYPE_8080) { + if (z80_unit.flags & UNIT_Z80_OPSTOP) { + reason = STOP_OPCODE; + goto end_decode; + } + else { + tStates += 10; /* RET 10 */ + CHECK_BREAK_WORD(SP); + POP(PC); + break; + } + } + tStates += 4; + temp = BC; + BC = BC1_S; + BC1_S = temp; + temp = DE; + DE = DE1_S; + DE1_S = temp; + temp = HL; + HL = HL1_S; + HL1_S = temp; + break; + + case 0xda: /* JP C,nnnn */ + JPC(TSTFLAG(C)); /* also updates tStates */ + break; + + case 0xdb: /* IN A,(nn) */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 10 : 11); /* IN nn 10 */ + SET_HIGH_REGISTER(AF, s100_bus_in(RAM_PP(PC))); + break; + + case 0xdc: /* CALL C,nnnn */ + CALLC(TSTFLAG(C)); /* also updates tStates */ + break; + + case 0xdd: /* DD prefix */ + if (z80_chiptype == CHIP_TYPE_8080) { + if (z80_unit.flags & UNIT_Z80_OPSTOP) { + reason = STOP_OPCODE; + goto end_decode; + } else { + CALLC(1); /* also updates tStates */ + break; + } + } + + INCR(1); + switch (RAM_PP(PC)) { + + case 0x09: /* ADD IX,BC */ + tStates += 15; + IX &= ADDRMASK; + BC &= ADDRMASK; + sum = IX + BC; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ BC ^ sum) >> 8]; + IX = sum; + break; + + case 0x19: /* ADD IX,DE */ + tStates += 15; + IX &= ADDRMASK; + DE &= ADDRMASK; + sum = IX + DE; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ DE ^ sum) >> 8]; + IX = sum; + break; + + case 0x21: /* LD IX,nnnn */ + tStates += 14; + IX = GET_WORD(PC); + PC += 2; + break; + + case 0x22: /* LD (nnnn),IX */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + PUT_WORD(temp, IX); + PC += 2; + break; + + case 0x23: /* INC IX */ + tStates += 10; + ++IX; + break; + + case 0x24: /* INC IXH */ + tStates += 9; + IX += 0x100; + AF = (AF & ~0xfe) | incZ80Table[HIGH_REGISTER(IX)]; + break; + + case 0x25: /* DEC IXH */ + tStates += 9; + IX -= 0x100; + AF = (AF & ~0xfe) | decZ80Table[HIGH_REGISTER(IX)]; + break; + + case 0x26: /* LD IXH,nn */ + tStates += 9; + SET_HIGH_REGISTER(IX, RAM_PP(PC)); + break; + + case 0x29: /* ADD IX,IX */ + tStates += 15; + IX &= ADDRMASK; + sum = IX + IX; + AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + IX = sum; + break; + + case 0x2a: /* LD IX,(nnnn) */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + IX = GET_WORD(temp); + PC += 2; + break; + + case 0x2b: /* DEC IX */ + tStates += 10; + --IX; + break; + + case 0x2c: /* INC IXL */ + tStates += 9; + temp = LOW_REGISTER(IX) + 1; + SET_LOW_REGISTER(IX, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x2d: /* DEC IXL */ + tStates += 9; + temp = LOW_REGISTER(IX) - 1; + SET_LOW_REGISTER(IX, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x2e: /* LD IXL,nn */ + tStates += 9; + SET_LOW_REGISTER(IX, RAM_PP(PC)); + break; + + case 0x34: /* INC (IX+dd) */ + tStates += 23; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = s100_bus_memr(adr) + 1; + s100_bus_memw(adr, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x35: /* DEC (IX+dd) */ + tStates += 23; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = s100_bus_memr(adr) - 1; + s100_bus_memw(adr, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x36: /* LD (IX+dd),nn */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, RAM_PP(PC)); + break; + + case 0x39: /* ADD IX,SP */ + tStates += 15; + IX &= ADDRMASK; + SP &= ADDRMASK; + sum = IX + SP; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ SP ^ sum) >> 8]; + IX = sum; + break; + + case 0x44: /* LD B,IXH */ + tStates += 9; + SET_HIGH_REGISTER(BC, HIGH_REGISTER(IX)); + break; + + case 0x45: /* LD B,IXL */ + tStates += 9; + SET_HIGH_REGISTER(BC, LOW_REGISTER(IX)); + break; + + case 0x46: /* LD B,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(BC, s100_bus_memr(adr)); + break; + + case 0x4c: /* LD C,IXH */ + tStates += 9; + SET_LOW_REGISTER(BC, HIGH_REGISTER(IX)); + break; + + case 0x4d: /* LD C,IXL */ + tStates += 9; + SET_LOW_REGISTER(BC, LOW_REGISTER(IX)); + break; + + case 0x4e: /* LD C,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_LOW_REGISTER(BC, s100_bus_memr(adr)); + break; + + case 0x54: /* LD D,IXH */ + tStates += 9; + SET_HIGH_REGISTER(DE, HIGH_REGISTER(IX)); + break; + + case 0x55: /* LD D,IXL */ + tStates += 9; + SET_HIGH_REGISTER(DE, LOW_REGISTER(IX)); + break; + + case 0x56: /* LD D,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(DE, s100_bus_memr(adr)); + break; + + case 0x5c: /* LD E,IXH */ + tStates += 9; + SET_LOW_REGISTER(DE, HIGH_REGISTER(IX)); + break; + + case 0x5d: /* LD E,IXL */ + tStates += 9; + SET_LOW_REGISTER(DE, LOW_REGISTER(IX)); + break; + + case 0x5e: /* LD E,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_LOW_REGISTER(DE, s100_bus_memr(adr)); + break; + + case 0x60: /* LD IXH,B */ + tStates += 9; + SET_HIGH_REGISTER(IX, HIGH_REGISTER(BC)); + break; + + case 0x61: /* LD IXH,C */ + tStates += 9; + SET_HIGH_REGISTER(IX, LOW_REGISTER(BC)); + break; + + case 0x62: /* LD IXH,D */ + tStates += 9; + SET_HIGH_REGISTER(IX, HIGH_REGISTER(DE)); + break; + + case 0x63: /* LD IXH,E */ + tStates += 9; + SET_HIGH_REGISTER(IX, LOW_REGISTER(DE)); + break; + + case 0x64: /* LD IXH,IXH */ + tStates += 9; + break; + + case 0x65: /* LD IXH,IXL */ + tStates += 9; + SET_HIGH_REGISTER(IX, LOW_REGISTER(IX)); + break; + + case 0x66: /* LD H,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(HL, s100_bus_memr(adr)); + break; + + case 0x67: /* LD IXH,A */ + tStates += 9; + SET_HIGH_REGISTER(IX, HIGH_REGISTER(AF)); + break; + + case 0x68: /* LD IXL,B */ + tStates += 9; + SET_LOW_REGISTER(IX, HIGH_REGISTER(BC)); + break; + + case 0x69: /* LD IXL,C */ + tStates += 9; + SET_LOW_REGISTER(IX, LOW_REGISTER(BC)); + break; + + case 0x6a: /* LD IXL,D */ + tStates += 9; + SET_LOW_REGISTER(IX, HIGH_REGISTER(DE)); + break; + + case 0x6b: /* LD IXL,E */ + tStates += 9; + SET_LOW_REGISTER(IX, LOW_REGISTER(DE)); + break; + + case 0x6c: /* LD IXL,IXH */ + tStates += 9; + SET_LOW_REGISTER(IX, HIGH_REGISTER(IX)); + break; + + case 0x6d: /* LD IXL,IXL */ + tStates += 9; + break; + + case 0x6e: /* LD L,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_LOW_REGISTER(HL, s100_bus_memr(adr)); + break; + + case 0x6f: /* LD IXL,A */ + tStates += 9; + SET_LOW_REGISTER(IX, HIGH_REGISTER(AF)); + break; + + case 0x70: /* LD (IX+dd),B */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (IX+dd),C */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (IX+dd),D */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (IX+dd),E */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (IX+dd),H */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (IX+dd),L */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, LOW_REGISTER(HL)); + break; + + case 0x77: /* LD (IX+dd),A */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, HIGH_REGISTER(AF)); + break; + + case 0x7c: /* LD A,IXH */ + tStates += 9; + SET_HIGH_REGISTER(AF, HIGH_REGISTER(IX)); + break; + + case 0x7d: /* LD A,IXL */ + tStates += 9; + SET_HIGH_REGISTER(AF, LOW_REGISTER(IX)); + break; + + case 0x7e: /* LD A,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(AF, s100_bus_memr(adr)); + break; + + case 0x84: /* ADD A,IXH */ + tStates += 9; + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x85: /* ADD A,IXL */ + tStates += 9; + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x86: /* ADD A,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = s100_bus_memr(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8c: /* ADC A,IXH */ + tStates += 9; + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8d: /* ADC A,IXL */ + tStates += 9; + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8e: /* ADC A,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = s100_bus_memr(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x96: /* SUB (IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = s100_bus_memr(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x94: /* SUB IXH */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9c: /* SBC A,IXH */ + tStates += 9; + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x95: /* SUB IXL */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9d: /* SBC A,IXL */ + tStates += 9; + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x9e: /* SBC A,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = s100_bus_memr(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xa4: /* AND IXH */ + tStates += 9; + AF = andTable[((AF & IX) >> 8) & 0xff]; + break; + + case 0xa5: /* AND IXL */ + tStates += 9; + AF = andTable[((AF >> 8) & IX) & 0xff]; + break; + + case 0xa6: /* AND (IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + AF = andTable[((AF >> 8) & s100_bus_memr(adr)) & 0xff]; + break; + + case 0xac: /* XOR IXH */ + tStates += 9; + AF = xororTable[((AF ^ IX) >> 8) & 0xff]; + break; + + case 0xad: /* XOR IXL */ + tStates += 9; + AF = xororTable[((AF >> 8) ^ IX) & 0xff]; + break; + + case 0xae: /* XOR (IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + AF = xororTable[((AF >> 8) ^ s100_bus_memr(adr)) & 0xff]; + break; + + case 0xb4: /* OR IXH */ + tStates += 9; + AF = xororTable[((AF | IX) >> 8) & 0xff]; + break; + + case 0xb5: /* OR IXL */ + tStates += 9; + AF = xororTable[((AF >> 8) | IX) & 0xff]; + break; + + case 0xb6: /* OR (IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + AF = xororTable[((AF >> 8) | s100_bus_memr(adr)) & 0xff]; + break; + + case 0xbc: /* CP IXH */ + tStates += 9; + temp = HIGH_REGISTER(IX); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbd: /* CP IXL */ + tStates += 9; + temp = LOW_REGISTER(IX); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbe: /* CP (IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = s100_bus_memr(adr); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xcb: /* CB prefix */ + adr = IX + (int8) RAM_PP(PC); + switch ((op = s100_bus_memr(PC)) & 7) { + + case 0: + ++PC; + acu = HIGH_REGISTER(BC); + break; + + case 1: + ++PC; + acu = LOW_REGISTER(BC); + break; + + case 2: + ++PC; + acu = HIGH_REGISTER(DE); + break; + + case 3: + ++PC; + acu = LOW_REGISTER(DE); + break; + + case 4: + ++PC; + acu = HIGH_REGISTER(HL); + break; + + case 5: + ++PC; + acu = LOW_REGISTER(HL); + break; + + case 6: + CHECK_BREAK_BYTE(adr); + ++PC; + acu = s100_bus_memr(adr); + break; + + case 7: + ++PC; + acu = HIGH_REGISTER(AF); + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + tStates += 23; + switch (op & 0x38) { + + case 0x00: /* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg2; + + case 0x08: /* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg2; + + case 0x10: /* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg2; + + case 0x18: /* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg2; + + case 0x20: /* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg2; + + case 0x28: /* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg2; + + case 0x30: /* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg2; + + case 0x38: /* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg2: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + /* !!cbits == 0 if cbits == 0 !!cbits == 1 if cbits > 0 */ + } + break; + + case 0x40: /* BIT */ + tStates += 20; + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else + AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) + AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + tStates += 23; + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + tStates += 23; + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + s100_bus_memw(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xe1: /* POP IX */ + tStates += 14; + CHECK_BREAK_WORD(SP); + POP(IX); + break; + + case 0xe3: /* EX (SP),IX */ + tStates += 23; + CHECK_BREAK_WORD(SP); + temp = IX; + POP(IX); + PUSH(temp); + break; + + case 0xe5: /* PUSH IX */ + tStates += 15; + CHECK_BREAK_WORD(SP - 2); + PUSH(IX); + break; + + case 0xe9: /* JP (IX) */ + tStates += 8; + PC = IX; + break; + + case 0xf9: /* LD SP,IX */ + tStates += 10; + SP = IX; + break; + + default: /* ignore DD */ + CHECK_CPU_Z80; + PC--; + } + break; + + case 0xde: /* SBC A,nn */ + tStates += 7; /* SBI nn 7 */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0xdf: /* RST 18H */ + tStates += 11; /* RST 3 11 */ + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PC = 0x18; + break; + + case 0xe0: /* RET PO */ + if (TSTFLAG(P)) { + tStates += 5; /* RPO 5 */ + } else { + CHECK_BREAK_WORD(SP); + POP(PC); + tStates += 11; /* RPO 11 */ + } + break; + + case 0xe1: /* POP HL */ + tStates += 10; /* POP H 10 */ + CHECK_BREAK_WORD(SP); + POP(HL); + break; + + case 0xe2: /* JP PO,nnnn */ + JPC(!TSTFLAG(P)); /* also updates tStates */ + break; + + case 0xe3: /* EX (SP),HL */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 18 : 19); /* XTHL 18 */ + CHECK_BREAK_WORD(SP); + temp = HL; + POP(HL); + PUSH(temp); + break; + + case 0xe4: /* CALL PO,nnnn */ + CALLC(!TSTFLAG(P)); /* also updates tStates */ + break; + + case 0xe5: /* PUSH HL */ + tStates += 11; /* PUSH H 11 */ + CHECK_BREAK_WORD(SP - 2); + PUSH(HL); + break; + + case 0xe6: /* AND nn */ + tStates += 7; /* ANI nn 7 */ + temp = RAM_PP(PC); + acu = andTable[((AF >> 8) & temp) & 0xff]; + if (z80_chiptype == CHIP_TYPE_8080 && (((AF >> 8) | temp) & 0x08) == 0) + acu &= ~0x10; + AF = acu; + break; + + case 0xe7: /* RST 20H */ + tStates += 11; /* RST 4 11 */ + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PC = 0x20; + break; + + case 0xe8: /* RET PE */ + if (TSTFLAG(P)) { + CHECK_BREAK_WORD(SP); + POP(PC); + tStates += 11; /* RPE 11 */ + } else { + tStates += 5; /* RPE 5 */ + } + break; + + case 0xe9: /* JP (HL) */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* PCHL 5 */ + PC = HL; + break; + + case 0xea: /* JP PE,nnnn */ + JPC(TSTFLAG(P)); /* also updates tStates */ + break; + + case 0xeb: /* EX DE,HL */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 4); /* XCHG 5 */ + temp = HL; + HL = DE; + DE = temp; + break; + + case 0xec: /* CALL PE,nnnn */ + CALLC(TSTFLAG(P)); /* also updates tStates */ + break; + + case 0xed: /* ED prefix */ + if (z80_chiptype == CHIP_TYPE_8080) { + if (z80_unit.flags & UNIT_Z80_OPSTOP) { + reason = STOP_OPCODE; + goto end_decode; + } else { + CALLC(1); /* also updates tStates */ + break; + } + } + + INCR(1); + switch (RAM_PP(PC)) { + + case 0x40: /* IN B,(C) */ + tStates += 12; + temp = s100_bus_in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(BC, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x41: /* OUT (C),B */ + tStates += 12; + s100_bus_out(LOW_REGISTER(BC), HIGH_REGISTER(BC)); + break; + + case 0x42: /* SBC HL,BC */ + tStates += 15; + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL - BC - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ BC ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x43: /* LD (nnnn),BC */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + PUT_WORD(temp, BC); + PC += 2; + break; + + case 0x44: /* NEG */ + + case 0x4C: /* NEG, unofficial */ + + case 0x54: /* NEG, unofficial */ + + case 0x5C: /* NEG, unofficial */ + + case 0x64: /* NEG, unofficial */ + + case 0x6C: /* NEG, unofficial */ + + case 0x74: /* NEG, unofficial */ + + case 0x7C: /* NEG, unofficial */ + tStates += 8; + temp = HIGH_REGISTER(AF); + AF = ((~(AF & 0xff00) + 1) & 0xff00); /* AF = (-(AF & 0xff00) & 0xff00); */ + AF |= ((AF >> 8) & 0xa8) | (((AF & 0xff00) == 0) << 6) | negTable[temp]; + break; + + case 0x45: /* RETN */ + + case 0x55: /* RETN, unofficial */ + + case 0x5D: /* RETN, unofficial */ + + case 0x65: /* RETN, unofficial */ + + case 0x6D: /* RETN, unofficial */ + + case 0x75: /* RETN, unofficial */ + + case 0x7D: /* RETN, unofficial */ + tStates += 14; + IFF_S |= IFF_S >> 1; + CHECK_BREAK_WORD(SP); + POP(PC); + break; + + case 0x46: /* IM 0 */ + IM_S = 0; + tStates += 8; /* interrupt mode 0 */ + break; + + case 0x47: /* LD I,A */ + tStates += 9; + IR_S = (IR_S & 0xff) | (AF & ~0xff); + break; + + case 0x48: /* IN C,(C) */ + tStates += 12; + temp = s100_bus_in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x49: /* OUT (C),C */ + tStates += 12; + s100_bus_out(LOW_REGISTER(BC), LOW_REGISTER(BC)); + break; + + case 0x4a: /* ADC HL,BC */ + tStates += 15; + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL + BC + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ BC ^ sum) >> 8]; + HL = sum; + break; + + case 0x4b: /* LD BC,(nnnn) */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + BC = GET_WORD(temp); + PC += 2; + break; + + case 0x4d: /* RETI */ + tStates += 14; + IFF_S |= IFF_S >> 1; + CHECK_BREAK_WORD(SP); + POP(PC); + break; + + case 0x4f: /* LD R,A */ + tStates += 9; + IR_S = (IR_S & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x50: /* IN D,(C) */ + tStates += 12; + temp = s100_bus_in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(DE, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x51: /* OUT (C),D */ + tStates += 12; + s100_bus_out(LOW_REGISTER(BC), HIGH_REGISTER(DE)); + break; + + case 0x52: /* SBC HL,DE */ + tStates += 15; + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL - DE - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ DE ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x53: /* LD (nnnn),DE */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + PUT_WORD(temp, DE); + PC += 2; + break; + + case 0x56: /* IM 1 */ + IM_S = 1; + tStates += 8; /* interrupt mode 1 */ + break; + + case 0x57: /* LD A,I */ + tStates += 9; + AF = (AF & 0x29) | (IR_S & ~0xff) | ((IR_S >> 8) & 0x80) | (((IR_S & ~0xff) == 0) << 6) | ((IFF_S & IFF2) << 1); + break; + + case 0x58: /* IN E,(C) */ + tStates += 12; + temp = s100_bus_in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x59: /* OUT (C),E */ + tStates += 12; + s100_bus_out(LOW_REGISTER(BC), LOW_REGISTER(DE)); + break; + + case 0x5a: /* ADC HL,DE */ + tStates += 15; + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL + DE + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ DE ^ sum) >> 8]; + HL = sum; + break; + + case 0x5b: /* LD DE,(nnnn) */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + DE = GET_WORD(temp); + PC += 2; + break; + + case 0x5e: /* IM 2 */ + IM_S = 2; + tStates += 8; /* interrupt mode 2 */ + break; + + case 0x5f: /* LD A,R */ + tStates += 9; + AF = (AF & 0x29) | ((IR_S & 0xff) << 8) | (IR_S & 0x80) | + (((IR_S & 0xff) == 0) << 6) | ((IFF_S & IFF2) << 1); + break; + + case 0x60: /* IN H,(C) */ + tStates += 12; + temp = s100_bus_in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(HL, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x61: /* OUT (C),H */ + tStates += 12; + s100_bus_out(LOW_REGISTER(BC), HIGH_REGISTER(HL)); + break; + + case 0x62: /* SBC HL,HL */ + tStates += 15; + HL &= ADDRMASK; + sum = HL - HL - TSTFLAG(C); + AF = (AF & ~0xff) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80DupTable[(sum >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x63: /* LD (nnnn),HL */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + PUT_WORD(temp, HL); + PC += 2; + break; + + case 0x67: /* RRD */ + tStates += 18; + temp = s100_bus_memr(HL); + acu = HIGH_REGISTER(AF); + s100_bus_memw(HL, HIGH_DIGIT(temp) | (LOW_DIGIT(acu) << 4)); + AF = rrdrldTable[(acu & 0xf0) | LOW_DIGIT(temp)] | (AF & 1); + break; + + case 0x68: /* IN L,(C) */ + tStates += 12; + temp = s100_bus_in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x69: /* OUT (C),L */ + tStates += 12; + s100_bus_out(LOW_REGISTER(BC), LOW_REGISTER(HL)); + break; + + case 0x6a: /* ADC HL,HL */ + tStates += 15; + HL &= ADDRMASK; + sum = HL + HL + TSTFLAG(C); + AF = (AF & ~0xff) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80DupTable[sum >> 8]; + HL = sum; + break; + + case 0x6b: /* LD HL,(nnnn) */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + HL = GET_WORD(temp); + PC += 2; + break; + + case 0x6f: /* RLD */ + tStates += 18; + temp = s100_bus_memr(HL); + acu = HIGH_REGISTER(AF); + s100_bus_memw(HL, (LOW_DIGIT(temp) << 4) | LOW_DIGIT(acu)); + AF = rrdrldTable[(acu & 0xf0) | HIGH_DIGIT(temp)] | (AF & 1); + break; + + case 0x70: /* IN (C) */ + tStates += 12; + temp = s100_bus_in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(temp, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x71: /* OUT (C),0 */ + tStates += 12; + s100_bus_out(LOW_REGISTER(BC), 0); + break; + + case 0x72: /* SBC HL,SP */ + tStates += 15; + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL - SP - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ SP ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x73: /* LD (nnnn),SP */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + PUT_WORD(temp, SP); + PC += 2; + break; + + case 0x78: /* IN A,(C) */ + tStates += 12; + temp = s100_bus_in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(AF, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x79: /* OUT (C),A */ + tStates += 12; + s100_bus_out(LOW_REGISTER(BC), HIGH_REGISTER(AF)); + break; + + case 0x7a: /* ADC HL,SP */ + tStates += 15; + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL + SP + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ SP ^ sum) >> 8]; + HL = sum; + break; + + case 0x7b: /* LD SP,(nnnn) */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + SP = GET_WORD(temp); + PC += 2; + break; + + case 0xa0: /* LDI */ + tStates += 16; + CHECK_BREAK_TWO_BYTES(HL, DE); + acu = RAM_PP(HL); + PUT_BYTE_PP(DE, acu); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | + (((--BC & ADDRMASK) != 0) << 2); + break; + + case 0xa1: /* CPI */ + tStates += 16; + CHECK_BREAK_BYTE(HL); + acu = HIGH_REGISTER(AF); + temp = RAM_PP(HL); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | + ((sum - ((cbits >> 4) & 1)) & 8) | + ((--BC & ADDRMASK) != 0) << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + +/* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + INI/INIR/IND/INDR use the C flag in stead of the L register. There is a + catch though, because not the value of C is used, but C + 1 if it's INI/INIR or + C - 1 if it's IND/INDR. So, first of all INI/INIR: + HF and CF Both set if ((HL) + ((C + 1) & 255) > 255) + PF The parity of (((HL) + ((C + 1) & 255)) & 7) xor B) */ + case 0xa2: /* INI */ + tStates += 16; + CHECK_BREAK_BYTE(HL); + acu = s100_bus_in(LOW_REGISTER(BC)); + s100_bus_memw(HL, acu); + ++HL; + BC -= 0x100; + INOUTFLAGS_NONZERO((LOW_REGISTER(BC) + 1) & 0xff); + break; + +/* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + And now the for OUTI/OTIR/OUTD/OTDR instructions. Take state of the L + after the increment or decrement of HL; add the value written to the I/O port + to; call that k for now. If k > 255, then the CF and HF flags are set. The PF + flags is set like the parity of k bitwise and'ed with 7, bitwise xor'ed with B. + HF and CF Both set if ((HL) + L > 255) + PF The parity of ((((HL) + L) & 7) xor B) */ + case 0xa3: /* OUTI */ + tStates += 16; + CHECK_BREAK_BYTE(HL); + acu = s100_bus_memr(HL); + s100_bus_out(LOW_REGISTER(BC), acu); + ++HL; + BC -= 0x100; + INOUTFLAGS_NONZERO(LOW_REGISTER(HL)); + break; + + case 0xa8: /* LDD */ + tStates += 16; + CHECK_BREAK_TWO_BYTES(HL, DE); + acu = RAM_MM(HL); + PUT_BYTE_MM(DE, acu); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | + (((--BC & ADDRMASK) != 0) << 2); + break; + + case 0xa9: /* CPD */ + tStates += 16; + CHECK_BREAK_BYTE(HL); + acu = HIGH_REGISTER(AF); + temp = RAM_MM(HL); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | + ((sum - ((cbits >> 4) & 1)) & 8) | + ((--BC & ADDRMASK) != 0) << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + +/* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + INI/INIR/IND/INDR use the C flag in stead of the L register. There is a + catch though, because not the value of C is used, but C + 1 if it's INI/INIR or + C - 1 if it's IND/INDR. And last IND/INDR: + HF and CF Both set if ((HL) + ((C - 1) & 255) > 255) + PF The parity of (((HL) + ((C - 1) & 255)) & 7) xor B) */ + case 0xaa: /* IND */ + tStates += 16; + CHECK_BREAK_BYTE(HL); + acu = s100_bus_in(LOW_REGISTER(BC)); + s100_bus_memw(HL, acu); + --HL; + BC -= 0x100; + INOUTFLAGS_NONZERO((LOW_REGISTER(BC) - 1) & 0xff); + break; + + case 0xab: /* OUTD */ + tStates += 16; + CHECK_BREAK_BYTE(HL); + acu = s100_bus_memr(HL); + s100_bus_out(LOW_REGISTER(BC), acu); + --HL; + BC -= 0x100; + INOUTFLAGS_NONZERO(LOW_REGISTER(HL)); + break; + + case 0xb0: /* LDIR */ + tStates -= 5; + BC &= ADDRMASK; + if (BC == 0) + BC = 0x10000; + do { + tStates += 21; + INCR(2); + CHECK_BREAK_TWO_BYTES(HL, DE); + acu = RAM_PP(HL); + PUT_BYTE_PP(DE, acu); + } while (--BC); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); + break; + + case 0xb1: /* CPIR */ + tStates -= 5; + acu = HIGH_REGISTER(AF); + BC &= ADDRMASK; + if (BC == 0) + BC = 0x10000; + do { + tStates += 21; + INCR(1); + CHECK_BREAK_BYTE(HL); + temp = RAM_PP(HL); + op = --BC != 0; + sum = acu - temp; + } while (op && sum != 0); + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | + (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | + op << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + + case 0xb2: /* INIR */ + tStates -= 5; + temp = HIGH_REGISTER(BC); + if (temp == 0) + temp = 0x100; + do { + tStates += 21; + INCR(1); + CHECK_BREAK_BYTE(HL); + acu = s100_bus_in(LOW_REGISTER(BC)); + s100_bus_memw(HL, acu); + ++HL; + } while (--temp); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO((LOW_REGISTER(BC) + 1) & 0xff); + break; + + case 0xb3: /* OTIR */ + tStates -= 5; + temp = HIGH_REGISTER(BC); + if (temp == 0) + temp = 0x100; + do { + tStates += 21; + INCR(1); + CHECK_BREAK_BYTE(HL); + acu = s100_bus_memr(HL); + s100_bus_out(LOW_REGISTER(BC), acu); + ++HL; + } while (--temp); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO(LOW_REGISTER(HL)); + break; + + case 0xb8: /* LDDR */ + tStates -= 5; + BC &= ADDRMASK; + if (BC == 0) + BC = 0x10000; + do { + tStates += 21; + INCR(2); + CHECK_BREAK_TWO_BYTES(HL, DE); + acu = RAM_MM(HL); + PUT_BYTE_MM(DE, acu); + } while (--BC); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); + break; + + case 0xb9: /* CPDR */ + tStates -= 5; + acu = HIGH_REGISTER(AF); + BC &= ADDRMASK; + if (BC == 0) + BC = 0x10000; + do { + tStates += 21; + INCR(1); + CHECK_BREAK_BYTE(HL); + temp = RAM_MM(HL); + op = --BC != 0; + sum = acu - temp; + } while (op && sum != 0); + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | + (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | + op << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) + AF &= ~8; + break; + + case 0xba: /* INDR */ + tStates -= 5; + temp = HIGH_REGISTER(BC); + if (temp == 0) + temp = 0x100; + do { + tStates += 21; + INCR(1); + CHECK_BREAK_BYTE(HL); + acu = s100_bus_in(LOW_REGISTER(BC)); + s100_bus_memw(HL, acu); + --HL; + } while (--temp); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO((LOW_REGISTER(BC) - 1) & 0xff); + break; + + case 0xbb: /* OTDR */ + tStates -= 5; + temp = HIGH_REGISTER(BC); + if (temp == 0) + temp = 0x100; + do { + tStates += 21; + INCR(1); + CHECK_BREAK_BYTE(HL); + acu = s100_bus_memr(HL); + s100_bus_out(LOW_REGISTER(BC), acu); + --HL; + } while (--temp); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO(LOW_REGISTER(HL)); + break; + + default: /* ignore ED and following byte */ + CHECK_CPU_Z80; + } + break; + + case 0xee: /* XOR nn */ + tStates += 7; /* XRI nn 7 */ + AF = xororTable[((AF >> 8) ^ RAM_PP(PC)) & 0xff]; + break; + + case 0xef: /* RST 28H */ + tStates += 11; /* RST 5 11 */ + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PC = 0x28; + break; + + case 0xf0: /* RET P */ + if (TSTFLAG(S)) { + tStates += 5; /* RP 5 */ + } else { + CHECK_BREAK_WORD(SP); + POP(PC); + tStates += 11; /* RP 11 */ + } + break; + + case 0xf1: /* POP AF */ + tStates += 10; /* POP PSW 10 */ + CHECK_BREAK_WORD(SP); + POP(AF); + break; + + case 0xf2: /* JP P,nnnn */ + JPC(!TSTFLAG(S)); /* also updates tStates */ + break; + + case 0xf3: /* DI */ + tStates += 4; /* DI 4 */ + IFF_S = 0; + break; + + case 0xf4: /* CALL P,nnnn */ + CALLC(!TSTFLAG(S)); /* also updates tStates */ + break; + + case 0xf5: /* PUSH AF */ + tStates += 11; /* PUSH PSW 11 */ + CHECK_BREAK_WORD(SP - 2); + PUSH(z80_chiptype == CHIP_TYPE_8080 ? (AF & ~0x28) | 0x02 : AF); + break; + + case 0xf6: /* OR nn */ + tStates += 7; /* ORI nn 7 */ + AF = xororTable[((AF >> 8) | RAM_PP(PC)) & 0xff]; + break; + + case 0xf7: /* RST 30H */ + tStates += 11; /* RST 6 11 */ + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PC = 0x30; + break; + + case 0xf8: /* RET M */ + if (TSTFLAG(S)) { + CHECK_BREAK_WORD(SP); + POP(PC); + tStates += 11; /* RM 11 */ + } else { + tStates += 5; /* RM 5 */ + } + break; + + case 0xf9: /* LD SP,HL */ + tStates += (z80_chiptype == CHIP_TYPE_8080 ? 5 : 6); /* SPHL 5 */ + SP = HL; + break; + + case 0xfa: /* JP M,nnnn */ + JPC(TSTFLAG(S)); /* also updates tStates */ + break; + + case 0xfb: /* EI */ + tStates += 4; /* EI 4 */ + IFF_S = (IFF1 | IFF2); + break; + + case 0xfc: /* CALL M,nnnn */ + CALLC(TSTFLAG(S)); /* also updates tStates */ + break; + + case 0xfd: /* FD prefix */ + if (z80_chiptype == CHIP_TYPE_8080) { + if (z80_unit.flags & UNIT_Z80_OPSTOP) { + reason = STOP_OPCODE; + goto end_decode; + } else { + CALLC(1); /* also updates tStates */ + break; + } + } + + INCR(1); + switch (RAM_PP(PC)) { + + case 0x09: /* ADD IY,BC */ + tStates += 15; + IY &= ADDRMASK; + BC &= ADDRMASK; + sum = IY + BC; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ BC ^ sum) >> 8]; + IY = sum; + break; + + case 0x19: /* ADD IY,DE */ + tStates += 15; + IY &= ADDRMASK; + DE &= ADDRMASK; + sum = IY + DE; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ DE ^ sum) >> 8]; + IY = sum; + break; + + case 0x21: /* LD IY,nnnn */ + tStates += 14; + IY = GET_WORD(PC); + PC += 2; + break; + + case 0x22: /* LD (nnnn),IY */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + PUT_WORD(temp, IY); + PC += 2; + break; + + case 0x23: /* INC IY */ + tStates += 10; + ++IY; + break; + + case 0x24: /* INC IYH */ + tStates += 9; + IY += 0x100; + AF = (AF & ~0xfe) | incZ80Table[HIGH_REGISTER(IY)]; + break; + + case 0x25: /* DEC IYH */ + tStates += 9; + IY -= 0x100; + AF = (AF & ~0xfe) | decZ80Table[HIGH_REGISTER(IY)]; + break; + + case 0x26: /* LD IYH,nn */ + tStates += 9; + SET_HIGH_REGISTER(IY, RAM_PP(PC)); + break; + + case 0x29: /* ADD IY,IY */ + tStates += 15; + IY &= ADDRMASK; + sum = IY + IY; + AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + IY = sum; + break; + + case 0x2a: /* LD IY,(nnnn) */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + IY = GET_WORD(temp); + PC += 2; + break; + + case 0x2b: /* DEC IY */ + tStates += 10; + --IY; + break; + + case 0x2c: /* INC IYL */ + tStates += 9; + temp = LOW_REGISTER(IY) + 1; + SET_LOW_REGISTER(IY, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x2d: /* DEC IYL */ + tStates += 9; + temp = LOW_REGISTER(IY) - 1; + SET_LOW_REGISTER(IY, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x2e: /* LD IYL,nn */ + tStates += 9; + SET_LOW_REGISTER(IY, RAM_PP(PC)); + break; + + case 0x34: /* INC (IY+dd) */ + tStates += 23; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = s100_bus_memr(adr) + 1; + s100_bus_memw(adr, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x35: /* DEC (IY+dd) */ + tStates += 23; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = s100_bus_memr(adr) - 1; + s100_bus_memw(adr, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x36: /* LD (IY+dd),nn */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, RAM_PP(PC)); + break; + + case 0x39: /* ADD IY,SP */ + tStates += 15; + IY &= ADDRMASK; + SP &= ADDRMASK; + sum = IY + SP; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ SP ^ sum) >> 8]; + IY = sum; + break; + + case 0x44: /* LD B,IYH */ + tStates += 9; + SET_HIGH_REGISTER(BC, HIGH_REGISTER(IY)); + break; + + case 0x45: /* LD B,IYL */ + tStates += 9; + SET_HIGH_REGISTER(BC, LOW_REGISTER(IY)); + break; + + case 0x46: /* LD B,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(BC, s100_bus_memr(adr)); + break; + + case 0x4c: /* LD C,IYH */ + tStates += 9; + SET_LOW_REGISTER(BC, HIGH_REGISTER(IY)); + break; + + case 0x4d: /* LD C,IYL */ + tStates += 9; + SET_LOW_REGISTER(BC, LOW_REGISTER(IY)); + break; + + case 0x4e: /* LD C,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_LOW_REGISTER(BC, s100_bus_memr(adr)); + break; + + case 0x54: /* LD D,IYH */ + tStates += 9; + SET_HIGH_REGISTER(DE, HIGH_REGISTER(IY)); + break; + + case 0x55: /* LD D,IYL */ + tStates += 9; + SET_HIGH_REGISTER(DE, LOW_REGISTER(IY)); + break; + + case 0x56: /* LD D,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(DE, s100_bus_memr(adr)); + break; + + case 0x5c: /* LD E,IYH */ + tStates += 9; + SET_LOW_REGISTER(DE, HIGH_REGISTER(IY)); + break; + + case 0x5d: /* LD E,IYL */ + tStates += 9; + SET_LOW_REGISTER(DE, LOW_REGISTER(IY)); + break; + + case 0x5e: /* LD E,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_LOW_REGISTER(DE, s100_bus_memr(adr)); + break; + + case 0x60: /* LD IYH,B */ + tStates += 9; + SET_HIGH_REGISTER(IY, HIGH_REGISTER(BC)); + break; + + case 0x61: /* LD IYH,C */ + tStates += 9; + SET_HIGH_REGISTER(IY, LOW_REGISTER(BC)); + break; + + case 0x62: /* LD IYH,D */ + tStates += 9; + SET_HIGH_REGISTER(IY, HIGH_REGISTER(DE)); + break; + + case 0x63: /* LD IYH,E */ + tStates += 9; + SET_HIGH_REGISTER(IY, LOW_REGISTER(DE)); + break; + + case 0x64: /* LD IYH,IYH */ + tStates += 9; + break; + + case 0x65: /* LD IYH,IYL */ + tStates += 9; + SET_HIGH_REGISTER(IY, LOW_REGISTER(IY)); + break; + + case 0x66: /* LD H,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(HL, s100_bus_memr(adr)); + break; + + case 0x67: /* LD IYH,A */ + tStates += 9; + SET_HIGH_REGISTER(IY, HIGH_REGISTER(AF)); + break; + + case 0x68: /* LD IYL,B */ + tStates += 9; + SET_LOW_REGISTER(IY, HIGH_REGISTER(BC)); + break; + + case 0x69: /* LD IYL,C */ + tStates += 9; + SET_LOW_REGISTER(IY, LOW_REGISTER(BC)); + break; + + case 0x6a: /* LD IYL,D */ + tStates += 9; + SET_LOW_REGISTER(IY, HIGH_REGISTER(DE)); + break; + + case 0x6b: /* LD IYL,E */ + tStates += 9; + SET_LOW_REGISTER(IY, LOW_REGISTER(DE)); + break; + + case 0x6c: /* LD IYL,IYH */ + tStates += 9; + SET_LOW_REGISTER(IY, HIGH_REGISTER(IY)); + break; + + case 0x6d: /* LD IYL,IYL */ + tStates += 9; + break; + + case 0x6e: /* LD L,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_LOW_REGISTER(HL, s100_bus_memr(adr)); + break; + + case 0x6f: /* LD IYL,A */ + tStates += 9; + SET_LOW_REGISTER(IY, HIGH_REGISTER(AF)); + break; + + case 0x70: /* LD (IY+dd),B */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (IY+dd),C */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (IY+dd),D */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (IY+dd),E */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (IY+dd),H */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (IY+dd),L */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, LOW_REGISTER(HL)); + break; + + case 0x77: /* LD (IY+dd),A */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + s100_bus_memw(adr, HIGH_REGISTER(AF)); + break; + + case 0x7c: /* LD A,IYH */ + tStates += 9; + SET_HIGH_REGISTER(AF, HIGH_REGISTER(IY)); + break; + + case 0x7d: /* LD A,IYL */ + tStates += 9; + SET_HIGH_REGISTER(AF, LOW_REGISTER(IY)); + break; + + case 0x7e: /* LD A,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(AF, s100_bus_memr(adr)); + break; + + case 0x84: /* ADD A,IYH */ + tStates += 9; + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x85: /* ADD A,IYL */ + tStates += 9; + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x86: /* ADD A,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = s100_bus_memr(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8c: /* ADC A,IYH */ + tStates += 9; + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8d: /* ADC A,IYL */ + tStates += 9; + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8e: /* ADC A,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = s100_bus_memr(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x96: /* SUB (IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = s100_bus_memr(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x94: /* SUB IYH */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9c: /* SBC A,IYH */ + tStates += 9; + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x95: /* SUB IYL */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9d: /* SBC A,IYL */ + tStates += 9; + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x9e: /* SBC A,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = s100_bus_memr(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xa4: /* AND IYH */ + tStates += 9; + AF = andTable[((AF & IY) >> 8) & 0xff]; + break; + + case 0xa5: /* AND IYL */ + tStates += 9; + AF = andTable[((AF >> 8) & IY) & 0xff]; + break; + + case 0xa6: /* AND (IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + AF = andTable[((AF >> 8) & s100_bus_memr(adr)) & 0xff]; + break; + + case 0xac: /* XOR IYH */ + tStates += 9; + AF = xororTable[((AF ^ IY) >> 8) & 0xff]; + break; + + case 0xad: /* XOR IYL */ + tStates += 9; + AF = xororTable[((AF >> 8) ^ IY) & 0xff]; + break; + + case 0xae: /* XOR (IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + AF = xororTable[((AF >> 8) ^ s100_bus_memr(adr)) & 0xff]; + break; + + case 0xb4: /* OR IYH */ + tStates += 9; + AF = xororTable[((AF | IY) >> 8) & 0xff]; + break; + + case 0xb5: /* OR IYL */ + tStates += 9; + AF = xororTable[((AF >> 8) | IY) & 0xff]; + break; + + case 0xb6: /* OR (IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + AF = xororTable[((AF >> 8) | s100_bus_memr(adr)) & 0xff]; + break; + + case 0xbc: /* CP IYH */ + tStates += 9; + temp = HIGH_REGISTER(IY); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbd: /* CP IYL */ + tStates += 9; + temp = LOW_REGISTER(IY); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbe: /* CP (IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = s100_bus_memr(adr); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xcb: /* CB prefix */ + adr = IY + (int8) RAM_PP(PC); + switch ((op = s100_bus_memr(PC)) & 7) { + + case 0: + ++PC; + acu = HIGH_REGISTER(BC); + break; + + case 1: + ++PC; + acu = LOW_REGISTER(BC); + break; + + case 2: + ++PC; + acu = HIGH_REGISTER(DE); + break; + + case 3: + ++PC; + acu = LOW_REGISTER(DE); + break; + + case 4: + ++PC; + acu = HIGH_REGISTER(HL); + break; + + case 5: + ++PC; + acu = LOW_REGISTER(HL); + break; + + case 6: + CHECK_BREAK_BYTE(adr); + ++PC; + acu = s100_bus_memr(adr); + break; + + case 7: + ++PC; + acu = HIGH_REGISTER(AF); + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + tStates += 23; + switch (op & 0x38) { + + case 0x00: /* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg3; + + case 0x08: /* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg3; + + case 0x10: /* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg3; + + case 0x18: /* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg3; + + case 0x20: /* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg3; + + case 0x28: /* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg3; + + case 0x30: /* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg3; + + case 0x38: /* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg3: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + /* !!cbits == 0 if cbits == 0 !!cbits == 1 if cbits > 0 */ + } + break; + + case 0x40: /* BIT */ + tStates += 20; + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else + AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) + AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + tStates += 23; + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + tStates += 23; + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + s100_bus_memw(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xe1: /* POP IY */ + tStates += 14; + CHECK_BREAK_WORD(SP); + POP(IY); + break; + + case 0xe3: /* EX (SP),IY */ + tStates += 23; + CHECK_BREAK_WORD(SP); + temp = IY; + POP(IY); + PUSH(temp); + break; + + case 0xe5: /* PUSH IY */ + tStates += 15; + CHECK_BREAK_WORD(SP - 2); + PUSH(IY); + break; + + case 0xe9: /* JP (IY) */ + tStates += 8; + PC = IY; + break; + + case 0xf9: /* LD SP,IY */ + tStates += 10; + SP = IY; + break; + + default: /* ignore FD */ + CHECK_CPU_Z80; + PC--; + } + break; + + case 0xfe: /* CP nn */ + tStates += 7; /* CPI nn 7 */ + temp = RAM_PP(PC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + if (z80_chiptype == CHIP_TYPE_8080) + AF ^= 0x10; + break; + + case 0xff: /* RST 38H */ + tStates += 11; /* RST 7 11 */ + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PC = 0x38; + } + + /* + ** Save in instruction history ring buffer + */ + if (hst_lnt && ((z80_chiptype == CHIP_TYPE_8080) || (z80_chiptype == CHIP_TYPE_Z80))) { + hst[hst_p].valid = 1; + hst[hst_p].pc = PCX; + hst[hst_p].sp = SP; + hst[hst_p].af = AF; + hst[hst_p].bc = BC; + hst[hst_p].de = DE; + hst[hst_p].hl = HL; + hst[hst_p].af1 = AF1_S; + hst[hst_p].bc1 = BC1_S; + hst[hst_p].de1 = DE1_S; + hst[hst_p].hl1 = HL1_S; + hst[hst_p].ix = IX; + hst[hst_p].iy = IY; + + for (i = 0; i < INST_MAX_BYTES; i++) { + hst[hst_p].op[i] = s100_bus_memr(PCX + i); + } + + if (++hst_p == hst_lnt) { + hst_p = 0; + } + } + + PC &= ADDRMASK; /* reestablish invariant */ + sim_interval--; + } + + end_decode: + + /* simulation halted */ + PC_S = ((reason == STOP_OPCODE) || (reason == STOP_MEM)) ? PCX : (PC & ADDRMASK); + AF_S = AF; + BC_S = BC; + DE_S = DE; + HL_S = HL; + IX_S = IX; + IY_S = IY; + SP_S = SP; + + executedTStates = tStates; + + return reason; +} + +/* + * This sequence of instructions is a mix that mimics + * a reasonable instruction set that is a close estimate + * to the calibrated result. + */ + +static const char *z80_clock_precalibrate_commands[] = { + "-m 100 LXI H,200H", + "-m 103 MVI B,0", + "-m 105 DCR B", + "-m 106 MOV M,B", + "-m 107 INX H", + "-m 108 JNZ 0105H", + "-m 10B JMP 0100H", + "PC 100", + NULL}; + +/* reset routine */ + +static t_stat z80_reset(DEVICE *dptr) +{ + if (dptr->flags & DEV_DIS) { + poc = TRUE; + } + else { + if (poc) { /* First time reset? */ + poc = FALSE; + } + } + + AF_S = AF1_S = 0; + BC_S = DE_S = HL_S = 0; + BC1_S = DE1_S = HL1_S = 0; + IR_S = IX_S = IY_S = SP_S = PC_S = 0; + IM_S = IFF_S = 0; /* Set IM0, reset IFF1 and IFF2 */ + sim_clock_precalibrate_commands = z80_clock_precalibrate_commands; + sim_brk_types = (SWMASK('E') | SWMASK('I') | SWMASK('M')); + sim_brk_dflt = SWMASK('E'); + + return SCPE_OK; +} + +t_bool z80_is_pc_a_subroutine_call (t_addr **ret_addrs) +{ + static t_addr returns[2] = {0, 0}; + + switch (z80_chiptype) { + case CHIP_TYPE_8080: + case CHIP_TYPE_Z80: + switch (s100_bus_memr(PC_S)) { + case 0xc4: /* CALL NZ,nnnn */ + case 0xcc: /* CALL Z,nnnn */ + case 0xcd: /* CALL nnnn */ + case 0xd4: /* CALL NC,nnnn */ + case 0xdc: /* CALL C,nnnn */ + case 0xe4: /* CALL PO,nnnn */ + case 0xec: /* CALL PE,nnnn */ + case 0xf4: /* CALL P,nnnn */ + case 0xfc: /* CALL M,nnnn */ + returns[0] = PC_S + 3; + *ret_addrs = returns; + return TRUE; + + default: + return FALSE; + } + break; + + default: + break; + } + + return FALSE; +} + +static t_stat chip_show(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + fprintf(st, z80_unit.flags & UNIT_Z80_OPSTOP ? "ITRAP, " : "NOITRAP, "); + if ((z80_chiptype >= 0) && (z80_chiptype < NUM_CHIP_TYPE)) { + fprintf(st, "%s", z80_mod[z80_chiptype].mstring); + } + + return SCPE_OK; +} + +static t_stat z80_show(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + if (z80_unit.flags & UNIT_CPU_VERBOSE) { + fprintf(st, "VERBOSE"); + } + + return SCPE_OK; +} + +static t_stat z80_set_chiptype(UNIT *uptr, int32 value, CONST char *cptr, void *desc) +{ + if (z80_chiptype != value) { + if (z80_unit.flags & UNIT_CPU_VERBOSE) { + sim_printf("CPU changed from %s to %s\n", cpu_get_chipname(z80_chiptype), cpu_get_chipname(value)); + } + + cpu_set_chiptype(value); + } + + return SCPE_OK; +} + +static t_stat z80_set_hist(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + uint32 i, lnt; + t_stat r; + + if ((z80_chiptype >= 0) && (z80_chiptype != CHIP_TYPE_8080) && (z80_chiptype != CHIP_TYPE_Z80)) { + sim_printf("History not supported for chiptype: %s\n", + (z80_chiptype < NUM_CHIP_TYPE) ? z80_mod[z80_chiptype].mstring : "????"); + return SCPE_NOFNC; + } + + /* + ** If cptr is NULL, reset ring buffer ("SET HISTORY") + */ + if (cptr == NULL) { + if (hst == NULL) { + sim_printf("History buffer not enabled.\n"); + return SCPE_NOFNC; + } + + for (i = 0; i < hst_lnt; i++) { + hst[i].valid = 0; + } + + hst_p = 0; + + return SCPE_OK; + } + + /* + ** Enable/Resize ring buffer ("SET HISTORY=") + */ + lnt = (uint32) get_uint (cptr, 10, HIST_MAX, &r); + + if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) { + sim_printf("History buffer minimum/maximum size: %d/%d\n", HIST_MIN, HIST_MAX); + return SCPE_ARG; + } + + /* + ** Delete old history buffer + */ + if (hst!=NULL) { + free (hst); + hst_lnt = 0; + hst = NULL; + } + + hst_p = 0; + + /* + ** If a length was specified, allocate new buffer ("SET HISTORY=" where n>0) + */ + if (lnt) { + hst = (insthist_t *) calloc (lnt, sizeof (insthist_t)); + if (hst == NULL) { + return SCPE_MEM; + } + hst_lnt = lnt; + } + + return SCPE_OK; +} + +t_stat z80_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + int32 k, di, lnt; + CONST char *cptr = (CONST char *) desc; + t_stat r; + insthist_t *h; + + if ((z80_chiptype != CHIP_TYPE_8080) && (z80_chiptype != CHIP_TYPE_Z80)) { + sim_printf("History not supported for chiptype: %s\n", + (0 <= z80_chiptype) && (z80_chiptype < NUM_CHIP_TYPE) ? + z80_mod[z80_chiptype].mstring : "????"); + return SCPE_NOFNC; + } + + if (hst_lnt == 0) { + return SCPE_NOFNC; /* enabled? */ + } + + if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + + if ((r != SCPE_OK) || (lnt == 0)) { + return SCPE_ARG; + } + } else { + lnt = hst_lnt; + } + + di = hst_p - lnt; + + if (di < 0) di = di + hst_lnt; + + for (k = 0; k < lnt; k++) { + if (stop_cpu) { /* Control-C (SIGINT) */ + stop_cpu = FALSE; + break; /* abandon remaining output */ + } + h = &hst[(di++) % hst_lnt]; + + if (h->valid) { /* valid entry? */ + if (z80_chiptype == CHIP_TYPE_8080) { + /* + ** Use DDT output: + ** CfZfMfEfIf A=bb B=dddd D=dddd H=dddd S=dddd P=dddd inst + */ + fprintf(st, "Z80: C%dZ%dM%dE%dI%d A=%02X B=%04X D=%04X H=%04X S=%04X P=%04X ", + TSTFLAG2(h->af, C), + TSTFLAG2(h->af, Z), + TSTFLAG2(h->af, S), + TSTFLAG2(h->af, P), + TSTFLAG2(h->af, H), + HIGH_REGISTER(h->af), h->bc, h->de, h->hl, h->sp, h->pc); + fprint_sym (st, h->pc, h->op, &z80_unit, SWMASK ('M')); + fprintf(st, "\n"); + } else { /* Z80 */ + /* + ** Use DDT/Z output: + */ + fprintf(st, "Z80: C%dZ%dS%dV%dH%dN%d A =%02X BC =%04X DE =%04X HL =%04X S =%04X P =%04X ", + TSTFLAG2(h->af, C), + TSTFLAG2(h->af, Z), + TSTFLAG2(h->af, S), + TSTFLAG2(h->af, P), + TSTFLAG2(h->af, H), + TSTFLAG2(h->af, N), + HIGH_REGISTER(h->af), h->bc, h->de, h->hl, h->sp, h->pc); + fprint_sym (st, h->pc, h->op, &z80_unit, SWMASK ('M')); + fprintf(st, "\n"); + fprintf(st, " A'=%02X BC'=%04X DE'=%04X HL'=%04X IX=%04X IY=%04X ", + HIGH_REGISTER(h->af1), h->bc1, h->de1, h->hl1, h->ix, h->iy); + fprintf(st, "\n"); + } + } + } + + return SCPE_OK; +} + +t_value z80_pc_value (void) { + return (t_value) PCX; +} + +t_stat z80_cmd_reg(int32 flag, CONST char *cptr) +{ + t_value op[INST_MAX_BYTES]; + int i; + + if (z80_chiptype != CHIP_TYPE_8080 && z80_chiptype != CHIP_TYPE_Z80) { + sim_printf("REG requires 8080 or Z80 CPU\n"); + return SCPE_NOFNC; + } + + for (i = 0; i < INST_MAX_BYTES; i++) { + op[i] = s100_bus_memr(PC_S + i); + } + + if (z80_chiptype == CHIP_TYPE_8080) { + /* + ** Use DDT output: + ** CfZfMfEfIf A=bb B=dddd D=dddd H=dddd S=dddd P=dddd inst + */ + sim_printf("C%dZ%dM%dE%dI%d A=%02X B=%04X D=%04X H=%04X S=%04X P=%04X ", + TSTFLAG2(AF_S, C), + TSTFLAG2(AF_S, Z), + TSTFLAG2(AF_S, S), + TSTFLAG2(AF_S, P), + TSTFLAG2(AF_S, H), + HIGH_REGISTER(AF_S), (uint16) BC_S, (uint16) DE_S, (uint16) HL_S, (uint16) SP_S, (uint16) PC_S); + fprint_sym (stdout, PC_S, op, &z80_unit, SWMASK ('M')); + } else { /* Z80 */ + /* + ** Use DDT/Z output: + */ + sim_printf("C%dZ%dS%dV%dH%dN%d A =%02X BC =%04X DE =%04X HL =%04X S =%04X P =%04X ", + TSTFLAG2(AF_S, C), + TSTFLAG2(AF_S, Z), + TSTFLAG2(AF_S, S), + TSTFLAG2(AF_S, P), + TSTFLAG2(AF_S, H), + TSTFLAG2(AF_S, N), + HIGH_REGISTER(AF_S), (uint16) BC_S, (uint16) DE_S, (uint16) HL_S, (uint16) SP_S, (uint16) PC_S); + fprint_sym (stdout, PC_S, op, &z80_unit, SWMASK ('M')); + sim_printf("\n"); + sim_printf(" A'=%02X BC'=%04X DE'=%04X HL'=%04X IX=%04X IY=%04X", + HIGH_REGISTER(AF1_S), (uint16) BC1_S, (uint16) DE1_S, (uint16) HL1_S, (uint16) IX_S, (uint16) IY_S); + } + + sim_printf("\n"); + + return SCPE_OK | SCPE_NOMESSAGE; +} + +static const char *const Mnemonics8080[] = { +/* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ + "NOP", "LXI B,#h", "STAX B", "INX B", "INR B", "DCR B", "MVI B,*h", "RLC", /* 00-07 */ + "_NOP", "DAD B", "LDAX B", "DCX B", "INR C", "DCR C", "MVI C,*h", "RRC", /* 08-0f */ + "_NOP", "LXI D,#h", "STAX D", "INX D", "INR D", "DCR D", "MVI D,*h", "RAL", /* 10-17 */ + "_NOP", "DAD D", "LDAX D", "DCX D", "INR E", "DCR E", "MVI E,*h", "RAR", /* 18-1f */ + "_NOP", "LXI H,#h", "SHLD #h", "INX H", "INR H", "DCR H", "MVI H,*h", "DAA", /* 20-27 */ + "_NOP", "DAD H", "LHLD #h", "DCX H", "INR L", "DCR L", "MVI L,*h", "CMA", /* 28-2f */ + "_NOP", "LXI SP,#h", "STA #h", "INX SP", "INR M", "DCR M", "MVI M,*h", "STC", /* 30-37 */ + "_NOP", "DAD SP", "LDA #h", "DCX SP", "INR A", "DCR A", "MVI A,*h", "CMC", /* 38-3f */ + "MOV B,B", "MOV B,C", "MOV B,D", "MOV B,E", "MOV B,H", "MOV B,L", "MOV B,M", "MOV B,A", /* 40-47 */ + "MOV C,B", "MOV C,C", "MOV C,D", "MOV C,E", "MOV C,H", "MOV C,L", "MOV C,M", "MOV C,A", /* 48-4f */ + "MOV D,B", "MOV D,C", "MOV D,D", "MOV D,E", "MOV D,H", "MOV D,L", "MOV D,M", "MOV D,A", /* 50-57 */ + "MOV E,B", "MOV E,C", "MOV E,D", "MOV E,E", "MOV E,H", "MOV E,L", "MOV E,M", "MOV E,A", /* 58-5f */ + "MOV H,B", "MOV H,C", "MOV H,D", "MOV H,E", "MOV H,H", "MOV H,L", "MOV H,M", "MOV H,A", /* 60-67 */ + "MOV L,B", "MOV L,C", "MOV L,D", "MOV L,E", "MOV L,H", "MOV L,L", "MOV L,M", "MOV L,A", /* 68-6f */ + "MOV M,B", "MOV M,C", "MOV M,D", "MOV M,E", "MOV M,H", "MOV M,L", "HLT", "MOV M,A", /* 70-77 */ + "MOV A,B", "MOV A,C", "MOV A,D", "MOV A,E", "MOV A,H", "MOV A,L", "MOV A,M", "MOV A,A", /* 78-7f */ + "ADD B", "ADD C", "ADD D", "ADD E", "ADD H", "ADD L", "ADD M", "ADD A", /* 80-87 */ + "ADC B", "ADC C", "ADC D", "ADC E", "ADC H", "ADC L", "ADC M", "ADC A", /* 88-8f */ + "SUB B", "SUB C", "SUB D", "SUB E", "SUB H", "SUB L", "SUB M", "SUB A", /* 90-97 */ + "SBB B", "SBB C", "SBB D", "SBB E", "SBB H", "SBB L", "SBB M", "SBB A", /* 98-9f */ + "ANA B", "ANA C", "ANA D", "ANA E", "ANA H", "ANA L", "ANA M", "ANA A", /* a0-a7 */ + "XRA B", "XRA C", "XRA D", "XRA E", "XRA H", "XRA L", "XRA M", "XRA A", /* a8-af */ + "ORA B", "ORA C", "ORA D", "ORA E", "ORA H", "ORA L", "ORA M", "ORA A", /* b0-b7 */ + "CMP B", "CMP C", "CMP D", "CMP E", "CMP H", "CMP L", "CMP M", "CMP A", /* b8-bf */ + "RNZ", "POP B", "JNZ #h", "JMP #h", "CNZ #h", "PUSH B", "ADI *h", "RST 0", /* c0-c7 */ + "RZ", "RET", "JZ #h", "_JMP #h", "CZ #h", "CALL #h", "ACI *h", "RST 1", /* c8-cf */ + "RNC", "POP D", "JNC #h", "OUT *h", "CNC #h", "PUSH D", "SUI *h", "RST 2", /* d0-d7 */ + "RC", "_RET", "JC #h", "IN *h", "CC #h", "_CALL #h", "SBI *h", "RST 3", /* d8-df */ + "RPO", "POP H", "JPO #h", "XTHL", "CPO #h", "PUSH H", "ANI *h", "RST 4", /* e0-e7 */ + "RPE", "PCHL", "JPE #h", "XCHG", "CPE #h", "_CALL #h", "XRI *h", "RST 5", /* e8-ef */ + "RP", "POP PSW", "JP #h", "DI", "CP #h", "PUSH PSW", "ORI *h", "RST 6", /* f0-f7 */ + "RM", "SPHL", "JM #h", "EI", "CM #h", "_CALL #h", "CPI *h", "RST 7" /* f8-ff */ +}; + +static const char *const MnemonicsZ80[256] = { +/* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ + "NOP", "LD BC,#h", "LD (BC),A", "INC BC", "INC B", "DEC B", "LD B,*h", "RLCA", /* 00-07 */ + "EX AF,AF'", "ADD HL,BC", "LD A,(BC)", "DEC BC", "INC C", "DEC C", "LD C,*h", "RRCA", /* 08-0f */ + "DJNZ $h", "LD DE,#h", "LD (DE),A", "INC DE", "INC D", "DEC D", "LD D,*h", "RLA", /* 10-17 */ + "JR $h", "ADD HL,DE", "LD A,(DE)", "DEC DE", "INC E", "DEC E", "LD E,*h", "RRA", /* 18-1f */ + "JR NZ,$h", "LD HL,#h", "LD (#h),HL", "INC HL", "INC H", "DEC H", "LD H,*h", "DAA", /* 20-27 */ + "JR Z,$h", "ADD HL,HL", "LD HL,(#h)", "DEC HL", "INC L", "DEC L", "LD L,*h", "CPL", /* 28-2f */ + "JR NC,$h", "LD SP,#h", "LD (#h),A", "INC SP", "INC (HL)", "DEC (HL)", "LD (HL),*h", "SCF", /* 30-37 */ + "JR C,$h", "ADD HL,SP", "LD A,(#h)", "DEC SP", "INC A", "DEC A", "LD A,*h", "CCF", /* 38-3f */ + "LD B,B", "LD B,C", "LD B,D", "LD B,E", "LD B,H", "LD B,L", "LD B,(HL)", "LD B,A", /* 40-47 */ + "LD C,B", "LD C,C", "LD C,D", "LD C,E", "LD C,H", "LD C,L", "LD C,(HL)", "LD C,A", /* 48-4f */ + "LD D,B", "LD D,C", "LD D,D", "LD D,E", "LD D,H", "LD D,L", "LD D,(HL)", "LD D,A", /* 50-57 */ + "LD E,B", "LD E,C", "LD E,D", "LD E,E", "LD E,H", "LD E,L", "LD E,(HL)", "LD E,A", /* 58-5f */ + "LD H,B", "LD H,C", "LD H,D", "LD H,E", "LD H,H", "LD H,L", "LD H,(HL)", "LD H,A", /* 60-67 */ + "LD L,B", "LD L,C", "LD L,D", "LD L,E", "LD L,H", "LD L,L", "LD L,(HL)", "LD L,A", /* 68-6f */ + "LD (HL),B", "LD (HL),C", "LD (HL),D", "LD (HL),E", "LD (HL),H", "LD (HL),L", "HALT", "LD (HL),A", /* 70-77 */ + "LD A,B", "LD A,C", "LD A,D", "LD A,E", "LD A,H", "LD A,L", "LD A,(HL)", "LD A,A", /* 78-7f */ + "ADD A,B", "ADD A,C", "ADD A,D", "ADD A,E", "ADD A,H", "ADD A,L", "ADD A,(HL)", "ADD A,A", /* 80-87 */ + "ADC A,B", "ADC A,C", "ADC A,D", "ADC A,E", "ADC A,H", "ADC A,L", "ADC A,(HL)", "ADC A,A", /* 88-8f */ + "SUB B", "SUB C", "SUB D", "SUB E", "SUB H", "SUB L", "SUB (HL)", "SUB A", /* 90-97 */ + "SBC A,B", "SBC A,C", "SBC A,D", "SBC A,E", "SBC A,H", "SBC A,L", "SBC A,(HL)", "SBC A,A", /* 98-9f */ + "AND B", "AND C", "AND D", "AND E", "AND H", "AND L", "AND (HL)", "AND A", /* a0-a7 */ + "XOR B", "XOR C", "XOR D", "XOR E", "XOR H", "XOR L", "XOR (HL)", "XOR A", /* a8-af */ + "OR B", "OR C", "OR D", "OR E", "OR H", "OR L", "OR (HL)", "OR A", /* b0-b7 */ + "CP B", "CP C", "CP D", "CP E", "CP H", "CP L", "CP (HL)", "CP A", /* b8-bf */ + "RET NZ", "POP BC", "JP NZ,#h", "JP #h", "CALL NZ,#h", "PUSH BC", "ADD A,*h", "RST 00h", /* c0-c7 */ + "RET Z", "RET", "JP Z,#h", "PFX_CB", "CALL Z,#h", "CALL #h", "ADC A,*h", "RST 08h", /* c8-cf */ + "RET NC", "POP DE", "JP NC,#h", "OUT (*h),A", "CALL NC,#h", "PUSH DE", "SUB *h", "RST 10h", /* d0-d7 */ + "RET C", "EXX", "JP C,#h", "IN A,(*h)", "CALL C,#h", "PFX_DD", "SBC A,*h", "RST 18h", /* d8-df */ + "RET PO", "POP HL", "JP PO,#h", "EX (SP),HL", "CALL PO,#h", "PUSH HL", "AND *h", "RST 20h", /* e0-e7 */ + "RET PE", "LD PC,HL", "JP PE,#h", "EX DE,HL", "CALL PE,#h", "PFX_ED", "XOR *h", "RST 28h", /* e8-ef */ + "RET P", "POP AF", "JP P,#h", "DI", "CALL P,#h", "PUSH AF", "OR *h", "RST 30h", /* f0-f7 */ + "RET M", "LD SP,HL", "JP M,#h", "EI", "CALL M,#h", "PFX_FD", "CP *h", "RST 38h" /* f8-ff */ +}; + +static const char *const MnemonicsCB[256] = { +/* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ + "RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (HL)", "RLC A", /* 00-07 */ + "RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC (HL)", "RRC A", /* 08-0f */ + "RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (HL)", "RL A", /* 10-17 */ + "RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR (HL)", "RR A", /* 18-1f */ + "SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (HL)", "SLA A", /* 20-27 */ + "SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA (HL)", "SRA A", /* 28-2f */ + "SLL B", "SLL C", "SLL D", "SLL E", "SLL H", "SLL L", "SLL (HL)", "SLL A", /* 30-37 */ + "SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL (HL)", "SRL A", /* 38-3f */ + "BIT 0,B", "BIT 0,C", "BIT 0,D", "BIT 0,E", "BIT 0,H", "BIT 0,L", "BIT 0,(HL)", "BIT 0,A", /* 40-47 */ + "BIT 1,B", "BIT 1,C", "BIT 1,D", "BIT 1,E", "BIT 1,H", "BIT 1,L", "BIT 1,(HL)", "BIT 1,A", /* 48-4f */ + "BIT 2,B", "BIT 2,C", "BIT 2,D", "BIT 2,E", "BIT 2,H", "BIT 2,L", "BIT 2,(HL)", "BIT 2,A", /* 50-57 */ + "BIT 3,B", "BIT 3,C", "BIT 3,D", "BIT 3,E", "BIT 3,H", "BIT 3,L", "BIT 3,(HL)", "BIT 3,A", /* 58-5f */ + "BIT 4,B", "BIT 4,C", "BIT 4,D", "BIT 4,E", "BIT 4,H", "BIT 4,L", "BIT 4,(HL)", "BIT 4,A", /* 60-67 */ + "BIT 5,B", "BIT 5,C", "BIT 5,D", "BIT 5,E", "BIT 5,H", "BIT 5,L", "BIT 5,(HL)", "BIT 5,A", /* 68-6f */ + "BIT 6,B", "BIT 6,C", "BIT 6,D", "BIT 6,E", "BIT 6,H", "BIT 6,L", "BIT 6,(HL)", "BIT 6,A", /* 70-77 */ + "BIT 7,B", "BIT 7,C", "BIT 7,D", "BIT 7,E", "BIT 7,H", "BIT 7,L", "BIT 7,(HL)", "BIT 7,A", /* 78-7f */ + "RES 0,B", "RES 0,C", "RES 0,D", "RES 0,E", "RES 0,H", "RES 0,L", "RES 0,(HL)", "RES 0,A", /* 80-87 */ + "RES 1,B", "RES 1,C", "RES 1,D", "RES 1,E", "RES 1,H", "RES 1,L", "RES 1,(HL)", "RES 1,A", /* 88-8f */ + "RES 2,B", "RES 2,C", "RES 2,D", "RES 2,E", "RES 2,H", "RES 2,L", "RES 2,(HL)", "RES 2,A", /* 90-97 */ + "RES 3,B", "RES 3,C", "RES 3,D", "RES 3,E", "RES 3,H", "RES 3,L", "RES 3,(HL)", "RES 3,A", /* 98-9f */ + "RES 4,B", "RES 4,C", "RES 4,D", "RES 4,E", "RES 4,H", "RES 4,L", "RES 4,(HL)", "RES 4,A", /* a0-a7 */ + "RES 5,B", "RES 5,C", "RES 5,D", "RES 5,E", "RES 5,H", "RES 5,L", "RES 5,(HL)", "RES 5,A", /* a8-af */ + "RES 6,B", "RES 6,C", "RES 6,D", "RES 6,E", "RES 6,H", "RES 6,L", "RES 6,(HL)", "RES 6,A", /* b0-b7 */ + "RES 7,B", "RES 7,C", "RES 7,D", "RES 7,E", "RES 7,H", "RES 7,L", "RES 7,(HL)", "RES 7,A", /* b8-bf */ + "SET 0,B", "SET 0,C", "SET 0,D", "SET 0,E", "SET 0,H", "SET 0,L", "SET 0,(HL)", "SET 0,A", /* c0-c7 */ + "SET 1,B", "SET 1,C", "SET 1,D", "SET 1,E", "SET 1,H", "SET 1,L", "SET 1,(HL)", "SET 1,A", /* c8-cf */ + "SET 2,B", "SET 2,C", "SET 2,D", "SET 2,E", "SET 2,H", "SET 2,L", "SET 2,(HL)", "SET 2,A", /* d0-d7 */ + "SET 3,B", "SET 3,C", "SET 3,D", "SET 3,E", "SET 3,H", "SET 3,L", "SET 3,(HL)", "SET 3,A", /* d8-df */ + "SET 4,B", "SET 4,C", "SET 4,D", "SET 4,E", "SET 4,H", "SET 4,L", "SET 4,(HL)", "SET 4,A", /* e0-e7 */ + "SET 5,B", "SET 5,C", "SET 5,D", "SET 5,E", "SET 5,H", "SET 5,L", "SET 5,(HL)", "SET 5,A", /* e8-ef */ + "SET 6,B", "SET 6,C", "SET 6,D", "SET 6,E", "SET 6,H", "SET 6,L", "SET 6,(HL)", "SET 6,A", /* f0-f7 */ + "SET 7,B", "SET 7,C", "SET 7,D", "SET 7,E", "SET 7,H", "SET 7,L", "SET 7,(HL)", "SET 7,A" /* f8-ff */ +}; + +static const char *const MnemonicsED[256] = { +/* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ + "DB EDh,00h", "DB EDh,01h", "DB EDh,02h", "DB EDh,03h", "DB EDh,04h", "DB EDh,05h", "DB EDh,06h", "DB EDh,07h", /* 00-07 */ + "DB EDh,08h", "DB EDh,09h", "DB EDh,0Ah", "DB EDh,0Bh", "DB EDh,0Ch", "DB EDh,0Dh", "DB EDh,0Eh", "DB EDh,0Fh", /* 08-0f */ + "DB EDh,10h", "DB EDh,11h", "DB EDh,12h", "DB EDh,13h", "DB EDh,14h", "DB EDh,15h", "DB EDh,16h", "DB EDh,17h", /* 10-17 */ + "DB EDh,18h", "DB EDh,19h", "DB EDh,1Ah", "DB EDh,1Bh", "DB EDh,1Ch", "DB EDh,1Dh", "DB EDh,1Eh", "DB EDh,1Fh", /* 18-1f */ + "DB EDh,20h", "DB EDh,21h", "DB EDh,22h", "DB EDh,23h", "DB EDh,24h", "DB EDh,25h", "DB EDh,26h", "DB EDh,27h", /* 20-27 */ + "DB EDh,28h", "DB EDh,29h", "DB EDh,2Ah", "DB EDh,2Bh", "DB EDh,2Ch", "DB EDh,2Dh", "DB EDh,2Eh", "DB EDh,2Fh", /* 28-2f */ + "DB EDh,30h", "DB EDh,31h", "DB EDh,32h", "DB EDh,33h", "DB EDh,34h", "DB EDh,35h", "DB EDh,36h", "DB EDh,37h", /* 30-37 */ + "DB EDh,38h", "DB EDh,39h", "DB EDh,3Ah", "DB EDh,3Bh", "DB EDh,3Ch", "DB EDh,3Dh", "DB EDh,3Eh", "DB EDh,3Fh", /* 38-3f */ + "IN B,(C)", "OUT (C),B", "SBC HL,BC", "LD (#h),BC", "NEG", "RETN", "IM 0", "LD I,A", /* 40-47 */ + "IN C,(C)", "OUT (C),C", "ADC HL,BC", "LD BC,(#h)", "DB EDh,4Ch", "RETI", "DB EDh,4Eh", "LD R,A", /* 48-4f */ + "IN D,(C)", "OUT (C),D", "SBC HL,DE", "LD (#h),DE", "DB EDh,54h", "DB EDh,55h", "IM 1", "LD A,I", /* 50-57 */ + "IN E,(C)", "OUT (C),E", "ADC HL,DE", "LD DE,(#h)", "DB EDh,5Ch", "DB EDh,5Dh", "IM 2", "LD A,R", /* 58-5f */ + "IN H,(C)", "OUT (C),H", "SBC HL,HL", "LD (#h),HL", "DB EDh,64h", "DB EDh,65h", "DB EDh,66h", "RRD", /* 60-67 */ + "IN L,(C)", "OUT (C),L", "ADC HL,HL", "LD HL,(#h)", "DB EDh,6Ch", "DB EDh,6Dh", "DB EDh,6Eh", "RLD", /* 68-6f */ + "IN F,(C)", "DB EDh,71h", "SBC HL,SP", "LD (#h),SP", "DB EDh,74h", "DB EDh,75h", "DB EDh,76h", "DB EDh,77h", /* 70-77 */ + "IN A,(C)", "OUT (C),A", "ADC HL,SP", "LD SP,(#h)", "DB EDh,7Ch", "DB EDh,7Dh", "DB EDh,7Eh", "DB EDh,7Fh", /* 78-7f */ + "DB EDh,80h", "DB EDh,81h", "DB EDh,82h", "DB EDh,83h", "DB EDh,84h", "DB EDh,85h", "DB EDh,86h", "DB EDh,87h", /* 80-87 */ + "DB EDh,88h", "DB EDh,89h", "DB EDh,8Ah", "DB EDh,8Bh", "DB EDh,8Ch", "DB EDh,8Dh", "DB EDh,8Eh", "DB EDh,8Fh", /* 88-8f */ + "DB EDh,90h", "DB EDh,91h", "DB EDh,92h", "DB EDh,93h", "DB EDh,94h", "DB EDh,95h", "DB EDh,96h", "DB EDh,97h", /* 90-97 */ + "DB EDh,98h", "DB EDh,99h", "DB EDh,9Ah", "DB EDh,9Bh", "DB EDh,9Ch", "DB EDh,9Dh", "DB EDh,9Eh", "DB EDh,9Fh", /* 98-9f */ + "LDI", "CPI", "INI", "OUTI", "DB EDh,A4h", "DB EDh,A5h", "DB EDh,A6h", "DB EDh,A7h", /* a0-a7 */ + "LDD", "CPD", "IND", "OUTD", "DB EDh,ACh", "DB EDh,ADh", "DB EDh,AEh", "DB EDh,AFh", /* a8-af */ + "LDIR", "CPIR", "INIR", "OTIR", "DB EDh,B4h", "DB EDh,B5h", "DB EDh,B6h", "DB EDh,B7h", /* b0-b7 */ + "LDDR", "CPDR", "INDR", "OTDR", "DB EDh,BCh", "DB EDh,BDh", "DB EDh,BEh", "DB EDh,BFh", /* b8-bf */ + "DB EDh,C0h", "DB EDh,C1h", "DB EDh,C2h", "DB EDh,C3h", "DB EDh,C4h", "DB EDh,C5h", "DB EDh,C6h", "DB EDh,C7h", /* c0-c7 */ + "DB EDh,C8h", "DB EDh,C9h", "DB EDh,CAh", "DB EDh,CBh", "DB EDh,CCh", "DB EDh,CDh", "DB EDh,CEh", "DB EDh,CFh", /* c8-cf */ + "DB EDh,D0h", "DB EDh,D1h", "DB EDh,D2h", "DB EDh,D3h", "DB EDh,D4h", "DB EDh,D5h", "DB EDh,D6h", "DB EDh,D7h", /* d0-d7 */ + "DB EDh,D8h", "DB EDh,D9h", "DB EDh,DAh", "DB EDh,DBh", "DB EDh,DCh", "DB EDh,DDh", "DB EDh,DEh", "DB EDh,DFh", /* d8-df */ + "DB EDh,E0h", "DB EDh,E1h", "DB EDh,E2h", "DB EDh,E3h", "DB EDh,E4h", "DB EDh,E5h", "DB EDh,E6h", "DB EDh,E7h", /* e0-e7 */ + "DB EDh,E8h", "DB EDh,E9h", "DB EDh,EAh", "DB EDh,EBh", "DB EDh,ECh", "DB EDh,EDh", "DB EDh,EEh", "DB EDh,EFh", /* e8-ef */ + "DB EDh,F0h", "DB EDh,F1h", "DB EDh,F2h", "DB EDh,F3h", "DB EDh,F4h", "DB EDh,F5h", "DB EDh,F6h", "DB EDh,F7h", /* f0-f7 */ + "DB EDh,F8h", "DB EDh,F9h", "DB EDh,FAh", "DB EDh,FBh", "DB EDh,FCh", "DB EDh,FDh", "DB EDh,FEh", "DB EDh,FFh" /* f8-ff */ +}; + +static const char *const MnemonicsXX[256] = { +/* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ + "NOP", "LD BC,#h", "LD (BC),A", "INC BC", "INC B", "DEC B", "LD B,*h", "RLCA", /* 00-07 */ + "EX AF,AF'", "ADD I%,BC", "LD A,(BC)", "DEC BC", "INC C", "DEC C", "LD C,*h", "RRCA", /* 08-0f */ + "DJNZ $h", "LD DE,#h", "LD (DE),A", "INC DE", "INC D", "DEC D", "LD D,*h", "RLA", /* 10-17 */ + "JR $h", "ADD I%,DE", "LD A,(DE)", "DEC DE", "INC E", "DEC E", "LD E,*h", "RRA", /* 18-1f */ + "JR NZ,$h", "LD I%,#h", "LD (#h),I%", "INC I%", "INC I%H", "DEC I%H", "LD I%H,*h", "DAA", /* 20-27 */ + "JR Z,$h", "ADD I%,I%", "LD I%,(#h)", "DEC I%", "INC I%L", "DEC I%L", "LD I%L,*h", "CPL", /* 28-2f */ + "JR NC,$h", "LD SP,#h", "LD (#h),A", "INC SP", "INC (I%+^h)", "DEC (I%+^h)", "LD (I%+^h),*h","SCF", /* 30-37 */ + "JR C,$h", "ADD I%,SP", "LD A,(#h)", "DEC SP", "INC A", "DEC A", "LD A,*h", "CCF", /* 38-3f */ + "LD B,B", "LD B,C", "LD B,D", "LD B,E", "LD B,I%H", "LD B,I%L", "LD B,(I%+^h)", "LD B,A", /* 40-47 */ + "LD C,B", "LD C,C", "LD C,D", "LD C,E", "LD C,I%H", "LD C,I%L", "LD C,(I%+^h)", "LD C,A", /* 48-4f */ + "LD D,B", "LD D,C", "LD D,D", "LD D,E", "LD D,I%H", "LD D,I%L", "LD D,(I%+^h)", "LD D,A", /* 50-57 */ + "LD E,B", "LD E,C", "LD E,D", "LD E,E", "LD E,I%H", "LD E,I%L", "LD E,(I%+^h)", "LD E,A", /* 58-5f */ + "LD I%H,B", "LD I%H,C", "LD I%H,D", "LD I%H,E", "LD I%H,I%H", "LD I%H,I%L", "LD H,(I%+^h)", "LD I%H,A", /* 60-67 */ + "LD I%L,B", "LD I%L,C", "LD I%L,D", "LD I%L,E", "LD I%L,I%H", "LD I%L,I%L", "LD L,(I%+^h)", "LD I%L,A", /* 68-6f */ + "LD (I%+^h),B", "LD (I%+^h),C", "LD (I%+^h),D", "LD (I%+^h),E", "LD (I%+^h),H", "LD (I%+^h),L", "HALT", "LD (I%+^h),A", /* 70-77 */ + "LD A,B", "LD A,C", "LD A,D", "LD A,E", "LD A,I%H", "LD A,I%L", "LD A,(I%+^h)", "LD A,A", /* 78-7f */ + "ADD A,B", "ADD A,C", "ADD A,D", "ADD A,E", "ADD A,I%H", "ADD A,I%L", "ADD A,(I%+^h)","ADD A,A", /* 80-87 */ + "ADC A,B", "ADC A,C", "ADC A,D", "ADC A,E", "ADC A,I%H", "ADC A,I%L", "ADC A,(I%+^h)","ADC A,A", /* 88-8f */ + "SUB B", "SUB C", "SUB D", "SUB E", "SUB I%H", "SUB I%L", "SUB (I%+^h)", "SUB A", /* 90-97 */ + "SBC A,B", "SBC A,C", "SBC A,D", "SBC A,E", "SBC A,I%H", "SBC A,I%L", "SBC A,(I%+^h)","SBC A,A", /* 98-9f */ + "AND B", "AND C", "AND D", "AND E", "AND I%H", "AND I%L", "AND (I%+^h)", "AND A", /* a0-a7 */ + "XOR B", "XOR C", "XOR D", "XOR E", "XOR I%H", "XOR I%L", "XOR (I%+^h)", "XOR A", /* a8-af */ + "OR B", "OR C", "OR D", "OR E", "OR I%H", "OR I%L", "OR (I%+^h)", "OR A", /* b0-b7 */ + "CP B", "CP C", "CP D", "CP E", "CP I%H", "CP I%L", "CP (I%+^h)", "CP A", /* b8-bf */ + "RET NZ", "POP BC", "JP NZ,#h", "JP #h", "CALL NZ,#h", "PUSH BC", "ADD A,*h", "RST 00h", /* c8-cf */ + "RET Z", "RET", "JP Z,#h", "PFX_CB", "CALL Z,#h", "CALL #h", "ADC A,*h", "RST 08h", /* c8-cf */ + "RET NC", "POP DE", "JP NC,#h", "OUT (*h),A", "CALL NC,#h", "PUSH DE", "SUB *h", "RST 10h", /* d0-d7 */ + "RET C", "EXX", "JP C,#h", "IN A,(*h)", "CALL C,#h", "PFX_DD", "SBC A,*h", "RST 18h", /* d8-df */ + "RET PO", "POP I%", "JP PO,#h", "EX (SP),I%", "CALL PO,#h", "PUSH I%", "AND *h", "RST 20h", /* e0-e7 */ + "RET PE", "LD PC,I%", "JP PE,#h", "EX DE,I%", "CALL PE,#h", "PFX_ED", "XOR *h", "RST 28h", /* e8-ef */ + "RET P", "POP AF", "JP P,#h", "DI", "CALL P,#h", "PUSH AF", "OR *h", "RST 30h", /* f0-f7 */ + "RET M", "LD SP,I%", "JP M,#h", "EI", "CALL M,#h", "PFX_FD", "CP *h", "RST 38h" /* f8-ff */ +}; + +static const char *const MnemonicsXCB[256] = { +/*0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ + "RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (I%@h)", "RLC A", /* 00-07 */ + "RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC (I%@h)", "RRC A", /* 08-0f */ + "RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (I%@h)", "RL A", /* 10-17 */ + "RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR (I%@h)", "RR A", /* 18-1f */ + "SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (I%@h)", "SLA A", /* 20-27 */ + "SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA (I%@h)", "SRA A", /* 28-2f */ + "SLL B", "SLL C", "SLL D", "SLL E", "SLL H", "SLL L", "SLL (I%@h)", "SLL A", /* 30-37 */ + "SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL (I%@h)", "SRL A", /* 38-3f */ + "BIT 0,B", "BIT 0,C", "BIT 0,D", "BIT 0,E", "BIT 0,H", "BIT 0,L", "BIT 0,(I%@h)", "BIT 0,A", /* 40-47 */ + "BIT 1,B", "BIT 1,C", "BIT 1,D", "BIT 1,E", "BIT 1,H", "BIT 1,L", "BIT 1,(I%@h)", "BIT 1,A", /* 48-4f */ + "BIT 2,B", "BIT 2,C", "BIT 2,D", "BIT 2,E", "BIT 2,H", "BIT 2,L", "BIT 2,(I%@h)", "BIT 2,A", /* 50-57 */ + "BIT 3,B", "BIT 3,C", "BIT 3,D", "BIT 3,E", "BIT 3,H", "BIT 3,L", "BIT 3,(I%@h)", "BIT 3,A", /* 58-5f */ + "BIT 4,B", "BIT 4,C", "BIT 4,D", "BIT 4,E", "BIT 4,H", "BIT 4,L", "BIT 4,(I%@h)", "BIT 4,A", /* 60-67 */ + "BIT 5,B", "BIT 5,C", "BIT 5,D", "BIT 5,E", "BIT 5,H", "BIT 5,L", "BIT 5,(I%@h)", "BIT 5,A", /* 68-6f */ + "BIT 6,B", "BIT 6,C", "BIT 6,D", "BIT 6,E", "BIT 6,H", "BIT 6,L", "BIT 6,(I%@h)", "BIT 6,A", /* 70-77 */ + "BIT 7,B", "BIT 7,C", "BIT 7,D", "BIT 7,E", "BIT 7,H", "BIT 7,L", "BIT 7,(I%@h)", "BIT 7,A", /* 78-7f */ + "RES 0,B", "RES 0,C", "RES 0,D", "RES 0,E", "RES 0,H", "RES 0,L", "RES 0,(I%@h)", "RES 0,A", /* 80-87 */ + "RES 1,B", "RES 1,C", "RES 1,D", "RES 1,E", "RES 1,H", "RES 1,L", "RES 1,(I%@h)", "RES 1,A", /* 88-8f */ + "RES 2,B", "RES 2,C", "RES 2,D", "RES 2,E", "RES 2,H", "RES 2,L", "RES 2,(I%@h)", "RES 2,A", /* 90-97 */ + "RES 3,B", "RES 3,C", "RES 3,D", "RES 3,E", "RES 3,H", "RES 3,L", "RES 3,(I%@h)", "RES 3,A", /* 98-9f */ + "RES 4,B", "RES 4,C", "RES 4,D", "RES 4,E", "RES 4,H", "RES 4,L", "RES 4,(I%@h)", "RES 4,A", /* a0-a7 */ + "RES 5,B", "RES 5,C", "RES 5,D", "RES 5,E", "RES 5,H", "RES 5,L", "RES 5,(I%@h)", "RES 5,A", /* a8-af */ + "RES 6,B", "RES 6,C", "RES 6,D", "RES 6,E", "RES 6,H", "RES 6,L", "RES 6,(I%@h)", "RES 6,A", /* b0-b7 */ + "RES 7,B", "RES 7,C", "RES 7,D", "RES 7,E", "RES 7,H", "RES 7,L", "RES 7,(I%@h)", "RES 7,A", /* b8-bf */ + "SET 0,B", "SET 0,C", "SET 0,D", "SET 0,E", "SET 0,H", "SET 0,L", "SET 0,(I%@h)", "SET 0,A", /* c0-c7 */ + "SET 1,B", "SET 1,C", "SET 1,D", "SET 1,E", "SET 1,H", "SET 1,L", "SET 1,(I%@h)", "SET 1,A", /* c8-cf */ + "SET 2,B", "SET 2,C", "SET 2,D", "SET 2,E", "SET 2,H", "SET 2,L", "SET 2,(I%@h)", "SET 2,A", /* d0-d7 */ + "SET 3,B", "SET 3,C", "SET 3,D", "SET 3,E", "SET 3,H", "SET 3,L", "SET 3,(I%@h)", "SET 3,A", /* d8-df */ + "SET 4,B", "SET 4,C", "SET 4,D", "SET 4,E", "SET 4,H", "SET 4,L", "SET 4,(I%@h)", "SET 4,A", /* e0-e7 */ + "SET 5,B", "SET 5,C", "SET 5,D", "SET 5,E", "SET 5,H", "SET 5,L", "SET 5,(I%@h)", "SET 5,A", /* e8-ef */ + "SET 6,B", "SET 6,C", "SET 6,D", "SET 6,E", "SET 6,H", "SET 6,L", "SET 6,(I%@h)", "SET 6,A", /* f0-f7 */ + "SET 7,B", "SET 7,C", "SET 7,D", "SET 7,E", "SET 7,H", "SET 7,L", "SET 7,(I%@h)", "SET 7,A" /* f8-ff */ +}; + +static void prepareMemoryAccessMessage(const t_addr loc) { + sprintf(memoryAccessMessage, "Memory access breakpoint [%05xh]", loc); +} + +static void prepareInstructionMessage(const t_addr loc, const uint32 op) { + sprintf(instructionMessage, "Instruction \"%s\" breakpoint [%05xh]", z80_chiptype == CHIP_TYPE_8080 ? Mnemonics8080[op & 0xff] : + (z80_chiptype == CHIP_TYPE_Z80 ? MnemonicsZ80[op & 0xff] : "???"), loc); +} + +/* Ensure that hex number starts with a digit when printed */ +static void printHex2(char* string, const uint32 value) { + sprintf(string, (value <= 0x9f ? "%02X" : "%03X"), value); +} + +static void printHex4(char* string, const uint32 value) { + sprintf(string, (value <= 0x9fff ? "%04X" : "%05X"), value); +} + +/* Symbolic disassembler + + Inputs: + *val = instructions to disassemble + addr = current PC + Outputs: + *S = output text + return = length of instruction in bytes + + DAsm is Copyright (C) Marat Fayzullin 1995,1996,1997 + You are not allowed to distribute this software + commercially. + +*/ + +int32 z80_dasm(char *S, const uint32 *val, const int32 addr) { + char R[128], H[10], C = '\0', *P; + const char *T, *T1; + uint8 J = 0, Offset = 0; + uint16 B = 0; + + if (z80_chiptype == CHIP_TYPE_Z80) + switch(val[B]) { + + case 0xcb: + B++; + T = MnemonicsCB[val[B++]]; + break; + + case 0xed: + B++; + T = MnemonicsED[val[B++]]; + break; + + case 0xdd: + + case 0xfd: + C = (val[B++] == 0xdd) ? 'X' : 'Y'; + if (val[B] == 0xcb) { + B++; + Offset = val[B++]; + J = 1; + T = MnemonicsXCB[val[B++]]; + } else + T = MnemonicsXX[val[B++]]; + break; + + default: + T = MnemonicsZ80[val[B++]]; + } + else + T = Mnemonics8080[val[B++]]; + + if ( (T1 = strchr(T, '^')) ) { + strncpy(R, T, T1 - T); + R[T1 - T] = '\0'; + printHex2(H, val[B++]); + strlcat(R, H, sizeof (R)); + strlcat(R, T1 + 1, sizeof (R)); /* ok, since T1 is a short sub-string coming from one of the tables */ + } else + strlcpy(R, T, sizeof (R)); /* ok, since T is a short string coming from one of the tables */ + if ( (P = strchr(R, '%')) ) { + *P = C; + if ( (P = strchr(P + 1, '%')) ) + *P = C; + } + + if ( (P = strchr(R, '*')) ) { + strncpy(S, R, P - R); + S[P - R] = '\0'; + printHex2(H, val[B++]); + strcat(S, H); + strcat(S, P + 1); + } else if ( (P = strchr(R, '@')) ) { + strncpy(S, R, P - R); + S[P - R] = '\0'; + if (!J) + Offset = val[B++]; + strcat(S, Offset & 0x80 ? "-" : "+"); + J = Offset & 0x80 ? 256 - Offset : Offset; + printHex2(H, J); + strcat(S, H); + strcat(S, P + 1); + } else if ( (P = strchr(R, '$')) ) { + strncpy(S, R, P - R); + S[P - R] = '\0'; + Offset = val[B++]; + printHex4(H, (addr + 2 + (Offset & 0x80 ? (Offset - 256) : Offset)) & 0xFFFF); + strcat(S, H); + strcat(S, P + 1); + } else if ( (P = strchr(R, '#')) ) { + strncpy(S, R, P - R); + S[P - R] = '\0'; + printHex4(H, val[B] + 256 * val[B + 1]); + strcat(S, H); + strcat(S, P + 1); + B += 2; + } else + strcpy(S, R); + return(B); +} + +/* checkbase determines the base of the number (ch, *numString) + and returns FALSE if the number is bad */ +static int32 checkbase(char ch, const char *numString) { + int32 decimal = (ch <= '9'); + if (toupper(ch) == 'H') + return FALSE; + while (isxdigit(ch = *numString++)) + if (ch > '9') + decimal = FALSE; + return toupper(ch) == 'H' ? 16 : (decimal ? 10 : FALSE); +} + +static int32 numok(char ch, const char **numString, const int32 minvalue, + const int32 maxvalue, const int32 requireSign, int32 *result) { + int32 sign = 1, value = 0, base; + if (requireSign) { + if (ch == '+') + ch = *(*numString)++; + else if (ch == '-') { + sign = -1; + ch = *(*numString)++; + } else + return FALSE; + } + if (!(base = checkbase(ch, *numString))) + return FALSE; + while (isxdigit(ch)) { + value = base * value + ((ch <= '9') ? (ch - '0') : (toupper(ch) - 'A' + 10)); + ch = *(*numString)++; + } + if (toupper(ch) != 'H') + (*numString)--; + *result = value * sign; + return (minvalue <= value) && (value <= maxvalue); +} + +static int32 match(const char *pattern, const char *input, char *xyFirst, char *xy, int32 *number, int32 *star, + int32 *at, int32 *hat, int32 *dollar) { + char pat = *pattern++; + char inp = *input++; + while ((pat) && (inp)) { + switch(pat) { + + case '_': /* patterns containing '_' should never match */ + return FALSE; + + case ',': + if (inp == ' ') { + inp = *input++; + continue; + } /* otherwise fall through */ + + case ' ': + if (inp != pat) + return FALSE; + pat = *pattern++; + inp = *input++; + while (inp == ' ') + inp = *input++; + continue; + + case '%': + inp = toupper(inp); + if ((inp == 'X') || (inp == 'Y')) + if (*xyFirst) /* make sure that second '%' corresponds to first */ + if (*xyFirst == inp) + *xy = inp; + else + return FALSE; + else { /* take note of first '%' for later */ + *xyFirst = inp; + *xy = inp; + } + else + return FALSE; + break; + + case '#': + if (numok(inp, &input, 0, 65535, FALSE, number)) + pattern++; /* skip h */ + else + return FALSE; + break; + + case '*': + if (numok(inp, &input, 0, 255, FALSE, star)) + pattern++; /* skip h */ + else + return FALSE; + break; + + case '@': + if (numok(inp, &input, -128, 65535, TRUE, at)) + pattern++; /* skip h */ + else + return FALSE; + break; + + case '$': + if (numok(inp, &input, 0, 65535, FALSE, dollar)) + pattern++; /* skip h */ + else + return FALSE; + break; + + case '^': + if (numok(inp, &input, 0, 255, FALSE, hat)) + pattern++; /* skip h */ + else + return FALSE; + break; + + default: + if (toupper(pat) != toupper(inp)) + return FALSE; + } + + pat = *pattern++; + inp = *input++; + } + + while (inp == ' ') + inp = *input++; + + return (pat == 0) && (inp == 0); +} + +static int32 checkXY(const char xy) { + return xy == 'X' ? 0xdd : 0xfd; /* else is 'Y' */ +} + + +static int32 parse_X80(const char *cptr, const int32 addr, uint32 *val, const char *const Mnemonics[]) { + char xyFirst = 0, xy; + int32 op, number, star, at, hat, dollar; + for (op = 0; op < 256; op++) { + number = star = at = dollar = -129; + if (match(Mnemonics[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { + val[0] = op; + if (number >= 0) { + val[1] = (0xff) & number; + val[2] = (0xff) & (number >> 8); + return -2; /* two additional bytes returned */ + } else if (star >= 0) { + val[1] = (0xff) & star; + return -1; /* one additional byte returned */ + } else if (at > -129) + if ((-128 <= at) && (at <= 127)) { + val[1] = (int8)(at); + return -1; /* one additional byte returned */ + } else + return SCPE_ARG; + else if (dollar >= 0) { + dollar -= addr + 2; /* relative translation */ + if ((-128 <= dollar) && (dollar <= 127)) { + val[1] = (int8)(dollar); + return -1; /* one additional byte returned */ + } else + return SCPE_ARG; + } else + return SCPE_OK; + } + } + if (Mnemonics == Mnemonics8080) + return SCPE_ARG; + + for (op = 0; op < 256; op++) + if (match(MnemonicsCB[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { + val[0] = 0xcb; + val[1] = op; + return -1; /* one additional byte returned */ + } + + for (op = 0; op < 256; op++) { + number = -1; + if (match(MnemonicsED[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { + val[0] = 0xed; + val[1] = op; + if (number >= 0) { + val[2] = (0xff) & number; + val[3] = (0xff) & (number >> 8); + return -3; /* three additional bytes returned */ + } else + return -1; /* one additional byte returned */ + } + } + + for (op = 0; op < 256; op++) { + number = star = hat = -1; + xy = 0; + if (match(MnemonicsXX[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { + /* all matches must have contained a '%' character */ + if (!(val[0] = checkXY(xy))) + return SCPE_ARG; + val[1] = op; + if (number >= 0) { + val[2] = (0xff) & number; + val[3] = (0xff) & (number >> 8); + return -3; /* three additional bytes returned */ + } else if ((star >= 0) && (hat >= 0)) { + val[2] = (0xff) & hat; + val[3] = (0xff) & star; + return -3; /* three additional bytes returned */ + } else if (star >= 0) { + val[2] = (0xff) & star; + return -2; /* two additional bytes returned */ + } else if (hat >= 0) { + val[2] = (0xff) & hat; + return -2; /* two additional bytes returned */ + } else + return -1; /* one additional byte returned */ + } + } + + for (op = 0; op < 256; op++) { + at = -129; + xy = 0; + if (match(MnemonicsXCB[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { + /* all matches must have contained a '%' character */ + if (!(val[0] = checkXY(xy))) + return SCPE_ARG; + val[1] = 0xcb; + if (at > -129) + val[2] = (int8) (at); + else { + sim_printf("Offset expected.\n"); + return SCPE_ARG; + } + val[3] = op; + return -3; /* three additional bytes returned */ + } + } + return SCPE_ARG; +} + +t_stat z80_parse_sym(CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ + return (parse_X80(cptr, addr, val, z80_chiptype == CHIP_TYPE_Z80 ? MnemonicsZ80 : Mnemonics8080)); +} + +t_stat z80_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "\nIntel 8080 / Zilog Z80 CPU (%s)\n", dptr->name); + + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + fprint_reg_help (st, dptr); + + return SCPE_OK; +} + diff --git a/Altair8800/s100_z80.h b/Altair8800/s100_z80.h new file mode 100644 index 00000000..a52b3902 --- /dev/null +++ b/Altair8800/s100_z80.h @@ -0,0 +1,70 @@ +/* s100_z80.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/07/25 Initial version + +*/ + +#ifndef S100_Z80_H_ +#define S100_Z80_H_ + +#include "s100_cpu.h" + +#define LDA_INSTRUCTION 0x3e /* op-code for LD A,<8-bit value> instruction */ +#define UNIT_NO_OFFSET_1 0x37 /* LD A, */ +#define UNIT_NO_OFFSET_2 0xb4 /* LD a,80h | */ +#define LDB_INSTRUCTION 0x06 /* op-code for LD B,<8-bit value> instruction */ +#define START_SECTOR_OFFSET 0x57 /* LD B, */ + +#define CPU_INDEX_8080 4 /* index of default PC register */ + +/* simulator stop codes */ +#define STOP_IBKPT 1 /* breakpoint (program counter) */ +#define STOP_MEM 2 /* breakpoint (memory access) */ +#define STOP_INSTR 3 /* breakpoint (instruction access) */ +#define STOP_OPCODE 4 /* invalid operation encountered (8080, Z80, 8086) */ +#define STOP_HALT 5 /* HALT */ + +#define UNIT_Z80_V_OPSTOP (UNIT_V_UF+0) /* stop on invalid operation */ +#define UNIT_Z80_OPSTOP (1 << UNIT_Z80_V_OPSTOP) +#define UNIT_Z80_V_STOPONHALT (UNIT_V_UF+1) /* stop simulation on HALT */ +#define UNIT_Z80_STOPONHALT (1 << UNIT_Z80_V_STOPONHALT) + +#define PLURAL(x) (x), (x) == 1 ? "" : "s" + +extern ChipType z80_chiptype; +extern REG *z80_pc_reg; + +extern t_stat z80_instr(void); +extern t_value z80_pc_value(void); +extern t_stat z80_parse_sym(CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw); +extern t_bool z80_is_pc_a_subroutine_call (t_addr **ret_addrs); +extern int32 z80_dasm(char *S, const uint32 *val, const int32 addr); +extern t_stat z80_cmd_reg(int32 flag, CONST char *cptr); +extern t_bool z80_is_pc_a_subroutine_call(t_addr **ret_addrs); +extern t_stat z80_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); + +#endif diff --git a/Altair8800/sds_sbc200.c b/Altair8800/sds_sbc200.c new file mode 100644 index 00000000..d42dd371 --- /dev/null +++ b/Altair8800/sds_sbc200.c @@ -0,0 +1,1294 @@ +/* sds_sbc200.c: SD Systems SBC-200 + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 07-Nov-2025 Initial version + +*/ + +#include "sim_tmxr.h" +#include "s100_bus.h" +#include "s100_cpu.h" + +#define SBC200_NAME "SD SYSTEMS SBC-200" +#define SBC200_SNAME "SBC200" + +#define SBC200_WAIT 250 /* Service Wait Interval */ + +#define SBC200_IOBASE 0x78 +#define SBC200_IOSIZE 8 + +#define SBC200_MON_BASE 0xe000 +#define SBC200_MON_SIZE 2048 +#define SBC200_MON_MASK (SBC200_MON_SIZE-1) + +#define SBC200_DDB_BASE 0xf000 +#define SBC200_DDB_SIZE 2048 +#define SBC200_DDB_MASK (SBC200_DDB_SIZE-1) + +RES sbc200_iores = { SBC200_IOBASE, SBC200_IOSIZE, 0x0000, 0 }; +RES sbc200_monres = { 0x00, 0, SBC200_MON_BASE, SBC200_MON_SIZE }; +RES sbc200_ddbres = { 0x00, 0, SBC200_DDB_BASE, SBC200_DDB_SIZE }; + +#define SBC200_TRDY 0x01 /* Transmit Data Ready */ +#define SBC200_RDRF 0x02 /* Receive Data Register Full */ +#define SBC200_TDRE 0x04 /* Transmit Data Register Empty */ +#define SBC200_PE 0x08 /* Parity Error */ +#define SBC200_OVRN 0x10 /* Overrun */ +#define SBC200_FE 0x20 /* Framing Error */ +#define SBC200_DSR 0x80 /* Data Set Ready */ +#define SBC200_IR 0x40 /* Internal Reset */ +#define SBC200_CLK1 0x01 /* Divide Clock by 1 */ +#define SBC200_CLK16 0x02 /* Divide Clock by 16 */ +#define SBC200_CLK64 0x03 /* Divide Clock by 64 */ +#define SBC200_72E 0xf8 /* 7-2-E */ +#define SBC200_72O 0xd8 /* 7-2-O */ +#define SBC200_72N 0xc8 /* 7-2-O */ +#define SBC200_71E 0x78 /* 7-1-E */ +#define SBC200_71O 0x58 /* 7-1-O */ +#define SBC200_71N 0x48 /* 7-1-O */ +#define SBC200_82E 0xfc /* 8-2-E */ +#define SBC200_82O 0xdc /* 8-2-O */ +#define SBC200_82N 0xcc /* 8-2-O */ +#define SBC200_81E 0x7c /* 8-1-E */ +#define SBC200_81O 0xbc /* 8-1-O */ +#define SBC200_81N 0x4c /* 8-1-N */ +#define SBC200_FMTMSK 0xfc /* Length, Parity, Stop Mask */ +#define SBC200_DTR 0x02 /* RTS Bit Mask */ +#define SBC200_RTS 0x20 /* RTS Bit Mask */ +#define SBC200_RIE 0x80 /* Receive Int Enabled */ + +#define SBC200_BAUD 9600 /* Default baud rate */ + +/* SBC-200 Monitor 2.1 @ E000 */ +static uint8 sbc200_mon_rom[SBC200_MON_SIZE] = { + 0xc3, 0x0f, 0xe0, 0xc3, 0x79, 0xe0, 0xc3, 0x0f, + 0xe7, 0xc3, 0x17, 0xe7, 0xc3, 0x25, 0xe7, 0xdb, + 0x7f, 0x21, 0x60, 0xff, 0x22, 0xe6, 0xff, 0xaf, + 0x32, 0xc0, 0xff, 0x3e, 0x4e, 0xd3, 0x7d, 0x3e, + 0x37, 0xd3, 0x7d, 0x3e, 0x45, 0xd3, 0x78, 0x0e, + 0x7d, 0x11, 0x01, 0x00, 0xed, 0x78, 0xf2, 0x2c, + 0xe0, 0x13, 0xed, 0x78, 0xfa, 0x31, 0xe0, 0x31, + 0x59, 0xe0, 0xc1, 0xe1, 0x37, 0xed, 0x52, 0x38, + 0xf9, 0xc1, 0x3e, 0x40, 0xd3, 0x7d, 0x78, 0xd3, + 0x7d, 0x3e, 0x37, 0xd3, 0x7d, 0x79, 0xd3, 0x78, + 0x3e, 0x64, 0x06, 0xc8, 0x10, 0xfe, 0x3d, 0x20, + 0xf9, 0x18, 0x1c, 0x16, 0x00, 0x0d, 0x4e, 0x2c, + 0x00, 0x1a, 0x4e, 0x57, 0x00, 0x34, 0x4e, 0xae, + 0x00, 0x68, 0x4e, 0x5c, 0x01, 0xd0, 0x4e, 0xb8, + 0x02, 0x68, 0x4f, 0xff, 0x7f, 0xd0, 0x4f, 0xdb, + 0x7c, 0xaf, 0x32, 0xfa, 0xff, 0x3c, 0x32, 0xc6, + 0xff, 0x31, 0xc0, 0xff, 0xcd, 0x2f, 0xe7, 0x0e, + 0x2e, 0xcd, 0x0c, 0xe0, 0xcd, 0x21, 0xe7, 0xfe, + 0x2e, 0x28, 0xee, 0xc5, 0xcd, 0x39, 0xe7, 0xcd, + 0xb6, 0xe7, 0xc1, 0x79, 0x21, 0x81, 0xe0, 0xe5, + 0x2a, 0xd2, 0xff, 0xed, 0x5b, 0xd0, 0xff, 0xfd, + 0x21, 0xc0, 0xff, 0xfe, 0x42, 0x20, 0x3c, 0xd5, + 0x3a, 0xc0, 0xff, 0xcb, 0x47, 0x28, 0x10, 0xed, + 0x5b, 0xc4, 0xff, 0x21, 0xc1, 0xff, 0x01, 0x03, + 0x00, 0xed, 0xb0, 0xaf, 0x32, 0xc0, 0xff, 0x3a, + 0xc7, 0xff, 0xa7, 0xe1, 0x28, 0x19, 0x22, 0xc4, + 0xff, 0xe5, 0x11, 0xc1, 0xff, 0x01, 0x03, 0x00, + 0xc5, 0xed, 0xb0, 0x21, 0xe8, 0xe0, 0xc1, 0xd1, + 0xed, 0xb0, 0x3e, 0x01, 0x32, 0xc0, 0xff, 0xc9, + 0xc3, 0xd3, 0xe5, 0xfe, 0x47, 0x20, 0x13, 0x3a, + 0xc7, 0xff, 0xa7, 0x28, 0x05, 0xed, 0x53, 0xfe, + 0xff, 0xf1, 0x3e, 0x01, 0x32, 0xc9, 0xff, 0xc3, + 0x14, 0xe6, 0xfe, 0x52, 0x20, 0x06, 0xcd, 0x16, + 0xe1, 0xc3, 0x2d, 0xf0, 0xfe, 0x57, 0x20, 0x27, + 0xcd, 0x16, 0xe1, 0xc3, 0x30, 0xf0, 0x3a, 0xc7, + 0xff, 0xfe, 0x05, 0xda, 0x49, 0xe4, 0xed, 0x53, + 0x40, 0x00, 0x7d, 0x21, 0x42, 0x00, 0x77, 0x23, + 0x3a, 0xd6, 0xff, 0x77, 0x23, 0x3a, 0xd4, 0xff, + 0x77, 0x23, 0x3a, 0xd8, 0xff, 0x77, 0xc9, 0xfe, + 0x5a, 0x20, 0x45, 0x3a, 0xc7, 0xff, 0x3d, 0xc2, + 0x49, 0xe4, 0x7b, 0x32, 0x42, 0x00, 0xaf, 0x32, + 0x44, 0x00, 0x3c, 0x32, 0x43, 0x00, 0xcd, 0x18, + 0xf0, 0xdb, 0x63, 0xf6, 0x50, 0xd3, 0x63, 0xcd, + 0x33, 0xf0, 0xcd, 0x6e, 0xe1, 0x28, 0x07, 0xcb, + 0xa7, 0xd3, 0x63, 0xcd, 0x33, 0xf0, 0xcd, 0x73, + 0xe1, 0x20, 0xe6, 0xc3, 0x18, 0xf0, 0xdb, 0x63, + 0xcb, 0x6f, 0xc9, 0x3a, 0x44, 0x00, 0x3c, 0x32, + 0x44, 0x00, 0x47, 0x3a, 0x40, 0xf0, 0xb8, 0xc9, + 0xfe, 0x51, 0x20, 0x34, 0x7b, 0x32, 0x42, 0x00, + 0xaf, 0x32, 0x44, 0x00, 0x3e, 0x01, 0x32, 0x43, + 0x00, 0x3a, 0x3f, 0xf0, 0x47, 0xcd, 0x6e, 0xe1, + 0x28, 0x02, 0xcb, 0x20, 0x78, 0x32, 0x45, 0x00, + 0x21, 0x00, 0x01, 0x22, 0x40, 0x00, 0xcd, 0x2d, + 0xf0, 0xcd, 0xbe, 0xe5, 0xcd, 0x73, 0xe1, 0x20, + 0xdb, 0x0e, 0x50, 0xcd, 0x0c, 0xe0, 0x18, 0xd0, + 0xfe, 0x4d, 0x20, 0x23, 0xcd, 0xcb, 0xe5, 0x44, + 0x4d, 0xeb, 0xed, 0x5b, 0xd4, 0xff, 0xb7, 0xed, + 0x52, 0x30, 0x0e, 0x2a, 0xd4, 0xff, 0x09, 0x2b, + 0x54, 0x5d, 0x2a, 0xd2, 0xff, 0xed, 0xb8, 0x18, + 0x05, 0x2a, 0xd0, 0xff, 0xed, 0xb0, 0xc9, 0xfe, + 0x43, 0xca, 0x03, 0xf0, 0xfe, 0x48, 0x20, 0x1d, + 0xe5, 0x19, 0x0e, 0x2b, 0xcd, 0x0c, 0xe0, 0xcd, + 0xf0, 0xe7, 0xcd, 0x39, 0xe7, 0xe1, 0xeb, 0xb7, + 0xed, 0x52, 0x0e, 0x2d, 0xcd, 0x0c, 0xe0, 0xcd, + 0xf0, 0xe7, 0xc3, 0x81, 0xe0, 0xfe, 0x58, 0x20, + 0x15, 0xaf, 0xbb, 0xc4, 0x3e, 0xe7, 0x3a, 0xc7, + 0xff, 0xfe, 0x02, 0x20, 0x06, 0x3a, 0xd2, 0xff, + 0x32, 0xc6, 0xff, 0xc3, 0x88, 0xe6, 0xfe, 0x49, + 0x20, 0x36, 0xfd, 0xcb, 0x0a, 0x86, 0x3a, 0xd0, + 0xff, 0x4f, 0x06, 0x01, 0x3a, 0xc7, 0xff, 0xfe, + 0x02, 0x38, 0x0b, 0x3a, 0xd2, 0xff, 0x47, 0xb7, + 0x20, 0x04, 0xfd, 0xcb, 0x0a, 0xc6, 0x79, 0xcd, + 0xf5, 0xe7, 0xed, 0x78, 0xcd, 0xf5, 0xe7, 0xcd, + 0xbe, 0xe5, 0xc5, 0xcd, 0x2f, 0xe7, 0xc1, 0xfd, + 0xcb, 0x0a, 0x46, 0x20, 0xe9, 0x10, 0xe7, 0xc9, + 0xfe, 0x4f, 0x20, 0x2e, 0xfd, 0xcb, 0x0a, 0x86, + 0x3a, 0xd0, 0xff, 0x4f, 0x3a, 0xd2, 0xff, 0x57, + 0x06, 0x01, 0x3a, 0xc7, 0xff, 0xfe, 0x03, 0x38, + 0x0b, 0x3a, 0xd4, 0xff, 0x47, 0xb7, 0x20, 0x04, + 0xfd, 0xcb, 0x0a, 0xc6, 0xed, 0x51, 0xcd, 0xbe, + 0xe5, 0xfd, 0xcb, 0x0a, 0x46, 0x20, 0xf5, 0x10, + 0xf3, 0xc9, 0xfe, 0x46, 0x20, 0x12, 0x3a, 0xd4, + 0xff, 0xe5, 0xcd, 0xcb, 0xe5, 0xe1, 0x12, 0xe5, + 0xb7, 0xed, 0x52, 0xe1, 0x13, 0x20, 0xf7, 0xc9, + 0xfe, 0x4c, 0x20, 0x4d, 0xcd, 0xcb, 0xe5, 0x3a, + 0xc7, 0xff, 0xd6, 0x03, 0xda, 0x49, 0xe4, 0x47, + 0x3c, 0x32, 0xc7, 0xff, 0x21, 0xd5, 0xff, 0x11, + 0xd6, 0xff, 0x1a, 0x77, 0x23, 0x13, 0x13, 0x10, + 0xf9, 0x3a, 0xc7, 0xff, 0x47, 0x2a, 0xd0, 0xff, + 0x11, 0xd4, 0xff, 0x1a, 0xbe, 0x20, 0x10, 0x23, + 0x13, 0x10, 0xf8, 0x2a, 0xd0, 0xff, 0xcd, 0xf0, + 0xe7, 0xcd, 0x2f, 0xe7, 0xcd, 0xbe, 0xe5, 0x2a, + 0xd2, 0xff, 0xed, 0x5b, 0xd0, 0xff, 0xb7, 0xed, + 0x52, 0xc8, 0x13, 0xed, 0x53, 0xd0, 0xff, 0x18, + 0xd0, 0xfe, 0x56, 0x20, 0x38, 0xcd, 0xcb, 0xe5, + 0xe5, 0xc1, 0xeb, 0xed, 0x5b, 0xd4, 0xff, 0x1a, + 0xed, 0xa1, 0x13, 0x20, 0x03, 0xe0, 0x18, 0xf7, + 0xf5, 0xc5, 0xd5, 0x2b, 0xcd, 0xf0, 0xe7, 0x7e, + 0x23, 0xcd, 0xf5, 0xe7, 0xd1, 0xd5, 0xe5, 0xeb, + 0x2b, 0xcd, 0xf0, 0xe7, 0x7e, 0xcd, 0x4d, 0xe7, + 0xcd, 0x2f, 0xe7, 0xe1, 0xd1, 0xc1, 0xf1, 0xe0, + 0xcd, 0xbe, 0xe5, 0x18, 0xd2, 0xfe, 0x54, 0x20, + 0x3f, 0xeb, 0x13, 0x06, 0x00, 0x2a, 0xd0, 0xff, + 0x7d, 0xac, 0xa8, 0x77, 0x23, 0xe5, 0xb7, 0xed, + 0x52, 0xe1, 0x20, 0xf4, 0x2a, 0xd0, 0xff, 0x7d, + 0xac, 0xa8, 0xbe, 0xc4, 0x61, 0xe3, 0x23, 0xe5, + 0xb7, 0xed, 0x52, 0xe1, 0x20, 0xf1, 0x04, 0xcd, + 0xbe, 0xe5, 0x0e, 0x50, 0xcd, 0x0c, 0xe0, 0x18, + 0xd4, 0xf5, 0xcd, 0xf0, 0xe7, 0xf1, 0xcd, 0xf5, + 0xe7, 0x7e, 0xcd, 0xf5, 0xe7, 0xc3, 0x2f, 0xe7, + 0xfe, 0x44, 0x20, 0x5d, 0xfd, 0xcb, 0x0a, 0x86, + 0xeb, 0x3a, 0xc7, 0xff, 0xfe, 0x02, 0x30, 0x09, + 0x11, 0xff, 0x00, 0xe5, 0x19, 0x22, 0xd2, 0xff, + 0xe1, 0xcd, 0x2f, 0xe7, 0xe5, 0xc1, 0xe5, 0x2a, + 0xd2, 0xff, 0xb7, 0xed, 0x42, 0xda, 0x49, 0xe4, + 0x01, 0x0f, 0x00, 0xb7, 0xed, 0x42, 0x06, 0x10, + 0x28, 0x06, 0xd2, 0xac, 0xe3, 0x7d, 0x80, 0x47, + 0xfd, 0xcb, 0x0a, 0xc6, 0xe1, 0xc5, 0xcd, 0xf0, + 0xe7, 0xc1, 0xcd, 0xa8, 0xe6, 0xcd, 0xbe, 0xe5, + 0xfd, 0xcb, 0x0a, 0x46, 0x28, 0xcb, 0xcd, 0x09, + 0xe0, 0xfe, 0x2e, 0xc8, 0xfe, 0x20, 0x20, 0xf6, + 0xfd, 0xcb, 0x0a, 0x86, 0xcd, 0x2f, 0xe7, 0x18, + 0xaf, 0xfe, 0x45, 0x20, 0x38, 0xeb, 0xcd, 0xf0, + 0xe7, 0x7e, 0xcd, 0x4d, 0xe7, 0x0e, 0x2d, 0xcd, + 0x0c, 0xe0, 0xe5, 0xcd, 0xb6, 0xe7, 0xe1, 0x3a, + 0xc8, 0xff, 0xfe, 0x2e, 0xc8, 0x3a, 0xc7, 0xff, + 0xe6, 0x03, 0x28, 0x0f, 0x3a, 0xd0, 0xff, 0x77, + 0x23, 0x3a, 0xc8, 0xff, 0xfe, 0x0d, 0x28, 0xd6, + 0x2b, 0x18, 0xd3, 0x3a, 0xc8, 0xff, 0xfe, 0x5e, + 0x28, 0xf6, 0x23, 0x18, 0xc9, 0xfe, 0x50, 0x20, + 0x34, 0x3a, 0xd0, 0xff, 0x4f, 0x79, 0xcd, 0xf5, + 0xe7, 0xed, 0x78, 0xcd, 0xf5, 0xe7, 0xc5, 0xcd, + 0xb6, 0xe7, 0xc1, 0x3a, 0xc8, 0xff, 0xfe, 0x2e, + 0xc8, 0x67, 0x3a, 0xc7, 0xff, 0xa7, 0x28, 0x0d, + 0x3a, 0xd0, 0xff, 0xed, 0x79, 0x3e, 0x5e, 0xbc, + 0x28, 0xdb, 0x0c, 0x18, 0xd8, 0x3e, 0x5e, 0xbc, + 0x20, 0xf8, 0x0d, 0x18, 0xd0, 0xfe, 0x53, 0x28, + 0x08, 0x0e, 0x3f, 0xcd, 0x0c, 0xe0, 0xc3, 0x81, + 0xe0, 0xf1, 0xcd, 0x3e, 0xe7, 0x3a, 0xc7, 0xff, + 0xa7, 0x28, 0x04, 0xeb, 0x22, 0xfe, 0xff, 0x3a, + 0xd2, 0xff, 0xa7, 0x20, 0x01, 0x3c, 0x32, 0xc9, + 0xff, 0xaf, 0x32, 0xc7, 0xff, 0xcd, 0xaf, 0xe0, + 0xed, 0x5b, 0xfe, 0xff, 0x1a, 0xfe, 0x40, 0x38, + 0x05, 0xfe, 0xc0, 0xda, 0x53, 0xe5, 0xe6, 0x03, + 0x47, 0x1a, 0x1f, 0x1f, 0xe6, 0x1f, 0xc5, 0x01, + 0x9a, 0xe5, 0x81, 0x6f, 0x60, 0xc1, 0x7e, 0x04, + 0x10, 0x29, 0xe6, 0x03, 0xca, 0x3e, 0xe5, 0xf5, + 0x1a, 0x2a, 0xf4, 0xff, 0xfe, 0xe9, 0xca, 0x07, + 0xe6, 0xfe, 0xc3, 0x28, 0x70, 0xfe, 0xcd, 0x28, + 0x6c, 0xfe, 0xc9, 0x28, 0x5f, 0xfe, 0x10, 0x20, + 0x10, 0x21, 0xf9, 0xff, 0x35, 0x20, 0x47, 0x34, + 0xc3, 0x74, 0xe5, 0xcb, 0x3f, 0xcb, 0x3f, 0x18, + 0xcf, 0xfe, 0x18, 0x28, 0x39, 0xfe, 0x80, 0x30, + 0x0a, 0xee, 0x20, 0x47, 0xe6, 0x67, 0x20, 0xe8, + 0x78, 0x18, 0x0d, 0xe6, 0xc7, 0xfe, 0xc2, 0x28, + 0x06, 0xe6, 0xc3, 0xfe, 0xc0, 0x20, 0x43, 0x1a, + 0xe6, 0x30, 0x21, 0xb9, 0xe5, 0x23, 0xd6, 0x10, + 0x30, 0xfb, 0x3a, 0xfc, 0xff, 0xa6, 0x1a, 0x28, + 0x01, 0x2f, 0xcb, 0x5f, 0x20, 0xc2, 0xf1, 0xf5, + 0xfe, 0x02, 0x38, 0x10, 0x20, 0x17, 0x13, 0x1a, + 0x13, 0x6f, 0x17, 0x26, 0x00, 0x30, 0x01, 0x25, + 0x19, 0x18, 0x30, 0x1b, 0x2a, 0xe6, 0xff, 0x7e, + 0x23, 0x66, 0x6f, 0x18, 0x64, 0x1a, 0xcb, 0x57, + 0x13, 0x1a, 0x6f, 0x13, 0x1a, 0x67, 0x20, 0x59, + 0x18, 0x19, 0x1a, 0xe6, 0xc7, 0xfe, 0xc7, 0x1a, + 0x20, 0x07, 0xe6, 0x38, 0x6f, 0x26, 0x00, 0x18, + 0x48, 0xfe, 0xfb, 0x20, 0x3f, 0x2f, 0x32, 0xfa, + 0xff, 0xeb, 0x23, 0xc3, 0x07, 0xe6, 0x1a, 0x13, + 0xfe, 0xed, 0x20, 0x13, 0x06, 0x03, 0x1a, 0xe6, + 0xf7, 0xfe, 0x45, 0x28, 0xbe, 0xe6, 0xc7, 0xfe, + 0x43, 0x28, 0x22, 0x06, 0x01, 0x18, 0x1e, 0xfe, + 0xdd, 0x2a, 0xea, 0xff, 0x28, 0x03, 0x2a, 0xe8, + 0xff, 0x1a, 0xfe, 0xe9, 0x28, 0xd5, 0x21, 0x95, + 0xe5, 0x01, 0x05, 0x00, 0xed, 0xb1, 0x20, 0x0f, + 0x04, 0x04, 0x04, 0xc5, 0xc1, 0xeb, 0x23, 0x10, + 0xfd, 0xcd, 0xce, 0xe0, 0xc3, 0x14, 0xe6, 0xe6, + 0xfe, 0xfe, 0x34, 0x28, 0xec, 0x1a, 0xe6, 0x07, + 0xfe, 0x06, 0x28, 0xe5, 0x1a, 0xe6, 0xf8, 0xfe, + 0x70, 0x28, 0xde, 0x18, 0xdd, 0x21, 0x22, 0x2a, + 0x36, 0xcb, 0x5d, 0x65, 0x55, 0x65, 0x5e, 0x65, + 0x56, 0x65, 0x7e, 0x65, 0x76, 0x65, 0x7e, 0x65, + 0x76, 0x65, 0xf5, 0x67, 0xb5, 0x6f, 0xb5, 0x67, + 0xb5, 0x63, 0x75, 0x67, 0x75, 0x63, 0x75, 0x67, + 0x75, 0x63, 0x40, 0x01, 0x04, 0x80, 0xcd, 0x06, + 0xe0, 0xc8, 0xcd, 0x09, 0xe0, 0xfe, 0x2e, 0xc0, + 0xc3, 0x81, 0xe0, 0xb7, 0xed, 0x52, 0xda, 0x49, + 0xe4, 0x23, 0xc9, 0xed, 0x73, 0xe6, 0xff, 0x31, + 0x00, 0x00, 0xf5, 0xf5, 0xb7, 0xed, 0x57, 0xf5, + 0xf3, 0xc5, 0xd5, 0xe5, 0xd9, 0x08, 0xf5, 0xc5, + 0xd5, 0xe5, 0x3a, 0xfa, 0xff, 0xe6, 0x04, 0x32, + 0xfa, 0xff, 0xd9, 0x08, 0xdd, 0xe5, 0xfd, 0xe5, + 0x21, 0xc1, 0xff, 0xed, 0x5b, 0xc4, 0xff, 0x01, + 0x03, 0x00, 0xed, 0xb0, 0x2a, 0xc4, 0xff, 0x3e, + 0x80, 0x32, 0xc0, 0xff, 0x22, 0xfe, 0xff, 0x31, + 0xc0, 0xff, 0x18, 0x32, 0xed, 0x7b, 0xe6, 0xff, + 0x2a, 0xfe, 0xff, 0xe5, 0x2a, 0xfc, 0xff, 0xe5, + 0xed, 0x73, 0xe4, 0xff, 0x31, 0xe8, 0xff, 0xfd, + 0xe1, 0xdd, 0xe1, 0xd9, 0x08, 0xe1, 0xd1, 0xc1, + 0xf1, 0xd9, 0x08, 0xe1, 0xd1, 0xc1, 0xf1, 0xed, + 0x47, 0xed, 0x7b, 0xe4, 0xff, 0xea, 0x43, 0xe6, + 0xf1, 0xf3, 0xc9, 0xf1, 0xfb, 0xc9, 0xcd, 0x88, + 0xe6, 0x3a, 0xc0, 0xff, 0x47, 0xaf, 0x32, 0xc0, + 0xff, 0x21, 0xc9, 0xff, 0x35, 0x28, 0x07, 0xcd, + 0x06, 0xe0, 0x20, 0x02, 0x18, 0x23, 0xcd, 0x21, + 0xe7, 0xfe, 0x2e, 0xca, 0x81, 0xe0, 0xfe, 0x0d, + 0x28, 0x15, 0xfe, 0x20, 0x20, 0xf0, 0xaf, 0xbe, + 0x36, 0x00, 0x20, 0xea, 0xcd, 0x2f, 0xe7, 0xcd, + 0x3e, 0xe7, 0x3e, 0x0b, 0xc3, 0x66, 0xe4, 0x36, + 0x01, 0xcd, 0x2f, 0xe7, 0x7e, 0xc3, 0x66, 0xe4, + 0x2a, 0xfe, 0xff, 0xcd, 0xf0, 0xe7, 0x3a, 0xc6, + 0xff, 0x1f, 0x06, 0x01, 0x30, 0x02, 0x06, 0x0c, + 0x21, 0xfd, 0xff, 0x7e, 0xcd, 0x4d, 0xe7, 0x2b, + 0x7e, 0xcd, 0xf5, 0xe7, 0x2b, 0x10, 0xf4, 0xc9, + 0xc5, 0xe5, 0x7e, 0xcd, 0xf5, 0xe7, 0x23, 0x10, + 0xf9, 0xcd, 0x39, 0xe7, 0xcd, 0x39, 0xe7, 0xe1, + 0xc1, 0x7e, 0xe6, 0x7f, 0x4f, 0xfe, 0x20, 0x38, + 0x04, 0xfe, 0x7b, 0x38, 0x02, 0x0e, 0x2e, 0xcd, + 0x0c, 0xe0, 0x23, 0x10, 0xec, 0xc9, 0x50, 0x43, + 0x20, 0x20, 0x20, 0x41, 0x46, 0x20, 0x20, 0x49, + 0x20, 0x49, 0x46, 0x20, 0x20, 0x42, 0x43, 0x20, + 0x20, 0x20, 0x44, 0x45, 0x20, 0x20, 0x20, 0x48, + 0x4c, 0x20, 0x20, 0x41, 0x27, 0x46, 0x27, 0x20, + 0x42, 0x27, 0x43, 0x27, 0x20, 0x44, 0x27, 0x45, + 0x27, 0x20, 0x48, 0x27, 0x4c, 0x27, 0x20, 0x20, + 0x49, 0x58, 0x20, 0x20, 0x20, 0x49, 0x59, 0x20, + 0x20, 0x20, 0x53, 0x50, 0x0d, 0x0a, 0x03, 0xdb, + 0x7d, 0xe6, 0x02, 0xc8, 0x3e, 0xff, 0xc9, 0xcd, + 0x0f, 0xe7, 0x28, 0xfb, 0xdb, 0x7c, 0xe6, 0x7f, + 0xc9, 0xcd, 0x09, 0xe0, 0x4f, 0xdb, 0x7d, 0xe6, + 0x01, 0x28, 0xfa, 0x79, 0xd3, 0x7c, 0xc9, 0x0e, + 0x0d, 0xcd, 0x0c, 0xe0, 0x0e, 0x0a, 0xc3, 0x0c, + 0xe0, 0x0e, 0x20, 0xc3, 0x0c, 0xe0, 0x21, 0xce, + 0xe6, 0x7e, 0xfe, 0x03, 0xc8, 0x4f, 0xcd, 0x0c, + 0xe0, 0x23, 0x18, 0xf5, 0x00, 0xf5, 0x0f, 0x0f, + 0x0f, 0x0f, 0xcd, 0x56, 0xe7, 0xf1, 0xe6, 0x0f, + 0xc6, 0x90, 0x27, 0xce, 0x40, 0x27, 0x4f, 0xc3, + 0x0c, 0xe0, 0xd6, 0x30, 0xfe, 0x0a, 0xf8, 0xd6, + 0x07, 0xc9, 0xfe, 0x30, 0x38, 0x0e, 0xfe, 0x3a, + 0x38, 0x08, 0xfe, 0x40, 0x38, 0x06, 0xfe, 0x47, + 0x30, 0x02, 0xaf, 0xc9, 0xaf, 0x3c, 0xc9, 0xfe, + 0x20, 0xc8, 0xfe, 0x5e, 0x28, 0x08, 0xfe, 0x2e, + 0xca, 0x81, 0xe0, 0xfe, 0x0d, 0xc0, 0xc5, 0xcd, + 0x2f, 0xe7, 0xc1, 0xaf, 0xc9, 0xe5, 0xe5, 0xe5, + 0xe5, 0xe5, 0x21, 0x00, 0x00, 0x45, 0xcd, 0x21, + 0xe7, 0x04, 0xcd, 0x7f, 0xe7, 0xc8, 0xcd, 0x6a, + 0xe7, 0xc0, 0x79, 0xcd, 0x62, 0xe7, 0x29, 0x29, + 0x29, 0x29, 0x85, 0x6f, 0x18, 0xe8, 0xaf, 0x21, + 0xd0, 0xff, 0xe5, 0xdd, 0xe1, 0x77, 0x01, 0x09, + 0x00, 0x11, 0xd1, 0xff, 0xed, 0xb0, 0x32, 0xc7, + 0xff, 0xcd, 0x9a, 0xe7, 0xc2, 0x49, 0xe4, 0x79, + 0x32, 0xc8, 0xff, 0xfe, 0x20, 0x28, 0x02, 0x05, + 0xc8, 0xdd, 0x75, 0x00, 0xdd, 0x74, 0x01, 0x3a, + 0xc7, 0xff, 0x3c, 0x32, 0xc7, 0xff, 0xdd, 0x23, + 0xdd, 0x23, 0x79, 0xfe, 0x20, 0x28, 0xda, 0xc9, + 0x7c, 0xcd, 0x4d, 0xe7, 0x7d, 0xc5, 0xcd, 0x4d, + 0xe7, 0xcd, 0x39, 0xe7, 0xc1, 0xc9, 0xff, 0xff, + }; + +/* SBC-200 DDBIOS 3.3 @ F000 */ +static uint8 sbc200_ddb_rom[SBC200_DDB_SIZE] = { + 0x00, 0x1e, 0x00, 0xc3, 0x6c, 0xf0, 0xc3, 0x06, + 0xe0, 0xc3, 0x09, 0xe0, 0xc3, 0x0c, 0xe0, 0xc3, + 0x1b, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc3, 0x6a, 0xf1, 0xc3, 0x79, 0xf1, 0xc3, 0x7e, + 0xf1, 0xc3, 0x83, 0xf1, 0xc3, 0x88, 0xf1, 0xc3, + 0x8d, 0xf1, 0xc3, 0xa9, 0xf1, 0xc3, 0xc3, 0xf3, + 0xc3, 0xd0, 0xf3, 0xc3, 0x14, 0xf4, 0x00, 0x3a, + 0x80, 0x00, 0x5f, 0x16, 0x00, 0x21, 0x80, 0x1a, + 0x4d, 0x49, 0x06, 0x0b, 0x1b, 0x08, 0x18, 0x1c, + 0x32, 0x4d, 0x54, 0x08, 0x16, 0x10, 0x08, 0x18, + 0x1c, 0x1a, 0x4d, 0x2e, 0x08, 0x16, 0x36, 0x08, + 0x18, 0x1c, 0x12, 0x23, 0x0c, 0x06, 0x0b, 0x08, + 0x0b, 0x1b, 0x1f, 0x1d, 0x23, 0x54, 0x08, 0x16, + 0x10, 0x0b, 0x1b, 0x1f, 0x31, 0x80, 0x00, 0xdb, + 0x7f, 0x3e, 0x00, 0x32, 0x42, 0x00, 0x7b, 0xfe, + 0x04, 0xd2, 0x03, 0xe8, 0xf5, 0x3e, 0xef, 0xe5, + 0xed, 0x73, 0x4d, 0x00, 0xe1, 0x0f, 0x30, 0x1a, + 0xd3, 0x63, 0xcd, 0xb7, 0xf3, 0xe6, 0x04, 0x28, + 0x0d, 0xaf, 0xd3, 0x65, 0x3e, 0x02, 0x32, 0x44, + 0x00, 0x3e, 0x1a, 0xcd, 0x0a, 0xf3, 0xdb, 0x63, + 0x18, 0xe3, 0xf1, 0xf5, 0xcd, 0x6a, 0xf1, 0xf1, + 0xcd, 0x1b, 0xf1, 0x20, 0x19, 0xcd, 0xd0, 0xf0, + 0x21, 0x80, 0x00, 0x7e, 0xe6, 0xce, 0x20, 0x05, + 0xcb, 0x46, 0xc2, 0x80, 0x00, 0x3e, 0x31, 0x21, + 0x00, 0xe8, 0xbe, 0xca, 0x00, 0xe8, 0x3e, 0xe0, + 0x21, 0x02, 0xe0, 0xbe, 0xca, 0x03, 0xe0, 0x76, + 0x21, 0x80, 0x00, 0x22, 0x40, 0x00, 0xaf, 0x32, + 0x44, 0x00, 0x3c, 0x32, 0x43, 0x00, 0xcd, 0x8d, + 0xf1, 0xc8, 0x18, 0xe2, 0x3a, 0x42, 0x00, 0x5f, + 0x3e, 0x04, 0xb8, 0x28, 0x19, 0x3d, 0xb8, 0x28, + 0x0c, 0x3d, 0xb8, 0x28, 0x18, 0x3d, 0xb8, 0x28, + 0x1b, 0xf6, 0x01, 0xe1, 0xc9, 0x7b, 0xcb, 0xb7, + 0xcb, 0xbf, 0x32, 0x42, 0x00, 0xc9, 0x7b, 0xcb, + 0xff, 0x32, 0x42, 0x00, 0xc9, 0x7b, 0xcb, 0xef, + 0x32, 0x42, 0x00, 0xc9, 0x7b, 0xcb, 0xf7, 0x32, + 0x42, 0x00, 0xc9, 0x06, 0x04, 0xe6, 0x0f, 0xf6, + 0x40, 0x32, 0x42, 0x00, 0x2a, 0x40, 0x00, 0x22, + 0x51, 0x00, 0xcd, 0x4d, 0xf1, 0x2a, 0x51, 0x00, + 0x22, 0x40, 0x00, 0x3a, 0x42, 0x00, 0xc0, 0x30, + 0x02, 0xcb, 0xff, 0x5f, 0x3a, 0x49, 0x00, 0xb7, + 0x7b, 0xcb, 0xa7, 0xf2, 0x48, 0xf1, 0xcb, 0xe7, + 0x32, 0x42, 0x00, 0xaf, 0xc9, 0xc5, 0xdd, 0xe5, + 0xe5, 0xed, 0x73, 0x4d, 0x00, 0xe1, 0xcd, 0xe4, + 0xf1, 0xcd, 0x37, 0xf2, 0xdd, 0xe1, 0xc1, 0x3a, + 0x4b, 0x00, 0x0f, 0xc8, 0x05, 0xcd, 0xe4, 0xf0, + 0x18, 0xe3, 0xed, 0x73, 0x4d, 0x00, 0xdd, 0xe5, + 0xcd, 0xe4, 0xf1, 0xcd, 0xd8, 0xf1, 0xdd, 0xe1, + 0xc9, 0x79, 0x32, 0x42, 0x00, 0xc9, 0x79, 0x32, + 0x44, 0x00, 0xc9, 0x79, 0x32, 0x43, 0x00, 0xc9, + 0xed, 0x43, 0x40, 0x00, 0xc9, 0x01, 0x01, 0x03, + 0xed, 0x43, 0x56, 0x00, 0xdd, 0xe5, 0xdd, 0x2a, + 0x53, 0x00, 0xc5, 0xcd, 0x6c, 0xf2, 0xc1, 0x28, + 0x05, 0xcd, 0xc2, 0xf1, 0x18, 0xf4, 0xdd, 0xe1, + 0xc9, 0x01, 0x01, 0x03, 0xed, 0x43, 0x56, 0x00, + 0xdd, 0xe5, 0xdd, 0x2a, 0x53, 0x00, 0xc5, 0xcd, + 0xbd, 0xf2, 0xc1, 0x28, 0xe9, 0xcd, 0xc2, 0xf1, + 0x18, 0xf4, 0x10, 0x13, 0x3a, 0x57, 0x00, 0x47, + 0x0d, 0xf2, 0xd2, 0xf1, 0xf1, 0xdd, 0xe1, 0xaf, + 0x3c, 0xc9, 0xc5, 0xcd, 0xd8, 0xf1, 0xc1, 0xc9, + 0xed, 0x73, 0x4d, 0x00, 0xdd, 0x7e, 0x06, 0xcd, + 0x0a, 0xf3, 0xaf, 0xc9, 0x11, 0x42, 0x00, 0x1a, + 0xe6, 0xe0, 0x4f, 0x1a, 0xe6, 0x03, 0x47, 0x3e, + 0x01, 0x28, 0x03, 0x07, 0x10, 0xfd, 0xb1, 0xe6, + 0x7f, 0x47, 0x79, 0xdd, 0x21, 0x3f, 0xf0, 0xfe, + 0x00, 0x28, 0x1c, 0xdd, 0x21, 0x48, 0xf0, 0xfe, + 0x40, 0x28, 0x14, 0xdd, 0x21, 0x51, 0xf0, 0xfe, + 0xc0, 0x28, 0x0c, 0xdd, 0x21, 0x5a, 0xf0, 0xfe, + 0x20, 0x28, 0x04, 0xdd, 0x21, 0x63, 0xf0, 0xdd, + 0x22, 0x53, 0x00, 0xc5, 0xf1, 0x2f, 0xd3, 0x63, + 0x1a, 0x32, 0x55, 0x00, 0xcd, 0x59, 0xf2, 0xdb, + 0x64, 0xe6, 0x80, 0xc2, 0xe1, 0xf2, 0xc9, 0xcd, + 0x42, 0xf3, 0x21, 0x48, 0x00, 0x01, 0x67, 0x06, + 0x3e, 0xf8, 0x32, 0x46, 0x00, 0xcd, 0x75, 0xf3, + 0x3e, 0xc0, 0xcd, 0x77, 0xf2, 0x3a, 0x48, 0x00, + 0xfe, 0x4d, 0xd2, 0xfe, 0xf2, 0xd3, 0x65, 0xaf, + 0xc9, 0x3a, 0x42, 0x00, 0xcb, 0x6f, 0x3e, 0x27, + 0x28, 0x02, 0x3e, 0x3c, 0x06, 0x00, 0x10, 0xfe, + 0x3d, 0x20, 0xf9, 0xc9, 0xcd, 0x9c, 0xf2, 0x3e, + 0x88, 0xcd, 0x90, 0xf2, 0x2a, 0x40, 0x00, 0x32, + 0x4c, 0x00, 0xed, 0x5b, 0x4c, 0x00, 0xd5, 0xf3, + 0xd3, 0x64, 0x18, 0x00, 0x18, 0x00, 0xed, 0xb2, + 0xd1, 0xed, 0x53, 0x4c, 0x00, 0xfb, 0x18, 0x45, + 0x67, 0x3a, 0x49, 0x00, 0x6f, 0xe6, 0x01, 0x7c, + 0xc8, 0xcb, 0xcf, 0xc9, 0xe1, 0xed, 0x73, 0x4d, + 0x00, 0xe5, 0x3a, 0x42, 0x00, 0x57, 0x3a, 0x55, + 0x00, 0xba, 0x28, 0x06, 0xcd, 0xe4, 0xf1, 0xcd, + 0x37, 0xf2, 0xcd, 0xf1, 0xf2, 0x3e, 0xfe, 0x32, + 0x46, 0x00, 0xc3, 0x7c, 0xf3, 0xcd, 0x9c, 0xf2, + 0x3e, 0xa8, 0xcd, 0x90, 0xf2, 0x2a, 0x40, 0x00, + 0x32, 0x4c, 0x00, 0xf3, 0xd3, 0x64, 0x18, 0x00, + 0x18, 0x00, 0xed, 0xb3, 0xfb, 0xcd, 0x42, 0xf3, + 0xdb, 0x64, 0x57, 0x3a, 0x46, 0x00, 0xa2, 0xc8, + 0x7a, 0x32, 0x47, 0x00, 0xcd, 0x59, 0xf2, 0xf6, + 0x01, 0xed, 0x7b, 0x4d, 0x00, 0xcd, 0xf4, 0xf7, + 0xc9, 0xcd, 0x2f, 0xf2, 0xdd, 0x7e, 0x01, 0x4f, + 0x3a, 0x44, 0x00, 0xb9, 0x38, 0x04, 0x3e, 0x0f, + 0x18, 0xdf, 0x4f, 0xdb, 0x65, 0xb9, 0xc8, 0xdd, + 0x7e, 0x08, 0x32, 0x4c, 0x00, 0x06, 0xd2, 0x10, + 0xfe, 0xcd, 0x42, 0xf3, 0x3a, 0x44, 0x00, 0xd3, + 0x67, 0x3e, 0x80, 0x32, 0x46, 0x00, 0x3a, 0x4c, + 0x00, 0xd3, 0x64, 0x06, 0x0a, 0x10, 0xfe, 0xcd, + 0xd5, 0xf2, 0xcd, 0x59, 0xf2, 0x3a, 0x4c, 0x00, + 0xdd, 0xbe, 0x06, 0xc8, 0xdb, 0x64, 0xe6, 0x10, + 0x20, 0x04, 0xdb, 0x65, 0xb9, 0xc8, 0x3e, 0x20, + 0x18, 0x9f, 0x1e, 0x00, 0xc5, 0x0e, 0x02, 0xdb, + 0x64, 0xe6, 0x01, 0x28, 0x20, 0x10, 0xf8, 0x1d, + 0x20, 0xf5, 0x0d, 0x20, 0xf2, 0xc1, 0xdb, 0x63, + 0xf6, 0x80, 0xd3, 0x60, 0x10, 0xfe, 0xdb, 0x60, + 0xcd, 0xb7, 0xf3, 0xdd, 0x7e, 0x06, 0xcd, 0x0a, + 0xf3, 0x3e, 0xfe, 0x18, 0xd3, 0xc1, 0xdb, 0x63, + 0xf6, 0x80, 0xd3, 0x63, 0xc9, 0xdb, 0x63, 0xe6, + 0x7f, 0xd3, 0x63, 0xc9, 0x06, 0x00, 0xdd, 0x7e, + 0x00, 0x3c, 0x4f, 0x3a, 0x43, 0x00, 0xb9, 0x38, + 0x04, 0x06, 0x10, 0x0d, 0x91, 0xf5, 0xcd, 0xa1, + 0xf3, 0xf1, 0xd3, 0x66, 0x01, 0x67, 0x80, 0x3a, + 0x42, 0x00, 0x07, 0x30, 0xd8, 0x06, 0x00, 0x18, + 0xd4, 0xdb, 0x63, 0x2f, 0x5f, 0xe6, 0x10, 0xb8, + 0xc8, 0x7b, 0xe6, 0x6f, 0xb0, 0x2f, 0xd3, 0x63, + 0x06, 0xd2, 0x10, 0xfe, 0xc3, 0x37, 0xf2, 0x3e, + 0xd0, 0xd3, 0x64, 0x3e, 0x0a, 0x3d, 0x20, 0xfd, + 0xdb, 0x64, 0xc9, 0xcd, 0xf4, 0xf7, 0xcd, 0x8d, + 0xf1, 0xc0, 0xcd, 0xdd, 0xf3, 0x20, 0xf7, 0xc9, + 0xcd, 0xf4, 0xf7, 0xcd, 0xa9, 0xf1, 0xc0, 0xcd, + 0xdd, 0xf3, 0x20, 0xf7, 0xc9, 0x2a, 0x40, 0x00, + 0x11, 0x80, 0x00, 0x3a, 0x42, 0x00, 0x07, 0x30, + 0x03, 0x11, 0x00, 0x01, 0x19, 0x22, 0x40, 0x00, + 0x21, 0x45, 0x00, 0x35, 0xc8, 0x2b, 0x2b, 0x34, + 0x3a, 0x49, 0x00, 0xb7, 0xdd, 0xe5, 0xdd, 0x2a, + 0x53, 0x00, 0xdd, 0x7e, 0x00, 0xdd, 0xe1, 0xf2, + 0x0b, 0xf4, 0x07, 0x3c, 0xbe, 0xc0, 0x36, 0x01, + 0x23, 0x34, 0xb7, 0xc9, 0xcd, 0xf4, 0xf7, 0xed, + 0x73, 0x51, 0x00, 0xfd, 0xe5, 0xdd, 0xe5, 0xe5, + 0x21, 0x00, 0x08, 0x22, 0x40, 0x00, 0xed, 0x73, + 0x4d, 0x00, 0xe1, 0xcd, 0xe4, 0xf1, 0xcc, 0xd8, + 0xf1, 0x20, 0x30, 0xcd, 0x6a, 0xf4, 0xcd, 0xd9, + 0xf4, 0xaf, 0xcd, 0x0d, 0xf5, 0xc5, 0xcd, 0x61, + 0xf5, 0xcd, 0xaa, 0xf5, 0xc1, 0x20, 0x05, 0xdd, + 0xbe, 0x01, 0x20, 0xee, 0x3e, 0x4c, 0x32, 0x44, + 0x00, 0xdb, 0x63, 0xcb, 0xaf, 0xd3, 0x63, 0x11, + 0x55, 0x00, 0x1a, 0xcb, 0xef, 0x12, 0xdd, 0xe1, + 0xfd, 0xe1, 0xc9, 0xdd, 0xe1, 0xed, 0x7b, 0x51, + 0x00, 0xc9, 0x2a, 0x40, 0x00, 0xdd, 0x7e, 0x04, + 0x0f, 0x3e, 0x4e, 0xd2, 0x78, 0xf4, 0x3e, 0xff, + 0x5f, 0xdd, 0x46, 0x02, 0xcd, 0xd4, 0xf4, 0xdd, + 0x46, 0x05, 0xcd, 0xd4, 0xf4, 0xaf, 0xdd, 0x46, + 0x03, 0xcd, 0xd4, 0xf4, 0xcd, 0xcd, 0xf4, 0x3e, + 0xfe, 0x77, 0x23, 0xe5, 0xd9, 0xd1, 0xd9, 0xaf, + 0x06, 0x04, 0xcd, 0xd4, 0xf4, 0x3e, 0xf7, 0x77, + 0x23, 0xdd, 0x46, 0x04, 0x78, 0xcb, 0x3f, 0x3c, + 0x4f, 0x7b, 0xcd, 0xd4, 0xf4, 0x41, 0xaf, 0xcd, + 0xd4, 0xf4, 0xcd, 0xcd, 0xf4, 0x3e, 0xfb, 0x77, + 0x23, 0x06, 0x80, 0x3a, 0x42, 0x00, 0x07, 0x30, + 0x02, 0x06, 0x00, 0x3e, 0xe5, 0xcd, 0xd4, 0xf4, + 0x3e, 0xf7, 0x77, 0x23, 0xc9, 0x7b, 0xb7, 0xf8, + 0x3e, 0xf5, 0x06, 0x03, 0x77, 0x23, 0x10, 0xfc, + 0xc9, 0xd9, 0xdd, 0x46, 0x00, 0x05, 0xd9, 0xed, + 0x5b, 0x40, 0x00, 0xe5, 0xb7, 0xdd, 0x4e, 0x02, + 0x06, 0x00, 0xeb, 0x09, 0xeb, 0xed, 0x52, 0xe5, + 0xc1, 0xe1, 0xc5, 0xeb, 0xd9, 0xd9, 0xed, 0xb0, + 0xc1, 0xc5, 0xd9, 0x10, 0xf8, 0xd9, 0xc1, 0xeb, + 0x3a, 0x42, 0x00, 0xcb, 0x67, 0x3e, 0x80, 0x20, + 0x01, 0xaf, 0x6f, 0x65, 0xc9, 0xe5, 0xc5, 0xd9, + 0xc1, 0xe1, 0xd5, 0xfd, 0xe1, 0xd5, 0x08, 0x16, + 0x01, 0xdd, 0x7e, 0x00, 0xfe, 0x1a, 0x3e, 0x01, + 0x28, 0x08, 0xdd, 0x7e, 0x00, 0xcb, 0x3f, 0x3c, + 0x30, 0x26, 0x08, 0xfd, 0x77, 0x00, 0xb5, 0xf2, + 0x37, 0xf5, 0xfd, 0x74, 0x01, 0xcb, 0xbf, 0x08, + 0xfd, 0x77, 0x02, 0xf5, 0x3a, 0x42, 0x00, 0x07, + 0x30, 0x05, 0x3e, 0x01, 0xfd, 0x77, 0x03, 0xf1, + 0xdd, 0xbe, 0x00, 0x28, 0x10, 0xfd, 0x09, 0x3c, + 0x5f, 0x3e, 0x1a, 0xdd, 0xbe, 0x00, 0x7b, 0x28, + 0xd1, 0x7a, 0x53, 0x18, 0xcd, 0x08, 0xd1, 0xd9, + 0xc9, 0xd5, 0xc5, 0xd9, 0x08, 0xc1, 0xd5, 0x5f, + 0x51, 0xdd, 0x46, 0x02, 0x0e, 0x67, 0x2a, 0x40, + 0x00, 0x3e, 0x80, 0x32, 0x46, 0x00, 0xcd, 0x75, + 0xf3, 0x3a, 0x42, 0x00, 0x07, 0x3e, 0x00, 0x30, + 0x02, 0x3e, 0x01, 0x32, 0x50, 0x00, 0x3e, 0xf4, + 0x32, 0x4c, 0x00, 0xf3, 0xd3, 0x64, 0x3a, 0x50, + 0x00, 0xed, 0xb3, 0x42, 0xed, 0xb3, 0xb7, 0x28, + 0x02, 0xed, 0xb3, 0x42, 0x1d, 0x20, 0xf5, 0xd1, + 0xe1, 0x7e, 0xed, 0x79, 0x10, 0xfc, 0xfb, 0xd9, + 0x08, 0xc9, 0x08, 0x3e, 0x2f, 0x10, 0xfe, 0x3d, + 0x20, 0xfb, 0xdb, 0x63, 0xf6, 0x80, 0xcb, 0x7d, + 0x28, 0x10, 0xcb, 0x44, 0xcb, 0x84, 0x20, 0x0a, + 0x24, 0xcb, 0xa7, 0xd3, 0x63, 0x10, 0xfe, 0x08, + 0xbf, 0xc9, 0xcb, 0xe7, 0xd3, 0x63, 0x08, 0x3c, + 0xdd, 0xbe, 0x01, 0xc8, 0x32, 0x44, 0x00, 0xe5, + 0xed, 0x73, 0x4d, 0x00, 0xe1, 0x10, 0xfe, 0xd9, + 0xeb, 0xdd, 0x7e, 0x07, 0xcd, 0x0a, 0xf3, 0xeb, + 0xd9, 0xdb, 0x65, 0x47, 0x3a, 0x44, 0x00, 0xb8, + 0x06, 0x00, 0xc9, 0x05, 0x58, 0xff, 0xff, 0x05, + 0x26, 0x16, 0x71, 0x00, 0x79, 0xff, 0xff, 0x05, + 0x31, 0x80, 0x00, 0x21, 0x5b, 0xf6, 0xcd, 0x38, + 0xf6, 0xcd, 0x41, 0xf6, 0x20, 0x20, 0x7d, 0x32, + 0x42, 0x00, 0x7c, 0x32, 0x39, 0x00, 0xcd, 0x18, + 0xf0, 0xdd, 0x2a, 0x53, 0x00, 0x3a, 0x39, 0x00, + 0xcd, 0x7c, 0xf6, 0xcd, 0x18, 0xf0, 0x21, 0x6e, + 0xf6, 0xcd, 0x38, 0xf6, 0x18, 0xd2, 0x0e, 0x3f, + 0xcd, 0x0c, 0xf0, 0xcd, 0xc6, 0xf6, 0x18, 0xc8, + 0xcd, 0x53, 0xf6, 0xc2, 0x41, 0xe7, 0xc3, 0x07, + 0xe7, 0xcd, 0x53, 0xf6, 0xc2, 0x9a, 0xe7, 0xc3, + 0x60, 0xe7, 0xcd, 0x53, 0xf6, 0xc2, 0x4d, 0xe7, + 0xc3, 0x13, 0xe7, 0x47, 0x3a, 0x28, 0xe0, 0xfe, + 0x01, 0x78, 0xc9, 0x54, 0x45, 0x53, 0x54, 0x23, + 0x44, 0x52, 0x56, 0x23, 0x20, 0x28, 0x54, 0x54, + 0x44, 0x44, 0x29, 0x3a, 0x20, 0x03, 0x0a, 0x0d, + 0x54, 0x41, 0x53, 0x4b, 0x20, 0x44, 0x4f, 0x4e, + 0x45, 0x0d, 0x0a, 0x03, 0xfe, 0x00, 0xca, 0xd1, + 0xf7, 0xfe, 0x05, 0x28, 0x19, 0x38, 0x60, 0xfe, + 0xff, 0xc0, 0x21, 0x94, 0xf6, 0xcd, 0x38, 0xf6, + 0xcd, 0x41, 0xf6, 0xe9, 0x41, 0x44, 0x44, 0x52, + 0x45, 0x53, 0x53, 0x3a, 0x20, 0x03, 0xcd, 0x33, + 0xf0, 0xc9, 0x21, 0xcf, 0xf6, 0xcd, 0x38, 0xf6, + 0x3a, 0x4c, 0x00, 0xcd, 0xec, 0xf7, 0x3a, 0x47, + 0x00, 0xcd, 0xec, 0xf7, 0x3a, 0x42, 0x00, 0xcd, + 0xec, 0xf7, 0x3a, 0x44, 0x00, 0xcd, 0xec, 0xf7, + 0x3a, 0x43, 0x00, 0xcd, 0x4a, 0xf6, 0xcd, 0x53, + 0xf6, 0xc2, 0x2f, 0xe7, 0xc3, 0xf5, 0xe6, 0x43, + 0x4d, 0x44, 0x20, 0x53, 0x54, 0x41, 0x54, 0x20, + 0x44, 0x52, 0x56, 0x20, 0x54, 0x52, 0x4b, 0x20, + 0x53, 0x43, 0x54, 0x52, 0x3e, 0x20, 0x03, 0xaf, + 0x32, 0x44, 0x00, 0x3c, 0x32, 0x43, 0x00, 0x18, + 0x54, 0x21, 0x00, 0x08, 0x22, 0x40, 0x00, 0xcd, + 0x2a, 0xf0, 0xc4, 0xa2, 0xf6, 0x21, 0x00, 0x09, + 0x22, 0x40, 0x00, 0xcd, 0x27, 0xf0, 0xc4, 0xa2, + 0xf6, 0x3a, 0x39, 0x00, 0xfe, 0x02, 0x28, 0x0f, + 0xf5, 0xcd, 0x59, 0xf7, 0xf1, 0xfe, 0x03, 0xca, + 0xae, 0xf7, 0xfe, 0x04, 0xca, 0x9e, 0xf7, 0x21, + 0x43, 0x00, 0x34, 0xdd, 0x7e, 0x00, 0xe5, 0x21, + 0x42, 0x00, 0xcb, 0x66, 0xe1, 0x28, 0x01, 0x07, + 0x3c, 0xbe, 0x20, 0x11, 0x36, 0x01, 0x23, 0x34, + 0xdd, 0x7e, 0x01, 0xbe, 0x20, 0x07, 0x36, 0x00, + 0x0e, 0x50, 0xcd, 0x0c, 0xf0, 0xcd, 0x06, 0xf0, + 0x28, 0x06, 0xcd, 0x09, 0xf0, 0xfe, 0x2e, 0xc8, + 0x3a, 0x39, 0x00, 0xfe, 0x02, 0x28, 0xa6, 0x18, + 0x98, 0x21, 0x00, 0x09, 0xeb, 0x21, 0x00, 0x08, + 0x3a, 0x42, 0x00, 0x07, 0x06, 0x80, 0x30, 0x02, + 0x06, 0x00, 0x1a, 0xbe, 0x3e, 0xff, 0x32, 0x47, + 0x00, 0xc2, 0xa2, 0xf6, 0x23, 0x13, 0x10, 0xf2, + 0x21, 0x38, 0x00, 0x11, 0x00, 0x08, 0x06, 0x40, + 0x3a, 0x42, 0x00, 0x07, 0x38, 0x02, 0xcb, 0x20, + 0x7e, 0x0f, 0x38, 0x01, 0x13, 0xed, 0x5f, 0x12, + 0x13, 0x13, 0x10, 0xf9, 0x7e, 0x0f, 0x38, 0x03, + 0x3c, 0x77, 0xc9, 0xaf, 0x77, 0xc9, 0xe5, 0x21, + 0x42, 0x00, 0x7e, 0xe6, 0xf0, 0x77, 0xed, 0x5f, + 0x00, 0xe6, 0x01, 0xb6, 0x77, 0xe1, 0xdd, 0x7e, + 0x00, 0x3c, 0x47, 0xed, 0x5f, 0xe6, 0x1f, 0xb7, + 0x28, 0xf4, 0x32, 0x43, 0x00, 0xb8, 0x30, 0xee, + 0xdd, 0x7e, 0x01, 0x47, 0xed, 0x5f, 0xe6, 0x7f, + 0x32, 0x44, 0x00, 0xb8, 0x30, 0xf2, 0xc3, 0x45, + 0xf7, 0xcd, 0x18, 0xf0, 0xdd, 0x7e, 0x01, 0x3d, + 0xd3, 0x67, 0xdd, 0x7e, 0x07, 0xd3, 0x64, 0xcd, + 0x06, 0xf0, 0x28, 0xed, 0xcd, 0x09, 0xf0, 0xfe, + 0x2e, 0xc8, 0x18, 0xe5, 0xcd, 0x4a, 0xf6, 0x0e, + 0x20, 0xc3, 0x0c, 0xf0, 0x3e, 0xff, 0x32, 0x55, + 0x00, 0xc9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + +/* Provide RAM if ROMs are disabled */ +static uint8 sbc200_mon_ram[SBC200_MON_SIZE] = {0}; +static uint8 sbc200_ddb_ram[SBC200_DDB_SIZE] = {0}; + +static int32 poc = TRUE; /* Power On Clear */ + +/* Debug flags */ +#define STATUS_MSG (1 << 0) +#define IRQ_MSG (1 << 1) +#define VERBOSE_MSG (1 << 3) + +typedef struct { + t_bool conn; /* Connected Status */ + TMLN *tmln; /* TMLN pointer */ + TMXR *tmxr; /* TMXR pointer */ + t_bool mif; /* Mode Instruction */ + int32 mode; /* Mode */ + int32 baud; /* Baud rate */ + int32 dtr; /* DTR Status */ + int32 rts; /* RTS Status */ + int32 cts; /* CTS Status */ + int32 rxb; /* Receive Buffer */ + int32 txb; /* Transmit Buffer */ + t_bool txp; /* Transmit Pending */ + int32 stb; /* Status Buffer */ + int32 ctb; /* Control Buffer */ + uint8 rxintenable; /* Interrupt Enable */ + uint8 txintenable; /* Interrupt Enable */ + uint8 rxintvector; /* Interrupt Vector */ + uint8 txintvector; /* Interrupt Vector */ + uint8 rxdatabus; /* Data Bus Value */ + uint8 txdatabus; /* Data Bus Value */ +} SBC200_INFO; + +static const char* sbc200_description(DEVICE *dptr); +static t_stat sbc200_svc(UNIT *uptr); +static t_stat sbc200_reset(DEVICE *dptr); +static t_stat sbc200_attach(UNIT *uptr, CONST char *cptr); +static t_stat sbc200_detach(UNIT *uptr); +static t_stat sbc200_set_console(UNIT *uptr, int32 value, const char *cptr, void *desc); +static t_stat sbc200_set_baud(UNIT *uptr, int32 value, const char *cptr, void *desc); +static t_stat sbc200_show_baud(FILE *st, UNIT *uptr, int32 value, const void *desc); +static t_stat sbc200_config_line(UNIT *uptr); +static t_stat sbc200_config_dtr(DEVICE *dptr, char rts); +static t_stat sbc200_config_rts(DEVICE *dptr, char rts); +static int32 sbc200_io(int32 addr, int32 io, int32 data); +static int32 sbc200_stat(DEVICE *dptr, int32 io, int32 data); +static int32 sbc200_data(DEVICE *dptr, int32 io, int32 data); +static int32 sbc200_rom(DEVICE *dptr, int32 io, int32 data); +static void sbc200_int(UNIT *uptr, int32 vector, int32 databus); +static int32 sbc200_mon(int32 Addr, int32 rw, int32 Data); +static int32 sbc200_ddb(int32 Addr, int32 rw, int32 Data); +static t_stat sbc200_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); + +extern uint32 vectorInterrupt; /* Vector Interrupt bits */ +extern uint8 dataBus[MAX_INT_VECTORS]; /* Data bus value */ + +/* Debug Flags */ +static DEBTAB sbc200_dt[] = { + { "STATUS", STATUS_MSG, "Status messages" }, + { "IRQ", IRQ_MSG, "Interrupt messages" }, + { "VERBOSE", VERBOSE_MSG, "Verbose messages" }, + { NULL, 0 } +}; + +/* Terminal multiplexer library descriptors */ + +static TMLN sbc200_tmln[] = { /* line descriptors */ + { 0 } +}; + +static TMXR sbc200_tmxr = { /* multiplexer descriptor */ + 1, /* number of terminal lines */ + 0, /* listening port (reserved) */ + 0, /* master socket (reserved) */ + sbc200_tmln, /* line descriptor array */ + NULL, /* line connection order */ + NULL /* multiplexer device (derived internally) */ +}; + +#define UNIT_V_SBC200_CONSOLE (UNIT_V_UF + 0) /* Port checks console for input */ +#define UNIT_SBC200_CONSOLE (1 << UNIT_V_SBC200_CONSOLE) +#define UNIT_V_SBC200_MONITOR (UNIT_V_UF + 1) /* Monitor ROM */ +#define UNIT_SBC200_MONITOR (1 << UNIT_V_SBC200_MONITOR) +#define UNIT_V_SBC200_DDBIOS (UNIT_V_UF + 2) /* DDBIOS ROM */ +#define UNIT_SBC200_DDBIOS (1 << UNIT_V_SBC200_DDBIOS) + +static MTAB sbc200_mod[] = { + { UNIT_SBC200_MONITOR, UNIT_SBC200_MONITOR, "MONITOR", "MONITOR", NULL, NULL, NULL, + "Enable ROM monitor at E000" }, + { UNIT_SBC200_MONITOR, 0, "NOMONITOR", "NOMONITOR", NULL, NULL, NULL, + "Disable ROM monitor at E000" }, + { UNIT_SBC200_DDBIOS, UNIT_SBC200_DDBIOS, "DDBIOS", "DDBIOS", NULL, NULL, NULL, + "Enable ROM DDBIOS at F000" }, + { UNIT_SBC200_DDBIOS, 0, "NODDBIOS", "NODDBIOS", NULL, NULL, NULL, + "Disable ROM DDBIOS at F000" }, + + { MTAB_XTD | MTAB_VUN, UNIT_SBC200_CONSOLE, NULL, "CONSOLE", &sbc200_set_console, NULL, NULL, "Set as CONSOLE" }, + { MTAB_XTD | MTAB_VUN, 0, NULL, "NOCONSOLE", &sbc200_set_console, NULL, NULL, "Remove as CONSOLE" }, + + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "BAUD", "BAUD", &sbc200_set_baud, &sbc200_show_baud, + NULL, "Set baud rate (default=9600)" }, + { 0 } +}; + +static SBC200_INFO sbc200_info = { 0, sbc200_tmln, &sbc200_tmxr, SBC200_BAUD, 1 }; + +static UNIT sbc200_unit[] = { + { UDATA (&sbc200_svc, UNIT_ATTABLE | UNIT_DISABLE | + UNIT_SBC200_CONSOLE | UNIT_SBC200_MONITOR | UNIT_SBC200_DDBIOS, 0), SBC200_WAIT }, +}; + +static REG sbc200_reg[] = { + { HRDATAD (SBCSTA, sbc200_info.stb, 8, "Status register"), }, + { HRDATAD (SBCCTL, sbc200_info.ctb, 8, "Control register"), }, + { HRDATAD (SBCRXD, sbc200_info.rxb, 8, "RX data buffer"), }, + { HRDATAD (SBCTXD, sbc200_info.txb, 8, "TX data buffer"), }, + { FLDATAD (SBCTXP, sbc200_info.txp, 0, "TX data pending"), }, + { FLDATAD (SBCCON, sbc200_info.conn, 0, "Connection status"), }, + { FLDATAD (SBCRTS, sbc200_info.rts, 0, "RTS status (active low)"), }, + { FLDATAD (SBCDTR, sbc200_info.dtr, 0, "DTR status (active low)"), }, + { FLDATAD (SBCRDRF, sbc200_info.stb, 0, "RDRF status"), }, + { FLDATAD (SBCTRDY, sbc200_info.stb, 1, "TRDY status"), }, + { FLDATAD (SBCTDRE, sbc200_info.stb, 2, "TDRE status"), }, + { FLDATAD (SBCOVRN, sbc200_info.stb, 4, "OVRN status"), }, + { FLDATAD (SBCCTS, sbc200_info.cts, 0, "CTS status (active low)"), }, + { FLDATAD (SBCDSR, sbc200_info.stb, 7, "DSR status (active low)"), }, + { DRDATAD (SBCWAIT, sbc200_unit[0].wait, 32, "Wait cycles"), }, + { FLDATAD (SBCRXINTEN, sbc200_info.rxintenable, 1, "Global vectored interrupt enable"), }, + { FLDATAD (SBCTXINTEN, sbc200_info.txintenable, 1, "Global vectored interrupt enable"), }, + { DRDATAD (SBCRXVEC, sbc200_info.rxintvector, 8, "RX interrupt vector"), }, + { DRDATAD (SBCTXVEC, sbc200_info.txintvector, 8, "TX interrupt vector"), }, + { HRDATAD (SBCRXDBVAL, sbc200_info.rxdatabus, 8, "RX data bus value"), }, + { HRDATAD (SBCTXDBVAL, sbc200_info.txdatabus, 8, "TX data bus value"), }, + { NULL } +}; + +DEVICE sbc200_dev = { + SBC200_SNAME, /* name */ + sbc200_unit, /* unit */ + sbc200_reg, /* registers */ + sbc200_mod, /* modifiers */ + 1, /* # units */ + ADDRRADIX, /* address radix */ + ADDRWIDTH, /* address width */ + 1, /* address increment */ + DATARADIX, /* data radix */ + DATAWIDTH, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &sbc200_reset, /* reset routine */ + NULL, /* boot routine */ + &sbc200_attach, /* attach routine */ + &sbc200_detach, /* detach routine */ + &sbc200_iores, /* context */ + (DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX), /* flags */ + 0, /* debug control */ + sbc200_dt, /* debug flags */ + NULL, /* mem size routine */ + NULL, /* logical name */ + &sbc200_show_help, /* help */ + NULL, /* attach help */ + NULL, /* context for help */ + &sbc200_description /* description */ +}; + +static const char* sbc200_description(DEVICE *dptr) +{ + return SBC200_NAME; +} + +static t_stat sbc200_reset(DEVICE *dptr) +{ + sim_debug(STATUS_MSG, dptr, "reset adapter.\n"); + + /* Connect/Disconnect I/O Ports at base address */ + if (dptr->flags & DEV_DIS) { + s100_bus_remio(sbc200_iores.io_base, sbc200_iores.io_size, &sbc200_io); + s100_bus_remmem(sbc200_monres.mem_base, sbc200_monres.mem_size, &sbc200_mon); + s100_bus_remmem(sbc200_ddbres.mem_base, sbc200_ddbres.mem_size, &sbc200_ddb); + + s100_bus_noconsole(&dptr->units[0]); + + poc = TRUE; + + return SCPE_OK; + } + + if (poc == TRUE) { + s100_bus_addio(sbc200_iores.io_base, sbc200_iores.io_size, &sbc200_io, dptr->name); + + cpu_set_chiptype(CHIP_TYPE_Z80); /* Set to Z80 */ + + poc = FALSE; + } + + /* Enable ROMs on Reset */ + s100_bus_addmem(sbc200_monres.mem_base, sbc200_monres.mem_size, &sbc200_mon, dptr->name); + s100_bus_addmem(sbc200_ddbres.mem_base, sbc200_ddbres.mem_size, &sbc200_ddb, dptr->name); + + /* Set as CONSOLE unit */ + if (dptr->units[0].flags & UNIT_SBC200_CONSOLE) { + s100_bus_console(&dptr->units[0]); + } + + /* Set DEVICE for this UNIT */ + dptr->units[0].dptr = dptr; + dptr->units[0].wait = SBC200_WAIT; + + /* Enable TMXR modem control passthrough */ + tmxr_set_modem_control_passthru(sbc200_info.tmxr); + + /* Reset status registers */ + dptr->units[0].flags |= (UNIT_SBC200_MONITOR | UNIT_SBC200_DDBIOS); + sbc200_info.mif = TRUE; + sbc200_info.stb = 0x00; + sbc200_info.txp = FALSE; + sbc200_info.cts = 0; /* Force CTS active (low) */ + sbc200_info.rxintenable = FALSE; + sbc200_info.txintenable = FALSE; + if (dptr->units[0].flags & UNIT_ATT) { + sbc200_config_dtr(dptr, 1); /* disable DTR */ + sbc200_config_rts(dptr, 1); /* disable RTS */ + } + + /* Activate Service Routine */ + sim_activate(&dptr->units[0], dptr->units[0].wait); + + return SCPE_OK; +} + +static t_stat sbc200_svc(UNIT *uptr) +{ + int32 c,s,stb,cts; + t_stat r = SCPE_OK; + + /* Check for new incoming connection */ + if (uptr->flags & UNIT_ATT) { + if (tmxr_poll_conn(sbc200_info.tmxr) >= 0) { /* poll connection */ + + sbc200_info.conn = TRUE; /* set connected */ + + sim_debug(STATUS_MSG, uptr->dptr, "new connection.\n"); + } + } + + /* Update incoming modem status bits */ + if (uptr->flags & UNIT_ATT) { + tmxr_set_get_modem_bits(sbc200_info.tmln, 0, 0, &s); + cts = sbc200_info.cts; + stb = sbc200_info.stb; + + sbc200_info.stb &= ~SBC200_DSR; + sbc200_info.stb |= (s & TMXR_MDM_DSR) ? 0 : SBC200_DSR; /* Active Low */ + if ((stb ^ sbc200_info.stb) & SBC200_DSR) { + sim_debug(STATUS_MSG, uptr->dptr, "DSR state changed to %s.\n", (sbc200_info.stb & SBC200_DSR) ? "LOW" : "HIGH"); + } + sbc200_info.cts = 0; + sbc200_info.cts = (s & TMXR_MDM_CTS) ? 0 : 1; /* Active Low */ + if (cts != sbc200_info.cts) { + sim_debug(STATUS_MSG, uptr->dptr, "CTS state changed to %s.\n", (sbc200_info.cts) ? "LOW" : "HIGH"); + } + } else { + /* + * The SBC-200 connects RXD to DSR to determine + * the baud rate. Simulate by toggling DSR. + */ + sbc200_info.stb ^= SBC200_DSR; + } + + /* TX data */ + if (sbc200_info.txp) { + if (uptr->flags & UNIT_ATT) { + if (!(sbc200_info.cts)) { /* Active low */ + r = tmxr_putc_ln(sbc200_info.tmln, sbc200_info.txb); + sbc200_info.txp = FALSE; /* Reset TX Pending */ + } else { + r = SCPE_STALL; + } + } else { + r = sim_putchar(sbc200_info.txb); + sbc200_info.txp = FALSE; /* Reset TX Pending */ + } + + if (r == SCPE_LOST) { + sbc200_info.conn = FALSE; /* Connection was lost */ + sim_debug(STATUS_MSG, uptr->dptr, "lost connection.\n"); + } + + /* If TX buffer now empty, send interrupt */ + if (!sbc200_info.txp && sbc200_info.txintenable) { + sim_debug(IRQ_MSG, uptr->dptr, "%s: TxRDY Vector=%d\n", sim_uname(uptr), sbc200_info.txintvector); + sbc200_int(uptr, sbc200_info.txintvector, sbc200_info.txdatabus); + } + + } + + /* Update TDRE if not set and no character pending */ + if (!sbc200_info.txp && !(sbc200_info.stb & SBC200_TDRE)) { + if (uptr->flags & UNIT_ATT) { + tmxr_poll_tx(sbc200_info.tmxr); + sbc200_info.stb |= (tmxr_txdone_ln(sbc200_info.tmln) && sbc200_info.conn) ? SBC200_TRDY | SBC200_TDRE : 0; + } else { + sbc200_info.stb |= (SBC200_TRDY | SBC200_TDRE); + } + } + + /* Check for Data if RX buffer empty */ + if (!(sbc200_info.stb & SBC200_RDRF)) { + if (uptr->flags & UNIT_ATT) { + tmxr_poll_rx(sbc200_info.tmxr); + + c = tmxr_getc_ln(sbc200_info.tmln); + } + else { + c = s100_bus_poll_kbd(uptr); + } + + if (c & (TMXR_VALID | SCPE_KFLAG)) { + sbc200_info.rxb = c & 0xff; + sbc200_info.stb |= SBC200_RDRF; + sbc200_info.stb &= ~(SBC200_FE | SBC200_OVRN | SBC200_PE); + sim_debug(IRQ_MSG, uptr->dptr, "%s: RxRDY Vector=%d\n", sim_uname(uptr), sbc200_info.rxintvector); + if (sbc200_info.rxintenable) { + sbc200_int(uptr, sbc200_info.rxintvector, sbc200_info.rxdatabus); + } + } + } + + /* Don't let TMXR clobber our wait time */ + uptr->wait = SBC200_WAIT; + + sim_activate_abs(uptr, uptr->wait); + + return r; +} + + +/* Attach routine */ +static t_stat sbc200_attach(UNIT *uptr, CONST char *cptr) +{ + t_stat r; + + sim_debug(VERBOSE_MSG, uptr->dptr, "attach (%s).\n", cptr); + + if ((r = tmxr_attach(sbc200_info.tmxr, uptr, cptr)) == SCPE_OK) { + + if (sbc200_info.tmln->serport) { + sbc200_config_dtr(uptr->dptr, sbc200_info.dtr); /* update DTR */ + sbc200_config_rts(uptr->dptr, sbc200_info.rts); /* update RTS */ + } + + sbc200_info.tmln->rcve = 1; + } + + return r; +} + + +/* Detach routine */ +static t_stat sbc200_detach(UNIT *uptr) +{ + if (uptr->dptr == NULL) { + return SCPE_IERR; + } + + sim_debug(VERBOSE_MSG, uptr->dptr, "detach.\n"); + + if (uptr->flags & UNIT_ATT) { + sim_cancel(uptr); + + return (tmxr_detach(sbc200_info.tmxr, uptr)); + } + + return SCPE_UNATT; +} + +static t_stat sbc200_set_console(UNIT *uptr, int32 value, const char *cptr, void *desc) +{ + if (value == UNIT_SBC200_CONSOLE) { + s100_bus_console(uptr); + } + else { + s100_bus_noconsole(uptr); + } + + return SCPE_OK; +} + +static t_stat sbc200_set_baud(UNIT *uptr, int32 value, const char *cptr, void *desc) +{ + int32 baud; + t_stat r = SCPE_ARG; + + if (!(uptr->flags & UNIT_ATT)) { + return SCPE_UNATT; + } + + if (cptr != NULL) { + if (sscanf(cptr, "%d", &baud)) { + switch (baud) { + case 110: + case 150: + case 300: + case 1200: + case 1800: + case 2400: + case 4800: + case 9600: + case 19200: + sbc200_info.baud = baud; + r = sbc200_config_line(uptr); + + return r; + + default: + break; + } + } + } + + return r; +} + +static t_stat sbc200_show_baud(FILE *st, UNIT *uptr, int32 value, const void *desc) +{ + if (uptr->flags & UNIT_ATT) { + fprintf(st, "Baud rate: %d", sbc200_info.baud); + } + + return SCPE_OK; +} + +static t_stat sbc200_config_line(UNIT *uptr) +{ + char config[20]; + const char *fmt; + t_stat r = SCPE_IERR; + + switch (sbc200_info.mode & SBC200_FMTMSK) { + case SBC200_72E: + fmt = "7E2"; + break; + case SBC200_72O: + fmt = "7O2"; + break; + case SBC200_71E: + fmt = "7E1"; + break; + case SBC200_71O: + fmt = "7O1"; + break; + case SBC200_82N: + fmt = "8N2"; + break; + case SBC200_81E: + fmt = "8E1"; + break; + case SBC200_81O: + fmt = "8O1"; + break; + case SBC200_81N: + default: + fmt = "8N1"; + break; + } + + sprintf(config, "%d-%s", sbc200_info.baud, fmt); + + r = tmxr_set_config_line(sbc200_info.tmln, config); + + sim_debug(STATUS_MSG, uptr->dptr, "port configuration set to '%s'.\n", config); + + return r; +} + +/* +** DTR is active low +** 0 = DTR active +** 1 = DTR inactive +*/ +static t_stat sbc200_config_dtr(DEVICE *dptr, char dtr) +{ + t_stat r = SCPE_OK; + int32 s; + + if (dptr->units[0].flags & UNIT_ATT) { + /* DTR Control */ + s = TMXR_MDM_DTR; + + if (!dtr) { + r = tmxr_set_get_modem_bits(sbc200_info.tmln, s, 0, NULL); + if (sbc200_info.dtr) { + sim_debug(STATUS_MSG, dptr, "DTR state changed to HIGH.\n"); + } + } else { + r = tmxr_set_get_modem_bits(sbc200_info.tmln, 0, s, NULL); + if (!sbc200_info.dtr) { + sim_debug(STATUS_MSG, dptr, "DTR state changed to LOW.\n"); + } + } + } + + sbc200_info.dtr = dtr; /* Active low */ + + return r; +} + +/* +** RTS is active low +** 0 = RTS active +** 1 = RTS inactive +*/ +static t_stat sbc200_config_rts(DEVICE *dptr, char rts) +{ + t_stat r = SCPE_OK; + int32 s; + + if (dptr->units[0].flags & UNIT_ATT) { + /* RTS Control */ + s = TMXR_MDM_RTS; + + if (!rts) { + r = tmxr_set_get_modem_bits(sbc200_info.tmln, s, 0, NULL); + if (sbc200_info.rts) { + sim_debug(STATUS_MSG, dptr, "RTS state changed to HIGH.\n"); + } + } else { + r = tmxr_set_get_modem_bits(sbc200_info.tmln, 0, s, NULL); + if (!sbc200_info.rts) { + sim_debug(STATUS_MSG, dptr, "RTS state changed to LOW.\n"); + } + } + } + + sbc200_info.rts = rts; /* Active low */ + + return r; +} + +static int32 sbc200_io(int32 addr, int32 io, int32 data) +{ + int32 r = 0xff; + + switch (addr - sbc200_iores.io_base) { + case 0x04: + r = sbc200_data(&sbc200_dev, io, data); + break; + + case 0x05: + r = sbc200_stat(&sbc200_dev, io, data); + break; + + case 0x07: + r = sbc200_rom(&sbc200_dev, io, data); + break; + + default: + break; + + } + + return(r); +} + +static int32 sbc200_stat(DEVICE *dptr, int32 io, int32 data) +{ + int32 r; + + if (io == S100_IO_READ) { + r = sbc200_info.stb; + } else { + if (sbc200_info.mif) { + sbc200_info.mif = FALSE; + sbc200_info.mode = data & 0xff; + + /* Set data bits, parity and stop bits format */ + sbc200_config_line(&dptr->units[0]); + } else { /* Command Mode */ + /* Internal Reset */ + if (data & SBC200_IR) { + sim_debug(STATUS_MSG, dptr, "8251 internal reset.\n"); + + sbc200_info.ctb = data & 0xff; /* save control byte */ + sbc200_info.stb = SBC200_DSR; /* Reset status register */ + sbc200_info.mif = TRUE; + sbc200_info.rxb = 0x00; + sbc200_info.txp = FALSE; + + sbc200_config_dtr(dptr, 1); /* disable DTR */ + sbc200_config_rts(dptr, 1); /* disable RTS */ + } else { + sbc200_config_dtr(dptr, (data & SBC200_DTR)); /* enable DTR */ + sbc200_config_rts(dptr, (data & SBC200_RTS)); /* enable RTS */ + } + } + + r = 0x00; + } + + return(r); +} + +static int32 sbc200_data(DEVICE *dptr, int32 io, int32 data) +{ + int32 r; + + if (io == S100_IO_READ) { + r = sbc200_info.rxb; + sbc200_info.stb &= ~(SBC200_RDRF | SBC200_FE | SBC200_OVRN | SBC200_PE); + } else { + sbc200_info.txb = data; + sbc200_info.stb &= ~(SBC200_TRDY | SBC200_TDRE); + sbc200_info.txp = TRUE; + + sbc200_svc(dptr->units); /* Process TX right now */ + + r = 0x00; + } + + return r; +} + +static int32 sbc200_rom(DEVICE *dptr, int32 io, int32 data) +{ + sim_debug(VERBOSE_MSG, dptr, "Switch %s ROMs\n", data & 0x02 ? "OUT" : "IN"); + + if (data & 0x02) { /* Disable ROMs */ + s100_bus_remmem(sbc200_monres.mem_base, sbc200_monres.mem_size, &sbc200_mon); + s100_bus_remmem(sbc200_ddbres.mem_base, sbc200_ddbres.mem_size, &sbc200_ddb); + } + else { + s100_bus_addmem(sbc200_monres.mem_base, sbc200_monres.mem_size, &sbc200_mon, dptr->name); + s100_bus_addmem(sbc200_ddbres.mem_base, sbc200_ddbres.mem_size, &sbc200_ddb, dptr->name); + } + + return data; +} + +static void sbc200_int(UNIT *uptr, int32 vector, int32 databus) +{ + vectorInterrupt |= (1 << vector); + dataBus[vector] = databus; + + sim_debug(IRQ_MSG, uptr->dptr, "%s: IRQ Vector=%d dataBus=%02X Status=%02X\n", sim_uname(uptr), vector, databus, sbc200_info.stb); +} + + +static int32 sbc200_mon(int32 Addr, int32 rw, int32 Data) +{ + /* If ROM enabled, return ROM byte */ + if (sbc200_dev.units->flags & UNIT_SBC200_MONITOR) { + return(sbc200_mon_rom[Addr & SBC200_MON_MASK]); + } else if (rw == S100_IO_WRITE) { + sbc200_mon_ram[Addr & SBC200_MON_MASK] = Data; + } + + return sbc200_mon_ram[Addr & SBC200_MON_MASK]; +} + +static int32 sbc200_ddb(int32 Addr, int32 rw, int32 Data) +{ + /* If ROM has been disabled, unmap and return byte from RAM */ + if (sbc200_dev.units->flags & UNIT_SBC200_DDBIOS) { + return(sbc200_ddb_rom[Addr & SBC200_DDB_MASK]); + } else if (rw == S100_IO_WRITE) { + sbc200_ddb_ram[Addr & SBC200_DDB_MASK] = Data; + } + + return sbc200_ddb_ram[Addr & SBC200_DDB_MASK]; +} + +static t_stat sbc200_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "\nSD Systems SBC-200 (%s)\n", dptr->name); + + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + fprint_reg_help (st, dptr); + fprintf(st, "\n\n"); + tmxr_attach_help(st, dptr, uptr, flag, cptr); + + fprintf(st, "----- NOTES -----\n\n"); + fprintf(st, "Only one device may poll the host keyboard for CONSOLE input.\n"); + fprintf(st, "Use SET %s CONSOLE to select this UNIT as the CONSOLE device.\n", sim_dname(dptr)); + fprintf(st, "\nUse SHOW BUS CONSOLE to display the current CONSOLE device.\n\n"); + + return SCPE_OK; +} + diff --git a/Altair8800/sds_vfii.c b/Altair8800/sds_vfii.c new file mode 100644 index 00000000..7e7cbcdd --- /dev/null +++ b/Altair8800/sds_vfii.c @@ -0,0 +1,340 @@ +/* vfii_fdc.c: SD Systems VersaFloppy II + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 13-Nov-2025 Initial version + +*/ + +#include "sim_defs.h" +#include "altair8800_sys.h" +#include "altair8800_dsk.h" +#include "s100_bus.h" +#include "sds_vfii.h" +#include "wd_17xx.h" + +#define DEV_NAME "VFII" + +static WD17XX_INFO *wd17xx = NULL; + +#define VFII_WD17XX_OFFSET 1 + +static int32 poc = TRUE; /* Power On Clear */ + +static uint8 drv_sel = 0; +static uint8 vfii_creg = 0; + +static RES vfii_res = { VFII_IO_BASE, VFII_IO_SIZE, 0x0000, 0x0000 }; +static DSK_INFO dsk_info[VFII_NUM_DRIVES]; + +static t_stat vfii_reset(DEVICE *vfii_dev); +static t_stat vfii_attach(UNIT *uptr, CONST char *cptr); +static t_stat vfii_detach(UNIT *uptr); +static t_stat vfii_boot(int32 unitno, DEVICE *dptr); +static int32 vfii_io(const int32 port, const int32 io, const int32 data); +static int32 vfii_io_in(const int32 port); +static void vfii_io_out(const int32 port, const int32 data); +static t_stat vfii_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +static const char* vfii_description(DEVICE *dptr); + +static UNIT vfii_unit[VFII_NUM_DRIVES] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFII_SD_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFII_SD_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFII_SD_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFII_SD_CAPACITY) } +}; + +static REG vfii_reg[] = { + { FLDATAD (POC, poc, 0x01, "Power on Clear flag"), }, + { DRDATAD (DRVSEL, drv_sel, 8, "Drive select"), }, + { NULL } +}; + +#define VFII_NAME "SD Systems VersaFloppy II" + +static const char* vfii_description(DEVICE *dptr) { + if (dptr == NULL) { + return NULL; + } + return VFII_NAME; +} + +static MTAB vfii_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", + &set_iobase, &show_iobase, NULL, "Sets disk controller I/O base address" }, + { 0 } +}; + +/* Debug flags */ +#define VERBOSE_MSG (1 << 0) +#define ERROR_MSG (1 << 1) +#define STATUS_MSG (1 << 2) +#define DRIVE_MSG (1 << 3) +#define IRQ_MSG (1 << 4) +#define READ_MSG (1 << 5) +#define WRITE_MSG (1 << 6) +#define COMMAND_MSG (1 << 7) +#define FORMAT_MSG (1 << 8) + +/* Debug Flags */ +static DEBTAB vfii_dt[] = { + { "VERBOSE", VERBOSE_MSG, "Verbose messages" }, + { "ERROR", ERROR_MSG, "Error messages" }, + { "STATUS", STATUS_MSG, "Status messages" }, + { "DRIVE", DRIVE_MSG, "Drive messages" }, + { "IRQ", IRQ_MSG, "IRQ messages" }, + { "READ", READ_MSG, "Read messages" }, + { "WRITE", WRITE_MSG, "Write messages" }, + { "COMMAND", COMMAND_MSG, "Command messages" }, + { "FORMAT", FORMAT_MSG, "Format messages" }, + { NULL, 0 } +}; + +DEVICE vfii_dev = { + DEV_NAME, vfii_unit, vfii_reg, vfii_mod, VFII_NUM_DRIVES, + ADDRRADIX, ADDRWIDTH, 1, DATARADIX, DATAWIDTH, + NULL, NULL, &vfii_reset, + &vfii_boot, &vfii_attach, &vfii_detach, + &vfii_res, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), 0, + vfii_dt, NULL, NULL, &vfii_show_help, NULL, NULL, &vfii_description +}; + +/* Reset routine */ +static t_stat vfii_reset(DEVICE *dptr) +{ + RES *res; + int i; + + if ((res = (RES *) dptr->ctxt) == NULL) { + sim_printf("CTX is NULL!\n"); + return SCPE_IERR; + } + + if(dptr->flags & DEV_DIS) { /* Unmap I/O Ports */ + wd17xx = wd17xx_release(wd17xx); + + s100_bus_remio(res->io_base, res->io_size, &vfii_io); + + poc = TRUE; + } else { + if (poc) { + for (i = 0; i < VFII_NUM_DRIVES; i++) { + vfii_unit[i].dptr = dptr; + dsk_init(&dsk_info[i], &vfii_unit[i], 77, 1, 0); + dsk_set_verbose_flag(&dsk_info[i], VERBOSE_MSG); + } + + if (wd17xx == NULL) { + if ((wd17xx = wd17xx_init(dptr)) == NULL) { + sim_printf("Could not init WD17XX\n"); + } + else { + wd17xx_set_fdctype(wd17xx, WD17XX_FDCTYPE_1795); /* Set to 1795 */ + wd17xx_set_verbose_flag(wd17xx, VERBOSE_MSG); + wd17xx_set_error_flag(wd17xx, ERROR_MSG); + wd17xx_set_read_flag(wd17xx, READ_MSG); + wd17xx_set_write_flag(wd17xx, WRITE_MSG); + wd17xx_set_command_flag(wd17xx, COMMAND_MSG); + wd17xx_set_format_flag(wd17xx, FORMAT_MSG); + } + } + + s100_bus_addio(res->io_base, res->io_size, &vfii_io, DEV_NAME); + + poc = FALSE; + } + + drv_sel = 0; + + if (wd17xx != NULL) { + wd17xx_reset(wd17xx); + wd17xx_set_dsk(wd17xx, &dsk_info[drv_sel]); + } + } + + return SCPE_OK; +} + +static t_stat vfii_boot(int32 unitno, DEVICE *dptr) +{ + + sim_debug(STATUS_MSG, &vfii_dev, DEV_NAME ": Booting Controller at 0x%04x\n", 0xE000); + + s100_bus_set_addr(0xE000); + + return SCPE_OK; +} + +/* Attach routine */ +static t_stat vfii_attach(UNIT *uptr, CONST char *cptr) +{ + t_stat r; + int d; + + /* Determine drive number */ + d = uptr - &vfii_unit[0]; + + if (d < 0 || d >= VFII_NUM_DRIVES) { + return SCPE_IERR; + } + + sim_switches |= SWMASK ('E'); /* File must exist */ + + if ((r = attach_unit(uptr, cptr)) != SCPE_OK) { /* attach unit */ + sim_printf(DEV_NAME ": ATTACH error=%d\n", r); + return r; + } + + /* Determine length of this disk */ + uptr->capac = sim_fsize(uptr->fileref); + + /* init format based on file size */ + switch (uptr->capac) { + case VFII_DD_CAPACITY: + dsk_init_format(&dsk_info[d], 0, 76, 0, 0, DSK_DENSITY_DD, 26, 256, 1); + break; + + default: + uptr->capac = VFII_SD_CAPACITY; + dsk_init_format(&dsk_info[d], 0, 76, 0, 0, DSK_DENSITY_DD, 26, 256, 1); + break; + } + +// dsk_show(&dsk_info[d]); + + return r; +} + +/* Detach routine */ +static t_stat vfii_detach(UNIT *uptr) +{ + t_stat r; + + r = detach_unit(uptr); /* detach unit */ + + return r; +} + +static int32 vfii_io(const int32 port, const int32 io, const int32 data) +{ + int32 result = 0xff; + + if (io == S100_IO_READ) { /* I/O Write */ + result = vfii_io_in(port); + } else { /* I/O Write */ + vfii_io_out(port, data); + } + + return result; +} + +static int32 vfii_io_in(const int32 port) +{ + int32 result = 0xff; + int32 offset = port - vfii_res.io_base; + + switch (offset) { + case VFII_REG_STATUS: + result = vfii_creg; + + sim_debug(STATUS_MSG, &vfii_dev, DEV_NAME ": " ADDRESS_FORMAT + " Read WAIT, Port 0x%02x Result 0x%02x\n", s100_bus_get_addr(), port, result); + break; + + case WD17XX_REG_STATUS + VFII_WD17XX_OFFSET: + case WD17XX_REG_TRACK + VFII_WD17XX_OFFSET: + case WD17XX_REG_SECTOR + VFII_WD17XX_OFFSET: + case WD17XX_REG_DATA + VFII_WD17XX_OFFSET: + result = wd17xx_inp(wd17xx, offset - VFII_WD17XX_OFFSET); + + sim_debug(STATUS_MSG, &vfii_dev, DEV_NAME ": " ADDRESS_FORMAT + " Read WD17XX, Port 0x%02x (0x%02x) Result 0x%02x\n", s100_bus_get_addr(), port, offset - VFII_WD17XX_OFFSET, result); + break; + + default: + break; + } + + return result; +} + +/* VersaFloppy II Control/Status + * + * BIT 0-3 Drive Select + * BIT 4 Side Select (1 = Side 0) + * BIT 5 5"/8" Drive (1 = 8") + * BIT 6 Double/Single Density (1 = SD) + * BIT 7 Wait Enable (Not used in simulator) + * + * All bits are inverted on the VFII + * + */ + +static void vfii_io_out(const int32 port, const int32 data) +{ + int32 offset = port - vfii_res.io_base; + + switch (offset) { + case VFII_REG_CONTROL: + vfii_creg = data & 0xff; + + drv_sel = sys_floorlog2((~data) & VFII_DSEL_MASK); + wd17xx_sel_side(wd17xx, (data & VFII_SIDE_MASK) ? 0 : 1); + wd17xx_sel_dden(wd17xx, (data & VFII_DDEN_MASK) ? FALSE : TRUE); + wd17xx_sel_drive_type(wd17xx, (data & VFII_SIZE_MASK) ? 8 : 5); + + sim_debug(DRIVE_MSG, &vfii_dev, DEV_NAME ": " ADDRESS_FORMAT " WR DRVSEL (0x%02x) = 0x%02x: Drive: %d\n", + s100_bus_get_addr(), port, data & DATAMASK, drv_sel); + + /* Tell WD17XX which drive is selected */ + wd17xx_set_dsk(wd17xx, &dsk_info[drv_sel]); + break; + + case WD17XX_REG_COMMAND + VFII_WD17XX_OFFSET: + case WD17XX_REG_TRACK + VFII_WD17XX_OFFSET: + case WD17XX_REG_SECTOR + VFII_WD17XX_OFFSET: + case WD17XX_REG_DATA + VFII_WD17XX_OFFSET: + wd17xx_outp(wd17xx, offset - VFII_WD17XX_OFFSET, data & DATAMASK); + + sim_debug(STATUS_MSG, &vfii_dev, DEV_NAME ": " ADDRESS_FORMAT + " Write WD17XX, Port 0x%02x (0x%02x) Data 0x%02x\n", s100_bus_get_addr(), port, offset - VFII_WD17XX_OFFSET, data & DATAMASK); + break; + + default: + break; + } +} + +static t_stat vfii_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "\nSD Systems VersaFlopyy II (%s)\n", dptr->name); + + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + fprint_reg_help (st, dptr); + + return SCPE_OK; +} + diff --git a/Altair8800/sds_vfii.h b/Altair8800/sds_vfii.h new file mode 100644 index 00000000..b7d16b51 --- /dev/null +++ b/Altair8800/sds_vfii.h @@ -0,0 +1,60 @@ +/* sds_vfii.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/16/25 Initial version + +*/ + +#ifndef _VFII_FDC_H +#define _VFII_FDC_H + +#define VFII_NUM_DRIVES 4 + +#define VFII_IO_BASE 0x63 +#define VFII_IO_SIZE 5 + +#define VFII_PROM_BASE 0x0000 +#define VFII_PROM_SIZE 32 +#define VFII_PROM_MASK (VFII_PROM_SIZE - 1) + +/* VFII Register Offsets */ +#define VFII_REG_STATUS 0x00 /* Status Port */ +#define VFII_REG_CONTROL 0x00 /* Control Port */ + +#define VFII_DSEL_MASK 0x0f +#define VFII_SIDE_MASK 0x10 +#define VFII_SIZE_MASK 0x20 +#define VFII_DDEN_MASK 0x40 +#define VFII_WAIT_MASK 0x80 + + +#define VFII_FLAG_DRQ 0x80 /* End of Job (DRQ) */ + +#define VFII_SD_CAPACITY (77*26*128) /* SSSD 8" (IBM 3740) Disk Capacity */ +#define VFII_DD_CAPACITY (77*26*256) /* SSDD 8" DD Disk Capacity */ + +#endif + diff --git a/Altair8800/tarbell_fdc.c b/Altair8800/tarbell_fdc.c new file mode 100644 index 00000000..4cf8968a --- /dev/null +++ b/Altair8800/tarbell_fdc.c @@ -0,0 +1,471 @@ +/* tarbell_fdc.c: Tarbell 1011/2022 Floppy Disk Controller + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 13-Nov-2025 Initial version + +*/ + +#include "sim_defs.h" +#include "s100_bus.h" +#include "altair8800_dsk.h" +#include "tarbell_fdc.h" +#include "wd_17xx.h" + +#define DEV_NAME "TARBELL" + +static WD17XX_INFO *wd17xx = NULL; + +/* Debug flags */ +#define VERBOSE_MSG (1 << 0) +#define ERROR_MSG (1 << 1) +#define STATUS_MSG (1 << 2) +#define DRIVE_MSG (1 << 3) +#define IRQ_MSG (1 << 4) +#define READ_MSG (1 << 5) +#define WRITE_MSG (1 << 6) +#define COMMAND_MSG (1 << 7) +#define FORMAT_MSG (1 << 8) + +static int32 poc = TRUE; /* Power On Clear */ + +static uint8 drv_sel = 0; +static int32 ddfdc_enabled = FALSE; +static int32 prom_enabled = TRUE; +static int32 prom_active = FALSE; + +static RES tarbell_res = { TARBELL_IO_BASE, TARBELL_IO_SIZE, TARBELL_PROM_BASE, TARBELL_PROM_SIZE }; +static MDEV mdev = { NULL, NULL }; +static DSK_INFO dsk_info[TARBELL_NUM_DRIVES]; + +/* Tarbell PROM is 32 bytes */ +static uint8 tarbell_prom[TARBELL_PROM_SIZE] = { + 0xdb, 0xfc, 0xaf, 0x6f, 0x67, 0x3c, 0xd3, 0xfa, + 0x3e, 0x8c, 0xd3, 0xf8, 0xdb, 0xfc, 0xb7, 0xf2, + 0x19, 0x00, 0xdb, 0xfb, 0x77, 0x23, 0xc3, 0x0c, + 0x00, 0xdb, 0xf8, 0xb7, 0xca, 0x7d, 0x00, 0x76 +}; + +static t_stat tarbell_reset(DEVICE *tarbell_dev); +static t_stat tarbell_attach(UNIT *uptr, CONST char *cptr); +static t_stat tarbell_detach(UNIT *uptr); +static t_stat tarbell_boot(int32 unitno, DEVICE *dptr); +static t_stat tarbell_set_model(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat tarbell_show_model(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat tarbell_set_prom(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat tarbell_show_prom(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static void tarbell_enable_prom(void); +static void tarbell_disable_prom(void); +static int32 tarbell_io(const int32 port, const int32 io, const int32 data); +static int32 tarbell_memio(const int32 addr, const int32 rw, const int32 data); +static t_stat tarbell_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +static const char* tarbell_description(DEVICE *dptr); + +static UNIT tarbell_unit[TARBELL_NUM_DRIVES] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, TARBELL_SD_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, TARBELL_SD_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, TARBELL_SD_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, TARBELL_SD_CAPACITY) } +}; + +static REG tarbell_reg[] = { + { FLDATAD (POC, poc, 0x01, "Power on Clear flag"), }, + { DRDATAD (DRVSEL, drv_sel, 8, "Drive select"), }, + { NULL } +}; + +#define TARBELL_NAME "Tarbell 2022 Double-Density FDC" + +static const char* tarbell_description(DEVICE *dptr) { + if (dptr == NULL) { + return NULL; + } + return TARBELL_NAME; +} + +static MTAB tarbell_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", + &set_iobase, &show_iobase, NULL, "Sets disk controller I/O base address" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "PROM", "PROM={ENABLE|DISABLE}", + &tarbell_set_prom, &tarbell_show_prom, NULL, "ROM enabled/disabled status"}, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "MODEL", "MODEL={SD|DD}", + &tarbell_set_model, &tarbell_show_model, NULL, "Set/Show the current controller model" }, + + { 0 } +}; + +/* Debug Flags */ +static DEBTAB tarbell_dt[] = { + { "VERBOSE", VERBOSE_MSG, "Verbose messages" }, + { "ERROR", ERROR_MSG, "Error messages" }, + { "STATUS", STATUS_MSG, "Status messages" }, + { "DRIVE", DRIVE_MSG, "Drive messages" }, + { "IRQ", IRQ_MSG, "IRQ messages" }, + { "READ", READ_MSG, "Read messages" }, + { "WRITE", WRITE_MSG, "Write messages" }, + { "COMMAND", COMMAND_MSG, "Command messages" }, + { "FORMAT", FORMAT_MSG, "Format messages" }, + { NULL, 0 } +}; + +DEVICE tarbell_dev = { + DEV_NAME, tarbell_unit, tarbell_reg, tarbell_mod, TARBELL_NUM_DRIVES, + ADDRRADIX, ADDRWIDTH, 1, DATARADIX, DATAWIDTH, + NULL, NULL, &tarbell_reset, + &tarbell_boot, &tarbell_attach, &tarbell_detach, + &tarbell_res, (DEV_DISABLE | DEV_DIS | DEV_DEBUG), 0, + tarbell_dt, NULL, NULL, &tarbell_show_help, NULL, NULL, &tarbell_description +}; + +/* Reset routine */ +static t_stat tarbell_reset(DEVICE *dptr) +{ + RES *res; + int i; + + if ((res = (RES *) dptr->ctxt) == NULL) { + sim_printf("CTX is NULL!\n"); + return SCPE_IERR; + } + + if(dptr->flags & DEV_DIS) { /* Unmap I/O Ports */ + wd17xx = wd17xx_release(wd17xx); + + s100_bus_remio(res->io_base, res->io_size, &tarbell_io); + + poc = TRUE; + } else { + if (poc) { + ddfdc_enabled = FALSE; + + for (i = 0; i < TARBELL_NUM_DRIVES; i++) { + tarbell_unit[i].dptr = dptr; + dsk_init(&dsk_info[i], &tarbell_unit[i], 77, 1, 0); + dsk_set_verbose_flag(&dsk_info[i], VERBOSE_MSG); + } + + if (wd17xx == NULL) { + if ((wd17xx = wd17xx_init(dptr)) == NULL) { + sim_printf("Could not init WD17XX\n"); + } + else { + wd17xx_set_fdctype(wd17xx, WD17XX_FDCTYPE_1771); /* Set to 1771 */ + wd17xx_set_verbose_flag(wd17xx, VERBOSE_MSG); + wd17xx_set_error_flag(wd17xx, ERROR_MSG); + wd17xx_set_read_flag(wd17xx, READ_MSG); + wd17xx_set_write_flag(wd17xx, WRITE_MSG); + wd17xx_set_command_flag(wd17xx, COMMAND_MSG); + wd17xx_set_format_flag(wd17xx, FORMAT_MSG); + } + } + + s100_bus_addio(res->io_base, res->io_size, &tarbell_io, DEV_NAME); + + if (prom_enabled) { + tarbell_enable_prom(); + } + + poc = FALSE; + } + + if (prom_enabled) { + prom_active = TRUE; + } + + drv_sel = 0; + + if (wd17xx != NULL) { + wd17xx_reset(wd17xx); + wd17xx_set_dsk(wd17xx, &dsk_info[drv_sel]); + } + } + + return SCPE_OK; +} + +static t_stat tarbell_boot(int32 unitno, DEVICE *dptr) +{ + + sim_debug(STATUS_MSG, &tarbell_dev, DEV_NAME ": Booting Controller at 0x%04x\n", tarbell_res.mem_base); + + s100_bus_set_addr(tarbell_res.mem_base); + + return SCPE_OK; +} + +/* Attach routine */ +static t_stat tarbell_attach(UNIT *uptr, CONST char *cptr) +{ + t_stat r; + int d; + + /* Determine drive number */ + d = uptr - &tarbell_unit[0]; + + if (d < 0 || d >= TARBELL_NUM_DRIVES) { + return SCPE_IERR; + } + + sim_switches |= SWMASK ('E'); /* File must exist */ + + if ((r = attach_unit(uptr, cptr)) != SCPE_OK) { /* attach unit */ + sim_printf(DEV_NAME ": ATTACH error=%d\n", r); + return r; + } + + /* Determine length of this disk */ + uptr->capac = sim_fsize(uptr->fileref); + + /* init format based on file size */ + switch (uptr->capac) { + case TARBELL_DD_CAPACITY: + dsk_init_format(&dsk_info[d], 0, 0, 0, 0, DSK_DENSITY_SD, 26, 128, 1); + dsk_init_format(&dsk_info[d], 1, 76, 0, 0, DSK_DENSITY_DD, 51, 128, 1); + break; + + default: + uptr->capac = TARBELL_SD_CAPACITY; + dsk_init_format(&dsk_info[d], 0, 76, 0, 0, DSK_DENSITY_SD, 26, 128, 1); + break; + } + +// dsk_show(&dsk_info[d]); + + return r; +} + +/* Detach routine */ +static t_stat tarbell_detach(UNIT *uptr) +{ + t_stat r; + + r = detach_unit(uptr); /* detach unit */ + + return r; +} + +static int32 tarbell_io(const int32 port, const int32 io, const int32 data) +{ + int32 result = 0xff; + + if (io == S100_IO_WRITE) { /* I/O Write */ + + switch (port & TARBELL_IO_MASK) { + case WD17XX_REG_COMMAND: + case WD17XX_REG_TRACK: + case WD17XX_REG_SECTOR: + case WD17XX_REG_DATA: + wd17xx_outp(wd17xx, port & TARBELL_IO_MASK, data & DATAMASK); + sim_debug(STATUS_MSG, &tarbell_dev, DEV_NAME ": " ADDRESS_FORMAT + " Write WD17XX, Port 0x%02x Data 0x%02x\n", s100_bus_get_addr(), port, data & DATAMASK); + break; + + case TARBELL_REG_DRVSEL: + if (ddfdc_enabled) { + drv_sel = (data & TARBELL_DSEL_MASK) >> 4; /* 2022 not inverted */ + wd17xx_sel_side(wd17xx, (data & TARBELL_SIDE_MASK) >> 6); + wd17xx_sel_dden(wd17xx, (data & TARBELL_DENS_MASK) ? TRUE : FALSE); + + sim_debug(DRIVE_MSG, &tarbell_dev, DEV_NAME ": " ADDRESS_FORMAT " WR DRVSEL (0x%02x) = 0x%02x: Drive: %d, Side: %d, %s-Density.\n", + s100_bus_get_addr(), port, data & DATAMASK, drv_sel, + (data & TARBELL_SIDE_MASK) >> 6, (data & TARBELL_DENS_MASK) ? "Double" : "Single"); + } + else { + drv_sel = (~data & TARBELL_DSEL_MASK) >> 4; /* 1011 inverted */ + wd17xx_sel_side(wd17xx, 0); + wd17xx_sel_dden(wd17xx, FALSE); + + sim_debug(DRIVE_MSG, &tarbell_dev, DEV_NAME ": " ADDRESS_FORMAT " WR DRVSEL (0x%02x) = 0x%02x: Drive: %d\n", + s100_bus_get_addr(), port, data & DATAMASK, drv_sel); + } + + /* Tell WD17XX which drive is selected */ + wd17xx_set_dsk(wd17xx, &dsk_info[drv_sel]); + + break; + + case TARBELL_REG_EXTADDR: + sim_debug(STATUS_MSG, &tarbell_dev, DEV_NAME ": " ADDRESS_FORMAT + " Write Extended Address, Port 0x%02x=0x%02x\n", s100_bus_get_addr(), port, data & DATAMASK); + break; + + default: + break; + } + } else { /* I/O Read */ + switch (port & TARBELL_IO_MASK) { + case WD17XX_REG_STATUS: + case WD17XX_REG_TRACK: + case WD17XX_REG_SECTOR: + case WD17XX_REG_DATA: + result = wd17xx_inp(wd17xx, port & TARBELL_IO_MASK); + sim_debug(STATUS_MSG, &tarbell_dev, DEV_NAME ": " ADDRESS_FORMAT + " Read WD17XX, Port 0x%02x Result 0x%02x\n", s100_bus_get_addr(), port, result); + break; + + case TARBELL_REG_WAIT: + result = (wd17xx_intrq(wd17xx)) ? 0 : TARBELL_FLAG_DRQ; + sim_debug(STATUS_MSG, &tarbell_dev, DEV_NAME ": " ADDRESS_FORMAT + " Read WAIT, Port 0x%02x Result 0x%02x\n", s100_bus_get_addr(), port, result); + break; + + case TARBELL_REG_DMASTAT: + result = 0x00; + break; + } + } + + return result; +} + +/* + * The Tarbell Floppy Disk Constroller has a 32-byte PROM + * located at 0x0000. The PROM loads the first sector of + * track 0 from drive 0 into 0x0000. Since the PROM is + * active at 0x0000, the Tarbell asserts /PHANTOM. While + * /PHANTOM is asserted, memory reads from 0x0000-0x001f + * will be provided by the Tarbell PROM, while memory + * writes to those locations will be handled by the RAM + * board. /PHANTOM is simulated below by passing requests + * to the RAM board configured on the BUS for the first + * page of RAM. The PROM is disabled and /PHANTOM is + * deasserted when A5 is active. + */ +static int32 tarbell_memio(const int32 addr, const int32 rw, const int32 data) +{ + if (rw == S100_IO_READ) { + if (prom_active && ((addr & TARBELL_PROM_MASK) == addr)) { + return tarbell_prom[addr]; + } + else if (mdev.routine != NULL) { + if (addr & 0x0020) { + prom_active = FALSE; + } + + return (mdev.routine)(addr, rw, data); + } + } + else { + /* If writing to RAM, call memory device routine */ + if (mdev.routine != NULL) { + return (mdev.routine)(addr, rw, data); + } + } + + return 0xff; +} + +static t_stat tarbell_set_model(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + if (!cptr) return SCPE_IERR; + + /* Remove IO mapping */ + s100_bus_remio(tarbell_res.io_base, tarbell_res.io_size, &tarbell_io); + + /* this assumes that the parameter has already been upcased */ + if (!strcmp(cptr, "DD")) { + ddfdc_enabled = TRUE; + tarbell_res.io_size = TARBELL_IO_SIZE; + wd17xx_set_fdctype(wd17xx, WD17XX_FDCTYPE_1791); /* Set to 1791 */ + } else { + ddfdc_enabled = FALSE; + tarbell_res.io_size = TARBELL_IO_SIZE - 1; + wd17xx_set_fdctype(wd17xx, WD17XX_FDCTYPE_1771); /* Set to 1771 */ + } + + /* Map new IO */ + s100_bus_addio(tarbell_res.io_base, tarbell_res.io_size, &tarbell_io, DEV_NAME); + + return SCPE_OK; +} + +static t_stat tarbell_show_model(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + fprintf(st, "MODEL=%s", (ddfdc_enabled) ? "2022DD" : "1011SD"); + + return SCPE_OK; +} + +static t_stat tarbell_set_prom(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + if (!cptr) return SCPE_IERR; + if (!strlen(cptr)) return SCPE_ARG; + + /* this assumes that the parameter has already been upcased */ + if (!strncmp(cptr, "ENABLE", strlen(cptr)) && prom_enabled == FALSE) { + tarbell_enable_prom(); + } else if (!strncmp(cptr, "DISABLE", strlen(cptr)) && prom_enabled == TRUE) { + tarbell_disable_prom(); + } else { + return SCPE_ARG; + } + + return SCPE_OK; +} + +static void tarbell_enable_prom(void) +{ + /* Save existing memory device */ + s100_bus_get_mdev(tarbell_res.mem_base, &mdev); + + /* Add PROM to bus */ + s100_bus_addmem(tarbell_res.mem_base, tarbell_res.mem_size, &tarbell_memio, DEV_NAME); + + prom_enabled = TRUE; +} + +static void tarbell_disable_prom(void) +{ + /* Restore memory device */ + if (mdev.routine != NULL) { + s100_bus_addmem(tarbell_res.mem_base, tarbell_res.mem_size, mdev.routine, mdev.name); + mdev.routine = NULL; + mdev.name = NULL; + } + else { + s100_bus_remmem(tarbell_res.mem_base, tarbell_res.mem_size, &tarbell_memio); + } + + prom_enabled = FALSE; +} + +static t_stat tarbell_show_prom(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + fprintf(st, "%s (%sactive)", (prom_enabled) ? "PROM" : "NOPROM", (prom_active) ? "" : "in"); + + return SCPE_OK; +} + +static t_stat tarbell_show_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "\nTarbell Model 1011/2022 Disk Controller (%s)\n", dptr->name); + + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + fprint_reg_help (st, dptr); + + return SCPE_OK; +} + diff --git a/Altair8800/tarbell_fdc.h b/Altair8800/tarbell_fdc.h new file mode 100644 index 00000000..b5bbf112 --- /dev/null +++ b/Altair8800/tarbell_fdc.h @@ -0,0 +1,60 @@ +/* tarbell_fdc.h + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 11/10/25 Initial version + +*/ + +#ifndef _TARBELL_FDC_H +#define _TARBELL_FDC_H + +#define TARBELL_NUM_DRIVES 4 + +#define TARBELL_IO_BASE 0xF8 +#define TARBELL_IO_SIZE 6 +#define TARBELL_IO_MASK 0x07 + +#define TARBELL_PROM_BASE 0x0000 +#define TARBELL_PROM_SIZE 32 +#define TARBELL_PROM_MASK (TARBELL_PROM_SIZE - 1) + +/* Tarbell Register Offsets */ +#define TARBELL_REG_WAIT 0x04 /* Wait Port */ +#define TARBELL_REG_DRVSEL 0x04 /* Drive Select */ +#define TARBELL_REG_DMASTAT 0x05 /* DMA INTRQ Status */ +#define TARBELL_REG_EXTADDR 0x05 /* Extended Address */ + +#define TARBELL_DENS_MASK 0x08 +#define TARBELL_DSEL_MASK 0x30 +#define TARBELL_SIDE_MASK 0x40 + +#define TARBELL_FLAG_DRQ 0x80 /* End of Job (DRQ) */ + +#define TARBELL_SD_CAPACITY (77*26*128) /* SSSD 8" (IBM 3740) Disk Capacity */ +#define TARBELL_DD_CAPACITY ((26*128) + (76*51*128)) /* SSDD 8" Tarbell DD Disk Capacity */ + +#endif + diff --git a/Altair8800/wd_17xx.c b/Altair8800/wd_17xx.c new file mode 100644 index 00000000..ba795de5 --- /dev/null +++ b/Altair8800/wd_17xx.c @@ -0,0 +1,860 @@ +/* wd_17xx.c: Western Digital FD17XX Floppy Disk Controller/Formatter + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + Based on work by Howard M. Harte 2007-2022 + + History: + 13-Nov-2025 Initial version + +*/ + +#include "sim_defs.h" +#include "altair8800_sys.h" +#include "s100_bus.h" +#include "wd_17xx.h" + +#define WD17XX_NAME "WD17XX" + +static uint8 sbuf[WD17XX_MAX_SECTOR_SIZE]; + +static void wd17xx_command(WD17XX_INFO *wd, uint8 data); +static int32 wd17xx_valid(WD17XX_INFO *wd); +static uint8 wd17xx_sec_len(WD17XX_INFO *wd); +static t_stat wd17xx_read_sector(WD17XX_INFO *wd, uint8 *sbuf, int32 *bytesread); +static t_stat wd17xx_write_sector(WD17XX_INFO *wd, uint8 *sbuf, int32 *byteswritten); +static void wd17xx_set_intrq(WD17XX_INFO *wd, int32 value); + +WD17XX_INFO * wd17xx_init(DEVICE *dptr) +{ + WD17XX_INFO *wd; + + if ((dptr == NULL) || (wd = malloc(sizeof(WD17XX_INFO))) == NULL) { + return NULL; + } + + memset(wd, 0x00, sizeof(WD17XX_INFO)); + + /* Save device */ + wd->dptr = dptr; + + return wd; +} + +WD17XX_INFO * wd17xx_release(WD17XX_INFO *wd) +{ + if (wd != NULL) { + free(wd); + } + + return NULL; +} + +void wd17xx_reset(WD17XX_INFO *wd) +{ + if (wd != NULL) { + wd->intrq = TRUE; + wd->drq = FALSE; + wd->status = 0; + wd->track = 0; + wd->fdc_write = FALSE; + wd->fdc_read = FALSE; + wd->fdc_write_track = FALSE; + wd->fdc_readadr = FALSE; + wd->fdc_datacount = 0; + wd->fdc_dataindex = 0; + wd->fdc_sec_len = wd17xx_sec_len(wd); + } +} + +void wd17xx_set_intena(WD17XX_INFO *wd, int32 ena) { + if (wd != NULL) { + wd->intenable = ena; + } +} + +void wd17xx_set_intvec(WD17XX_INFO *wd, int32 vec) { + if (wd != NULL) { + wd->intvector = vec; + } +} + +void wd17xx_set_verbose_flag(WD17XX_INFO *wd, uint32 flag) +{ + if (wd != NULL) { + wd->dbg_verbose = flag; + } +} + +void wd17xx_set_error_flag(WD17XX_INFO *wd, uint32 flag) +{ + if (wd != NULL) { + wd->dbg_error = flag; + } +} + +void wd17xx_set_command_flag(WD17XX_INFO *wd, uint32 flag) +{ + if (wd != NULL) { + wd->dbg_command = flag; + } +} + +void wd17xx_set_read_flag(WD17XX_INFO *wd, uint32 flag) +{ + if (wd != NULL) { + wd->dbg_read = flag; + } +} + +void wd17xx_set_write_flag(WD17XX_INFO *wd, uint32 flag) +{ + if (wd != NULL) { + wd->dbg_write = flag; + } +} + +void wd17xx_set_format_flag(WD17XX_INFO *wd, uint32 flag) +{ + if (wd != NULL) { + wd->dbg_format = flag; + } +} + +void wd17xx_sel_dden(WD17XX_INFO *wd, uint8 dden) +{ + if (wd != NULL) { + wd->dden = dden; + } +} + +void wd17xx_sel_side(WD17XX_INFO *wd, uint8 side) +{ + if (wd != NULL) { + wd->side = side; + wd->fdc_sec_len = wd17xx_sec_len(wd); + } +} + +void wd17xx_sel_drive_type(WD17XX_INFO *wd, uint8 type) +{ + if (wd != NULL) { + wd->drivetype = type; + } +} + +uint8 wd17xx_intrq(WD17XX_INFO *wd) +{ + if (wd != NULL) { + return wd->intrq; + } + + return 0xff; +} + +void wd17xx_set_fdctype(WD17XX_INFO *wd, int fdctype) +{ + if (wd != NULL) { + wd->fdctype = fdctype; + } +} + +void wd17xx_set_dsk(WD17XX_INFO *wd, DSK_INFO *dsk) +{ + if (wd != NULL) { + wd->dsk = dsk; + } +} + +uint8 wd17xx_inp(WD17XX_INFO *wd, uint8 port) +{ + uint8 r = 0xff; + int32 bytesread; + + sim_debug(wd->dbg_verbose, wd->dptr, WD17XX_NAME " INP %02X\n", port); + + if (wd == NULL || wd->dsk == NULL) { + return r; + } + + switch (port) { + case WD17XX_REG_STATUS: + /* Fix up status based on Command Type */ + if ((wd->cmdtype == 0) || (wd->cmdtype == 1) || (wd->cmdtype == 4)) { + wd->status ^= WD17XX_STAT_IDX; /* Generate Index pulses */ + wd->status &= ~WD17XX_STAT_TRK0; + wd->status |= (wd->track == 0) ? WD17XX_STAT_TRK0 : 0; + } + else { /* Command Type 3 */ + wd->status &= ~WD17XX_STAT_IDX; /* Mask index pulses */ + wd->status |= (wd->drq) ? WD17XX_STAT_DRQ : 0; + } + + wd->status &= ~WD17XX_STAT_NRDY; + wd->status |= (wd->dsk->unit == NULL || wd->dsk->unit->fileref == NULL) ? WD17XX_STAT_NRDY : 0; + + sim_debug(wd->dbg_verbose, wd->dptr, WD17XX_NAME ADDRESS_FORMAT + " RD STATUS = 0x%02x, CMDTYPE=%x\n", s100_bus_get_addr(), wd->status, wd->cmdtype); + + r = wd->status; + break; + + case WD17XX_REG_TRACK: + r = wd->track; + break; + + case WD17XX_REG_SECTOR: + r = wd->sector; + break; + + case WD17XX_REG_DATA: + r = 0xFF; /* Return High-Z data */ + if (wd->fdc_read == TRUE) { + if (wd->fdc_dataindex < wd->fdc_datacount) { + wd->status &= ~(WD17XX_STAT_BUSY); /* Clear BUSY */ + wd->data = sbuf[wd->fdc_dataindex]; + r = wd->data; + + if (wd->fdc_readadr == TRUE) { + sim_debug(wd->dbg_read, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " READ_ADDR[%d/%d] = 0x%02x\n", s100_bus_get_addr(), wd->fdc_dataindex, wd->fdc_datacount, wd->data); + } + + wd->fdc_dataindex++; + if (wd->fdc_dataindex == wd->fdc_datacount) { + if (wd->fdc_multi == FALSE) { + wd->status &= ~(WD17XX_STAT_DRQ | WD17XX_STAT_BUSY); /* Clear DRQ, BUSY */ + wd17xx_set_intrq(wd, TRUE); + wd->fdc_read = FALSE; + wd->fdc_readadr = FALSE; + } else { + wd->sector++; + + sim_debug(wd->dbg_read, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " MULTI_READ_REC, T:%2d/H:%d/S:%2d, %s, len=%d\n", + s100_bus_get_addr(), wd->track, wd->side, wd->sector, wd->dden ? "DD" : "SD", + dsk_sector_size(wd->dsk, wd->track, wd->side)); + + if (wd->dsk->unit->fileref == NULL) { + sim_debug(wd->dbg_error, wd->dptr, ".fileref is NULL!\n"); + } else { + dsk_read_sector(wd->dsk, wd->track, wd->side, wd->sector, sbuf, &bytesread); + } + } + } + } + } + break; + + default: + break; + } + + return r; +} + +void wd17xx_outp(WD17XX_INFO *wd, uint8 port, uint8 data) +{ + int32 byteswritten; + + sim_debug(wd->dbg_verbose, wd->dptr, WD17XX_NAME " OUTP %02X %02X\n", port, data); + + if (wd == NULL || wd->dsk == NULL) { + return; + } + + switch (port) { + case WD17XX_REG_COMMAND: + wd->fdc_read = FALSE; + wd->fdc_write = FALSE; + wd->fdc_write_track = FALSE; + wd->fdc_datacount = 0; + wd->fdc_dataindex = 0; + if (wd->intenable) { + s100_bus_int(1 << wd->intvector, wd->intvector * 2); + } + + wd17xx_command(wd, data); + break; + + case WD17XX_REG_TRACK: + wd->track = data; + wd->fdc_sec_len = wd17xx_sec_len(wd); + break; + + case WD17XX_REG_SECTOR: + wd->sector = data; + break; + + case WD17XX_REG_DATA: + sim_debug(wd->dbg_verbose, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " WR DATA = 0x%02x\n", s100_bus_get_addr(), data); + if (wd->fdc_write == TRUE) { + if (wd->fdc_dataindex < wd->fdc_datacount) { + sbuf[wd->fdc_dataindex] = data; + + wd->fdc_dataindex++; + if (wd->fdc_dataindex == wd->fdc_datacount) { + wd->status &= ~(WD17XX_STAT_DRQ | WD17XX_STAT_BUSY); /* Clear DRQ, BUSY */ + wd17xx_set_intrq(wd, TRUE); + if (wd->intenable) { + s100_bus_int(1 << wd->intvector, wd->intvector * 2); + } + + sim_debug(wd->dbg_write, wd->dptr, WD17XX_NAME ADDRESS_FORMAT + " Writing sector, T:%2d/S:%d/H:%2d, Len=%d\n", + s100_bus_get_addr(), wd->track, wd->side, wd->sector, + dsk_sector_size(wd->dsk, wd->track, wd->side)); + + wd17xx_write_sector(wd, sbuf, &byteswritten); + + wd->fdc_write = FALSE; + } + } + } + + if (wd->fdc_write_track == TRUE) { + if (wd->fdc_fmt_state == WD17XX_FMT_GAP1) { + if (data != 0xFC && (data != 0x00 && wd->fdc_gap[0] < 32)) { + wd->fdc_gap[0]++; + } + else { + sim_debug(wd->dbg_format, wd->dptr, WD17XX_NAME ADDRESS_FORMAT + " FMT GAP1 Length = %d\n", s100_bus_get_addr(), wd->fdc_gap[0]); + wd->fdc_gap[1] = 0; + wd->fdc_fmt_state = WD17XX_FMT_GAP2; + } + } else if (wd->fdc_fmt_state == WD17XX_FMT_GAP2) { + if (data != 0xFE) { + wd->fdc_gap[1]++; + } + else { + sim_debug(wd->dbg_format, wd->dptr, WD17XX_NAME ADDRESS_FORMAT + " FMT GAP2 Length = %d\n", s100_bus_get_addr(), wd->fdc_gap[1]); + wd->fdc_gap[2] = 0; + wd->fdc_fmt_state = WD17XX_FMT_HEADER; + wd->fdc_header_index = 0; + } + } else if (wd->fdc_fmt_state == WD17XX_FMT_HEADER) { + if (wd->fdc_header_index == 5) { + wd->fdc_gap[2] = 0; + wd->fdc_fmt_state = WD17XX_FMT_GAP3; + } else { + sim_debug(wd->dbg_format, wd->dptr, WD17XX_NAME ADDRESS_FORMAT + " HEADER[%d]=%02x\n", s100_bus_get_addr(), wd->fdc_header_index, data); + + switch (wd->fdc_header_index) { + case 0: + wd->fdc_fmt_track = data; + break; + case 1: + wd->fdc_fmt_side = data; + break; + case 2: + wd->fdc_fmt_sector = data; + break; + case 3: + case 4: + break; + } + + wd->fdc_header_index++; + } + } else if (wd->fdc_fmt_state == WD17XX_FMT_GAP3) { + if (data != 0xFB) { + wd->fdc_gap[2]++; + } + else { + sim_debug(wd->dbg_format, wd->dptr, WD17XX_NAME ADDRESS_FORMAT + " FMT GAP3 Length = %d\n", s100_bus_get_addr(), wd->fdc_gap[2]); + wd->fdc_fmt_state = WD17XX_FMT_DATA; + wd->fdc_dataindex = 0; + } + } else if (wd->fdc_fmt_state == WD17XX_FMT_DATA) { /* data bytes */ + if (data != 0xF7) { + sbuf[wd->fdc_dataindex] = data; + wd->fdc_dataindex++; + } + else { + wd->fdc_sec_len = sys_floorlog2(wd->fdc_dataindex) - 7; + + if (wd->fdc_sec_len > wd17xx_sec_len(wd)) { /* Error calculating N or N too large */ + sim_debug(wd->dbg_error, wd->dptr, WD17XX_NAME ADDRESS_FORMAT + " Invalid sector size!\n", s100_bus_get_addr()); + wd->fdc_sec_len = 0; + } + if (wd->fdc_fmt_sector_count >= dsk_sectors(wd->dsk, wd->track, wd->side)) { + sim_debug(wd->dbg_error, wd->dptr, WD17XX_NAME ADDRESS_FORMAT + " Illegal sector count\n", s100_bus_get_addr()); + + wd->fdc_fmt_sector_count = 0; + } + + wd->fdc_fmt_sector_count++; + + /* Write the sector to disk */ + dsk_write_sector(wd->dsk, wd->track, wd->side, wd->fdc_fmt_sector_count, sbuf, NULL); + + sim_debug(wd->dbg_format, wd->dptr, WD17XX_NAME ADDRESS_FORMAT + " FMT Data Length = %d\n", s100_bus_get_addr(), wd->fdc_dataindex); + + sim_debug(wd->dbg_format, wd->dptr, WD17XX_NAME ADDRESS_FORMAT + " FORMAT T:%2d (%02d)/H:%d (%02d)/S:%2d (%02d)/L=%d (%02X)\n", s100_bus_get_addr(), + wd->track, wd->fdc_fmt_track, wd->side, wd->fdc_fmt_side, + wd->fdc_fmt_sector_count, wd->fdc_fmt_sector, + wd->fdc_dataindex, wd->fdc_sec_len); + + wd->fdc_gap[1] = 0; + wd->fdc_fmt_state = WD17XX_FMT_GAP2; + + if (wd->fdc_fmt_sector_count == dsk_sectors(wd->dsk, wd->track, wd->side)) { + wd->status &= ~(WD17XX_STAT_BUSY | WD17XX_STAT_LOSTD); /* Clear BUSY, LOST_DATA */ + wd17xx_set_intrq(wd, TRUE); + if (wd->intenable) { + s100_bus_int(1 << wd->intvector, wd->intvector * 2); + } + + /* Recalculate disk size */ + wd->dsk->unit->capac = sim_fsize(wd->dsk->unit->fileref); + } + } + } + } + + wd->data = data; + break; + + default: + break; + } +} + +static void wd17xx_command(WD17XX_INFO *wd, uint8 cmd) +{ + int32 bytesread; + + if (wd->status & WD17XX_STAT_BUSY) { + if (((cmd & WD17XX_CMD_MASK) != WD17XX_CMD_FI)) { + sim_debug(wd->dbg_error, wd->dptr, WD17XX_NAME " " ADDRESS_FORMAT + " ERROR: Command 0x%02x ignored because controller is BUSY\n\n", s100_bus_get_addr(), cmd); + } + return; + } + + switch(cmd & WD17XX_CMD_MASK) { + /* Type I Commands */ + case WD17XX_CMD_RESTORE: + case WD17XX_CMD_SEEK: + case WD17XX_CMD_STEP: + case WD17XX_CMD_STEPU: + case WD17XX_CMD_STEPIN: + case WD17XX_CMD_STEPINU: + case WD17XX_CMD_STEPOUT: + case WD17XX_CMD_STEPOUTU: + wd->cmdtype = 1; + wd->status |= WD17XX_STAT_BUSY; /* Set BUSY */ + wd->status &= ~(WD17XX_STAT_CRC | WD17XX_STAT_SEEK | WD17XX_STAT_DRQ); + wd17xx_set_intrq(wd, FALSE); + wd->hld = cmd & WD17XX_FLG_H; + wd->verify = cmd & WD17XX_FLG_V; + if (wd->fdctype == WD17XX_FDCTYPE_1795) { + /* WD1795 and WD1797 have a side select output. */ + wd->side = (cmd & WD17XX_FLG_F1) >> 1; + } + break; + + /* Type II Commands */ + case WD17XX_CMD_RD: + case WD17XX_CMD_RDM: + case WD17XX_CMD_WR: + case WD17XX_CMD_WRM: + wd->cmdtype = 2; + wd->status = WD17XX_STAT_BUSY; /* Set BUSY, clear all others */ + wd17xx_set_intrq(wd, FALSE); + wd->hld = 1; /* Load the head immediately, E Flag not checked. */ + if (wd->fdctype != WD17XX_FDCTYPE_1771) { + /* WD1795 and WD1797 have a side select output. */ + wd->side = (cmd & WD17XX_FLG_F1) >> 1; + } + break; + + /* Type III Commands */ + case WD17XX_CMD_RDADR: + case WD17XX_CMD_RDTRK: + case WD17XX_CMD_WRTRK: + wd->cmdtype = 3; + break; + + /* Type IV Commands */ + case WD17XX_CMD_FI: + wd->cmdtype = 4; + break; + + default: + wd->cmdtype = 0; + sim_debug(wd->dbg_error, wd->dptr, WD17XX_NAME " Invalid command %02X\n", cmd); + break; + } + + + switch(cmd & WD17XX_CMD_MASK) { + + /* Type I Commands */ + case WD17XX_CMD_RESTORE: + wd->track = 0; + wd17xx_set_intrq(wd, TRUE); + + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=RESTORE %s\n", s100_bus_get_addr(), wd->verify ? "[VERIFY]" : ""); + break; + + case WD17XX_CMD_SEEK: + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT + " CMD=SEEK, track=%d, new=%d %s\n", s100_bus_get_addr(), wd->track, wd->data, wd->verify ? "[VERIFY]" : ""); + + wd->track = wd->data; + break; + + case WD17XX_CMD_STEP: + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=STEP %s\n", s100_bus_get_addr(), wd->verify ? "[VERIFY]" : ""); + break; + + case WD17XX_CMD_STEPU: + if (wd->fdc_step_dir == 1) { + if (wd->track < wd->dsk->fmt.tracks - 1) { + wd->track++; + } + } else if (wd->fdc_step_dir == -1) { + if (wd->track > 0) { + wd->track--; + } + } + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=STEP_U dir=%d track=%d %s\n", s100_bus_get_addr(), wd->fdc_step_dir, wd->track, wd->verify ? "[VERIFY]" : ""); + break; + + case WD17XX_CMD_STEPIN: + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=STEP_IN %s\n", s100_bus_get_addr(), wd->verify ? "[VERIFY]" : ""); + break; + + case WD17XX_CMD_STEPINU: + if (wd->track < wd->dsk->fmt.tracks - 1) { + wd->track++; + } + + wd->fdc_step_dir = 1; + + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=STEP_IN_U, track=%d %s\n", s100_bus_get_addr(), wd->track, wd->verify ? "[VERIFY]" : ""); + break; + + case WD17XX_CMD_STEPOUT: + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=STEP_OUT %s\n", s100_bus_get_addr(), wd->verify ? "[VERIFY]" : ""); + break; + + case WD17XX_CMD_STEPOUTU: + if (wd->track > 0) { + wd->track--; + } + + wd->fdc_step_dir = -1; + + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=STEP_OUT_U, track=%d %s\n", s100_bus_get_addr(), wd->track, wd->verify ? "[VERIFY]" : ""); + break; + + /* Type II Commands */ + case WD17XX_CMD_RD: + case WD17XX_CMD_RDM: + wd->fdc_multi = (cmd & WD17XX_FLG_M) ? TRUE : FALSE; + + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=READ_REC, T:%2d/H:%d/S:%2d, %s, %s len=%d\n", s100_bus_get_addr(), wd->track, + wd->side, wd->sector, + wd->fdc_multi ? "Multiple" : "Single", + wd->dden ? "DD" : "SD", wd->dsk->fmt.track[wd->track][wd->side].sectorsize); + + if (wd17xx_valid(wd) == FALSE) { + wd->status |= WD17XX_STAT_RNF; /* Record not found */ + wd->status &= ~WD17XX_STAT_BUSY; + wd17xx_set_intrq(wd, TRUE); + } else { + wd17xx_read_sector(wd, sbuf, &bytesread); + } + break; + + case WD17XX_CMD_WR: + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=WRITE_REC, T:%2d/H:%d/S:%2d, %s.\n", s100_bus_get_addr(), wd->track, wd->side, wd->sector, (cmd & WD17XX_FLG_M) ? "Multiple" : "Single"); + + wd->status |= (WD17XX_STAT_DRQ); /* Set DRQ */ + wd->status |= (wd->dsk->unit->flags & UNIT_RO) ? WD17XX_STAT_WP : 0; /* Set WP */ + wd->drq = 1; + wd->fdc_datacount = dsk_sector_size(wd->dsk, wd->track, wd->side); + wd->fdc_dataindex = 0; + wd->fdc_write = TRUE; + wd->fdc_write_track = FALSE; + wd->fdc_read = FALSE; + wd->fdc_readadr = FALSE; + + sbuf[wd->fdc_dataindex] = wd->data; + break; + + case WD17XX_CMD_WRM: + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " Error: WRITE_RECS not implemented.\n", s100_bus_get_addr()); + break; + + /* Type III Commands */ + case WD17XX_CMD_RDADR: + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=READ_ADDR, T:%d/S:%d, %s\n", + s100_bus_get_addr(), wd->track, wd->side, wd->dden ? "DD" : "SD"); + + if (wd17xx_valid(wd) == FALSE) { + wd->status = WD17XX_STAT_RNF; /* Record not found */ + wd17xx_set_intrq(wd, TRUE); + } else { + wd->status = (WD17XX_STAT_DRQ | WD17XX_STAT_BUSY); /* Set DRQ, BUSY */ + wd->drq = 1; + wd->fdc_datacount = 6; + wd->fdc_dataindex = 0; + wd->fdc_read = TRUE; + wd->fdc_readadr = TRUE; + + sbuf[0] = wd->track; + sbuf[1] = wd->side; + sbuf[2] = (wd->sector < dsk_start_sector(wd->dsk, wd->track, wd->side)) ? wd->sector : dsk_start_sector(wd->dsk, wd->track, wd->side); + sbuf[3] = wd->fdc_sec_len; + sbuf[4] = 0xAA; /* CRC1 */ + sbuf[5] = 0x55; /* CRC2 */ + + wd->sector = wd->track; + + wd->status &= ~(WD17XX_STAT_BUSY); /* Clear BUSY */ + + wd17xx_set_intrq(wd, TRUE); + } + break; + + case WD17XX_CMD_RDTRK: + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=READ_TRACK\n", s100_bus_get_addr()); + sim_debug(wd->dbg_error, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " Error: READ_TRACK not implemented.\n", s100_bus_get_addr()); + break; + + case WD17XX_CMD_WRTRK: + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=WRITE_TRACK, T:%2d/H:%d/S:%d.\n", + s100_bus_get_addr(), wd->track, wd->side, + wd->dsk->fmt.track[wd->track][wd->side].sectorsize); + + wd->status |= (WD17XX_STAT_DRQ); /* Set DRQ */ + wd->status |= (wd->dsk->unit->flags & UNIT_RO) ? WD17XX_STAT_WP : 0; /* Set WP */ + wd17xx_set_intrq(wd, FALSE); + wd->fdc_datacount = dsk_sector_size(wd->dsk, wd->track, wd->side); + wd->fdc_dataindex = 0; + wd->fdc_write = FALSE; + wd->fdc_write_track = TRUE; + wd->fdc_read = FALSE; + wd->fdc_readadr = FALSE; + wd->fdc_fmt_state = WD17XX_FMT_GAP1; /* TRUE when writing an entire track */ + wd->fdc_fmt_sector_count = 0; + + break; + + /* Type IV Commands */ + case WD17XX_CMD_FI: + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " CMD=FORCE_INTR\n", s100_bus_get_addr()); + if ((cmd & WD17XX_CMD_MASK) == 0) { /* I0-I3 == 0, no intr, but clear BUSY and terminate command */ + wd->status &= ~(WD17XX_STAT_DRQ | WD17XX_STAT_BUSY); /* Clear DRQ, BUSY */ + wd->drq = 0; + wd->fdc_write = FALSE; + wd->fdc_read = FALSE; + wd->fdc_write_track = FALSE; + wd->fdc_readadr = FALSE; + wd->fdc_datacount = 0; + wd->fdc_dataindex = 0; + } + else if (cmd & 0x08) { /* Immediate Interrupt */ + wd17xx_set_intrq(wd, TRUE); + + if (wd->intenable) { + s100_bus_int(1 << wd->intvector, wd->intvector * 2); + } + wd->status &= ~(WD17XX_STAT_BUSY); /* Clear BUSY */ + } + else { /* Other interrupts not implemented yet */ + wd->status &= ~(WD17XX_STAT_BUSY); /* Clear BUSY */ + } + break; + + default: + sim_debug(wd->dbg_command, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " ERROR: Unknown command 0x%02x.\n\n", s100_bus_get_addr(), cmd); + break; + } + + /* Post processing of Type-specific command */ + switch(cmd & WD17XX_CMD_MASK) { + + /* Type I Commands */ + case WD17XX_CMD_RESTORE: + case WD17XX_CMD_SEEK: + case WD17XX_CMD_STEP: + case WD17XX_CMD_STEPU: + case WD17XX_CMD_STEPIN: + case WD17XX_CMD_STEPINU: + case WD17XX_CMD_STEPOUT: + case WD17XX_CMD_STEPOUTU: + if (wd->verify) { /* Verify the selected track/side is ok. */ + sim_debug(wd->dbg_verbose, wd->dptr, WD17XX_NAME ADDRESS_FORMAT " Verify ", s100_bus_get_addr()); + if (dsk_validate(wd->dsk, wd->track, 0, 1) != SCPE_OK) { /* Not validating side or sector */ + sim_debug(wd->dbg_verbose, wd->dptr, WD17XX_NAME "FAILED\n"); + wd->status |= WD17XX_STAT_SEEK; /* Seek error */ + } else { + sim_debug(wd->dbg_verbose, wd->dptr, WD17XX_NAME "Ok\n"); + } + } + + wd->status &= ~(WD17XX_STAT_TRK0); + if (wd->track == 0) { + wd->status |= WD17XX_STAT_TRK0; + } + + wd->fdc_sec_len = wd17xx_sec_len(wd); + + wd->status &= ~(WD17XX_STAT_BUSY); /* Clear BUSY */ + wd17xx_set_intrq(wd, TRUE); + + if (wd->intenable) { + s100_bus_int(1 << wd->intvector, wd->intvector * 2); + } + + break; + + /* Type II Commands */ + case WD17XX_CMD_RD: + case WD17XX_CMD_RDM: + case WD17XX_CMD_WR: + case WD17XX_CMD_WRM: + + /* Type III Commands */ + case WD17XX_CMD_RDADR: + case WD17XX_CMD_RDTRK: + case WD17XX_CMD_WRTRK: + wd->status &= ~(WD17XX_STAT_BUSY); /* Clear BUSY */ + + if (wd->intenable) { + wd17xx_set_intrq(wd, TRUE); + s100_bus_int(1 << wd->intvector, wd->intvector * 2); + } + + wd->drq = 1; + break; + + /* Type IV Commands */ + case WD17XX_CMD_FI: + default: + break; + } +} + +static t_stat wd17xx_read_sector(WD17XX_INFO *wd, uint8 *sbuf, int32 *bytesread) +{ + t_stat r; + + if (wd == NULL || wd->dsk == NULL) { + return SCPE_ARG; + } + + if ((r = dsk_read_sector(wd->dsk, wd->track, wd->side, wd->sector, sbuf, bytesread)) == SCPE_OK) { + wd->status |= (WD17XX_STAT_DRQ | WD17XX_STAT_BUSY); /* Set DRQ, BUSY */ + wd17xx_set_intrq(wd, FALSE); + wd->fdc_datacount = dsk_sector_size(wd->dsk, wd->track, wd->side); + wd->fdc_dataindex = 0; + wd->fdc_read = TRUE; + wd->fdc_readadr = FALSE; + } + else { + wd->status &= ~WD17XX_STAT_BUSY; /* Clear DRQ, BUSY */ + wd->status |= WD17XX_STAT_RNF; + wd17xx_set_intrq(wd, TRUE); + wd->fdc_read = FALSE; + wd->fdc_readadr = FALSE; + } + + return r; +} + +static t_stat wd17xx_write_sector(WD17XX_INFO *wd, uint8 *sbuf, int32 *byteswritten) +{ + t_stat r; + + if (wd == NULL || wd->dsk == NULL) { + return SCPE_ARG; + } + + r = dsk_write_sector(wd->dsk, wd->track, wd->side, wd->sector, sbuf, byteswritten); + + return r; +} + +static int32 wd17xx_valid(WD17XX_INFO *wd) +{ + return TRUE; +} + +/* Convert sector size to sector length field */ +static uint8 wd17xx_sec_len(WD17XX_INFO *wd) +{ + uint8 i; + int32 secsize; + + if (wd == NULL || wd->dsk == NULL) { + return 0; + } + + secsize = dsk_sector_size(wd->dsk, wd->track, wd->side); + + for (i = 0; i <= 4; i++) { + if ( (0x80 << i) == secsize ) { + sim_debug(wd->dbg_verbose | wd->dbg_write, wd->dptr, "%d sector size = %02X sector len field\n", secsize, i); + return i; + } + } + + return 0; /* default to 128-byte sectors */ +} + +static void wd17xx_set_intrq(WD17XX_INFO *wd, int32 value) +{ + wd->intrq = (value) ? TRUE : FALSE; /* INTRQ and DRQ are mutually exclusive */ + wd->drq = !wd->intrq; +} + +void wd17xx_show(WD17XX_INFO *wd) +{ + sim_debug(wd->dbg_verbose, wd->dptr, "fdctype: %02X\n", wd->fdctype); + sim_debug(wd->dbg_verbose, wd->dptr, "intenable: %02X\n", wd->intenable); + sim_debug(wd->dbg_verbose, wd->dptr, "intvector: %02X\n", wd->intvector); + sim_debug(wd->dbg_verbose, wd->dptr, "drq: %02X\n", wd->drq); + sim_debug(wd->dbg_verbose, wd->dptr, "intrq: %02X\n", wd->intrq); + sim_debug(wd->dbg_verbose, wd->dptr, "hld: %02X\n", wd->hld); + sim_debug(wd->dbg_verbose, wd->dptr, "dden: %02X\n", wd->dden); + sim_debug(wd->dbg_verbose, wd->dptr, "side: %02X\n", wd->side); + sim_debug(wd->dbg_verbose, wd->dptr, "drivetype: %02X\n", wd->drivetype); + sim_debug(wd->dbg_verbose, wd->dptr, "status: %02X\n", wd->status); + sim_debug(wd->dbg_verbose, wd->dptr, "command: %02X\n", wd->command); + sim_debug(wd->dbg_verbose, wd->dptr, "track: %02X\n", wd->track); + sim_debug(wd->dbg_verbose, wd->dptr, "sector: %02X\n", wd->sector); + sim_debug(wd->dbg_verbose, wd->dptr, "data: %02X\n", wd->data); +} + diff --git a/Altair8800/wd_17xx.h b/Altair8800/wd_17xx.h new file mode 100644 index 00000000..4efb3047 --- /dev/null +++ b/Altair8800/wd_17xx.h @@ -0,0 +1,163 @@ +/* wd_17xx.h: Western Digital FD17XX Floppy Disk Controller/Formatter + + Copyright (c) 2025 Patrick A. Linstruth + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Patrick Linstruth shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Patrick Linstruth. + + History: + 13-Nov-2025 Initial version + +*/ + +#ifndef _WD_17XX_H +#define _WD_17XX_H + +#include "altair8800_dsk.h" + +#define WD17XX_MAX_SECTOR_SIZE 4096 + +typedef struct { + uint16 fdctype; /* Default is 1771 */ + uint8 intenable; /* Interrupt Enable */ + uint8 intvector; /* Interrupt Vector */ + uint8 drq; /* WD17XX DMA Request Output */ + uint8 intrq; /* WD17XX Interrupt Request Output (EOJ) */ + uint8 hld; /* WD17XX Head Load Output */ + uint8 dden; /* WD17XX Double-Density Input */ + uint8 verify; /* Verify */ + uint8 drivetype; /* 8 or 5 depending on disk type. */ + uint8 status; /* Status Register */ + uint8 command; /* Command Register */ + uint8 track; /* Track Register */ + uint8 side; /* Side select */ + uint8 sector; /* Sector Register */ + uint8 data; /* Data Register */ + DSK_INFO *dsk; /* Current DSK information */ + uint8 cmdtype; /* Command type */ + uint8 fdc_read; /* TRUE when reading */ + uint8 fdc_readadr; /* TRUE when reading address */ + uint8 fdc_write; /* TRUE when writing */ + uint8 fdc_write_track; /* TRUE when writing an entire track */ + uint8 fdc_multi; /* Multiple records */ + uint8 fdc_sec_len; /* Sector length field */ + uint16 fdc_datacount; /* Read or Write data remaining transfer length */ + uint16 fdc_dataindex; /* index of current byte in sector data */ + uint8 fdc_fmt_state; /* Format track statemachine state */ + uint8 fdc_fmt_track; + uint8 fdc_fmt_side; + uint8 fdc_fmt_sector; + uint8 fdc_gap[4]; /* Gap I - Gap IV lengths */ + uint8 fdc_fmt_sector_count; /* sector count for format track */ + uint8 fdc_header_index; /* Index into header */ + int8 fdc_step_dir; + DEVICE *dptr; /* Device Owner */ + uint32 dbg_verbose; /* Verbose debug flag */ + uint32 dbg_error; /* Error debug flag */ + uint32 dbg_read; /* read debug flag */ + uint32 dbg_write; /* write debug flag */ + uint32 dbg_command; /* command debug flag */ + uint32 dbg_format; /* format debug flag */ +} WD17XX_INFO; + +#define WD17XX_FDCTYPE_1771 0x01 +#define WD17XX_FDCTYPE_1791 0x02 +#define WD17XX_FDCTYPE_1793 0x02 +#define WD17XX_FDCTYPE_1795 0x04 +#define WD17XX_FDCTYPE_1797 0x04 + +#define WD17XX_REG_STATUS 0x00 +#define WD17XX_REG_COMMAND 0x00 +#define WD17XX_REG_TRACK 0x01 +#define WD17XX_REG_SECTOR 0x02 +#define WD17XX_REG_DATA 0x03 + +#define WD17XX_CMD_MASK 0xf0 /* Command Mask */ +#define WD17XX_CMD_RESTORE 0x00 +#define WD17XX_CMD_SEEK 0x10 +#define WD17XX_CMD_STEP 0x20 +#define WD17XX_CMD_STEPU 0x30 +#define WD17XX_CMD_STEPIN 0x40 +#define WD17XX_CMD_STEPINU 0x50 +#define WD17XX_CMD_STEPOUT 0x60 +#define WD17XX_CMD_STEPOUTU 0x70 +#define WD17XX_CMD_RD 0x80 +#define WD17XX_CMD_RDM 0x90 +#define WD17XX_CMD_WR 0xA0 +#define WD17XX_CMD_WRM 0xB0 +#define WD17XX_CMD_RDADR 0xC0 +#define WD17XX_CMD_RDTRK 0xE0 +#define WD17XX_CMD_WRTRK 0xF0 +#define WD17XX_CMD_FI 0xD0 + +#define WD17XX_FLG_F1 0x02 +#define WD17XX_FLG_V 0x04 +#define WD17XX_FLG_F2 0x08 +#define WD17XX_FLG_H 0x08 +#define WD17XX_FLG_B 0x08 /* Block Length Flag (1771) */ +#define WD17XX_FLG_U 0x10 /* Update track register */ +#define WD17XX_FLG_M 0x10 /* Multiple record flag */ + +#define WD17XX_STAT_BUSY 0x01 /* S0 - Busy */ +#define WD17XX_STAT_IDX 0x02 /* S1 - Index */ +#define WD17XX_STAT_DRQ 0x02 /* S1 - Data Request */ +#define WD17XX_STAT_TRK0 0x04 /* S2 - Track 0 */ +#define WD17XX_STAT_LOSTD 0x04 /* S2 - Lost Data */ +#define WD17XX_STAT_CRC 0x08 /* S3 - CRC Error */ +#define WD17XX_STAT_SEEK 0x10 /* S4 - Seek Error */ +#define WD17XX_STAT_RNF 0x10 /* S4 - Record Not Found */ +#define WD17XX_STAT_HDLD 0x20 /* S5 - Head Loaded */ +#define WD17XX_STAT_RT 0x20 /* S5 - Record Type */ +#define WD17XX_STAT_WF 0x20 /* S5 - Write Fault */ +#define WD17XX_STAT_WP 0x40 /* S6 - Write Protect */ +#define WD17XX_STAT_NRDY 0x80 /* S7 - Not Ready */ + +/* Write Track (format) Statemachine states */ +#define WD17XX_FMT_GAP1 1 +#define WD17XX_FMT_GAP2 2 +#define WD17XX_FMT_GAP3 3 +#define WD17XX_FMT_GAP4 4 +#define WD17XX_FMT_HEADER 5 +#define WD17XX_FMT_DATA 6 + +extern WD17XX_INFO * wd17xx_init(DEVICE *dptr); +extern WD17XX_INFO * wd17xx_release(WD17XX_INFO *wd); +extern void wd17xx_reset(WD17XX_INFO *wd); +extern void wd17xx_sel_dden(WD17XX_INFO *wd, uint8 dden); +extern void wd17xx_sel_side(WD17XX_INFO *wd, uint8 side); +extern void wd17xx_sel_drive_type(WD17XX_INFO *wd, uint8 type); +extern void wd17xx_set_fdctype(WD17XX_INFO *wd, int fdctype); +extern void wd17xx_set_dsk(WD17XX_INFO *wd, DSK_INFO *dsk); +extern void wd17xx_set_intena(WD17XX_INFO *wd, int32 ena); +extern void wd17xx_set_intvec(WD17XX_INFO *wd, int32 vec); +extern void wd17xx_set_verbose_flag(WD17XX_INFO *wd, uint32 flag); +extern void wd17xx_set_error_flag(WD17XX_INFO *wd, uint32 flag); +extern void wd17xx_set_read_flag(WD17XX_INFO *wd, uint32 flag); +extern void wd17xx_set_write_flag(WD17XX_INFO *wd, uint32 flag); +extern void wd17xx_set_command_flag(WD17XX_INFO *wd, uint32 flag); +extern void wd17xx_set_format_flag(WD17XX_INFO *wd, uint32 flag); +extern uint8 wd17xx_intrq(WD17XX_INFO *wd); +extern uint8 wd17xx_inp(WD17XX_INFO *wd, uint8 port); +extern void wd17xx_outp(WD17XX_INFO *wd, uint8 port, uint8 data); +extern void wd17xx_show(WD17XX_INFO *wd); + +#endif + diff --git a/Visual Studio Projects/Altair8800.vcproj b/Visual Studio Projects/Altair8800.vcproj new file mode 100644 index 00000000..99815a9e --- /dev/null +++ b/Visual Studio Projects/Altair8800.vcproj @@ -0,0 +1,464 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/Simh.sln b/Visual Studio Projects/Simh.sln index 6c815f31..7913d532 100755 --- a/Visual Studio Projects/Simh.sln +++ b/Visual Studio Projects/Simh.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 10.00 # Visual C++ Express 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VAX", "VAX.vcproj", "{D5D873F7-D286-43E7-958A-3D838FAA0856}" @@ -398,6 +398,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "linc", "linc.vcproj", "{58E {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Altair8800", "Altair8800.vcproj", "{473F61F7-FCE3-4157-B1A8-A8BC054C789F}" + ProjectSection(ProjectDependencies) = postProject + {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -724,6 +729,10 @@ Global {58E9E172-1CC9-4BA6-8176-F832B11DB1EC}.Debug|Win32.Build.0 = Debug|Win32 {58E9E172-1CC9-4BA6-8176-F832B11DB1EC}.Release|Win32.ActiveCfg = Release|Win32 {58E9E172-1CC9-4BA6-8176-F832B11DB1EC}.Release|Win32.Build.0 = Release|Win32 + {473F61F7-FCE3-4157-B1A8-A8BC054C789F}.Debug|Win32.ActiveCfg = Debug|Win32 + {473F61F7-FCE3-4157-B1A8-A8BC054C789F}.Debug|Win32.Build.0 = Debug|Win32 + {473F61F7-FCE3-4157-B1A8-A8BC054C789F}.Release|Win32.ActiveCfg = Release|Win32 + {473F61F7-FCE3-4157-B1A8-A8BC054C789F}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/doc/altair8800_doc.docx b/doc/altair8800_doc.docx new file mode 100644 index 00000000..69ed4327 Binary files /dev/null and b/doc/altair8800_doc.docx differ diff --git a/makefile b/makefile index 0986dcbb..e5e2d53c 100644 --- a/makefile +++ b/makefile @@ -216,6 +216,10 @@ ifneq (3,${SIM_MAJOR}) ifneq (,$(or $(findstring pdp6,${MAKECMDGOALS}),$(findstring pdp10-ka,${MAKECMDGOALS}),$(findstring pdp10-ki,${MAKECMDGOALS}))) VIDEO_USEFUL = true endif + # building the Altair8800 could use video support + ifneq (,$(findstring altair8800,${MAKECMDGOALS})) + VIDEO_USEFUL = true + endif # building the AltairZ80 could use video support ifneq (,$(findstring altairz80,${MAKECMDGOALS})) VIDEO_USEFUL = true @@ -2218,6 +2222,29 @@ ALTAIR = ${ALTAIRD}/altair_sio.c ${ALTAIRD}/altair_cpu.c ${ALTAIRD}/altair_dsk.c ALTAIR_OPT = -I ${ALTAIRD} +ALTAIR8800D = ${SIMHD}/Altair8800 +ALTAIR8800 = \ + ${ALTAIR8800D}/altair8800_sys.c \ + ${ALTAIR8800D}/altair8800_dsk.c \ + ${ALTAIR8800D}/s100_bus.c \ + ${ALTAIR8800D}/s100_bram.c \ + ${ALTAIR8800D}/s100_cpu.c \ + ${ALTAIR8800D}/s100_po.c \ + ${ALTAIR8800D}/s100_simh.c \ + ${ALTAIR8800D}/s100_sio.c \ + ${ALTAIR8800D}/s100_ssw.c \ + ${ALTAIR8800D}/s100_ram.c \ + ${ALTAIR8800D}/s100_rom.c \ + ${ALTAIR8800D}/s100_z80.c \ + ${ALTAIR8800D}/mits_2sio.c \ + ${ALTAIR8800D}/mits_dsk.c \ + ${ALTAIR8800D}/sds_sbc200.c \ + ${ALTAIR8800D}/sds_vfii.c \ + ${ALTAIR8800D}/tarbell_fdc.c \ + ${ALTAIR8800D}/wd_17xx.c +ALTAIR8800_OPT = $(ALTAIR8800_GCC_OPT) -I ${ALTAIR8800D} -DUSE_SIM_VIDEO ${VIDEO_CCDEFS} + + ALTAIRZ80D = ${SIMHD}/AltairZ80 ALTAIRZ80 = ${ALTAIRZ80D}/altairz80_cpu.c ${ALTAIRZ80D}/altairz80_cpu_nommu.c \ ${ALTAIRZ80D}/s100_dazzler.c \ @@ -2540,8 +2567,8 @@ ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 \ microvax2000 infoserver100 infoserver150vxt microvax3100 microvax3100e \ vaxstation3100m30 vaxstation3100m38 vaxstation3100m76 vaxstation4000m60 \ microvax3100m80 vaxstation4000vlc infoserver1000 \ - nd100 nova eclipse hp2100 hp3000 i1401 i1620 s3 altair altairz80 gri \ - i7094 ibm1130 id16 id32 sds lgp h316 cdc1700 \ + nd100 nova eclipse hp2100 hp3000 i1401 i1620 s3 altair altair8800 \ + altairz80 gri i7094 ibm1130 id16 id32 sds lgp h316 cdc1700 \ swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 intel-mds \ scelbi 3b2 3b2-700 i701 i704 i7010 i7070 i7080 i7090 \ sigma uc15 pdp10-ka pdp10-ki pdp10-kl pdp10-ks pdp6 i650 \ @@ -2869,6 +2896,12 @@ $(BIN)altair$(EXE) : ${ALTAIR} ${SIM} $(MAKEIT) OPTS="$(ALTAIR_OPT)" +altair8800 : $(BIN)altair8800$(EXE) + +$(BIN)altair8800$(EXE) : ${ALTAIR8800} ${SIM} + $(MAKEIT) OPTS="$(ALTAIR8800_OPT)" + + altairz80 : $(BIN)altairz80$(EXE) $(BIN)altairz80$(EXE) : ${ALTAIRZ80} ${SIM}