mirror of
https://github.com/simh/simh.git
synced 2026-04-12 15:07:31 +00:00
Notes For V3.7-0
1. New Features 1.1 3.7-0 1.1.1 SCP - Added SET THROTTLE and SET NOTHROTTLE commands to regulate simulator execution rate and host resource utilization. - Added idle support (based on work by Mark Pizzolato). - Added -e to control error processing in nested DO commands (from Dave Bryan). 1.1.2 HP2100 - Added Double Integer instructions, 1000-F CPU, and Floating Point Processor (from Dave Bryan). - Added 2114 and 2115 CPUs, 12607B and 12578A DMA controllers, and 21xx binary loader protection (from Dave Bryan). 1.1.3 Interdata - Added SET IDLE and SET NOIDLE commands to idle the simulator in wait state. 1.1.4 PDP-11 - Added SET IDLE and SET NOIDLE commands to idle the simulator in wait state (WAIT instruction executed). - Added TA11/TU60 cassette support. 1.1.5 PDP-8 - Added SET IDLE and SET NOIDLE commands to idle the simulator in wait state (keyboard poll loop or jump-to-self). - Added TA8E/TU60 cassette support. 1.1.6 PDP-1 - Added support for 16-channel sequence break system. - Added support for PDP-1D extended features and timesharing clock. - Added support for Type 630 data communications subsystem. 1.1.6 PDP-4/7/9/15 - Added SET IDLE and SET NOIDLE commands to idle the simulator in wait state (keyboard poll loop or jump-to-self). 1.1.7 VAX, VAX780 - Added SET IDLE and SET NOIDLE commands to idle the simulator in wait state (more than 200 cycles at IPL's 0, 1, or 3 in kernel mode). 1.1.8 PDP-10 - Added SET IDLE and SET NOIDLE commands to idle the simulator in wait state (operating system dependent). - Added CD20 (CD11) support. 2. Bugs Fixed Please see the revision history on http://simh.trailing-edge.com or in the source module sim_rev.h.
This commit is contained in:
committed by
Mark Pizzolato
parent
15919a2dd7
commit
53d02f7fa7
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/* altairz80_defs.h: MITS Altair simulator definitions
|
||||
|
||||
Copyright (c) 2002-2006, Peter Schorn
|
||||
Copyright (c) 2002-2007, Peter Schorn
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
@@ -30,19 +30,19 @@
|
||||
|
||||
#define MAXMEMSIZE 65536 /* maximum memory size */
|
||||
#define ADDRMASK (MAXMEMSIZE - 1) /* address mask */
|
||||
#define bootrom_size 256 /* size of boot rom */
|
||||
#define BOOTROM_SIZE 256 /* size of boot rom */
|
||||
#define MAXBANKS 8 /* max number of memory banks */
|
||||
#define MAXBANKSLOG2 3 /* log2 of MAXBANKS */
|
||||
#define BANKMASK (MAXBANKS-1) /* bank mask */
|
||||
#define MEMSIZE (cpu_unit.capac) /* actual memory size */
|
||||
#define KB 1024 /* kilo byte */
|
||||
#define defaultROMLow 0xff00 /* default for lowest address of ROM */
|
||||
#define defaultROMHigh 0xffff /* default for highest address of ROM */
|
||||
#define DEFAULT_ROM_LOW 0xff00 /* default for lowest address of ROM */
|
||||
#define DEFAULT_ROM_HIGH 0xffff /* default for highest address of ROM */
|
||||
|
||||
#define NUM_OF_DSK 8 /* NUM_OF_DSK must be power of two */
|
||||
#define LDAInstruction 0x3e /* op-code for LD A,<8-bit value> instruction */
|
||||
#define unitNoOffset1 0x37 /* LD A,<unitno> */
|
||||
#define unitNoOffset2 0xb4 /* LD a,80h | <unitno> */
|
||||
#define LDA_INSTRUCTION 0x3e /* op-code for LD A,<8-bit value> instruction */
|
||||
#define UNIT_NO_OFFSET_1 0x37 /* LD A,<unitno> */
|
||||
#define UNIT_NO_OFFSET_2 0xb4 /* LD a,80h | <unitno> */
|
||||
|
||||
#define UNIT_V_OPSTOP (UNIT_V_UF+0) /* stop on invalid operation */
|
||||
#define UNIT_OPSTOP (1 << UNIT_V_OPSTOP)
|
||||
@@ -59,17 +59,22 @@
|
||||
#define UNIT_V_WARNROM (UNIT_V_UF+6) /* warn if ROM is written to */
|
||||
#define UNIT_WARNROM (1 << UNIT_V_WARNROM)
|
||||
|
||||
#define AddressFormat "[%04xh]"
|
||||
#define PCformat "\n" AddressFormat " "
|
||||
#define message1(p1) \
|
||||
sprintf(messageBuffer,PCformat p1,PCX); printMessage()
|
||||
#define message2(p1,p2) \
|
||||
sprintf(messageBuffer,PCformat p1,PCX,p2); printMessage()
|
||||
#define message3(p1,p2,p3) \
|
||||
sprintf(messageBuffer,PCformat p1,PCX,p2,p3); printMessage()
|
||||
#define message4(p1,p2,p3,p4) \
|
||||
sprintf(messageBuffer,PCformat p1,PCX,p2,p3,p4); printMessage()
|
||||
#define message5(p1,p2,p3,p4,p5) \
|
||||
sprintf(messageBuffer,PCformat p1,PCX,p2,p3,p4,p5); printMessage()
|
||||
#define message6(p1,p2,p3,p4,p5,p6) \
|
||||
sprintf(messageBuffer,PCformat p1,PCX,p2,p3,p4,p5,p6); printMessage()
|
||||
#define UNIX_PLATFORM (defined (__linux) || defined(__NetBSD__) || defined (__OpenBSD__) || \
|
||||
defined (__FreeBSD__) || defined (__APPLE__))
|
||||
|
||||
#define ADDRESS_FORMAT "[%04xh]"
|
||||
#define PC_FORMAT "\n" ADDRESS_FORMAT " "
|
||||
#define MESSAGE_1(p1) \
|
||||
sprintf(messageBuffer,PC_FORMAT p1,PCX); printMessage()
|
||||
#define MESSAGE_2(p1,p2) \
|
||||
sprintf(messageBuffer,PC_FORMAT p1,PCX,p2); printMessage()
|
||||
#define MESSAGE_3(p1,p2,p3) \
|
||||
sprintf(messageBuffer,PC_FORMAT p1,PCX,p2,p3); printMessage()
|
||||
#define MESSAGE_4(p1,p2,p3,p4) \
|
||||
sprintf(messageBuffer,PC_FORMAT p1,PCX,p2,p3,p4); printMessage()
|
||||
#define MESSAGE_5(p1,p2,p3,p4,p5) \
|
||||
sprintf(messageBuffer,PC_FORMAT p1,PCX,p2,p3,p4,p5); printMessage()
|
||||
#define MESSAGE_6(p1,p2,p3,p4,p5,p6) \
|
||||
sprintf(messageBuffer,PC_FORMAT p1,PCX,p2,p3,p4,p5,p6); printMessage()
|
||||
#define MESSAGE_7(p1,p2,p3,p4,p5,p6,p7) \
|
||||
sprintf(messageBuffer,PC_FORMAT p1,PCX,p2,p3,p4,p5,p6,p7); printMessage()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* altairz80_dsk.c: MITS Altair 88-DISK Simulator
|
||||
|
||||
Copyright (c) 2002-2006, Peter Schorn
|
||||
Copyright (c) 2002-2007, Peter Schorn
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
@@ -132,7 +132,6 @@ int32 dsk12(const int32 port, const int32 io, const int32 data);
|
||||
static int32 dskseek(const UNIT *xptr);
|
||||
static t_stat dsk_boot(int32 unitno, DEVICE *dptr);
|
||||
static t_stat dsk_reset(DEVICE *dptr);
|
||||
static t_stat dsk_svc(UNIT *uptr);
|
||||
static void writebuf(void);
|
||||
static t_stat dsk_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc);
|
||||
static void resetDSKWarningFlags(void);
|
||||
@@ -142,11 +141,12 @@ static char* selectInOut(const int32 io);
|
||||
extern int32 PCX;
|
||||
extern int32 saved_PC;
|
||||
extern FILE *sim_log;
|
||||
extern void printMessage(void);
|
||||
extern char messageBuffer[];
|
||||
extern int32 install_bootrom(void);
|
||||
extern UNIT cpu_unit;
|
||||
|
||||
extern void printMessage(void);
|
||||
extern int32 install_bootrom(void);
|
||||
|
||||
/* global data on status */
|
||||
|
||||
static int32 current_disk = NUM_OF_DSK; /* currently selected drive (values are 0 .. NUM_OF_DSK)
|
||||
@@ -170,7 +170,7 @@ static int32 warnDSK12 = 0;
|
||||
static int8 dskbuf[DSK_SECTSIZE]; /* data Buffer */
|
||||
|
||||
/* Altair MITS modified BOOT EPROM, fits in upper 256 byte of memory */
|
||||
int32 bootrom[bootrom_size] = {
|
||||
int32 bootrom[BOOTROM_SIZE] = {
|
||||
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 */
|
||||
@@ -208,14 +208,14 @@ int32 bootrom[bootrom_size] = {
|
||||
/* 88DSK Standard I/O Data Structures */
|
||||
|
||||
static UNIT dsk_unit[] = {
|
||||
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
|
||||
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
|
||||
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
|
||||
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
|
||||
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
|
||||
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
|
||||
{ UDATA (&dsk_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) },
|
||||
{ UDATA (&dsk_svc, 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) },
|
||||
{ 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) },
|
||||
{ UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }
|
||||
};
|
||||
|
||||
static REG dsk_reg[] = {
|
||||
@@ -290,13 +290,6 @@ static char* selectInOut(const int32 io) {
|
||||
}
|
||||
|
||||
/* service routines to handle simulator functions */
|
||||
|
||||
/* service routine - actually gets char & places in buffer */
|
||||
|
||||
static t_stat dsk_svc(UNIT *uptr) {
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* reset routine */
|
||||
|
||||
static t_stat dsk_reset(DEVICE *dptr) {
|
||||
@@ -317,16 +310,16 @@ static t_stat dsk_boot(int32 unitno, DEVICE *dptr) {
|
||||
printf("ALTAIR boot ROM installed.\n");
|
||||
}
|
||||
/* check whether we are really modifying an LD A,<> instruction */
|
||||
if ((bootrom[unitNoOffset1 - 1] == LDAInstruction) && (bootrom[unitNoOffset2 - 1] == LDAInstruction)) {
|
||||
bootrom[unitNoOffset1] = unitno & 0xff; /* LD A,<unitno> */
|
||||
bootrom[unitNoOffset2] = 0x80 | (unitno & 0xff); /* LD a,80h | <unitno> */
|
||||
if ((bootrom[UNIT_NO_OFFSET_1 - 1] == LDA_INSTRUCTION) && (bootrom[UNIT_NO_OFFSET_2 - 1] == LDA_INSTRUCTION)) {
|
||||
bootrom[UNIT_NO_OFFSET_1] = unitno & 0xff; /* LD A,<unitno> */
|
||||
bootrom[UNIT_NO_OFFSET_2] = 0x80 | (unitno & 0xff); /* LD a,80h | <unitno> */
|
||||
}
|
||||
else { /* Attempt to modify non LD A,<> instructions is refused. */
|
||||
printf("Incorrect boot ROM offsets detected.\n");
|
||||
return SCPE_IERR;
|
||||
}
|
||||
}
|
||||
saved_PC = defaultROMLow;
|
||||
saved_PC = DEFAULT_ROM_LOW;
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
@@ -356,7 +349,7 @@ int32 dsk10(const int32 port, const int32 io, const int32 data) {
|
||||
if (current_disk >= NUM_OF_DSK) {
|
||||
if (hasVerbose() && (warnDSK10 < warnLevelDSK)) {
|
||||
warnDSK10++;
|
||||
/*01*/ message1("Attempt of IN 0x08 on unattached disk - ignored.");
|
||||
/*01*/ MESSAGE_1("Attempt of IN 0x08 on unattached disk - ignored.");
|
||||
}
|
||||
return 0xff; /* no drive selected - can do nothing */
|
||||
}
|
||||
@@ -368,14 +361,14 @@ int32 dsk10(const int32 port, const int32 io, const int32 data) {
|
||||
writebuf();
|
||||
}
|
||||
if (trace_flag & TRACE_IN_OUT) {
|
||||
message2("OUT 0x08: %x", data);
|
||||
MESSAGE_2("OUT 0x08: %x", data);
|
||||
}
|
||||
current_disk = data & NUM_OF_DSK_MASK; /* 0 <= current_disk < NUM_OF_DSK */
|
||||
current_disk_flags = (dsk_dev.units + current_disk) -> flags;
|
||||
if ((current_disk_flags & UNIT_ATT) == 0) { /* nothing attached? */
|
||||
if ( (current_disk_flags & UNIT_DSK_VERBOSE) && (warnAttached[current_disk] < warnLevelDSK) ) {
|
||||
warnAttached[current_disk]++;
|
||||
/*02*/message2("Attempt to select unattached DSK%d - ignored.", current_disk);
|
||||
/*02*/MESSAGE_2("Attempt to select unattached DSK%d - ignored.", current_disk);
|
||||
}
|
||||
current_disk = NUM_OF_DSK;
|
||||
}
|
||||
@@ -395,7 +388,7 @@ int32 dsk11(const int32 port, const int32 io, const int32 data) {
|
||||
if (current_disk >= NUM_OF_DSK) {
|
||||
if (hasVerbose() && (warnDSK11 < warnLevelDSK)) {
|
||||
warnDSK11++;
|
||||
/*03*/ message2("Attempt of %s 0x09 on unattached disk - ignored.", selectInOut(io));
|
||||
/*03*/ MESSAGE_2("Attempt of %s 0x09 on unattached disk - ignored.", selectInOut(io));
|
||||
}
|
||||
return 0; /* no drive selected - can do nothing */
|
||||
}
|
||||
@@ -405,10 +398,10 @@ int32 dsk11(const int32 port, const int32 io, const int32 data) {
|
||||
in9_count++;
|
||||
if ((trace_flag & TRACE_SECTOR_STUCK) && (in9_count > 2 * DSK_SECT) && (!in9_message)) {
|
||||
in9_message = TRUE;
|
||||
message2("Looping on sector find %d.", current_disk);
|
||||
MESSAGE_2("Looping on sector find %d.", current_disk);
|
||||
}
|
||||
if (trace_flag & TRACE_IN_OUT) {
|
||||
message1("IN 0x09");
|
||||
MESSAGE_1("IN 0x09");
|
||||
}
|
||||
if (dirty) {/* implies that current_disk < NUM_OF_DSK */
|
||||
writebuf();
|
||||
@@ -430,12 +423,12 @@ int32 dsk11(const int32 port, const int32 io, const int32 data) {
|
||||
/* drive functions */
|
||||
|
||||
if (trace_flag & TRACE_IN_OUT) {
|
||||
message2("OUT 0x09: %x", data);
|
||||
MESSAGE_2("OUT 0x09: %x", data);
|
||||
}
|
||||
if (data & 0x01) { /* step head in */
|
||||
if (trace_flag & TRACE_TRACK_STUCK) {
|
||||
if (current_track[current_disk] == (tracks[current_disk] - 1)) {
|
||||
message2("Unnecessary step in for disk %d", current_disk);
|
||||
MESSAGE_2("Unnecessary step in for disk %d", current_disk);
|
||||
}
|
||||
}
|
||||
current_track[current_disk]++;
|
||||
@@ -452,7 +445,7 @@ int32 dsk11(const int32 port, const int32 io, const int32 data) {
|
||||
if (data & 0x02) { /* step head out */
|
||||
if (trace_flag & TRACE_TRACK_STUCK) {
|
||||
if (current_track[current_disk] == 0) {
|
||||
message2("Unnecessary step out for disk %d", current_disk);
|
||||
MESSAGE_2("Unnecessary step out for disk %d", current_disk);
|
||||
}
|
||||
}
|
||||
current_track[current_disk]--;
|
||||
@@ -506,7 +499,7 @@ int32 dsk12(const int32 port, const int32 io, const int32 data) {
|
||||
if (current_disk >= NUM_OF_DSK) {
|
||||
if (hasVerbose() && (warnDSK12 < warnLevelDSK)) {
|
||||
warnDSK12++;
|
||||
/*04*/ message2("Attempt of %s 0x0a on unattached disk - ignored.", selectInOut(io));
|
||||
/*04*/ MESSAGE_2("Attempt of %s 0x0a on unattached disk - ignored.", selectInOut(io));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -518,7 +511,7 @@ int32 dsk12(const int32 port, const int32 io, const int32 data) {
|
||||
if (current_byte[current_disk] >= DSK_SECTSIZE) {
|
||||
/* physically read the sector */
|
||||
if (trace_flag & TRACE_READ_WRITE) {
|
||||
message4("IN 0x0a (READ) D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]);
|
||||
MESSAGE_4("IN 0x0a (READ) D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]);
|
||||
}
|
||||
for (i = 0; i < DSK_SECTSIZE; i++) {
|
||||
dskbuf[i] = 0;
|
||||
@@ -552,20 +545,20 @@ static void writebuf(void) {
|
||||
uptr = dsk_dev.units + current_disk;
|
||||
if (((uptr -> flags) & UNIT_DSKWLK) == 0) { /* write enabled */
|
||||
if (trace_flag & TRACE_READ_WRITE) {
|
||||
message4("OUT 0x0a (WRITE) D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]);
|
||||
MESSAGE_4("OUT 0x0a (WRITE) D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]);
|
||||
}
|
||||
if (dskseek(uptr)) {
|
||||
message4("fseek failed D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]);
|
||||
MESSAGE_4("fseek failed D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]);
|
||||
}
|
||||
rtn = fwrite(dskbuf, DSK_SECTSIZE, 1, uptr -> fileref);
|
||||
if (rtn != 1) {
|
||||
message4("fwrite failed T%d S%d Return=%d", current_track[current_disk], current_sector[current_disk], rtn);
|
||||
MESSAGE_4("fwrite failed T%d S%d Return=%d", current_track[current_disk], current_sector[current_disk], rtn);
|
||||
}
|
||||
}
|
||||
else if ( ((uptr -> flags) & UNIT_DSK_VERBOSE) && (warnLock[current_disk] < warnLevelDSK) ) {
|
||||
/* write locked - print warning message if required */
|
||||
warnLock[current_disk]++;
|
||||
/*05*/ message2("Attempt to write to locked DSK%d - ignored.", current_disk);
|
||||
/*05*/ MESSAGE_2("Attempt to write to locked DSK%d - ignored.", current_disk);
|
||||
}
|
||||
current_flag[current_disk] &= 0xfe; /* ENWD off */
|
||||
current_byte[current_disk] = 0xff;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/* altairz80_sys.c: MITS Altair system interface
|
||||
|
||||
Copyright (c) 2002-2006, Peter Schorn
|
||||
Copyright (c) 2002-2007, Peter Schorn
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
@@ -39,6 +39,7 @@ extern DEVICE sio_dev;
|
||||
extern DEVICE simh_device;
|
||||
extern DEVICE ptr_dev;
|
||||
extern DEVICE ptp_dev;
|
||||
extern DEVICE net_dev;
|
||||
extern int32 saved_PC;
|
||||
|
||||
int32 fprint_sym(FILE *of, int32 addr, uint32 *val, UNIT *uptr, int32 sw);
|
||||
@@ -65,7 +66,7 @@ char sim_name[] = "Altair 8800 (Z80)";
|
||||
REG *sim_PC = &cpu_reg[0];
|
||||
int32 sim_emax = 4;
|
||||
DEVICE *sim_devices[] = {
|
||||
&cpu_dev, &sio_dev, &simh_device, &ptr_dev, &ptp_dev, &dsk_dev, &hdsk_dev, NULL
|
||||
&cpu_dev, &sio_dev, &simh_device, &ptr_dev, &ptp_dev, &dsk_dev, &hdsk_dev, &net_dev, NULL
|
||||
};
|
||||
|
||||
char memoryAccessMessage[80];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
292
AltairZ80/altairz80_net.c
Normal file
292
AltairZ80/altairz80_net.c
Normal file
@@ -0,0 +1,292 @@
|
||||
/* altairz80_net.c: networking capability
|
||||
|
||||
Copyright (c) 2002-2007, Peter Schorn
|
||||
|
||||
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 Peter Schorn shall not
|
||||
be used in advertising or otherwise to promote the sale, use or other dealings
|
||||
in this Software without prior written authorization from Peter Schorn.
|
||||
*/
|
||||
|
||||
#include "altairz80_defs.h"
|
||||
#include "sim_sock.h"
|
||||
|
||||
#define UNIT_V_SERVER (UNIT_V_UF + 0) /* define machine as a server */
|
||||
#define UNIT_SERVER (1 << UNIT_V_SERVER)
|
||||
#define NET_INIT_POLL_SERVER 16000
|
||||
#define NET_INIT_POLL_CLIENT 15000
|
||||
|
||||
/*#define DEBUG_NETWORK TRUE*/
|
||||
|
||||
static t_stat net_attach (UNIT *uptr, char *cptr);
|
||||
static t_stat net_detach (UNIT *uptr);
|
||||
static t_stat net_reset (DEVICE *dptr);
|
||||
static t_stat net_svc (UNIT *uptr);
|
||||
static t_stat set_net (UNIT *uptr, int32 value, char *cptr, void *desc);
|
||||
int32 netStatus (const int32 port, const int32 io, const int32 data);
|
||||
int32 netData (const int32 port, const int32 io, const int32 data);
|
||||
|
||||
#define MAX_CONNECTIONS 2 /* maximal number of server connections */
|
||||
#define BUFFER_LENGTH 512 /* length of input and output buffer */
|
||||
|
||||
static struct {
|
||||
int32 Z80StatusPort; /* Z80 status port associated with this ioSocket, read only */
|
||||
int32 Z80DataPort; /* Z80 data port associated with this ioSocket, read only */
|
||||
SOCKET masterSocket; /* server master socket, only defined at [1] */
|
||||
SOCKET ioSocket; /* accepted server socket or connected client socket, 0 iff free */
|
||||
char inputBuffer[BUFFER_LENGTH]; /* buffer for input characters read from ioSocket */
|
||||
int32 inputPosRead; /* position of next character to read from buffer */
|
||||
int32 inputPosWrite; /* position of next character to append to input buffer from ioSocket */
|
||||
int32 inputSize; /* number of characters in circular input buffer */
|
||||
char outputBuffer[BUFFER_LENGTH];/* buffer for output characters to be written to ioSocket */
|
||||
int32 outputPosRead; /* position of next character to write to ioSocket */
|
||||
int32 outputPosWrite; /* position of next character to append to output buffer */
|
||||
int32 outputSize; /* number of characters in circular output buffer */
|
||||
} serviceDescriptor[MAX_CONNECTIONS+1] = { /* serviceDescriptor[0] holds the information for a client */
|
||||
/* stat dat ms ios in inPR inPW inS out outPR outPW outS */
|
||||
{50, 51, 0, 0, {0}, 0, 0, 0, {0}, 0, 0, 0}, /* client Z80 port 50 and 51 */
|
||||
{40, 41, 0, 0, {0}, 0, 0, 0, {0}, 0, 0, 0}, /* server Z80 port 40 and 41 */
|
||||
{42, 43, 0, 0, {0}, 0, 0, 0, {0}, 0, 0, 0} /* server Z80 port 42 and 43 */
|
||||
};
|
||||
|
||||
static UNIT net_unit = {
|
||||
UDATA (&net_svc, UNIT_ATTABLE, 0),
|
||||
0, /* wait, set in attach */
|
||||
0, /* u3 = Port */
|
||||
0, /* u4 = IP of host */
|
||||
0, /* u5, unused */
|
||||
0, /* u6, unused */
|
||||
};
|
||||
|
||||
static REG net_reg[] = {
|
||||
{ DRDATA (POLL, net_unit.wait, 32) },
|
||||
{ HRDATA (IPHOST, net_unit.u4, 32), REG_RO },
|
||||
{ DRDATA (PORT, net_unit.u3, 32), REG_RO },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static MTAB net_mod[] = {
|
||||
{ UNIT_SERVER, 0, "CLIENT", "CLIENT", &set_net}, /* machine is a client */
|
||||
{ UNIT_SERVER, UNIT_SERVER, "SERVER", "SERVER", &set_net}, /* machine is a server */
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
DEVICE net_dev = {
|
||||
"NET", &net_unit, net_reg, net_mod,
|
||||
1, 10, 31, 1, 8, 8,
|
||||
NULL, NULL, &net_reset,
|
||||
NULL, &net_attach, &net_detach,
|
||||
NULL, 0, 0,
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
|
||||
static t_stat set_net(UNIT *uptr, int32 value, char *cptr, void *desc) {
|
||||
char temp[CBUFSIZE];
|
||||
if ((net_unit.flags & UNIT_ATT) && ((net_unit.flags & UNIT_SERVER) != value)) {
|
||||
strncpy(temp, net_unit.filename, CBUFSIZE); /* save name for later attach */
|
||||
net_detach(&net_unit);
|
||||
net_unit.flags ^= UNIT_SERVER; /* now switch from client to server and vice versa */
|
||||
net_attach(uptr, temp);
|
||||
return SCPE_OK;
|
||||
}
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
static void serviceDescriptor_reset(const uint32 i) {
|
||||
serviceDescriptor[i].inputPosRead = 0;
|
||||
serviceDescriptor[i].inputPosWrite = 0;
|
||||
serviceDescriptor[i].inputSize = 0;
|
||||
serviceDescriptor[i].outputPosRead = 0;
|
||||
serviceDescriptor[i].outputPosWrite = 0;
|
||||
serviceDescriptor[i].outputSize = 0;
|
||||
}
|
||||
|
||||
static t_stat net_reset(DEVICE *dptr) {
|
||||
uint32 i;
|
||||
if (net_unit.flags & UNIT_ATT)
|
||||
sim_activate(&net_unit, net_unit.wait); /* start poll */
|
||||
for (i = 0; i <= MAX_CONNECTIONS; i++)
|
||||
serviceDescriptor_reset(i);
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
static t_stat net_attach(UNIT *uptr, char *cptr) {
|
||||
uint32 i, ipa, ipp;
|
||||
t_stat r = get_ipaddr(cptr, &ipa, &ipp);
|
||||
if (r != SCPE_OK) return SCPE_ARG;
|
||||
if (ipa == 0) ipa = 0x7F000001; /* localhost = 127.0.0.1 */
|
||||
if (ipp == 0) ipp = 3000;
|
||||
net_unit.u3 = ipp;
|
||||
net_unit.u4 = ipa;
|
||||
net_reset(&net_dev);
|
||||
for (i = 0; i <= MAX_CONNECTIONS; i++) serviceDescriptor[i].ioSocket = 0;
|
||||
if (net_unit.flags & UNIT_SERVER) {
|
||||
net_unit.wait = NET_INIT_POLL_SERVER;
|
||||
serviceDescriptor[1].masterSocket = sim_master_sock(ipp);
|
||||
if (serviceDescriptor[1].masterSocket == INVALID_SOCKET) return SCPE_IOERR;
|
||||
}
|
||||
else {
|
||||
net_unit.wait = NET_INIT_POLL_CLIENT;
|
||||
serviceDescriptor[0].ioSocket = sim_connect_sock(ipa, ipp);
|
||||
if (serviceDescriptor[0].ioSocket == INVALID_SOCKET) return SCPE_IOERR;
|
||||
}
|
||||
net_unit.flags |= UNIT_ATT;
|
||||
net_unit.filename = (char *) calloc(CBUFSIZE, sizeof (char)); /* alloc name buf */
|
||||
if (net_unit.filename == NULL) return SCPE_MEM;
|
||||
strncpy(net_unit.filename, cptr, CBUFSIZE); /* save name */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
static t_stat net_detach(UNIT *uptr) {
|
||||
uint32 i;
|
||||
if (!(net_unit.flags & UNIT_ATT)) return SCPE_OK; /* if not attached simply return */
|
||||
if (net_unit.flags & UNIT_SERVER)
|
||||
sim_close_sock(serviceDescriptor[1].masterSocket, TRUE);
|
||||
for (i = 0; i <= MAX_CONNECTIONS; i++)
|
||||
if (serviceDescriptor[i].ioSocket)
|
||||
sim_close_sock(serviceDescriptor[i].ioSocket, FALSE);
|
||||
free(net_unit.filename); /* free port string */
|
||||
net_unit.filename = NULL;
|
||||
net_unit.flags &= ~UNIT_ATT; /* not attached */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* cannot use sim_check_conn to check whether read will return an error */
|
||||
static t_stat net_svc(UNIT *uptr) {
|
||||
int32 i, j, k, r;
|
||||
SOCKET s;
|
||||
static char svcBuffer[BUFFER_LENGTH];
|
||||
if (net_unit.flags & UNIT_ATT) {
|
||||
sim_activate(&net_unit, net_unit.wait); /* continue poll */
|
||||
if (net_unit.flags & UNIT_SERVER) {
|
||||
for (i = 1; i <= MAX_CONNECTIONS; i++)
|
||||
if (serviceDescriptor[i].ioSocket == 0) {
|
||||
s = sim_accept_conn(serviceDescriptor[1].masterSocket, NULL);
|
||||
if (s != INVALID_SOCKET) {
|
||||
serviceDescriptor[i].ioSocket = s;
|
||||
#ifdef DEBUG_NETWORK
|
||||
printf("Accepted connection %i with socket %i.\n\r", i, s);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (serviceDescriptor[0].ioSocket == 0) {
|
||||
serviceDescriptor[0].ioSocket = sim_connect_sock(net_unit.u4, net_unit.u3);
|
||||
if (serviceDescriptor[0].ioSocket == INVALID_SOCKET) return SCPE_IOERR;
|
||||
printf("\rWaiting for server ... Type g<return> (possibly twice) when ready\n\r");
|
||||
return SCPE_STOP;
|
||||
}
|
||||
for (i = 0; i <= MAX_CONNECTIONS; i++)
|
||||
if (serviceDescriptor[i].ioSocket) {
|
||||
if (serviceDescriptor[i].inputSize < BUFFER_LENGTH) { /* there is space left in inputBuffer */
|
||||
r = sim_read_sock(serviceDescriptor[i].ioSocket, svcBuffer,
|
||||
BUFFER_LENGTH - serviceDescriptor[i].inputSize);
|
||||
if (r == -1) {
|
||||
#ifdef DEBUG_NETWORK
|
||||
printf("Drop connection %i with socket %i.\n\r", i, serviceDescriptor[i].ioSocket);
|
||||
#endif
|
||||
sim_close_sock(serviceDescriptor[i].ioSocket, FALSE);
|
||||
serviceDescriptor[i].ioSocket = 0;
|
||||
serviceDescriptor_reset(i);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
for (j = 0; j < r; j++) {
|
||||
serviceDescriptor[i].inputBuffer[serviceDescriptor[i].inputPosWrite++] = svcBuffer[j];
|
||||
if (serviceDescriptor[i].inputPosWrite == BUFFER_LENGTH)
|
||||
serviceDescriptor[i].inputPosWrite = 0;
|
||||
}
|
||||
serviceDescriptor[i].inputSize += r;
|
||||
}
|
||||
}
|
||||
if (serviceDescriptor[i].outputSize > 0) { /* there is something to write in outputBuffer */
|
||||
k = serviceDescriptor[i].outputPosRead;
|
||||
for (j = 0; j < serviceDescriptor[i].outputSize; j++) {
|
||||
svcBuffer[j] = serviceDescriptor[i].outputBuffer[k++];
|
||||
if (k == BUFFER_LENGTH) k = 0;
|
||||
}
|
||||
r = sim_write_sock(serviceDescriptor[i].ioSocket, svcBuffer, serviceDescriptor[i].outputSize);
|
||||
if (r >= 0) {
|
||||
serviceDescriptor[i].outputSize -= r;
|
||||
serviceDescriptor[i].outputPosRead += r;
|
||||
if (serviceDescriptor[i].outputPosRead >= BUFFER_LENGTH)
|
||||
serviceDescriptor[i].outputPosRead -= BUFFER_LENGTH;
|
||||
}
|
||||
else printf("write %i\r\n", r);
|
||||
}
|
||||
}
|
||||
}
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
int32 netStatus(const int32 port, const int32 io, const int32 data) {
|
||||
uint32 i;
|
||||
net_svc(&net_unit);
|
||||
if (io == 0) { /* IN */
|
||||
for (i = 0; i <= MAX_CONNECTIONS; i++)
|
||||
if (serviceDescriptor[i].Z80StatusPort == port)
|
||||
return (serviceDescriptor[i].inputSize > 0 ? 1 : 0) |
|
||||
(serviceDescriptor[i].outputSize < BUFFER_LENGTH ? 2 : 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 netData(const int32 port, const int32 io, const int32 data) {
|
||||
uint32 i;
|
||||
char result;
|
||||
net_svc(&net_unit);
|
||||
for (i = 0; i <= MAX_CONNECTIONS; i++)
|
||||
if (serviceDescriptor[i].Z80DataPort == port)
|
||||
if (io == 0) { /* IN */
|
||||
if (serviceDescriptor[i].inputSize == 0) {
|
||||
printf("re-read from %i\r\n", port);
|
||||
result = serviceDescriptor[i].inputBuffer[serviceDescriptor[i].inputPosRead > 0 ?
|
||||
serviceDescriptor[i].inputPosRead - 1 : BUFFER_LENGTH - 1];
|
||||
}
|
||||
else {
|
||||
result = serviceDescriptor[i].inputBuffer[serviceDescriptor[i].inputPosRead++];
|
||||
if (serviceDescriptor[i].inputPosRead == BUFFER_LENGTH)
|
||||
serviceDescriptor[i].inputPosRead = 0;
|
||||
serviceDescriptor[i].inputSize--;
|
||||
}
|
||||
#ifdef DEBUG_NETWORK
|
||||
printf(" IN(%i)=%03xh (%c)\r\n", port, (result & 0xff),
|
||||
(32 <= (result & 0xff)) && ((result & 0xff) <= 127) ? (result & 0xff) : '?');
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
else { /* OUT */
|
||||
if (serviceDescriptor[i].outputSize == BUFFER_LENGTH) {
|
||||
printf("over-write %i to %i\r\n", data, port);
|
||||
serviceDescriptor[i].outputBuffer[serviceDescriptor[i].outputPosWrite > 0 ?
|
||||
serviceDescriptor[i].outputPosWrite - 1 : BUFFER_LENGTH - 1] = data;
|
||||
}
|
||||
else {
|
||||
serviceDescriptor[i].outputBuffer[serviceDescriptor[i].outputPosWrite++] = data;
|
||||
if (serviceDescriptor[i].outputPosWrite== BUFFER_LENGTH)
|
||||
serviceDescriptor[i].outputPosWrite = 0;
|
||||
serviceDescriptor[i].outputSize++;
|
||||
}
|
||||
#ifdef DEBUG_NETWORK
|
||||
printf("OUT(%i)=%03xh (%c)\r\n", port, data, (32 <= data) && (data <= 127) ? data : '?');
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user