1
0
mirror of https://github.com/open-simh/simh.git synced 2026-01-21 18:24:47 +00:00

TT2500: Turtle Terminal 2500, from General Turtle Inc.

Custom TTL design by Marvin Minsky.  There are two displays: one
raster scan for bitmapped characters, and another random scan for
vector graphics.  There is also a keyboard, and a UART for talking
to a host computer.

The computer is normally booted off a ROM which reads and starts a
secondary loader from the UART.  The loader is responsible for reading
the payload, which comes in checksummed blocks.  The LOAD command
accepts files in same format.
This commit is contained in:
Lars Brinkhoff 2020-12-02 08:17:47 +01:00
parent a6e6968cfd
commit 44cc9f3f6d
19 changed files with 3324 additions and 2 deletions

View File

@ -9,7 +9,7 @@ env:
- SIM="microvax2 vax730 vax750 vax780 vax8200 vax8600 microvax2000 infoserver100 infoserver150vxt microvax3100 microvax3100e vaxstation3100m30 vaxstation3100m38 vaxstation3100m76 vaxstation4000m60"
- SIM="microvax3100m80 vaxstation4000vlc infoserver1000 nova eclipse hp2100 hp3000 i1401 i1620 s3 altair altairz80 gri i7094 ibm1130"
- SIM="id16 id32 sds lgp h316 cdc1700 swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 isys8010 isys8020 isys8030 isys8024"
- SIM="besm6 imds-210 imds-220 imds-225 imds-230 imds-800 imds-810 imlac"
- SIM="besm6 imds-210 imds-220 imds-225 imds-230 imds-800 imds-810 imlac tt2500"
- SIM="scelbi 3b2 i701 i704 i7010 i7070 i7080 i7090 sigma uc15 i650"
sudo: required
install: sh -ex .travis/deps.sh

View File

@ -414,8 +414,19 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imlac", "imlac.vcproj", "{7
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "isdk80", "isdk80.vcproj", "{6641B210-6A57-4A74-A484-042D7E10EA14}"
ProjectSection(ProjectDependencies) = postProject
{D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ids880", "ids880.vcproj", "{7544004D-B821-4577-BA3A-24C38CE27AFA}"
ProjectSection(ProjectDependencies) = postProject
{D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tt2500", "tt2500.vcproj", "{5CC55A0C-32F3-429F-8661-2E82BF999217}"
ProjectSection(ProjectDependencies) = postProject
{D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -763,6 +774,10 @@ Global
{7544004D-B821-4577-BA3A-24C38CE27AFA}.Debug|Win32.Build.0 = Debug|Win32
{7544004D-B821-4577-BA3A-24C38CE27AFA}.Release|Win32.ActiveCfg = Release|Win32
{7544004D-B821-4577-BA3A-24C38CE27AFA}.Release|Win32.Build.0 = Release|Win32
{5CC55A0C-32F3-429F-8661-2E82BF999217}.Debug|Win32.ActiveCfg = Debug|Win32
{5CC55A0C-32F3-429F-8661-2E82BF999217}.Debug|Win32.Build.0 = Debug|Win32
{5CC55A0C-32F3-429F-8661-2E82BF999217}.Release|Win32.ActiveCfg = Release|Win32
{5CC55A0C-32F3-429F-8661-2E82BF999217}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,387 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="tt2500"
ProjectGUID="{7AB92E01-D278-4A8F-8493-ED58187AF3A1}"
RootNamespace="tt2500"
Keyword="Win32Proj"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="..\BIN\NT\$(PlatformName)-$(ConfigurationName)"
IntermediateDirectory="..\BIN\NT\Project\simh\$(ProjectName)\$(PlatformName)-$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="0"
>
<Tool
Name="VCPreBuildEventTool"
Description="Check for required build dependencies &amp; git commit id"
CommandLine="Pre-Build-Event.cmd &quot;$(TargetDir)$(TargetName).exe&quot; LIBPCRE ROM BUILD LIBSDL"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="../tt2500/;./;../;../../windows-build/include;;../../windows-build/include/SDL2"
PreprocessorDefinitions="USE_DISPLAY;SIM_BUILD_TOOL=simh-Visual-Studio-Project;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;PTW32_STATIC_LIB;SIM_ASYNCH_IO;USE_READER_THREAD;SIM_NEED_GIT_COMMIT_ID;HAVE_PCRE_H;PCRE_STATIC;USE_SIM_VIDEO;HAVE_LIBSDL;HAVE_LIBPNG"
KeepComments="false"
BasicRuntimeChecks="0"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="3"
CompileAs="1"
ShowIncludes="false"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="libcmtd.lib wsock32.lib winmm.lib Iphlpapi.lib pcrestaticd.lib SDL2-StaticD.lib SDL2_ttf-StaticD.lib freetype2412MT_D.lib libpng16.lib zlib.lib dxguid.lib Imm32.lib Version.lib"
LinkIncremental="1"
AdditionalLibraryDirectories="../../windows-build/lib/Debug/"
GenerateDebugInformation="true"
SubSystem="1"
StackReserveSize="10485760"
StackCommitSize="10485760"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
Description="Running Available Tests"
CommandLine="Post-Build-Event.cmd tt2500 &quot;$(TargetDir)$(TargetName).exe&quot;"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="..\BIN\NT\$(PlatformName)-$(ConfigurationName)"
IntermediateDirectory="..\BIN\NT\Project\simh\$(ProjectName)\$(PlatformName)-$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="0"
>
<Tool
Name="VCPreBuildEventTool"
Description="Check for required build dependencies &amp; git commit id"
CommandLine="Pre-Build-Event.cmd &quot;$(TargetDir)$(TargetName).exe&quot; LIBPCRE ROM BUILD LIBSDL"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
InlineFunctionExpansion="1"
OmitFramePointers="true"
WholeProgramOptimization="true"
AdditionalIncludeDirectories="../tt2500/;./;../;../../windows-build/include;;../../windows-build/include/SDL2"
PreprocessorDefinitions="USE_DISPLAY;SIM_BUILD_TOOL=simh-Visual-Studio-Project;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;PTW32_STATIC_LIB;SIM_ASYNCH_IO;USE_READER_THREAD;SIM_NEED_GIT_COMMIT_ID;HAVE_PCRE_H;PCRE_STATIC;USE_SIM_VIDEO;HAVE_LIBSDL;HAVE_LIBPNG"
StringPooling="true"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="3"
CompileAs="1"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="libcmt.lib wsock32.lib winmm.lib Iphlpapi.lib pcrestatic.lib SDL2-Static.lib SDL2_ttf-Static.lib freetype2412MT.lib libpng16.lib zlib.lib dxguid.lib Imm32.lib Version.lib"
LinkIncremental="1"
AdditionalLibraryDirectories="../../windows-build/lib/Release/"
GenerateDebugInformation="false"
SubSystem="1"
StackReserveSize="10485760"
StackCommitSize="10485760"
OptimizeReferences="2"
EnableCOMDATFolding="2"
LinkTimeCodeGeneration="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
Description="Running Available Tests"
CommandLine="Post-Build-Event.cmd tt2500 &quot;$(TargetDir)$(TargetName).exe&quot;"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm"
>
<File
RelativePath="..\display\display.c"
>
</File>
<File
RelativePath="..\display\tt2500.c"
>
</File>
<File
RelativePath="..\tt2500\tt2500_cpu.c"
>
</File>
<File
RelativePath="..\tt2500\tt2500_crt.c"
>
</File>
<File
RelativePath="..\tt2500\tt2500_dpy.c"
>
</File>
<File
RelativePath="..\tt2500\tt2500_key.c"
>
</File>
<File
RelativePath="..\tt2500\tt2500_rom.c"
>
</File>
<File
RelativePath="..\tt2500\tt2500_sys.c"
>
</File>
<File
RelativePath="..\tt2500\tt2500_tv.c"
>
</File>
<File
RelativePath="..\tt2500\tt2500_uart.c"
>
</File>
<File
RelativePath="..\..\windows-build\pthreads\pthread.c"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="HAVE_CONFIG_H;PTW32_BUILD_INLINED;PTW32_STATIC_LIB;__CLEANUP_C;$(NOINHERIT)"
CompileAs="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
WholeProgramOptimization="false"
PreprocessorDefinitions="HAVE_CONFIG_H;PTW32_BUILD_INLINED;PTW32_STATIC_LIB;__CLEANUP_C;$(NOINHERIT)"
CompileAs="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\scp.c"
>
</File>
<File
RelativePath="..\sim_console.c"
>
</File>
<File
RelativePath="..\sim_disk.c"
>
</File>
<File
RelativePath="..\sim_ether.c"
>
</File>
<File
RelativePath="..\sim_fio.c"
>
</File>
<File
RelativePath="..\sim_serial.c"
>
</File>
<File
RelativePath="..\sim_sock.c"
>
</File>
<File
RelativePath="..\sim_tape.c"
>
</File>
<File
RelativePath="..\sim_timer.c"
>
</File>
<File
RelativePath="..\sim_tmxr.c"
>
</File>
<File
RelativePath="..\sim_video.c"
>
</File>
<File
RelativePath="..\display\sim_ws.c"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc"
>
<File
RelativePath="..\display\display.h"
>
</File>
<File
RelativePath="..\display\tt2500.h"
>
</File>
<File
RelativePath="..\tt2500\tt2500_defs.h"
>
</File>
<File
RelativePath="..\scp.h"
>
</File>
<File
RelativePath="..\sim_console.h"
>
</File>
<File
RelativePath="..\sim_defs.h"
>
</File>
<File
RelativePath="..\sim_disk.h"
>
</File>
<File
RelativePath="..\sim_ether.h"
>
</File>
<File
RelativePath="..\sim_fio.h"
>
</File>
<File
RelativePath="..\sim_rev.h"
>
</File>
<File
RelativePath="..\sim_serial.h"
>
</File>
<File
RelativePath="..\sim_sock.h"
>
</File>
<File
RelativePath="..\sim_tape.h"
>
</File>
<File
RelativePath="..\sim_timer.h"
>
</File>
<File
RelativePath="..\sim_tmxr.h"
>
</File>
<File
RelativePath="..\sim_video.h"
>
</File>
<File
RelativePath="..\display\ws.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -259,7 +259,15 @@ static struct display displays[] = {
* P31 phosphor according to "Heads-Up Display for Flight
* Simulator for Advanced Aircraft"
*/
{ DIS_IMLAC, "Imlac Display", &color_p31, NULL, 1024, 1024 }
{ DIS_IMLAC, "Imlac Display", &color_p31, NULL, 1024, 1024 },
/*
* TT2500 display
* 512x512 addressable points.
* P31 phosphor according to "Heads-Up Display for Flight
* Simulator for Advanced Aircraft"
*/
{ DIS_TT2500, "TT2500 Display", &color_p31, NULL, 512, 512 }
};
/*

View File

@ -51,6 +51,7 @@ enum display_type {
DIS_III = 111,
DIS_TYPE340 = 340,
DIS_NG = 999,
DIS_TT2500 = 2500,
};
/*

115
display/tt2500.c Normal file
View File

@ -0,0 +1,115 @@
/* tt2500.c: TT2500 display interface.
Copyright (c) 2020, Lars Brinkhoff
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
LARS BRINKHOFF 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 Lars Brinkhoff shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Lars Brinkhoff
*/
#include "display.h"
#include "tt2500.h"
#if defined(__cplusplus)
extern "C" {
#endif
int tt2500_init(void *dev, int debug)
{
return display_init (DIS_TT2500, 1, dev);
}
static void tt2500_point (int x, int y, int i)
{
if (i < 7)
display_point (x, y, DISPLAY_INT_MAX*(7-i)/7, 0);
}
int tt2500_cycle(int us, int slowdown)
{
return display_age (us, slowdown);
}
#define ABS(_X) ((_X) >= 0 ? (_X) : -(_X))
#define SIGN(_X) ((_X) >= 0 ? 1 : -1)
static void
xline (int x, int y, int x2, int dx, int dy, int i)
{
int ix = SIGN(dx);
int iy = SIGN(dy);
int ay;
dx = ABS(dx);
dy = ABS(dy);
ay = dy/2;
for (;;) {
tt2500_point (x, y, i);
if (x == x2)
break;
if (ay > 0) {
y += iy;
ay -= dx;
}
ay += dy;
x += ix;
}
}
static void
yline (int x, int y, int y2, int dx, int dy, int i)
{
int ix = SIGN(dx);
int iy = SIGN(dy);
int ax;
dx = ABS(dx);
dy = ABS(dy);
ax = dx/2;
for (;;) {
tt2500_point (x, y, i);
if (y == y2)
break;
if (ax > 0) {
x += ix;
ax -= dy;
}
ax += dx;
y += iy;
}
}
void
tt2500_line (int x1, int y1, int x2, int y2, int i)
{
int dx = x2 - x1;
int dy = y2 - y1;
if (ABS (dx) > ABS(dy))
xline (x1, y1, x2, dx, dy, i);
else
yline (x1, y1, y2, dx, dy, i);
}
#if defined(__cplusplus)
}
#endif

37
display/tt2500.h Normal file
View File

@ -0,0 +1,37 @@
/* tt2500.h: TT2500 display interface.
Copyright (c) 2020, Lars Brinkhoff.
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
LARS BRINKHOFF 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 Lars Brinkhoff shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Lars Brinkhoff
*/
#if defined(__cplusplus)
extern "C" {
#endif
extern int tt2500_init(void *, int);
extern int tt2500_cycle(int, int);
extern void tt2500_line(int x1, int y1, int x2, int y2, int i);
#if defined(__cplusplus)
}
#endif

View File

@ -115,6 +115,10 @@ endif
ifneq (,$(findstring imlac,${MAKECMDGOALS}))
VIDEO_USEFUL = true
endif
# building the TT2500 needs video support
ifneq (,$(findstring tt2500,${MAKECMDGOALS}))
VIDEO_USEFUL = true
endif
# building the PDP6, KA10 or KI10 needs video support
ifneq (,$(or $(findstring pdp6,${MAKECMDGOALS}),$(findstring pdp10-ka,${MAKECMDGOALS}),$(findstring pdp10-ki,${MAKECMDGOALS})))
VIDEO_USEFUL = true
@ -642,6 +646,7 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin)
DISPLAYNG = ${DISPLAYD}/ng.c
DISPLAYIII = ${DISPLAYD}/iii.c
DISPLAYIMLAC = ${DISPLAYD}/imlac.c
DISPLAYTT2500 = ${DISPLAYD}/tt2500.c
DISPLAY_OPT += -DUSE_DISPLAY $(VIDEO_CCDEFS) $(VIDEO_LDFLAGS)
$(info using libSDL2: $(call find_include,SDL2/SDL))
ifeq (Darwin,$(OSTYPE))
@ -1561,6 +1566,14 @@ IMLAC = ${IMLACD}/imlac_sys.c ${IMLACD}/imlac_cpu.c \
IMLAC_OPT = -I ${IMLACD} ${DISPLAY_OPT}
TT2500D = ${SIMHD}/tt2500
TT2500 = ${TT2500D}/tt2500_sys.c ${TT2500D}/tt2500_cpu.c \
${TT2500D}/tt2500_dpy.c ${TT2500D}/tt2500_crt.c ${TT2500D}/tt2500_tv.c \
${TT2500D}/tt2500_key.c ${TT2500D}/tt2500_uart.c ${TT2500D}/tt2500_rom.c \
${DISPLAYL} ${DISPLAYTT2500}
TT2500_OPT = -I ${TT2500D} ${DISPLAY_OPT}
PDP8D = ${SIMHD}/PDP8
PDP8 = ${PDP8D}/pdp8_cpu.c ${PDP8D}/pdp8_clk.c ${PDP8D}/pdp8_df.c \
${PDP8D}/pdp8_dt.c ${PDP8D}/pdp8_lp.c ${PDP8D}/pdp8_mt.c \
@ -2247,6 +2260,15 @@ ifneq (,$(call find_test,${IMLAC},imlac))
$@ $(call find_test,${IMLACD},imlac) ${TEST_ARG}
endif
tt2500 : ${BIN}tt2500${EXE}
${BIN}tt2500${EXE} : ${TT2500} ${SIM}
${MKDIRBIN}
${CC} ${TT2500} ${SIM} ${TT2500_OPT} ${CC_OUTSPEC} ${LDFLAGS}
ifneq (,$(call find_test,${TT2500},tt2500))
$@ $(call find_test,${TT2500D},tt2500) ${TEST_ARG}
endif
pdp11 : ${BIN}pdp11${EXE}
${BIN}pdp11${EXE} : ${PDP11} ${SIM}

1
tt2500/tests/test.ascii Normal file
View File

@ -0,0 +1 @@
LOGO@@@@@@@@JAN@@A@A@@@AEJ\JtbJAN@@@EAA

View File

@ -0,0 +1,17 @@
cd %~p0
set tv disabled
set crt disabled
on error goto failed
break 1235
load test.ascii
run 100
:failed
if (PC != 669) echof "\n*** TEST FAILED\n"; exit 1
echof "\n*** TEST PASSED\n"
exit 0

708
tt2500/tt2500_cpu.c Normal file
View File

@ -0,0 +1,708 @@
/* tt2500_cpu.c: TT2500 CPU simulator
Copyright (c) 2020, Lars Brinkhoff
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
LARS BRINKHOFF 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 Lars Brinkhoff shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Lars Brinkhoff.
*/
#include "tt2500_defs.h"
#ifdef USE_DISPLAY
#include "display/display.h"
#endif
/* Debug */
#define DBG_CPU 0001
#define DBG_FET 0002
#define DBG_EXE 0004
#define DBG_STATE 0010
#define DBG_INT 0020
/* CPU state. */
static uint16 PC;
static uint16 IR;
static int ROM = 1;
int C, V, N, Z;
static uint16 IM = 0;
static uint16 STACK[16];
static uint16 SP = 0;
static uint16 R[64];
static uint16 RES, FLAGS, INTS, STARS;
static uint16 new_XR;
static int halt;
typedef struct {
uint16 PC;
uint16 IR;
uint16 MA;
uint16 MB;
uint16 AC;
uint16 L;
} HISTORY;
static HISTORY *history = NULL;
static uint32 history_i, history_m, history_n;
/* Function declaration. */
static t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
static t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
static t_stat cpu_ex (t_value *vptr, t_addr ea, UNIT *uptr, int32 sw);
static t_stat cpu_dep (t_value val, t_addr ea, UNIT *uptr, int32 sw);
static t_stat cpu_reset (DEVICE *dptr);
static void cpu_update (void);
static UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, 020000) };
static BITFIELD flags_bits[] = {
BIT(KB),
BIT(RSD),
BITNCF(2),
ENDBITS
};
static BITFIELD ints_bits[] = {
BIT(2KHZ),
BIT(RRD),
BIT(60HZ),
BITNC,
ENDBITS
};
static BITFIELD stars_bits[] = {
BIT(WRAP),
BIT(MINUS1),
ENDBITS
};
REG cpu_reg[] = {
{ ORDATAD (PC, PC, 13, "Program Counter") },
{ ORDATAD (ROM, ROM, 1, "Read from ROM") },
{ ORDATAD (IR, IR, 16, "Instruction") },
{ ORDATAD (XR, R[REG_XR], 12, "Execute register") },
{ ORDATAD (A, R[REG_ALATCH], 16, "A latch") },
{ ORDATAD (IM, IM, 16, "Immediate") },
{ ORDATAD (RES, RES, 16, "Result") },
{ HRDATADF (FLAGS, FLAGS, 4, "Flags", flags_bits ) },
{ HRDATADF (INTS, INTS, 4, "Interrupts", ints_bits ) },
{ HRDATADF (STARS, STARS, 4, "Stars", stars_bits ) },
{ BRDATAD (REG, R, 8, 16, 64, "Registers") },
{ NULL }
};
static MTAB cpu_mod[] = {
{ MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle },
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",
&cpu_set_hist, &cpu_show_hist },
{ 0 }
};
static DEBTAB cpu_deb[] = {
{ "CPU", DBG_CPU },
{ "FETCH", DBG_FET },
{ "EXECUTE", DBG_EXE },
{ "STATE", DBG_STATE },
{ "INT", DBG_INT },
{ NULL, 0 }
};
DEVICE cpu_dev = {
"CPU", &cpu_unit, cpu_reg, cpu_mod,
0, 8, 16, 1, 8, 16,
&cpu_ex, &cpu_dep, &cpu_reset,
NULL, NULL, NULL, NULL, DEV_DEBUG, 0, cpu_deb,
NULL, NULL, NULL, NULL, NULL, NULL
};
static uint16 crm_read (uint16 addr)
{
if (ROM && addr < 32)
return tt2500_rom[addr];
ROM = 0;
return CRM[addr];
}
static uint16 bus_read (uint16 reg)
{
if ((reg & 060) == 020) {
RES = dev_tab[reg]->read (reg);
sim_debug (DBG_STATE, &cpu_dev, "%06o <= BUS[%02o]\n", RES, reg);
} else {
RES = R[reg];
}
return RES;
}
static uint16 cpu_rot (uint16 data, uint16 n)
{
return (data >> n) + (data << (16 - n));
}
static uint16 cpu_ars (uint16 data, uint16 n)
{
uint32 sign = 0;
if (data & 0100000)
sign = 0177777 << 16;
return (data >> n) + (sign >> n);
}
uint16 cpu_alu (uint16 insn, uint16 op, uint16 adata, uint16 bdata)
{
uint32 result;
V = 0;
switch (op) {
case ALU_A: result = adata; break;
case ALU_ANDN: result = adata & ~bdata; break;
case ALU_AND: result = adata & bdata; break;
case ALU_NOR: result = (~(adata | bdata)) & 0177777; break;
case ALU_IOR: result = adata | bdata; break;
case ALU_XOR: result = adata ^ bdata; break;
case ALU_MROT: result = adata & cpu_rot (R[R[REG_ALATCH]], insn & 017); break;
case ALU_ROT: result = cpu_rot (adata, insn & 017); break;
case ALU_DEC: result = adata - 1; V = (result == 077777); break;
case ALU_XADD: bdata += C; /* Fall through. */
case ALU_ADD:
result = adata + bdata;
V = (((~adata ^ bdata) & (bdata ^ result)) >> 15) & 1;
break;
case ALU_XSUB: bdata += C; /* Fall through. */
case ALU_SUB:
result = adata - bdata;
V = (((adata ^ bdata) & (~bdata ^ result)) >> 15) & 1;
break;
case ALU_INC: result = adata + 1; V = (result == 0100000); break;
case ALU_ARS: result = cpu_ars (adata, insn & 017); break;
default: result = 0; break;
}
C = !!(result & 0200000);
result &= 0177777;
N = !!(result & 0100000);
Z = (result == 0);
sim_debug (DBG_STATE, &cpu_dev, "ALU: %06o %06o => %06o (%c%c%c%c)\n",
adata, bdata, result,
C ? 'C' : '-',
V ? 'V' : '-',
N ? 'N' : '-',
Z ? 'Z' : '-');
return result;
}
static uint16 mem_read (uint16 address)
{
if ((address & 0170000) == 0170000 && (DSR & DSR_TVON) == 0)
return FONT[address - 0170000];
else
return MEM[address];
}
static void mem_write (uint16 address, uint16 data)
{
if ((address & 0170000) == 0170000 && (DSR & DSR_TVON) == 0)
FONT[address - 0170000] = (uint8)data;
else
MEM[address] = data;
}
static void cpu_reg_op (uint16 insn)
{
uint16 a = (insn >> 6) & 7;
uint16 b = insn & 7;
uint16 alu_op;
uint16 adata;
uint16 bdata;
uint16 result;
if (IM != 0) {
adata = IR;
IM = 0;
} else if ((insn & 01000) != 0 && (insn & 030000) != 020000) {
IM = IR;
return;
} else
adata = R[a];
if (insn & 010)
bdata = 0;
else
bdata = R[b];
alu_op = insn & 06060;
if ((insn & 030000) == 020000) {
if ((insn & 01000) == 0) {
if (insn & 04000) {
sim_debug (DBG_STATE, &cpu_dev, "MEM[%06o] <= %06o <= REG[%02o]\n",
adata, bdata, b);
mem_write (adata, bdata);
} else {
bdata = R[b] = mem_read(adata);
sim_debug (DBG_STATE, &cpu_dev, "REG[%02o] <= %06o <= MEM[%06o]\n",
b, bdata, adata);
}
}
if (alu_op != 0)
alu_op |= 04000;
}
result = cpu_alu (insn, alu_op, adata, bdata);
switch (insn & 030000) {
case 030000:
if (C) {
case 000000:
sim_debug (DBG_STATE, &cpu_dev, "REG[%02o] <= %06o\n", a, result);
R[a] = result;
}
break;
case 010000:
break;
case 020000:
sim_debug (DBG_STATE, &cpu_dev, "REG[%02o] <= %06o\n", a, result);
R[a] = result;
if (insn & 01000) {
IM = 0;
if (insn & 04000) {
sim_debug (DBG_STATE, &cpu_dev, "CWRITE[%04o]\n", RES);
CRM[RES] = result;
} else {
sim_debug (DBG_STATE, &cpu_dev, "CREAD[%04o]\n", RES);
R[a] = crm_read (RES);
V = 0;
C = 0;
N = !!(R[a] & 0100000);
Z = (R[a] == 0);
sim_debug (DBG_STATE, &cpu_dev, "REG[%02o] <= %06o (%c%c%c%c)\n",
a, R[a],
C ? 'C' : '-',
V ? 'V' : '-',
N ? 'N' : '-',
Z ? 'Z' : '-');
}
}
break;
}
R[REG_ALATCH] = a;
sim_debug (DBG_STATE, &cpu_dev, "A <= %o\n", R[REG_ALATCH]);
RES = result;
}
static void cpu_jump (uint16 insn, int push)
{
if (push) {
STACK[SP] = PC;
sim_debug (DBG_STATE, &cpu_dev, "STACK[%02o] <= %04o\n", SP, PC);
SP = (SP + 1) & 017;
}
PC = insn & 07777;
}
static void cpu_dis (uint16 insn)
{
uint16 data;
uint16 mask;
switch (insn & 01400) {
case 00000:
data = ((RES >> 15) & 1) | ((RES >> 13) & 2) |
((RES >> 11) & 4) | ((RES >> 9) & 8);
break;
case 00400:
data = FLAGS;
break;
case 01000:
data = INTS;
break;
case 01400:
data = STARS;
break;
default:
return;
}
mask = (insn >> 4) & 017;
PC = PC + (data & ~mask);
}
static void cpu_popj (void)
{
SP = (SP - 1) & 017;
PC = STACK[SP];
sim_debug (DBG_STATE, &cpu_dev, "PC <= %04o <= STACK[%02o]\n", PC, SP);
}
static void bus_write (uint16 reg, uint16 data)
{
switch (reg) {
case 012: PC = data & 07777; break;
case 014: dpy_magic (data, &R[2], &R[3], R[4], R[5]); break;
case 015: dpy_chartv (data); break;
case 016: cpu_popj (); break;
case 023: new_XR = data; break;
case 020: case 021: case 022: case 024: case 025: case 026: case 027:
case 030: case 031: case 032: case 033: case 034: case 035: case 036: case 037:
sim_debug (DBG_STATE, &cpu_dev, "BUS[%02o] <= %06o\n", reg, data);
dev_tab[reg]->write (reg, data);
break;
default: /* 40-77 is scratchpad. */
R[reg] = data;
sim_debug (DBG_STATE, &cpu_dev, "REG[%02o] <= %06o\n", reg, data);
break;
}
}
static void cpu_bus (uint16 insn)
{
uint16 a = (insn >> 6) & 7;
uint16 b = insn & 077;
uint16 bb = insn & 01000;
if ((insn & 0176000) == 0072000) {
cpu_dis (insn);
return;
}
if (bb) {
switch (a) {
case 2: PC = RES;
case 4: dpy_magic (RES, &R[2], &R[3], R[4], R[5]); return;
case 5: dpy_chartv (R[b]); return;
case 6: cpu_popj (); return;
default:
sim_debug (DBG_CPU, &cpu_dev, "Unknown instruction: %06o\n", IR);
break;
}
}
if (insn & 02000) {
bus_write (b, R[a]);
} else {
R[a] = bus_read (b);
sim_debug (DBG_STATE, &cpu_dev, "REG[%02o] <= %06o\n", a, R[a]);
}
}
static void cpu_branch (uint16 insn)
{
uint16 target = insn & 03777;
int jump = 0;
switch (insn & 070000) {
case 000000: jump = !C; break;
case 010000: jump = !V; break;
case 020000: jump = N; break;
case 030000: jump = !Z; break;
case 040000: jump = N ^ V; break;
case 050000: jump = INTS; break;
case 060000: jump = !(R[REG_XR] & 04000); new_XR = R[REG_XR] + 1; break;
case 070000: jump = FLAGS; break;
}
if (insn & 04000)
jump = !jump;
if (jump) {
if (insn & 02000)
target -= 04000;
PC = (PC + target) & 07777;
}
}
static void
cpu_fetch (void)
{
/* Fetch cycle. */
IR = crm_read (PC);
sim_debug (DBG_FET, &cpu_dev, "%04o: %06o\n", PC, IR);
sim_interval--;
if (history) {
history[history_i].PC = PC;
history[history_i].IR = IR;
}
PC = (PC + 1) & 07777;
}
static void cpu_update (void)
{
new_XR &= 07777;
if (R[REG_XR] != new_XR)
sim_debug (DBG_STATE, &cpu_dev, "XR <= %04o\n", new_XR);
R[REG_XR] = new_XR;
R[011] = (new_XR >> 6) & 077;
R[012] = new_XR & 077;
R[015] = (new_XR >> 6) & 7;
R[016] = new_XR & 7;
}
static void
cpu_execute (void)
{
if (IM != 0) {
sim_debug (DBG_EXE, &cpu_dev, "%06o\n", IM);
cpu_reg_op (IM);
return;
}
if (cpu_dev.dctrl & DBG_EXE) {
t_value val = IR;
sim_debug (DBG_EXE, &cpu_dev, "%06o (", IR);
fprint_sym (sim_deb, PC-1, &val, NULL, SWMASK ('M'));
sim_debug (DBG_EXE, &cpu_dev, ")\n");
}
switch ((IR >> 12) & 017) {
case 000: case 001: case 002: case 003:
cpu_reg_op (IR);
break;
case 004:
cpu_jump (IR, 1);
break;
case 005:
cpu_jump (IR, 0);
break;
case 007:
cpu_bus (IR);
break;
case 010: case 011: case 012: case 013:
case 014: case 015: case 016: case 017:
cpu_branch (IR);
break;
default:
sim_debug (DBG_CPU, &cpu_dev, "Unknown instruction: %06o\n", IR);
break;
}
}
static void
cpu_insn (void)
{
cpu_update ();
cpu_execute ();
cpu_fetch ();
if (history) {
history_i = (history_i + 1) % history_m;
if (history_n < history_m)
history_n++;
}
}
t_stat sim_instr (void)
{
t_stat reason;
if ((reason = build_dev_tab ()) != SCPE_OK)
return reason;
halt = 0;
for (;;) {
AIO_CHECK_EVENT;
if (sim_interval <= 0) {
if ((reason = sim_process_event()) != SCPE_OK)
return reason;
}
if (sim_brk_summ && sim_brk_test(PC, SWMASK('E')))
return STOP_IBKPT;
cpu_insn ();
if (sim_step != 0) {
if (--sim_step == 0)
return SCPE_STEP;
}
if (halt)
return STOP_HALT;
}
return SCPE_OK;
}
static t_stat
cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
t_stat r;
uint32 x;
if (cptr == NULL)
return SCPE_ARG;
x = get_uint (cptr, 10, 1000000, &r);
if (r != SCPE_OK)
return r;
history = (HISTORY *)calloc (x, sizeof (*history));
if (history == NULL)
return SCPE_MEM;
history_m = x;
history_n = 0;
history_i = 0;
return SCPE_OK;
}
static t_stat
cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
t_value insn;
uint32 i, j;
fprintf (st, "PC____ IR____\n");
if (history_i >= history_n)
j = history_i - history_n;
else
j = history_m + history_i - history_n;
for (i = 0; i < history_n; i++) {
fprintf (st, "%06o %06o ",
history[j].PC,
history[j].IR);
insn = history[j].IR;
fprint_sym (st, history[j].PC, &insn, NULL, SWMASK ('M'));
fputc ('\n', st);
j = (j + 1) % history_m;
}
return SCPE_OK;
}
static t_stat cpu_ex (t_value *vptr, t_addr ea, UNIT *uptr, int32 sw)
{
if (vptr == NULL)
return SCPE_ARG;
if (sw & SIM_SW_STOP)
sw |= SWMASK ('C');
if (sw & SWMASK ('C')) {
if (ea >= 4096)
return SCPE_NXM;
*vptr = crm_read (ea);
} else {
if (ea >= 65536)
return SCPE_NXM;
*vptr = mem_read (ea);
}
return SCPE_OK;
}
static t_stat cpu_dep (t_value val, t_addr ea, UNIT *uptr, int32 sw)
{
if (sw & SWMASK ('C')) {
if (ea >= 4096)
return SCPE_NXM;
CRM[ea] = val & 0177777;
} else {
if (ea >= 65536)
return SCPE_NXM;
mem_write (ea, val & 0177777);
}
return SCPE_OK;
}
static t_bool cpu_is_pc_a_subroutine_call (t_addr **ret_addrs)
{
static t_addr returns[2] = { 0, 0 };
if ((CRM[PC] & 0170000) == 040000) {
returns[0] = PC + 1;
*ret_addrs = returns;
return TRUE;
}
return FALSE;
}
static t_stat
cpu_reset (DEVICE *dptr)
{
ROM = 1;
PC = 0;
IR = 010000;
IM = 0;
SP = 0;
C = V = N = Z = 0;
new_XR = 0;
RES = FLAGS = INTS = STARS = 0;
sim_brk_types = SWMASK ('E');
sim_brk_dflt = SWMASK ('E');
sim_vm_is_subroutine_call = &cpu_is_pc_a_subroutine_call;
return SCPE_OK;
}
static const char *flag_name (uint16 flag)
{
switch (flag) {
case FLAG_KB: return "KB";
case FLAG_RSD: return "RSD";
case INT_2KHZ: return "2KHZ";
case INT_RRD: return "RRD";
case INT_60HZ: return "60HZ";
case STAR_WRAP: return "WRAP";
case STAR_MINUS1: return "MINUS1";
default: return "(unknown)";
}
}
void flag_on (uint16 flag)
{
sim_debug (DBG_INT, &cpu_dev, "Flag on %03o (%s)\n", flag, flag_name (flag));
FLAGS |= flag & 017;
flag >>= 4;
INTS |= flag & 017;
flag >>= 4;
STARS |= flag & 017;
}
void flag_off (uint16 flag)
{
sim_debug (DBG_INT, &cpu_dev, "Flag off %03o (%s)\n",
flag, flag_name (flag));
FLAGS &= ~(flag & 017);
flag >>= 4;
INTS &= ~(flag & 017);
flag >>= 4;
STARS &= ~(flag & 017);
}
#ifdef USE_DISPLAY
/* Called from display library to get data switches. */
void
cpu_get_switches (unsigned long *p1, unsigned long *p2)
{
}
/* Called from display library to set data switches. */
void
cpu_set_switches (unsigned long p1, unsigned long p2)
{
}
#endif

106
tt2500/tt2500_crt.c Normal file
View File

@ -0,0 +1,106 @@
/* tt2500_crt.c: TT2500 CRT display
Copyright (c) 2020, Lars Brinkhoff
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
LARS BRINKHOFF 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 Lars Brinkhoff shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Lars Brinkhoff.
*/
#include "tt2500_defs.h"
#include "sim_video.h"
#include "display/tt2500.h"
#include "display/display.h"
/* Function declaration. */
static t_stat crt_svc (UNIT *uptr);
static t_stat crt_reset (DEVICE *dptr);
/* Debug */
#define DBG 0001
static UNIT crt_unit = {
UDATA (&crt_svc, UNIT_IDLE, 0)
};
static DEBTAB crt_deb[] = {
{ "DBG", DBG },
{ "KEY", SIM_VID_DBG_KEY },
{ NULL, 0 }
};
#ifdef USE_DISPLAY
#define CRT_DIS 0
#else
#define CRT_DIS DEV_DIS
#endif
DEVICE crt_dev = {
"CRT", &crt_unit, NULL, NULL,
1, 8, 16, 1, 8, 16,
NULL, NULL, &crt_reset,
NULL, NULL, NULL,
NULL, DEV_DISABLE | DEV_DEBUG | CRT_DIS, 0, crt_deb,
NULL, NULL, NULL, NULL, NULL, NULL
};
static t_stat
crt_svc(UNIT *uptr)
{
#ifdef USE_DISPLAY
tt2500_cycle (100, 0);
if (!display_is_blank ())
sim_activate_after (uptr, 100);
if (dpy_quit) {
dpy_quit = FALSE;
return SCPE_STOP;
}
#endif
return SCPE_OK;
}
static t_stat
crt_reset (DEVICE *dptr)
{
#ifdef USE_DISPLAY
if (dptr->flags & DEV_DIS || sim_switches & SWMASK('P')) {
display_close (dptr);
sim_cancel (&crt_unit);
} else {
display_reset ();
tt2500_init (dptr, 1);
vid_register_quit_callback (&dpy_quit_callback);
}
#endif
return SCPE_OK;
}
void
crt_line (uint16 x1, uint16 y1, uint16 x2, uint16 y2, uint16 i)
{
sim_debug (DBG, &crt_dev, "Line %d,%d - %d,%d @ %d\n", x1, y1, x2, y2, i);
#ifdef USE_DISPLAY
if ((crt_dev.flags & DEV_DIS) == 0 && !sim_is_active (&crt_unit))
sim_activate_abs (&crt_unit, 0);
if (crt_dev.flags & DEV_DIS)
return;
tt2500_line (x1, y1, x2, y2, i);
#endif
}

104
tt2500/tt2500_defs.h Normal file
View File

@ -0,0 +1,104 @@
/* tt2500_defs.h: TT2500 simulator definitions
Copyright (c) 2020, Lars Brinkhoff
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
LARS BRINKHOFF 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 Lars Brinkhoff shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Lars Brinkhoff.
24-Sep-20 LB New simulator.
*/
#ifndef TT2500_DEFS_H_
#define TT2500_DEFS_H_ 0
#include "sim_defs.h"
#define STOP_HALT 1
#define STOP_IBKPT 2
#define STOP_ACCESS 3
#define FLAG_KB 001
#define FLAG_RSD 002
#define INT_2KHZ (001 << 4)
#define INT_RRD (002 << 4)
#define INT_60HZ (004 << 4)
#define STAR_WRAP (001 << 8)
#define STAR_MINUS1 (002 << 8)
/* ALU operations. */
#define ALU_A 00000
#define ALU_ANDN 00020
#define ALU_AND 00040
#define ALU_NOR 00060
#define ALU_IOR 02000
#define ALU_XOR 02020
#define ALU_MROT 02040
#define ALU_ROT 04000
#define ALU_DEC 04020
#define ALU_XADD 04040
#define ALU_ADD 04060
#define ALU_SUB 06000
#define ALU_XSUB 06020
#define ALU_INC 06040
#define ALU_ARS 06060
/* Register. */
#define REG_ALATCH 010
#define REG_YCOR 020
#define REG_XCOR 021
#define REG_SCROLL 022
#define REG_XR 023
#define REG_UART 024
#define REG_DSR 025
#define REG_KEY 026
/* DSR TV on bit. */
#define DSR_TVON 0010000
typedef struct {
uint16 reg[4];
uint16 (*read)(uint16 reg);
void (*write)(uint16 reg, uint16 data);
} TTDEV;
extern t_bool build_dev_tab (void);
extern void flag_on (uint16 flag);
extern void flag_off (uint16 flag);
extern uint16 cpu_alu (uint16 insn, uint16 op, uint16 adata, uint16 bdata);
extern void dpy_magic (uint16 data, uint16 *r2, uint16 *r3, uint16 r4, uint16 r5);
extern void dpy_chartv (uint16 data);
extern void dpy_quit_callback (void);
extern void crt_line (uint16 x1, uint16 y1, uint16 x2, uint16 y2, uint16 i);
extern void tv_line (int row, uint8 *data, uint8 *chars);
extern void tv_refresh (void);
extern REG cpu_reg[];
extern uint16 MEM[], CRM[];
extern uint16 DSR;
extern uint8 FONT[];
extern DEVICE cpu_dev, dpy_dev, crt_dev, tv_dev, key_dev, uart_dev;
extern TTDEV *dev_tab[];
extern int C, V, N, Z;
extern int dpy_quit;
extern uint16 tt2500_rom[];
#endif /* TT2500_DEFS_H_ */

284
tt2500/tt2500_dpy.c Normal file
View File

@ -0,0 +1,284 @@
/* imlac_dpy.c: TT2500 display.
Copyright (c) 2020, Lars Brinkhoff
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
LARS BRINKHOFF 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 Lars Brinkhoff shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Lars Brinkhoff.
*/
#include <string.h>
#include "tt2500_defs.h"
/* Debug */
#define DBG_REG 0001
#define DBG_VEC 0002
#define DBG_TXT 0004
#define DBG_60HZ 0010
#define DBG_2KHZ 0020
/* DSR */
#define DSR_VEC 0160000
#define DSR_TXT 0006000
static t_stat dpy_60hz_svc (UNIT *uptr);
static t_stat dpy_2khz_svc (UNIT *uptr);
static t_stat dpy_reset (DEVICE *dptr);
static void dpy_text_line (void);
static uint8 black[4096], green[4096];
uint8 FONT[4096];
uint8 LINE[73];
static uint16 YCOR;
static uint16 XCOR;
static uint16 SCROLL;
uint16 DSR = 0;
static uint16 ROW = 0;
static uint16 COL = 0;
int dpy_quit = FALSE;
/* DSR
160000 Vector beam: 0=on, 7=off.
10000 TV on.
6000 Color mode.
0000
2000 Dark background.
4000
6000
tv-off tv-off-const 1 001 (unused)
tv-green-const 2 010 (unused)
tv-blank green-blank-const 4 100 (flash)
tv-active tv-dark-const 5 101 (normal) "green chars on field ebony"
dark-blank-const 7 111 (unused)
*/
/* Function declaration. */
static uint16 dpy_read (uint16);
static void dpy_write (uint16, uint16);
static UNIT dpy_unit = {
UDATA (dpy_2khz_svc, UNIT_IDLE, 0)
};
static BITFIELD dsr_bits[] = {
BITNCF(10),
BITF(TXT,2),
BITF(ON,1),
BITF(VEC,3),
ENDBITS
};
static REG dpy_reg[] = {
{ ORDATAD (YCOR, YCOR, 9, "Y coordinate") },
{ ORDATAD (XCOR, XCOR, 9, "X coordinate") },
{ ORDATAD (SCROLL, SCROLL, 16, "Scroll") },
{ ORDATADF (DSR, DSR, 16, "Status register", dsr_bits ) },
{ NULL }
};
static DEBTAB dpy_deb[] = {
{ "REG", DBG_REG },
{ "VEC", DBG_VEC },
{ "TXT", DBG_TXT },
{ "60HZ", DBG_60HZ },
{ "2KHZ", DBG_2KHZ },
{ NULL, 0 }
};
static TTDEV dpy_ttdev = {
{ REG_YCOR, REG_XCOR, REG_SCROLL, REG_DSR },
dpy_read,
dpy_write,
};
DEVICE dpy_dev = {
"DPY", &dpy_unit, dpy_reg, NULL,
1, 8, 16, 1, 8, 16,
NULL, NULL, dpy_reset,
NULL, NULL, NULL, &dpy_ttdev, DEV_DEBUG, 0, dpy_deb,
NULL, NULL, NULL, NULL, NULL, NULL
};
/* To ensure the two clocks are always in sync, dpy_60hz_svc is called
from dpy_2khz_svc. */
static t_stat dpy_60hz_svc (UNIT *uptr)
{
sim_debug (DBG_60HZ, &dpy_dev, "60 Hz interrupt\n");
flag_on (INT_60HZ);
tv_refresh ();
return SCPE_OK;
}
static t_stat dpy_2khz_svc (UNIT *uptr)
{
static int n = 0;
t_stat r;
/* 30 text lines, plus one extra for the page refresh interrupt, per
60 Hz page comes to 538 microseconds. */
r = sim_activate_after (uptr, 538);
if (r != SCPE_OK)
return r;
if (++n == 31) {
n = 0;
return dpy_60hz_svc (&dpy_unit);
}
sim_debug (DBG_2KHZ, &dpy_dev, "2 kHz interrupt\n");
dpy_text_line ();
flag_on (INT_2KHZ);
return SCPE_OK;
}
static uint16 dpy_read (uint16 reg)
{
uint16 data = 0;
switch (reg) {
case REG_YCOR:
data = YCOR;
sim_debug (DBG_REG, &dpy_dev, "%06o <= YCOR\n", data);
break;
case REG_XCOR:
data = XCOR;
sim_debug (DBG_REG, &dpy_dev, "%06o <= XCOR\n", data);
break;
case REG_SCROLL:
data = SCROLL;
sim_debug (DBG_REG, &dpy_dev, "%06o <= SCROLL\n", data);
break;
case REG_DSR:
data = DSR;
sim_debug (DBG_REG, &dpy_dev, "DSR <= %06o\n", data);
break;
}
return data;
}
static void dpy_write (uint16 reg, uint16 data)
{
switch (reg) {
case REG_YCOR:
sim_debug (DBG_REG, &dpy_dev, "YCOR <= %06o\n", data);
YCOR = data;
break;
case REG_XCOR:
sim_debug (DBG_REG, &dpy_dev, "XCOR <= %06o\n", data);
XCOR = data;
break;
case REG_SCROLL:
sim_debug (DBG_REG, &dpy_dev, "SCROLL <= %06o\n", data);
SCROLL = data;
flag_off (INT_60HZ);
COL = 0;
ROW = 29;
break;
case REG_DSR:
sim_debug (DBG_REG, &dpy_dev, "DSR <= %06o\n", data);
DSR = data;
break;
}
}
static t_stat dpy_reset (DEVICE *dptr)
{
memset (black, 0, sizeof black);
memset (green, 0377, sizeof green);
sim_activate_abs (&dpy_unit, 0);
return SCPE_OK;
}
void dpy_magic (uint16 xr, uint16 *r2, uint16 *r3, uint16 r4, uint16 r5)
{
uint32 x = *r2, y = *r3;
uint16 x0, y0, x1, y1, dx, dy;
sim_debug (DBG_VEC, &dpy_dev, "MAGIC %06o\n", xr);
sim_debug (DBG_VEC, &dpy_dev, "X,YCOR = %06o, %06o\n", XCOR, YCOR);
sim_debug (DBG_VEC, &dpy_dev, "X,YPOS = %06o, %06o\n", *r2, *r3);
sim_debug (DBG_VEC, &dpy_dev, "SIN,COS = %06o, %06o\n", r4, r5);
x0 = x1 = XCOR;
y0 = y1 = YCOR;
dx = (r4 & 0100000) ? -1 : 1;
dy = (r5 & 0100000) ? -1 : 1;
flag_on (STAR_WRAP);
while (xr & 04000) {
sim_interval--;
x = cpu_alu (0, ALU_ADD, x, r4);
if (V)
x1 = (XCOR += dx);
sim_interval--;
y = cpu_alu (0, ALU_ADD, y, r5);
if (V)
y1 = (YCOR += dy);
if ((XCOR & 01000) != 0 || (YCOR & 01000) != 0) {
x1 -= dx;
y1 -= dy;
flag_off (STAR_WRAP);
break;
}
xr++;
}
crt_line (x0, y0, x1, y1, DSR >> 13);
*r2 = x;
*r3 = y;
}
void dpy_chartv (uint16 data)
{
sim_debug (DBG_TXT, &dpy_dev, "CHARTV %03o (%06o)\n", data & 0377, data);
flag_off (INT_2KHZ);
memmove (LINE, LINE + 1, 72);
LINE[72] = (uint8)data;
}
static void dpy_text_line (void)
{
uint8 *font;
if ((DSR & 016000) == 010000)
font = green;
else if (DSR & 010000)
font = FONT;
else
font = black;
tv_line (ROW, LINE, font);
if (dpy_dev.dctrl)
tv_refresh ();
ROW++;
if (ROW == 30)
ROW = 0;
}
void dpy_quit_callback (void)
{
dpy_quit = TRUE;
}

551
tt2500/tt2500_key.c Normal file
View File

@ -0,0 +1,551 @@
/* tt2500_key.c: TT2500 keyboard device
Copyright (c) 2020, Lars Brinkhoff
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
LARS BRINKHOFF 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 Lars Brinkhoff shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Lars Brinkhoff.
*/
#include "tt2500_defs.h"
#include "sim_video.h"
/* Debug */
#define DBG 0001
#define KEY_DISPLAY 1
#define KEY_CONSOLE 2
#define KEY_TYPE 3
#define SHFT 01000
#define CTRL 02000
#define META 04000
#define NOKEY 0177777
static uint16 KBUF;
static uint16 suffix = NOKEY;
static uint16 modifiers;
/* Function declaration. */
static t_stat key_svc (UNIT *uptr);
static t_stat key_reset (DEVICE *dptr);
static uint16 key_read (uint16 reg);
static void key_write (uint16 reg, uint16 data);
static UNIT key_unit = {
UDATA (&key_svc, UNIT_IDLE, 0)
};
static REG key_reg[] = {
{ ORDATAD (KBUF, KBUF, 16, "Keyboard buffer") },
{ NULL }
};
MTAB key_mod[] = {
{ KEY_TYPE, KEY_DISPLAY, "DISPLAY", "DISPLAY", NULL, NULL, NULL,
"Get keyboard events from display windows"},
{ KEY_TYPE, KEY_CONSOLE, "CONSOLE", "CONSOLE", NULL, NULL, NULL,
"Get keyboard events from console"},
{ 0 }
};
static DEBTAB key_deb[] = {
{ "DBG", DBG },
{ NULL, 0 }
};
static TTDEV key_ttdev = {
{ REG_KEY, 0, 0, 0 },
key_read,
key_write,
};
DEVICE key_dev = {
"KEY", &key_unit, key_reg, key_mod,
0, 8, 16, 1, 8, 16,
NULL, NULL, &key_reset,
NULL, NULL, NULL, &key_ttdev,
DEV_DISABLE | DEV_DEBUG, 0, key_deb,
NULL, NULL, NULL, NULL, NULL, NULL
};
static t_stat key_svc (UNIT *uptr)
{
t_stat ch = sim_poll_kbd ();
if ((ch & SCPE_KFLAG) == 0) {
sim_activate_after (&key_unit, 10000);
return ch;
}
if (ch & SCPE_BREAK)
KBUF = 0377;
else
KBUF = ch & 0177;
flag_on (FLAG_KB);
sim_debug (DBG, &key_dev, "Received character %03o\n", KBUF);
return SCPE_OK;
}
static int
key_modifiers (SIM_KEY_EVENT *ev)
{
int code = 0;
switch (ev->key) {
case SIM_KEY_SHIFT_L:
case SIM_KEY_SHIFT_R:
code = SHFT;
break;
case SIM_KEY_CTRL_L:
case SIM_KEY_CTRL_R:
case SIM_KEY_CAPS_LOCK:
code = CTRL;
break;
case SIM_KEY_ALT_L:
case SIM_KEY_ALT_R:
code = META;
break;
}
if (ev->state == SIM_KEYPRESS_DOWN)
modifiers |= code;
else if (ev->state == SIM_KEYPRESS_UP)
modifiers &= ~code;
return code != 0;
}
static int
key_both (uint32 key)
{
uint16 code = NOKEY;
switch (key) {
case SIM_KEY_TAB:
code = 0011;
break;
case SIM_KEY_PAGE_UP:
code = 0014;
break;
case SIM_KEY_ENTER:
code = 0015;
break;
case SIM_KEY_ESC:
code = 0033;
break;
case SIM_KEY_SPACE:
code = 0040;
break;
case SIM_KEY_BACKSPACE:
case SIM_KEY_DELETE:
code = 0177;
break;
case SIM_KEY_F11:
vid_set_fullscreen (!vid_is_fullscreen ());
break;
}
return code;
}
static int
key_shift (uint32 key)
{
uint16 code = key_both (key);
if (code != NOKEY)
return code;
switch (key) {
case SIM_KEY_0:
code = ')';
break;
case SIM_KEY_1:
code = '!';
break;
case SIM_KEY_2:
return '@';
break;
case SIM_KEY_3:
code = '#';
break;
case SIM_KEY_4:
code = '$';
break;
case SIM_KEY_5:
code = '%';
break;
case SIM_KEY_6:
return '^';
case SIM_KEY_7:
code = '&';
break;
case SIM_KEY_8:
code = '*';
break;
case SIM_KEY_9:
code = '(';
break;
case SIM_KEY_A:
code = 'A';
break;
case SIM_KEY_B:
code = 'B';
break;
case SIM_KEY_C:
code = 'C';
break;
case SIM_KEY_D:
code = 'D';
break;
case SIM_KEY_E:
code = 'E';
break;
case SIM_KEY_F:
code = 'F';
break;
case SIM_KEY_G:
code = 'G';
break;
case SIM_KEY_H:
code = 'H';
break;
case SIM_KEY_I:
code = 'I';
break;
case SIM_KEY_J:
code = 'J';
break;
case SIM_KEY_K:
code = 'K';
break;
case SIM_KEY_L:
code = 'L';
break;
case SIM_KEY_M:
code = 'M';
break;
case SIM_KEY_N:
code = 'N';
break;
case SIM_KEY_O:
code = 'O';
break;
case SIM_KEY_P:
code = 'P';
break;
case SIM_KEY_Q:
code = 'Q';
break;
case SIM_KEY_R:
code = 'R';
break;
case SIM_KEY_S:
code = 'S';
break;
case SIM_KEY_T:
code = 'T';
break;
case SIM_KEY_U:
code = 'U';
break;
case SIM_KEY_V:
code = 'V';
break;
case SIM_KEY_W:
code = 'W';
break;
case SIM_KEY_X:
code = 'X';
break;
case SIM_KEY_Y:
code = 'Y';
break;
case SIM_KEY_Z:
code = 'Z';
break;
case SIM_KEY_BACKQUOTE:
return '~';
case SIM_KEY_MINUS:
return '_';
case SIM_KEY_EQUALS:
code = '+';
break;
case SIM_KEY_LEFT_BRACKET:
return '{';
case SIM_KEY_RIGHT_BRACKET:
return '}';
case SIM_KEY_SEMICOLON:
code = ':';
break;
case SIM_KEY_SINGLE_QUOTE:
code = '"';
break;
case SIM_KEY_BACKSLASH:
case SIM_KEY_LEFT_BACKSLASH:
return '|';
case SIM_KEY_COMMA:
code = '<';
break;
case SIM_KEY_PERIOD:
code = '>';
break;
case SIM_KEY_SLASH:
code = '?';
break;
}
return code;
}
static int
key_noshift (uint32 key)
{
uint16 code = key_both (key);
if (code != NOKEY)
return code;
switch (key) {
case SIM_KEY_0:
code = '0';
break;
case SIM_KEY_1:
code = '1';
break;
case SIM_KEY_2:
code = '2';
break;
case SIM_KEY_3:
code = '3';
break;
case SIM_KEY_4:
code = '4';
break;
case SIM_KEY_5:
code = '5';
break;
case SIM_KEY_6:
code = '6';
break;
case SIM_KEY_7:
code = '7';
break;
case SIM_KEY_8:
code = '8';
break;
case SIM_KEY_9:
code = '9';
break;
case SIM_KEY_A:
code = 'a';
break;
case SIM_KEY_B:
code = 'b';
break;
case SIM_KEY_C:
code = 'c';
break;
case SIM_KEY_D:
code = 'd';
break;
case SIM_KEY_E:
code = 'e';
break;
case SIM_KEY_F:
code = 'f';
break;
case SIM_KEY_G:
code = 'g';
break;
case SIM_KEY_H:
code = 'h';
break;
case SIM_KEY_I:
code = 'i';
break;
case SIM_KEY_J:
code = 'j';
break;
case SIM_KEY_K:
code = 'k';
break;
case SIM_KEY_L:
code = 'l';
break;
case SIM_KEY_M:
code = 'm';
break;
case SIM_KEY_N:
code = 'n';
break;
case SIM_KEY_O:
code = 'o';
break;
case SIM_KEY_P:
code = 'p';
break;
case SIM_KEY_Q:
code = 'q';
break;
case SIM_KEY_R:
code = 'r';
break;
case SIM_KEY_S:
code = 's';
break;
case SIM_KEY_T:
code = 't';
break;
case SIM_KEY_U:
code = 'u';
break;
case SIM_KEY_V:
code = 'v';
break;
case SIM_KEY_W:
code = 'w';
break;
case SIM_KEY_X:
code = 'x';
break;
case SIM_KEY_Y:
code = 'y';
break;
case SIM_KEY_Z:
code = 'z';
break;
case SIM_KEY_BACKQUOTE:
code = '`';
break;
case SIM_KEY_MINUS:
code = '-';
break;
case SIM_KEY_EQUALS:
code = '=';
break;
case SIM_KEY_LEFT_BRACKET:
code = '[';
break;
case SIM_KEY_RIGHT_BRACKET:
code = ']';
break;
case SIM_KEY_SEMICOLON:
code = ';';
break;
case SIM_KEY_SINGLE_QUOTE:
code = '\'';
break;
case SIM_KEY_BACKSLASH:
case SIM_KEY_LEFT_BACKSLASH:
code = '/';
break;
case SIM_KEY_COMMA:
code = ',';
break;
case SIM_KEY_PERIOD:
code = '.';
break;
case SIM_KEY_SLASH:
code = '/';
break;
}
return code;
}
static int
key_event (SIM_KEY_EVENT *ev)
{
sim_debug (DBG, &key_dev, "Key %s %s\n",
ev->state == SIM_KEYPRESS_UP ? "up" : "down",
vid_key_name (ev->key));
if (key_modifiers (ev))
return 0;
if (ev->state == SIM_KEYPRESS_DOWN) {
uint16 code;
if (modifiers & SHFT)
code = key_shift (ev->key);
else
code = key_noshift (ev->key);
if (code == NOKEY)
return 1;
if (modifiers & CTRL)
code &= 037;
if (modifiers & META) {
suffix = code;
code = 033;
}
KBUF = code;
sim_debug (DBG, &key_dev, "Received character %03o\n", KBUF);
flag_on (FLAG_KB);
} else if (ev->state == SIM_KEYPRESS_UP)
KBUF = 0;
return 0;
}
static t_stat
key_reset (DEVICE *dptr)
{
#ifdef USE_DISPLAY
vid_display_kb_event_process = NULL;
#endif
if (dptr->flags & DEV_DIS)
return SCPE_OK;
if ((key_unit.flags & KEY_TYPE) == 0) {
#if defined(USE_DISPLAY) || (defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL))
key_unit.flags |= KEY_DISPLAY;
#else
key_unit.flags |= KEY_CONSOLE;
#endif
}
if (key_unit.flags & KEY_DISPLAY)
#ifdef USE_DISPLAY
vid_display_kb_event_process = key_event;
#else
;
#endif
else if (key_unit.flags & KEY_CONSOLE)
sim_activate_abs (&key_unit, 0);
else
return SCPE_ARG;
return SCPE_OK;
}
static uint16 key_read (uint16 reg)
{
uint16 code = KBUF;
sim_debug (DBG, &key_dev, "Read key %o\n", code);
if (suffix == NOKEY) {
flag_off (FLAG_KB);
if (key_unit.flags & KEY_CONSOLE)
sim_activate_abs (&key_unit, 0);
} else {
KBUF = suffix;
suffix = NOKEY;
}
return code;
}
static void key_write (uint16 reg, uint16 data)
{
}

99
tt2500/tt2500_rom.c Normal file
View File

@ -0,0 +1,99 @@
/* tt2500_cpu.c: TT2500 bootstrap ROM contents.
Copyright (c) 2020, Lars Brinkhoff
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
LARS BRINKHOFF 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 Lars Brinkhoff shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Lars Brinkhoff.
*/
#include "tt2500_defs.h"
uint16 tt2500_rom[] =
{
/* BEGIN 0 */ 0010000, /* (NOP) */
/* 1 */ 0010000, /* (NOP) */
/* 2 */ 0010000, /* (NOP) */
/* START 3 */ 0040025, /* (PUSHJ GETC & GET CHAR) */
/* 4 */ 0007001, /* (SUBI 0 WD & IS WORD "LOGO") */
/* 5 */ 0147577, /* (147577) */
/* 6 */ 0133774, /* (BNE START) */
/* 7 */ 0040022, /* (PUSHJ GETW) */
/* 10 */ 0074201, /* (GET ADR WD) */
/* 11 */ 0040022, /* (PUSHJ GETW) */
/* 12 */ 0074301, /* (GET CNT WD) */
/* LOOP 13 */ 0040022, /* (PUSHJ GETW) */
/* 14 */ 0004220, /* (DEC ADR) */
/* 15 */ 0025100, /* (CWRITE WD) */
/* 16 */ 0004320, /* (DEC CNT) */
/* 17 */ 0133773, /* (BNE LOOP) */
/* 20 */ 0074023, /* (GET 0 XR) */
/* 21 */ 0050100, /* (JUMP 100) */
/* GETW 22 */ 0040025, /* (PUSHJ GETC) */
/* 23 */ 0040025, /* (PUSHJ GETC) */
/* 24 */ 0040025, /* (PUSHJ GETC) */
/* GETC 25 */ 0073320, /* (DIS INTS 2) */
/* 26 */ 0050025, /* (JUMP GETC) */
/* 27 */ 0010000, /* (NOP) */
/* 30 */ 0074424, /* (GET CH UART) */
/* 31 */ 0001444, /* (ANDI CH CH) */
/* 32 */ 0000017, /* (17) */
/* 33 */ 0004114, /* (ROT WD 14) */
/* 34 */ 0001141, /* (ANDI WD WD) */
/* 35 */ 0177760, /* (177760) */
/* 36 */ 0002104, /* (IOR WD CH) */
/* 37 */ 0076016 /* (POPJ) */
#if 0
0010000, /* NOP */
0010000, /* NOP */
0010000, /* NOP */
0040025, /* PUSHJ GETC Call subroutine to read TTY character. */
0007001, /* SUBI 0 WD 32-bit instruction computes 147577 - WD */
0147577, /* 147577 (The constant "LOGO" is stored here.) */
0133774, /* BNE START Branch to START if result Not zero. */
0040022, /* PUSHJ GETW Reads 4 characters to make 16-bit word. */
0074201, /* GET ADR WD Move word from WD to ADR (register 2). */
0040022, /* PUSHJ GETW Get next data word into WD. */
0074301, /* GET CNT WD Move it to CNT. */
0040022, /* PUSHJ GETW Get another data word. */
0004220, /* DEC ADR Decrement the Address and use it to */
0025100, /* CWRITE WD write [WD] into control memory. */
0004320, /* DEC CNT Decrement the word in CNT (count). */
0133773, /* BNE LOOP Branch to LOOP unless CNT is zero. */
0074023, /* GET 0 XR Makes PC leave Bootstrap Loader. */
0050100, /* JUMP 100 Jump to location 100. */
0040025, /* PUSHJ GETC Get 4 bits and shift into WD. */
0040025, /* PUSHJ GETC Get 4 more. */
0040025, /* PUSHJ GETC Get 4 more. */
0073320, /* DIS INTS 2 Skip 2 steps if TTY interrupt active. */
0050025, /* JUMP GETC If not, go back and wait. */
0010000, /* NOP (Skip over this.) */
0074424, /* GET CH UART Put the character into CH. */
0001444, /* ANDI CH CH Mask out all but last four bits by */
0000017, /* 17 ANDing with 0 000 000 000 001 111. */
0004114, /* ROT WD 14 Rotate WD four places right. */
0001141, /* ANDI WD WD Zero out last four bits by */
0177760, /* 177760 ANDing with 1 111 111 111 110 000. */
0002104, /* IOR WD CH Finally, OR them together. */
0075600, /* POPJ Return to calling program. */
#endif
};

485
tt2500/tt2500_sys.c Normal file
View File

@ -0,0 +1,485 @@
/* tt2500_sys.c: TT2500 simulator interface
Copyright (c) 2020, Lars Brinkhoff
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
LARS BRINKHOFF 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 Lars Brinkhoff shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Lars Brinkhoff.
24-Sep-20 LB New simulator.
*/
#include "tt2500_defs.h"
static uint16 null_read (uint16 reg);
static void null_write (uint16 reg, uint16 data);
int32 sim_emax = 1;
char sim_name[] = "TT2500";
uint16 CRM[4096];
uint16 MEM[65536];
REG *sim_PC = &cpu_reg[0];
TTDEV *dev_tab[0100];
static t_addr sym_addr = -1;
static int sym_immediate = 0;
static TTDEV null_dev = {
{ 0, 0, 0, 0 },
null_read,
null_write,
};
DEVICE *sim_devices[] = {
&cpu_dev,
&dpy_dev,
&crt_dev,
&tv_dev,
&key_dev,
&uart_dev,
NULL
};
const char *sim_stop_messages[SCPE_BASE] = {
"Unknown error",
"HALT instruction",
"Breakpoint",
"Invalid access",
};
static t_stat
get4 (FILE *fileref, uint16 *x)
{
int c = Fgetc (fileref);
if (c == EOF)
return SCPE_FMT;
*x = c & 017;
return SCPE_OK;
}
static t_stat
get6 (FILE *fileref, uint16 *x)
{
int c = Fgetc (fileref);
if (c == EOF)
return SCPE_FMT;
*x = c & 077;
return SCPE_OK;
}
static t_stat
get8 (FILE *fileref, uint16 *x)
{
uint16 y;
t_stat r;
r = get4 (fileref, x);
if (r != SCPE_OK)
return r;
r = get4 (fileref, &y);
if (r != SCPE_OK)
return r;
*x = (*x << 4) | y;
return SCPE_OK;
}
static t_stat
get16 (FILE *fileref, uint16 *x)
{
uint16 y;
t_stat r;
r = get8 (fileref, x);
if (r != SCPE_OK)
return r;
r = get8 (fileref, &y);
if (r != SCPE_OK)
return r;
*x = (*x << 8) | y;
return SCPE_OK;
}
static uint16 checksum;
static t_stat
get18 (FILE *fileref, uint16 *x)
{
uint16 y;
t_stat r;
r = get6 (fileref, x);
if (r != SCPE_OK)
return r;
r = get6 (fileref, &y);
if (r != SCPE_OK)
return r;
*x = (*x << 6) | y;
r = get6 (fileref, &y);
if (r != SCPE_OK)
return r;
*x = (*x << 6) | y;
checksum = (checksum + *x) & 0177777;
return SCPE_OK;
}
static t_stat load_loader (FILE *f, int verbose)
{
uint16 i, x, y, count, addr;
t_stat r;
x = 0;
do {
r = get4 (f, &y);
if (r != SCPE_OK)
return r;
x = ((x << 4) + y) & 0177777;
} while (x != 0147577);
/* Read count and address. */
r = get16 (f, &addr);
if (r != SCPE_OK)
return r;
r = get16 (f, &count);
if (r != SCPE_OK)
return r;
if (verbose)
fprintf (stderr, "Loader: address %06o, %o words\n", addr, count);
for (i = 1; i <= count; i++) {
r = get16 (f, &x);
if (r != SCPE_OK)
return r;
CRM[addr - i] = x;
}
return SCPE_OK;
}
static t_stat load_block (FILE *f, int verbose)
{
uint16 i, x, y, type, count, addr;
t_stat r;
x = 0;
do {
r = get6 (f, &y);
if (r != SCPE_OK)
return r;
x = ((x << 6) + y) & 0177777;
} while (x != 0120116);
checksum = 0;
/* Read type. */
r = get18 (f, &type);
if (r != SCPE_OK)
return r;
switch (type) {
case 0:
r = get18 (f, &x);
if (r != SCPE_OK)
return r;
if (verbose) {
t_value val = x;
fprintf (stderr, "Execute: instruction %06o\n", x);
fprint_sym (stderr, 0, &val, 0, SWMASK ('M'));
fputc ('\n', stderr);
}
return SCPE_EOF;
case 1:
case 2:
break;
default:
return SCPE_FMT;
}
r = get18 (f, &addr);
if (r != SCPE_OK)
return r;
r = get18 (f, &count);
if (r != SCPE_OK)
return r;
if (type == 1) {
if (verbose)
fprintf (stderr, "Load control store: address %06o, %o words\n",
addr, count);
for (i = 0; i < count; i++) {
r = get18 (f, &x);
if (r != SCPE_OK)
return r;
CRM[(addr++) & 07777] = x;
}
} else if (type == 2) {
if (verbose)
fprintf (stderr, "Load RAM: address %06o, %o words\n",
addr, count);
for (i = 0; i < count; i++) {
r = get18 (f, &x);
if (r != SCPE_OK)
return r;
if ((addr & 0170000) == 0170000) {
FONT[(addr++) & 07777] = x & 0377;
FONT[(addr++) & 07777] = x >> 8;
i++;
} else
MEM[(addr++) & 0177777] = x;
}
}
r = get18 (f, &x);
if (r != SCPE_OK)
return r;
/* Should be 0, but some blocks checksum to 1. */
if (checksum != 0 && checksum != 1)
return SCPE_CSUM;
return SCPE_OK;
}
t_stat
sim_load (FILE *fileref, CONST char *cptr, CONST char *fnam, int flag)
{
int verbose = sim_switches & SWMASK ('V');
t_stat r;
r = load_loader (fileref, verbose);
if (r != SCPE_OK)
return r;
for (;;) {
r = load_block (fileref, verbose);
if (r == SCPE_EOF)
return SCPE_OK;
if (r != SCPE_OK)
return r;
}
}
static uint16 null_read (uint16 reg)
{
return 0;
}
static void null_write (uint16 reg, uint16 data)
{
}
t_bool build_dev_tab (void)
{
TTDEV *ttdev;
DEVICE *dev;
int i, j;
for (i = 0; i < 0100; i++)
dev_tab[i] = &null_dev;
for (i = 0; (dev = sim_devices[i]) != NULL; i++) {
ttdev = (TTDEV *)dev->ctxt;
if (ttdev != NULL) {
for (j = 0; j < 4; j++)
if (ttdev->reg[j] != 0)
dev_tab[ttdev->reg[j]] = ttdev;
}
}
return SCPE_OK;
}
static const char *register_names[] =
{
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"A-latch", "s11", "s12", "s13", "MAGIC", "CHARTV", "s16", "s17",
"YCOR", "XCOR", "SCROLL", "XR", "UART", "DSR", "KEY", "d27",
"d30", "d31", "d32", "d33", "d34", "d35", "d36", "d37",
"scratch40", "scratch41", "scratch42", "scratch43", "scratch44", "scratch45", "scratch46", "scratch47",
"scratch50", "scratch51", "scratch52", "scratch53", "scratch54", "scratch55", "scratch56", "scratch57",
"scratch60", "scratch61", "scratch62", "scratch63", "scratch64", "scratch65", "scratch66", "scratch67",
"scratch70", "scratch71", "scratch72", "scratch73", "scratch74", "scratch75", "scratch76", "scratch77"
};
static t_stat
fprint_sto (FILE *of, uint16 insn)
{
uint16 a = (insn >> 6) & 7;
uint16 b = insn & 017;
const char *op;
switch (insn & 077060) {
case 020000: op = "READ"; break;
case 020020: op = "READD"; break;
case 021000: op = "CREAD"; break;
case 021020: op = "CREADD"; break;
case 022040: op = "READI"; break;
case 023040: op = "CREADI"; break;
case 024000: op = "WRITE"; break;
case 024020: op = "WRITED"; break;
case 025000: op = "CWRITE"; break;
case 025020: op = "CWRITED"; break;
case 026040: op = "WRITEI"; break;
case 027040: op = "CWRITEI"; break;
case 074000: op = "GET"; break;
default: fprintf (of, "???"); return SCPE_OK;
}
fprintf (of, "%s %s %s", op, register_names[a], register_names[b]);
return SCPE_OK;
}
static t_stat
fprint_reg (FILE *of, uint16 insn)
{
static const char *name[] =
{ "A", "ANDN", "AND", "NOR", "IOR", "XOR", "MROT", "??",
"ROT", "DEC", "XADD", "ADD", "SUB", "XSUB", "INC", "ARS" };
uint16 op = (insn >> 4) & 3;
uint16 a = (insn >> 6) & 7;
uint16 b = insn & 017;
if (insn == 010000) {
fprintf (of, "NOP");
return SCPE_OK;
} else if ((insn & 037060) == 01000) {
sym_immediate = 1;
fprintf (of, "LOD %s", register_names[a]);
return SCPE_OK;
}
switch (insn & 030000) {
case 000000: break;
case 010000: fprintf (of, "T "); break;
case 020000: return fprint_sto (of, insn);
case 030000: fprintf (of, "IFC "); break;
}
sym_immediate = insn & 01000;
op += (insn >> 8) & 014;
fprintf (of, "%s%s %s %s", name[op], sym_immediate ? "I" : "",
register_names[a], register_names[b]);
return SCPE_OK;
}
static t_stat
fprint_dis (FILE *of, uint16 insn)
{
fprintf (of, "DIS ");
switch (insn & 01400) {
case 00000: fprintf (of, "BUS"); break;
case 00400: fprintf (of, "FLAGS"); break;
case 01000: fprintf (of, "INTS"); break;
case 01400: fprintf (of, "STARS"); break;
}
fprintf (of, " %o", (~insn >> 4) & 017);
return SCPE_OK;
}
static t_stat
fprint_bus (FILE *of, uint16 insn)
{
uint16 a = (insn >> 6) & 7;
uint16 b = insn & 077;
if ((insn & 076000) == 072000)
return fprint_dis (of, insn);
switch (insn) {
case 0075400: fprintf (of, "MAGIC"); return SCPE_OK;
case 0076014: fprintf (of, "MAGIC"); return SCPE_OK;
case 0076015: fprintf (of, "CHARTV"); return SCPE_OK;
case 0075500: fprintf (of, "CHARTV"); return SCPE_OK;
case 0075600: fprintf (of, "POPJ"); return SCPE_OK;
case 0076016: fprintf (of, "POPJ"); return SCPE_OK;
case 0076716: fprintf (of, "POPJI"); return SCPE_OK;
}
fprintf (of, "%s ", insn & 02000 ? "PUT" : "GET");
fprintf (of, "%s %s", register_names[a], register_names[b]);
return SCPE_OK;
}
static t_stat
fprint_branch (FILE *of, uint16 insn, uint16 addr)
{
static const char *condition[] =
{ "CC", "CS", "VS", "VC", "MI", "PL", "NE", "EQ",
"GE", "LT", "IS", "IC", "XCI", "XSI", "FS", "FC" };
uint16 target = insn & 03777;
if (insn & 02000)
target = target - 04000;
target += addr + 1;
fprintf (of, "B%s %06o", condition[(insn >> 11) & 017], target);
return SCPE_OK;
}
static t_stat
fprint_cpu (FILE *of, uint16 insn, uint16 addr)
{
switch ((insn >> 12) & 017) {
case 000: case 001: case 002: case 003:
return fprint_reg (of, insn);
case 004:
fprintf (of, "PUSHJ %04o", insn & 07777);
break;
case 005:
fprintf (of, "JUMP %04o", insn & 07777);
break;
case 006:
fprintf (of, "(undef)");
break;
case 007:
fprint_bus (of, insn);
break;
case 010: case 011: case 012: case 013:
case 014: case 015: case 016: case 017:
fprint_branch (of, insn, addr);
break;
}
return SCPE_OK;
}
t_stat fprint_sym (FILE *of, t_addr addr, t_value *val,
UNIT *uptr, int32 sw)
{
t_stat reason;
if ((reason = build_dev_tab ()) != SCPE_OK)
return reason;
if (sym_addr == addr - 1 && sym_immediate) {
fprintf (of, "%06o", (unsigned)*val);
return SCPE_OK;
}
sym_addr = addr;
sym_immediate = 0;
if (sw & SWMASK ('M'))
return fprint_cpu (of, *val, addr);
return SCPE_ARG;
}
t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr,
t_value *val, int32 sw)
{
t_stat reason;
*val = get_uint (cptr, 8, ~0, &reason);
if (reason != SCPE_OK)
return reason;
return 0;
}

145
tt2500/tt2500_tv.c Normal file
View File

@ -0,0 +1,145 @@
/* tt2500_tv.c: TT2500 TV text display
Copyright (c) 2020, Lars Brinkhoff
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
LARS BRINKHOFF 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 Lars Brinkhoff shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Lars Brinkhoff.
*/
#include "tt2500_defs.h"
#include "sim_video.h"
#include "display/tt2500.h"
#include "display/display.h"
/* Function declaration. */
static t_stat tv_svc (UNIT *uptr);
static t_stat tv_reset (DEVICE *dptr);
static uint32 surface[8 * 16 * 72];
static uint32 palette[2];
static VID_DISPLAY *vptr = NULL;
/* Debug */
#define DBG 0001
static UNIT tv_unit = {
UDATA (&tv_svc, UNIT_IDLE, 0)
};
static DEBTAB tv_deb[] = {
{ "DBG", DBG },
{ "KEY", SIM_VID_DBG_KEY },
{ NULL, 0 }
};
#if defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL)
#define TV_DIS 0
#else
#define TV_DIS DEV_DIS
#endif
DEVICE tv_dev = {
"TV", &tv_unit, NULL, NULL,
1, 8, 16, 1, 8, 16,
NULL, NULL, &tv_reset,
NULL, NULL, NULL,
NULL, DEV_DISABLE | DEV_DEBUG | TV_DIS, 0, tv_deb,
NULL, NULL, NULL, NULL, NULL, NULL
};
static t_stat
tv_svc(UNIT *uptr)
{
SIM_KEY_EVENT ev;
sim_activate_after (uptr, 10000);
if (dpy_quit) {
dpy_quit = FALSE;
return SCPE_STOP;
}
if (vid_poll_kb (&ev) == SCPE_OK) {
#ifdef USE_DISPLAY
if (vid_display_kb_event_process != NULL)
vid_display_kb_event_process (&ev);
#endif
}
return SCPE_OK;
}
static t_stat
tv_reset (DEVICE *dptr)
{
t_stat r;
if (dptr->flags & DEV_DIS || sim_switches & SWMASK('P')) {
sim_cancel (&tv_unit);
if (vptr != NULL)
vid_close_window (vptr);
vptr = NULL;
} else if (vptr == NULL) {
r = vid_open_window (&vptr, dptr, "Text display", 576, 480, 0);
if (r != SCPE_OK)
return r;
sim_activate_abs (&tv_unit, 0);
vid_register_quit_callback (&dpy_quit_callback);
palette[0] = vid_map_rgb_window (vptr, 0x00, 0x00, 0x00);
palette[1] = vid_map_rgb_window (vptr, 0x00, 0xFF, 0x30);
}
return SCPE_OK;
}
static void tv_character (int row, int col, uint8 c, uint8 *font)
{
uint16 i, j, pixels, address;
address = 16 * c;
for (i = 0; i < 16; i++) {
pixels = font[address + i];
for (j = 0; j < 8; j++) {
surface[8 * (72 * i + col) + j] = palette[(pixels >> 7) & 1];
pixels <<= 1;
}
}
}
void tv_line (int row, uint8 *line, uint8 *font)
{
int col;
line[72] = 0;
sim_debug (DBG, &tv_dev, "Text row %d: %s\n", row, (char *)line);
if (tv_dev.flags & DEV_DIS)
return;
for (col = 0; col < 72; col++)
tv_character (row, col, *line++, font);
vid_draw_window (vptr, 0, 16 * row, 8 * 72, 16, surface);
}
void tv_refresh (void)
{
if (tv_dev.flags & DEV_DIS)
return;
sim_debug (DBG, &tv_dev, "Refresh screen.\n");
vid_refresh_window (vptr);
}

237
tt2500/tt2500_uart.c Normal file
View File

@ -0,0 +1,237 @@
/* tt2500_uart.c: TT2500 serial port device
Copyright (c) 2020, Lars Brinkhoff
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
LARS BRINKHOFF 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 Lars Brinkhoff shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Lars Brinkhoff.
*/
#include "tt2500_defs.h"
#include "sim_tmxr.h"
/* Debug */
#define DBG_TX 0001
#define DBG_RX 0002
#define UART_FILE 1 /* Attached to a file. */
#define UART_PORT 2 /* Attached to a network port. */
#define UART_TYPE 3 /* File or port. */
#define UART_REVERSE 4 /* Transmit bits in reverse order. */
static uint16 RBUF, TBUF;
/* Function declaration. */
static t_stat uart_r_svc (UNIT *uptr);
static t_stat uart_t_svc (UNIT *uptr);
static t_stat uart_reset (DEVICE *dptr);
static t_stat uart_attach (UNIT *uptr, CONST char *cptr);
static t_stat uart_detach (UNIT *uptr);
static uint16 uart_read (uint16);
static void uart_write (uint16, uint16);
static TMLN uart_ldsc = { 0 };
static TMXR uart_desc = { 1, 0, 0, &uart_ldsc };
static UNIT uart_unit[] = {
{ UDATA (&uart_r_svc, UNIT_IDLE+UNIT_ATTABLE, 0) },
{ UDATA (&uart_t_svc, UNIT_IDLE+UNIT_ATTABLE, 0) }
};
static REG uart_reg[] = {
{ ORDATAD (RB, RBUF, 8, "Receive buffer") },
{ ORDATAD (TB, TBUF, 8, "Transmit buffer") },
{ NULL }
};
MTAB uart_mod[] = {
{ UART_TYPE, UART_PORT, "PORT", "PORT", NULL, NULL, NULL,
"Attach to port"},
{ UART_TYPE, UART_FILE, "FILE", "FILE", NULL, NULL, NULL,
"Attach to file"},
{ UART_REVERSE, UART_REVERSE, "REVERSE", "REVERSE", NULL, NULL, NULL,
"Transmit bits in reverse order"},
{ UART_REVERSE, 0, NULL, "NOREVERSE", NULL, NULL, NULL,
"Transmit bits in normal order"},
{ MTAB_VDV|MTAB_VALR, 1, NULL, "DISCONNECT",
&tmxr_dscln, NULL, &uart_desc, "Disconnect a specific line" },
{ UNIT_ATT, UNIT_ATT, "SUMMARY", NULL, NULL,
&tmxr_show_summ, (void *) &uart_desc, "Display a summary of line states" },
{ MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL, NULL,
&tmxr_show_cstat, (void *) &uart_desc, "Display current connections" },
{ MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL, NULL,
&tmxr_show_cstat, (void *) &uart_desc, "Display multiplexer statistics" },
{ 0 }
};
static DEBTAB uart_deb[] = {
{ "RX", DBG_RX },
{ "TX", DBG_TX },
{ NULL, 0 }
};
static TTDEV uart_ttdev = {
{ REG_UART, 0, 0, 0 },
uart_read,
uart_write,
};
DEVICE uart_dev = {
"UART", uart_unit, uart_reg, uart_mod,
2, 8, 16, 1, 8, 16,
NULL, NULL, uart_reset,
NULL, uart_attach, uart_detach,
&uart_ttdev, DEV_DEBUG, 0, uart_deb,
NULL, NULL, NULL, NULL, NULL, NULL
};
static t_stat
uart_r_svc(UNIT *uptr)
{
int32 ch;
if ((uptr->flags & UNIT_ATT) == 0)
return SCPE_OK;
if (uptr->fileref != NULL) {
unsigned char buf;
if (sim_fread (&buf, 1, 1, uptr->fileref) == 1) {
sim_debug (DBG_RX, &uart_dev, "Received character %03o\n", buf);
RBUF = buf;
flag_on (INT_RRD);
}
} else if (uart_ldsc.conn) {
tmxr_poll_rx (&uart_desc);
ch = tmxr_getc_ln (&uart_ldsc);
if (ch & TMXR_VALID) {
RBUF = sim_tt_inpcvt (ch, TT_GET_MODE (uart_unit[0].flags));
sim_debug (DBG_RX, &uart_dev, "Received character %03o\n", RBUF);
flag_on (INT_RRD);
return SCPE_OK;
}
sim_activate_after (uptr, 200);
} else {
int32 ln = tmxr_poll_conn (&uart_desc);
if (ln >= 0) {
uart_ldsc.rcve = 1;
sim_debug (DBG_RX, &uart_dev, "Connect\n");
sim_activate_after (uptr, 200);
} else {
sim_activate_after (uptr, 10000);
}
}
return SCPE_OK;
}
static t_stat
uart_t_svc(UNIT *uptr)
{
int32 ch;
tmxr_poll_tx (&uart_desc);
if (!tmxr_txdone_ln (&uart_ldsc))
return SCPE_OK;
ch = sim_tt_outcvt (TBUF, TT_GET_MODE (uart_unit[1].flags));
if (tmxr_putc_ln (&uart_ldsc, ch) == SCPE_STALL) {
sim_activate_after (&uart_unit[1], 200);
} else {
sim_debug (DBG_TX, &uart_dev, "Transmitted character %03o\n", TBUF);
tmxr_poll_tx (&uart_desc);
flag_on (FLAG_RSD);
}
return SCPE_OK;
}
static t_stat
uart_reset (DEVICE *dptr)
{
if ((uart_unit[0].flags & UART_TYPE) == 0)
uart_unit[0].flags |= UART_PORT;
flag_off (INT_RRD);
flag_on (FLAG_RSD);
return SCPE_OK;
}
static t_stat
uart_attach (UNIT *uptr, CONST char *cptr)
{
t_stat r;
if (uptr->flags & UART_PORT) {
r = tmxr_attach (&uart_desc, uptr, cptr);
if (r != SCPE_OK)
return r;
sim_activate_abs (uptr, 0);
} else if (uptr->flags & UART_FILE) {
r = attach_unit (uptr, cptr);
if (r != SCPE_OK)
return r;
sim_activate_abs (uptr, 0);
} else {
return SCPE_ARG;
}
return SCPE_OK;
}
static t_stat
uart_detach (UNIT *uptr)
{
if (!(uptr->flags & UNIT_ATT))
return SCPE_OK;
if (sim_is_active (uptr))
sim_cancel (uptr);
return detach_unit (uptr);
}
static uint16 uart_read (uint16 reg)
{
sim_debug (DBG_RX, &uart_dev, "Read character %03o\n", RBUF);
flag_off (INT_RRD);
sim_activate_after (&uart_unit[0], 200);
return RBUF;
}
static uint16 reverse (uint16 data)
{
uint16 i, x = 0;
for (i = 1; i <= 0200; i <<= 1) {
x <<= 1;
if (data & i)
x++;
}
return x;
}
static void uart_write (uint16 reg, uint16 data)
{
data &= 0377;
if (uart_unit[0].flags & UART_REVERSE)
data = reverse (data);
sim_debug (DBG_TX, &uart_dev, "Write character %03o\n", data);
TBUF = data;
sim_activate_after (&uart_unit[1], 200);
flag_off (FLAG_RSD);
}