1
0
mirror of https://github.com/simh/simh.git synced 2026-02-27 17:13:44 +00:00

LINC: New emulator for the classic LINC.

This emulates the classic LINC.  The design was settled in 1965,
increasing memory to 2048 words, and adding a Z register, an overflow
flag, and an interrupt facility.
This commit is contained in:
Lars Brinkhoff
2025-09-17 08:28:28 +02:00
committed by Mark Pizzolato
parent 3dd72309b5
commit f5f2930712
12 changed files with 2615 additions and 3 deletions

View File

@@ -131,6 +131,10 @@ struct color color_p31 = { p31, ELEMENTS(p31), 100000 };
static struct phosphor p39[] = {{0.2, 1.0, 0.0, 0.5, 0.01}};
struct color color_p39 = { p39, ELEMENTS(p39), 20000 };
/* orange phosphor for LINC */
static struct phosphor p19[] = {{1.0, 0.7, 0.0, 0.1, 0.22}};
struct color color_p19 = { p19, ELEMENTS(p19), 20000 };
static struct phosphor p40[] = {
/* P40 blue-white spot with yellow-green decay (.045s to 10%?) */
{0.4, 0.2, 0.924, 0.5, 0.0135},
@@ -253,6 +257,15 @@ static struct display displays[] = {
*/
{ DIS_III, "III Display", &color_p39, NULL, 1024, 1024 },
/*
* LINC display
* 512x511 addressable points.
* The horizontal position is a 9-bit unsigned value, but the
* vertical is a one's complement signed 9-bit value with
* both +0 and -0 referring to the same position.
*/
{ DIS_LINC, "LINC Display", &color_p19, NULL, 512, 511 },
/*
* Imlac display
* 1024x1024 addressable points.

View File

@@ -47,6 +47,7 @@ enum display_type {
DIS_VR17 = 17,
DIS_VR20 = 20,
DIS_TYPE30 = 30,
DIS_LINC = 40,
DIS_VR48 = 48,
DIS_III = 111,
DIS_TYPE340 = 340,

32
linc/CMakeLists.txt Normal file
View File

@@ -0,0 +1,32 @@
## LINC simulator
##
## This is an automagically generated file. Do NOT EDIT.
## Any changes you make will be overwritten!!
##
## Make changes to the SIMH top-level makefile and then run the
## "cmake/generate.py" script to regenerate these files.
##
## cd cmake; python -m generate --help
##
## ------------------------------------------------------------
if (HAVE_UNITY_FRAMEWORK AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/unit-tests/CMakeLists.txt")
add_subdirectory(unit-tests)
endif ()
add_simulator(linc
SOURCES
linc_cpu.c
linc_crt.c
linc_dpy.c
linc_kbd.c
linc_sys.c
linc_tape.c
linc_tty.c
INCLUDES
${CMAKE_CURRENT_SOURCE_DIR}
FEATURE_DISPLAY
USES_AIO
LABEL linc
PKG_FAMILY default_family
TEST linc)

863
linc/linc_cpu.c Normal file
View File

@@ -0,0 +1,863 @@
/* linc_cpu.c: LINC CPU simulator
Copyright (c) 2025, 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 "linc_defs.h"
/* Debug */
#define DBG_CPU 0001
#define WMASK 07777 /* Full word. */
#define HMASK 04000 /* H bit; half word select. */
#define AMASK 03777 /* Full memory address. */
#define XMASK 01777 /* X part; low memory address. */
#define DMASK 00777 /* Display coordinate. */
#define LMASK 07700 /* Left half word. */
#define RMASK 00077 /* Right half word; character. */
#define IMASK 00020 /* Index bit. */
#define BMASK 00017 /* Beta; index register. */
#define X(_X) ((_X) & XMASK)
/* CPU state. */
static uint16 P;
static uint16 C;
static uint16 S;
static uint16 B;
static uint16 A;
static uint16 L;
static uint16 Z;
static uint16 R;
static uint16 LSW, RSW, SSW;
static uint16 SAM[16];
static uint16 XL[12];
static int paused;
static int IBZ;
static int OVF;
static int PINFF;
static int interrupt;
static int interrupt_enable = 0;
static t_stat stop_reason;
typedef struct {
uint16 P;
uint16 C;
uint16 S;
uint16 B;
uint16 A;
uint16 L;
} HISTORY;
static HISTORY *history = NULL;
static uint32 history_i, history_m, history_n;
/* Function declaration. */
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 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 UNIT cpu_unit = { UDATA(NULL, UNIT_FIX + UNIT_BINK, MEMSIZE) };
REG cpu_reg[] = {
{ ORDATAD(P, P, 10, "Program Location") },
{ ORDATAD(C, C, 12, "Control Register") },
{ ORDATAD(A, A, 12, "Accumulator") },
{ ORDATAD(L, L, 1, "Link") },
{ ORDATAD(Z, Z, 12, "?") },
{ ORDATAD(R, R, 6, "Relay Register") },
{ ORDATAD(S, S, 12, "Memroy Address") },
{ ORDATAD(B, B, 12, "Memory Buffer") },
{ ORDATAD(LSW, LSW, 12, "Left Switches") },
{ ORDATAD(RSW, RSW, 12, "Right Switches") },
{ ORDATAD(SSW, SSW, 6, "Sense Switches") },
{ FLDATAD(paused, paused, 1, "Paused") },
{ FLDATAD(IBZ, IBZ, 1, "Interblock zone") },
{ FLDATAD(OVF, OVF, 1, "Overflow") },
{ BRDATAD(SAM, SAM, 8, 8, 16, "Sampled analog inputs") },
{ BRDATAD(XL, XL, 8, 1, 12, "External levels") },
{ NULL }
};
static MTAB cpu_mod[] = {
{ 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 },
{ NULL, 0 }
};
DEVICE cpu_dev = {
"CPU", &cpu_unit, cpu_reg, cpu_mod,
0, 8, 11, 1, 8, 12,
&cpu_ex, &cpu_dep, &cpu_reset,
NULL, NULL, NULL, NULL, DEV_DEBUG, 0, cpu_deb,
NULL, NULL, NULL, NULL, NULL, NULL
};
static void pcinc(int flag)
{
if (flag)
P = X(P + 1);
}
static void memaddr(uint16 addr)
{
S = addr & WMASK;
}
static void membuf(uint16 data)
{
B = data & WMASK;
}
static void memrd(void)
{
B = M[S & AMASK];
sim_interval--;
if (sim_brk_summ && sim_brk_test(S & AMASK, SWMASK('R')))
stop_reason = STOP_RBKPT;
}
static void memmd(void)
{
M[S & AMASK] = B;
if (sim_brk_summ && sim_brk_test(S & AMASK, SWMASK('W')))
stop_reason = STOP_WBKPT;
}
static void memwr(void)
{
sim_interval--;
memmd();
}
static int cpu_halfword(void)
{
switch (C & 07740) {
case 01300: //LDH
case 01340: //STH
case 01400: //SHD
return 1;
default:
return 0;
}
}
static void cpu_index(void)
{
uint16 tmp;
if (C & IMASK) {
if (cpu_halfword()) {
B += HMASK;
tmp = B >> 12;
} else {
tmp = 1;
}
membuf((B & 06000) | X(B + tmp));
memmd();
}
}
static void cpu_indexing(void)
{
uint16 a = C & BMASK;
if (a == 0)
{
pcinc(1);
memaddr(P);
if ((C & IMASK) == 0) {
memrd();
memaddr(B);
}
}
else
{
memaddr(a);
memrd();
cpu_index();
memaddr(B);
}
}
static void
cpu_misc(void)
{
switch (C) {
case 00000: //HLT
stop_reason = STOP_HALT;
break;
case 00005: //ZTA
A = Z >> 1;
break;
case 00010: //ENI
interrupt_enable = 1;
break;
case 00011: //CLR
A = L = Z = 0;
break;
case 00013: //Write gate on.
break;
case 00014: //ATR
R = A & RMASK;
break;
case 00015: //RTA
A = R & RMASK;
break;
case 00016: //NOP
break;
case 00017: //COM
A = (~A) & WMASK;
break;
}
}
static void cpu_set(void)
{
memaddr(P);
memrd();
pcinc(1);
if ((C & IMASK) == 0) {
memaddr(B);
memrd();
}
memaddr(C & BMASK);
memwr();
}
static void cpu_sam(void)
{
// sample analog input C & BMASK
// 0-7 are pots, 10-17 are high speed inputs
// i=0 wait 24 microseconds, i=1 do not wait
if ((C & IMASK) == 0)
sim_interval -= 3;
A = SAM[C & BMASK];
if (A & 0200) /* One's complement +/-177. */
A |= 07600;
}
static void cpu_dis(void)
{
memaddr(C & BMASK);
memrd();
cpu_index();
sim_debug(DBG_CPU, &cpu_dev, "DIS α=%02o B=%04o A=%04o\n", S, B, A);
dpy_dis(B >> 11, B & DMASK, A & DMASK);
}
static void cpu_xsk(void)
{
memaddr(C & BMASK);
memrd();
cpu_index();
pcinc(X(B) == 01777);
}
static void cpu_rol(void)
{
while (C & BMASK) {
if (C & IMASK) {
A = (A << 1) | L;
L = A >> 12;
} else {
A = (A << 1) | (A >> 11);
}
A &= WMASK;
C--;
}
}
static void cpu_ror(void)
{
while (C & BMASK) {
Z = (Z >> 1) | ((A & 1) << 11);
if (C & IMASK) {
A |= L << 12;
L = A & 1;
A = A >> 1;
} else {
A = (A >> 1) | (A << 11);
A &= WMASK;
}
C--;
}
}
static void cpu_scr(void)
{
while (C & BMASK) {
Z = (Z >> 1) | ((A & 1) << 11);
if (C & IMASK)
L = A & 1;
A = (A & 04000) | (A >> 1);
C--;
}
}
int cpu_skip(void)
{
int flag = 0;
switch (C & 057) {
case 000: case 001: case 002: case 003: case 004: case 005: case 006: case 007:
case 010: case 011: case 012: case 013: //SXL
flag = XL[C & BMASK];
break;
case 015: //KST
flag = kbd_struck();
break;
case 040: case 041: case 042: case 043: case 044: case 045: //SNS
flag = SSW & (1 << (C & 7));
break;
case 046: //PIN
flag = PINFF;
break;
case 050: //AZE
flag = (A == 0) || (A == WMASK);
break;
case 051: //APO
flag = (A & 04000) == 0;
break;
case 052: //LZE
flag = L == 0;
break;
case 053: //IBZ
flag = IBZ;
sim_debug(DBG_CPU, &cpu_dev, "IBZ%s => %d\n", C & IMASK ? " i" : "", flag);
break;
case 054: //OVF
flag = OVF;
break;
case 055: //ZZZ
flag = (Z & 1) == 0;
break;
default:
flag = 0;
break;
}
if (C & IMASK)
flag = !flag;
return flag;
}
static void cpu_opr(void)
{
switch (C & BMASK) {
case 000: case 001: case 002: case 003: case 004: case 005: case 006: case 007:
case 010: case 011: case 012: case 013: case 014:
if (C & IMASK)
; //Pause.
break;
case 015: //KBD
A = kbd_key(C & IMASK);
break;
case 016: //RSW
A = RSW;
break;
case 017: //LSW
A = LSW;
break;
}
}
static void cpu_lmb(void)
{
//Instruction field.
}
static void cpu_umb(void)
{
//Data field.
}
static void cpu_tape(void)
{
memaddr(P);
memrd();
pcinc(1);
tape_op();
}
static void cpu_lda(void)
{
memrd();
A = B;
}
static void cpu_sta(void)
{
membuf(A);
memwr();
}
static void cpu_ada(void)
{
memrd();
OVF = ~(A ^ B);
A += B;
A += A >> 12;
A &= WMASK;
OVF &= (A ^ B) & 04000;
}
static void cpu_adm(void)
{
cpu_ada();
membuf(A);
memmd();
}
static void cpu_lam(void)
{
memrd();
A += L;
L = A >> 12;
A &= WMASK;
A += B;
if (A & 010000)
L = 1;
A &= WMASK;
membuf(A);
memmd();
}
static int32 sign_extend(uint16 x)
{
if (x & 04000)
return -(x ^ WMASK);
else
return x;
}
static void cpu_mul(void)
{
int32 product;
memrd();
// C used for counting.
product = sign_extend(A) * sign_extend(B);
if (product == 0 && ((A ^ B) & HMASK))
product = 037777777;
else if (product < 0)
product--;
if (S & HMASK)
A = (product >> 11) & WMASK;
else if ((A ^ B) & HMASK)
A = 04000 | (product & 03777);
else
A = product & 03777;
L = A >> 11;
}
static void cpu_ldh(void)
{
memrd();
if ((S & HMASK) == 0)
B >>= 6;
A = B & RMASK;
}
static void cpu_sth(void)
{
memrd();
if (S & HMASK)
membuf((A & RMASK) | (B & LMASK));
else
membuf((A << 6) | (B & RMASK));
memmd();
}
static void cpu_shd(void)
{
memrd();
if ((S & HMASK) == 0)
B >>= 6;
pcinc((A & RMASK) != (B & RMASK));
}
static void cpu_sae(void)
{
memrd();
pcinc(A == B);
}
static void cpu_sro(void)
{
memrd();
pcinc((B & 1) == 0);
membuf((B >> 1) | (B << 11));
memmd();
}
static void cpu_bcl(void)
{
memrd();
A &= ~B;
}
static void cpu_bse(void)
{
memrd();
A |= B;
}
static void cpu_bco(void)
{
memrd();
A ^= B;
}
static void cpu_dsc(void)
{
int i, j;
uint16 data;
memrd();
data = B;
// C used for counting.
memaddr(1);
memrd();
sim_debug(DBG_CPU, &crt_dev, "DSC B=%04o A=%04o\n", B, A);
for (j = 0; j < 2; j++) {
membuf(B + 4);
A &= 07740;
for (i = 0; i < 6; i++) {
if (data & 1)
dpy_dis(B >> 11, B & DMASK, A & DMASK);
data >>= 1;
A += 4;
}
}
memmd();
}
static void cpu_add(void)
{
memaddr(X(C));
cpu_ada();
}
static void cpu_stc(void)
{
memaddr(X(C));
membuf(A);
A = 0;
memwr();
}
static void cpu_jmp(void)
{
uint16 tmp = P;
P = X(C);
if (P != 0) {
membuf(06000 | tmp);
memaddr(0);
memwr();
}
}
static void
cpu_insn(void)
{
/* Cycle 0, or I. */
memaddr(P);
memrd();
C = B;
/* Cycle 1, or X. */
if ((C & 07000) == 01000)
cpu_indexing();
if (history) {
history[history_i].P = P;
history[history_i].C = C;
history[history_i].S = S;
}
/* Cycle 2, or O. */
pcinc(1);
/* Cycle 3, or E. */
switch (C & 07740) {
case 00000:
cpu_misc();
break;
case 00040:
cpu_set();
break;
case 00100:
cpu_sam();
break;
case 00140:
cpu_dis();
break;
case 00200:
cpu_xsk();
break;
case 00240:
cpu_rol();
break;
case 00300:
cpu_ror();
break;
case 00340:
cpu_scr();
break;
case 00400:
case 00440:
pcinc(cpu_skip());
break;
case 00500:
case 00540:
cpu_opr();
break;
case 00600:
cpu_lmb();
break;
case 00640:
cpu_umb();
break;
case 00700:
case 00740:
cpu_tape();
break;
case 01000:
cpu_lda();
break;
case 01040:
cpu_sta();
break;
case 01100:
cpu_ada();
break;
case 01140:
cpu_adm();
break;
case 01200:
cpu_lam();
break;
case 01240:
cpu_mul();
break;
case 01300:
cpu_ldh();
break;
case 01340:
cpu_sth();
break;
case 01400:
cpu_shd();
break;
case 01440:
cpu_sae();
break;
case 01500:
cpu_sro();
break;
case 01540:
cpu_bcl();
break;
case 01600:
cpu_bse();
break;
case 01640:
cpu_bco();
break;
case 01740:
cpu_dsc();
break;
case 02000: case 02040: case 02100: case 02140: case 02200: case 02240: case 02300: case 02340:
case 02400: case 02440: case 02500: case 02540: case 02600: case 02640: case 02700: case 02740:
case 03000: case 03040: case 03100: case 03140: case 03200: case 03240: case 03300: case 03340:
case 03400: case 03440: case 03500: case 03540: case 03600: case 03640: case 03700: case 03740:
cpu_add();
break;
case 04000: case 04040: case 04100: case 04140: case 04200: case 04240: case 04300: case 04340:
case 04400: case 04440: case 04500: case 04540: case 04600: case 04640: case 04700: case 04740:
case 05000: case 05040: case 05100: case 05140: case 05200: case 05240: case 05300: case 05340:
case 05400: case 05440: case 05500: case 05540: case 05600: case 05640: case 05700: case 05740:
cpu_stc();
break;
case 06000: case 06040: case 06100: case 06140: case 06200: case 06240: case 06300: case 06340:
case 06400: case 06440: case 06500: case 06540: case 06600: case 06640: case 06700: case 06740:
case 07000: case 07040: case 07100: case 07140: case 07200: case 07240: case 07300: case 07340:
case 07400: case 07440: case 07500: case 07540: case 07600: case 07640: case 07700: case 07740:
cpu_jmp();
break;
}
if (history) {
history[history_i].B = B;
history[history_i].A = A;
history[history_i].L = L;
history_i = (history_i + 1) % history_m;
if (history_n < history_m)
history_n++;
}
/* Check for interrupts. */
if ((C & 06000) != 06000 && C != 00010 && interrupt && interrupt_enable) {
interrupt = interrupt_enable = 0;
memaddr(021);
memrd();
C = B;
cpu_jmp();
}
}
t_stat sim_instr(void)
{
t_stat stat;
if ((stat = build_dev_tab()) != SCPE_OK)
return stat;
stop_reason = 0;
paused = 0;
for (;;) {
AIO_CHECK_EVENT;
if (sim_interval <= 0) {
if ((stat = sim_process_event()) != SCPE_OK)
return stat;
}
if (sim_brk_summ && sim_brk_test(P, SWMASK('E')))
return STOP_IBKPT;
#if 0
if (paused)
sim_debug(DBG_CPU, &cpu_dev, "Paused\n");
#endif
if (paused)
sim_interval--;
else
cpu_insn();
if (sim_step != 0) {
if (--sim_step == 0)
return SCPE_STEP;
}
if (stop_reason)
return stop_reason;
}
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 (ea >= MEMSIZE)
return SCPE_NXM;
*vptr = M[ea];
return SCPE_OK;
}
static t_stat cpu_dep(t_value val, t_addr ea, UNIT *uptr, int32 sw)
{
if (ea >= MEMSIZE)
return SCPE_NXM;
M[ea] = val & WMASK;
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, "P___ C___ S___ B___ A___ L\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++) {
if (stop_cpu) { /* Control-C (SIGINT) */
stop_cpu = FALSE;
break; /* abandon remaining output */
}
fprintf (st, "%04o %04o %04o %04o %04o %d ",
history[j].P,
history[j].C,
history[j].S,
history[j].B,
history[j].A,
history[j].L);
insn = history[j].C;
fprint_sym (st, history[j].P, &insn, NULL, SWMASK ('M'));
fputc ('\n', st);
j = (j + 1) % history_m;
}
return SCPE_OK;
}
static t_bool pc_is_a_subroutine_call(t_addr **ret_addrs)
{
return FALSE;
}
static t_stat
cpu_reset(DEVICE *dptr)
{
sim_brk_types = SWMASK('E') | SWMASK('R') | SWMASK('W');
sim_brk_dflt = SWMASK('E');
sim_vm_is_subroutine_call = &pc_is_a_subroutine_call;
return SCPE_OK;
}

122
linc/linc_crt.c Normal file
View File

@@ -0,0 +1,122 @@
/* linc_crt.c: LINC CRT display
Copyright (c) 2025, 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 "linc_defs.h"
#include "sim_video.h"
#include "display/display.h"
/* Function declaration. */
static t_stat crt_svc (UNIT *uptr);
static t_stat crt_reset (DEVICE *dptr);
static int crt_quit = FALSE;
/* Debug */
#define DBG 0001
static UNIT crt_unit = {
UDATA (&crt_svc, UNIT_IDLE, 0)
};
static DEBTAB crt_deb[] = {
{ "DBG", DBG },
{ "VVID", SIM_VID_DBG_VIDEO },
{ 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, 12, 1, 8, 12,
NULL, NULL, &crt_reset,
NULL, NULL, NULL,
NULL, DEV_DISABLE | DEV_DEBUG | DEV_DISPLAY, 0, crt_deb,
NULL, NULL, NULL, NULL, NULL, NULL
};
static t_stat
crt_svc(UNIT *uptr)
{
#ifdef USE_DISPLAY
display_age (100, 0);
sim_activate_after (uptr, 100);
if (crt_quit) {
crt_quit = FALSE;
return SCPE_STOP;
}
#endif
return SCPE_OK;
}
static void crt_quit_callback (void)
{
crt_quit = TRUE;
}
static t_stat
crt_reset (DEVICE *dptr)
{
#ifdef USE_DISPLAY
if ((dptr->flags & DEV_DIS) != 0 || (sim_switches & SWMASK('P')) != 0) {
display_close (dptr);
sim_cancel (&crt_unit);
} else {
display_reset ();
display_init (DIS_LINC, 1, dptr);
vid_register_quit_callback (&crt_quit_callback);
sim_activate_abs (&crt_unit, 0);
}
#endif
return SCPE_OK;
}
void
crt_point (uint16 x, uint16 y)
{
sim_debug(DBG, &crt_dev, "Point %o,%o\n", x, y);
#ifdef USE_DISPLAY
if (crt_dev.flags & DEV_DIS)
return;
display_point(x, y, DISPLAY_INT_MAX, 0);
#endif
}
/* Hook called when CRT goes idle. */
void
crt_idle (void)
{
}
/* Display high voltage sync. */
void
crt_hvc (void)
{
}

58
linc/linc_defs.h Normal file
View File

@@ -0,0 +1,58 @@
/* linc_defs.h: LINC simulator definitions
Copyright (c) 2025, 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.
17-Sept-25 LB New simulator.
*/
#ifndef LINC_DEFS_H_
#define LINC_DEFS_H_ 0
#include "sim_defs.h"
#define STOP_HALT 1
#define STOP_IBKPT 2
#define STOP_RBKPT 3
#define STOP_WBKPT 3
#define MEMSIZE 2048
extern REG cpu_reg[];
extern uint16 M[];
extern DEVICE cpu_dev;
extern DEVICE crt_dev;
extern DEVICE dpy_dev;
extern DEVICE kbd_dev;
extern DEVICE tape_dev;
extern DEVICE tty_dev;
extern t_bool build_dev_tab(void);
extern void dpy_dis(uint16 h, uint16 x, uint16 y);
extern void crt_point (uint16 x, uint16 y);
extern uint16 kbd_key(uint16 wait);
extern int kbd_struck(void);
extern void tape_op(void);
#endif /* LINC_DEFS_H_ */

40
linc/linc_dpy.c Normal file
View File

@@ -0,0 +1,40 @@
#include "linc_defs.h"
/* Debug */
#define DBG 0001
static DEBTAB dpy_deb[] = {
{ "DBG", DBG },
{ NULL, 0 }
};
DEVICE dpy_dev = {
"DPY", NULL, NULL, NULL,
0, 8, 12, 1, 8, 12,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, DEV_DEBUG, 0, dpy_deb,
NULL, NULL, NULL, NULL, NULL, NULL
};
void dpy_dis(uint16 h, uint16 x, uint16 y)
{
sim_debug(DBG, &dpy_dev, "DIS %u;%03o, A=%03o\n", h, x, y);
/* Y coordinate +0 and -0 both refer to the same vertical position. */
if (y < 256)
y += 255;
else
y -= 256;
crt_point(x, y);
}
/* 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)
{
}

427
linc/linc_kbd.c Normal file
View File

@@ -0,0 +1,427 @@
#include "linc_defs.h"
#include "sim_video.h"
/* Debug */
#define DBG 0001
static t_stat kbd_svc(UNIT *uptr);
static t_stat kbd_reset(DEVICE *dptr);
static UNIT kbd_unit = {
UDATA(&kbd_svc, UNIT_IDLE, 0)
};
static DEBTAB kbd_deb[] = {
{ "DBG", DBG },
{ NULL, 0 }
};
DEVICE kbd_dev = {
"KBD", &kbd_unit, NULL, NULL,
1, 8, 12, 1, 8, 12,
NULL, NULL, &kbd_reset,
NULL, NULL, NULL, NULL, DEV_DEBUG, 0, kbd_deb,
NULL, NULL, NULL, NULL, NULL, NULL
};
/*
CASE 0 1 2 3 4 5 6 7 8 9 DEL
23 00 01 02 03 04 05 06 07 10 11 13
Q W E R T Y U I O P i=
44 52 30 45 47 54 50 34 42 43 15
A S D F G H J K L +. -,
24 46 27 31 32 33 35 36 37 10 17
#[ Z X C V B N M pu |⊟ META/EOL
22 55 53 26 51 25 41 40 16 21 12
SPACE
14
*/
static int kbd_pressed = 0;
static uint16 kbd_code;
void kbd_translate(int ch)
{
switch (ch) {
case '0':
kbd_code = 000;
break;
case '1':
kbd_code = 001;
break;
case '2':
kbd_code = 002;
break;
case '3':
kbd_code = 003;
break;
case '4':
kbd_code = 004;
break;
case '5':
kbd_code = 005;
break;
case '6':
kbd_code = 006;
break;
case '7':
kbd_code = 007;
break;
case '8':
kbd_code = 010;
break;
case '9':
kbd_code = 011;
break;
case '\r': case '\n':
kbd_code = 012;
break;
case '\b': case 0177:
kbd_code = 013;
break;
case ' ':
kbd_code = 014;
break;
case '=': case 'i':
kbd_code = 015;
break;
case 'p': case 'u':
kbd_code = 016;
break;
case ',': case '-':
kbd_code = 017;
break;
case '.': case '+':
kbd_code = 020;
break;
/*case '⊟':*/ case '|':
kbd_code = 021;
break;
case '[': case '#':
kbd_code = 022;
break;
/*case CASE:*/
kbd_code = 023;
break;
case 'A': case 'a':
kbd_code = 024;
break;
case 'B': case 'b':
kbd_code = 025;
break;
case 'C': case 'c':
kbd_code = 026;
break;
case 'D': case 'd':
kbd_code = 027;
break;
case 'E': case 'e':
kbd_code = 030;
break;
case 'F': case 'f':
kbd_code = 031;
break;
case 'G': case 'g':
kbd_code = 032;
break;
case 'H': case 'h':
kbd_code = 033;
break;
case 'I': /*case 'i':*/
kbd_code = 034;
break;
case 'J': case 'j':
kbd_code = 035;
break;
case 'K': case 'k':
kbd_code = 036;
break;
case 'L': case 'l':
kbd_code = 037;
break;
case 'M': case 'm':
kbd_code = 040;
break;
case 'N': case 'n':
kbd_code = 041;
break;
case 'O': case 'o':
kbd_code = 042;
break;
case 'P': /*case 'p':*/
kbd_code = 043;
break;
case 'Q': case 'q':
kbd_code = 044;
break;
case 'R': case 'r':
kbd_code = 045;
break;
case 'S': case 's':
kbd_code = 046;
break;
case 'T': case 't':
kbd_code = 047;
break;
case 'U': /*case 'u':*/
kbd_code = 050;
break;
case 'V': case 'v':
kbd_code = 051;
break;
case 'W': case 'w':
kbd_code = 052;
break;
case 'X': case 'x':
kbd_code = 053;
break;
case 'Y': case 'y':
kbd_code = 054;
break;
case 'Z': case 'z':
kbd_code = 055;
break;
default:
return;
}
sim_debug(DBG, &kbd_dev, "Key struck %c -> %02o\n", ch, kbd_code);
kbd_pressed = 1;
}
static t_stat kbd_svc(UNIT *uptr)
{
t_stat ch = sim_poll_kbd();
if ((ch & SCPE_KFLAG) == 0) {
sim_activate_after(&kbd_unit, 10000);
return ch;
}
if (ch & SCPE_BREAK)
;
else
kbd_translate(ch & 0177);
return SCPE_OK;
}
int kbd_struck(void)
{
if (kbd_pressed)
sim_debug(DBG, &kbd_dev, "KST\n");
return kbd_pressed;
}
uint16 kbd_key(uint16 wait)
{
if (kbd_pressed) {
sim_debug(DBG, &kbd_dev, "KEY %02o\n", kbd_code);
sim_activate_abs(&kbd_unit, 1);
kbd_pressed = 0;
return kbd_code;
} else {
sim_debug(DBG, &kbd_dev, "KEY paused\n");
}
return 0;
}
static void kbd_convert(uint32 key)
{
switch (key) {
case SIM_KEY_0: /* 0 Q */
case SIM_KEY_BACKQUOTE:
kbd_code = 000;
break;
case SIM_KEY_1: /* 1 R */
kbd_code = 001;
break;
case SIM_KEY_2: /* 2 S */
kbd_code = 002;
break;
case SIM_KEY_3: /* 3 T */
kbd_code = 003;
break;
case SIM_KEY_4: /* 4 U */
kbd_code = 004;
break;
case SIM_KEY_5: /* 5 V */
kbd_code = 005;
break;
case SIM_KEY_6: /* 6 W */
kbd_code = 006;
break;
case SIM_KEY_7: /* 7 X */
kbd_code = 007;
break;
case SIM_KEY_8: /* 8 Y */
kbd_code = 010;
break;
case SIM_KEY_9: /* 9 Z */
kbd_code = 011;
break;
case SIM_KEY_ENTER:
kbd_code = 012;
break;
case SIM_KEY_BACKSPACE:
case SIM_KEY_DELETE:
kbd_code = 013;
break;
case SIM_KEY_SPACE: /* Space ? */
case SIM_KEY_SLASH:
kbd_code = 014;
break;
case SIM_KEY_EQUALS: /* i = */
kbd_code = 015;
break;
case SIM_KEY_F1: /* p u */
kbd_code = 016;
break;
case SIM_KEY_MINUS: /* - , */
case SIM_KEY_COMMA:
kbd_code = 017;
break;
case SIM_KEY_PERIOD: /* + . */
kbd_code = 020;
break;
case SIM_KEY_BACKSLASH: /* | ⊟ */
kbd_code = 021;
break;
case SIM_KEY_LEFT_BRACKET: /* # [ */
case SIM_KEY_LEFT_BACKSLASH:
kbd_code = 022;
break;
case SIM_KEY_SHIFT_L: /* CASE _ */
case SIM_KEY_SHIFT_R:
kbd_code = 023;
break;
case SIM_KEY_A: /* A " */
case SIM_KEY_SINGLE_QUOTE:
kbd_code = 024;
break;
case SIM_KEY_B: /* B „ */
kbd_code = 025;
break;
case SIM_KEY_C: /* C < */
kbd_code = 026;
break;
case SIM_KEY_D: /* D > */
kbd_code = 027;
break;
case SIM_KEY_E: /* E ] */
case SIM_KEY_RIGHT_BRACKET:
kbd_code = 030;
break;
case SIM_KEY_F: /* F ˣ */
kbd_code = 031;
break;
case SIM_KEY_G: /* G : */
case SIM_KEY_SEMICOLON:
kbd_code = 032;
break;
case SIM_KEY_H: /* H */
kbd_code = 033;
break;
case SIM_KEY_I: /* I */
kbd_code = 034;
break;
case SIM_KEY_J: /* J */
kbd_code = 035;
break;
case SIM_KEY_K: /* K */
kbd_code = 036;
break;
case SIM_KEY_L: /* L */
kbd_code = 037;
break;
case SIM_KEY_M: /* M */
kbd_code = 040;
break;
case SIM_KEY_N: /* N */
kbd_code = 041;
break;
case SIM_KEY_O: /* O */
kbd_code = 042;
break;
case SIM_KEY_P: /* P */
kbd_code = 043;
break;
case SIM_KEY_Q: /* Q */
kbd_code = 044;
break;
case SIM_KEY_R: /* R */
kbd_code = 045;
break;
case SIM_KEY_S: /* S */
kbd_code = 046;
break;
case SIM_KEY_T: /* T */
kbd_code = 047;
break;
case SIM_KEY_U: /* U */
kbd_code = 050;
break;
case SIM_KEY_V: /* V */
kbd_code = 051;
break;
case SIM_KEY_W: /* W */
kbd_code = 052;
break;
case SIM_KEY_X: /* X */
kbd_code = 053;
break;
case SIM_KEY_Y: /* Y */
kbd_code = 054;
break;
case SIM_KEY_Z: /* Z */
kbd_code = 055;
break;
case SIM_KEY_ALT_L: /* META? */
case SIM_KEY_ALT_R:
kbd_code = 056;
break;
// → 57
// ? 60
// = 61
// u 62
// , 63
// . 64
// ⊟ 65
// [ 66
// _ 67
// " 70
// „ 71
// < 72
// > 73
// ] 74
// ˣ 75
// : 76
// ʸ 77
default:
return;
}
sim_debug(DBG, &kbd_dev, "Key struck %s -> %02o\n",
vid_key_name(key), kbd_code);
kbd_pressed = 1;
}
static int
kbd_event(SIM_KEY_EVENT *ev)
{
if (ev->state == SIM_KEYPRESS_DOWN)
kbd_convert(ev->key);
return 0;
}
static t_stat
kbd_reset(DEVICE *dptr)
{
#ifdef USE_DISPLAY
vid_display_kb_event_process = kbd_event;
#endif
sim_activate_abs(&kbd_unit, 0);
return SCPE_OK;
}

593
linc/linc_sys.c Normal file
View File

@@ -0,0 +1,593 @@
/* linc_sys.c: LINC simulator interface
Copyright (c) 2025, 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.
17-Sept-25 LB New simulator.
*/
#include "linc_defs.h"
int32 sim_emax = 1;
char sim_name[] = "LINC";
uint16 M[MEMSIZE];
REG *sim_PC = &cpu_reg[0];
DEVICE *sim_devices[] = {
&cpu_dev,
&crt_dev,
&dpy_dev,
&kbd_dev,
&tape_dev,
&tty_dev,
NULL
};
const char *sim_stop_messages[SCPE_BASE] = {
"Unknown error",
"HALT instruction",
"Breakpoint",
"Read Breakpoint",
"Write Breakpoint"
};
static t_stat
get_binary_word(FILE *fileref, uint16 *x)
{
uint16 y;
int c = Fgetc(fileref);
if (c == EOF)
return SCPE_EOF;
y = c & 0xFF;
c = Fgetc(fileref);
if (c == EOF)
return SCPE_IOERR;
if (c & 0xF0)
return SCPE_FMT;
*x = y | (c << 8);
return SCPE_OK;
}
static t_stat
get_octal_word(FILE *fileref, uint16 *x)
{
uint16 y, i;
int c;
for (i = 0;;) {
c = Fgetc(fileref);
if (c == EOF)
return SCPE_EOF;
if (c >= '0' && c <= '9') {
y = c - '0';
i++;
break;
}
}
for (; i < 4;) {
c = Fgetc(fileref);
if (c == EOF)
return SCPE_IOERR;
if (c < '0' || c > '9')
break;
y <<= 3;
y |= c - '0';
i++;
}
*x = y;
return SCPE_OK;
}
t_stat
sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag)
{
t_stat (*get_word)(FILE *fileref, uint16 *x) = get_binary_word;
t_addr addr, length = MEMSIZE, start = 0, end;
long offset = 0;
t_stat stat;
if (sim_switches & SWMASK('O'))
get_word = get_octal_word;
while (cptr != NULL && *cptr != 0) {
if (strncasecmp(cptr, "start=", 6) == 0)
start = (t_addr)get_uint(cptr + 6, 8, ~0, &stat);
else if (strncasecmp(cptr, "offset=", 7) == 0)
offset = 2 * (long)get_uint(cptr + 7, 8, ~0, &stat);
else if (strncasecmp(cptr, "block=", 6) == 0)
offset = 512 * (long)get_uint(cptr + 6, 8, ~0, &stat);
else if (strncasecmp(cptr, "length=", 7) == 0)
length = (t_addr)get_uint(cptr + 7, 8, ~0, &stat);
else
return SCPE_ARG;
cptr = strchr(cptr, ' ');
if (cptr == NULL)
break;
while (*cptr == ' ')
cptr++;
}
end = start + length;
if (end > MEMSIZE)
end = MEMSIZE;
sim_fseek(fileref, offset, SEEK_SET);
for (addr = start; addr < end; addr++) {
uint16 x;
t_stat stat = get_word(fileref, &x);
if (stat == SCPE_EOF)
return SCPE_OK;
if (stat != SCPE_OK)
return stat;
M[addr] = x;
}
return SCPE_OK;
}
t_bool build_dev_tab(void)
{
DEVICE *dev;
int i;
for (i = 0; (dev = sim_devices[i]) != NULL; i++) {
;
}
return SCPE_OK;
}
static t_stat fprint_next(FILE *of, uint16 addr)
{
fprintf(of, "\n");
fprint_val(of, ++addr & 01777, 8, 10, PV_LEFT);
fprintf(of, ":\t%04o", M[addr]);
return -1;
}
static void fprint_misc(FILE *of, uint16 insn)
{
switch (insn) {
case 00000:
fprintf(of, "HLT");
break;
case 00005:
fprintf(of, "ZTA");
break;
case 00010:
fprintf(of, "ENI");
break;
case 00011:
fprintf(of, "CLR");
break;
case 00013:
fprintf(of, "%04o", insn);
break;
case 00014:
fprintf(of, "ATR");
break;
case 00015:
fprintf(of, "RTA");
break;
case 00016:
fprintf(of, "NOP");
break;
case 00017:
fprintf(of, "COM");
break;
default:
fprintf(of, "%04o", insn);
break;
}
}
static t_stat fprint_index(FILE *of, uint16 insn, uint16 addr)
{
if (insn & 020)
fprintf(of, " i");
if (insn & 017)
fprintf(of, " %o", insn & 017);
else
return fprint_next(of, addr);
return SCPE_OK;
}
static void fprint_set(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "SET");
fprint_index(of, insn, addr);
fprint_next(of, addr);
}
static void fprint_sam(FILE *of, uint16 insn)
{
fprintf(of, "SAM ");
}
static t_stat fprint_dis(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "DIS");
return fprint_index(of, insn, addr);
}
static t_stat fprint_xsk(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "XSK");
return fprint_index(of, insn, addr);
}
static t_stat fprint_rol(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "ROL");
return fprint_index(of, insn, addr);
}
static t_stat fprint_ror(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "ROR");
return fprint_index(of, insn, addr);
}
static t_stat fprint_scr(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "SCR");
return fprint_index(of, insn, addr);
}
static void fprint_skip(FILE *of, uint16 insn)
{
char beta[3];
switch (insn & 057) {
case 000: case 001: case 002: case 003: case 004: case 005: case 006: case 007:
case 010: case 011: case 012: case 013:
fprintf(of, "SXL");
snprintf(beta, sizeof beta, "%o", insn & 017);
break;
case 015:
fprintf(of, "KST");
break;
case 040: case 041: case 042: case 043: case 044: case 045:
fprintf(of, "SNS ");
snprintf(beta, sizeof beta, "%o", insn & 7);
break;
case 046:
fprintf(of, "PIN");
break;
case 050:
fprintf(of, "AZE");
break;
case 051:
fprintf(of, "APO");
break;
case 052:
fprintf(of, "LZE");
break;
case 053:
fprintf(of, "IBZ");
break;
case 054:
fprintf(of, "OVF");
break;
case 055:
fprintf(of, "ZZZ");
break;
default:
fprintf(of, "%04o", insn);
return;
}
if (insn & 020)
fprintf(of, " i" );
fprintf(of, " %s", beta);
}
static void fprint_opr(FILE *of, uint16 insn)
{
switch (insn & 07757) {
case 0500: case 0501: case 0502: case 0503: case 0504: case 0505: case 0506: case 0507:
case 0510: case 0511: case 0512: case 0513:
break;
case 0535:
case 0515:
fprintf(of, "KBD ");
break;
case 0516:
fprintf(of, "RSW ");
break;
case 0517:
fprintf(of, "LSW ");
break;
default:
fprintf(of, "%04o", insn);
break;
}
if (insn & 020)
fprintf(of, "i" );
}
static void fprint_lmb(FILE *of, uint16 insn)
{
fprintf(of, "LMB ");
}
static void fprint_umb(FILE *of, uint16 insn)
{
fprintf(of, "UMB ");
}
static void fprint_tape(FILE *of, uint16 insn, uint16 addr)
{
switch (insn & 0707) {
case 0700:
fprintf(of, "RDC");
break;
case 0701:
fprintf(of, "RCG");
break;
case 0702:
fprintf(of, "RDE");
break;
case 0703:
fprintf(of, "MTB");
break;
case 0704:
fprintf(of, "WRC");
break;
case 0705:
fprintf(of, "WCG");
break;
case 0706:
fprintf(of, "WRI");
break;
case 0707:
fprintf(of, "CHK");
break;
}
if (insn & 020)
fprintf(of, " i");
if (insn & 010)
fprintf(of, " u");
fprint_next(of, addr);
}
static t_stat fprint_lda(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "LDA");
return fprint_index(of, insn, addr);
}
static t_stat fprint_sta(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "STA");
return fprint_index(of, insn, addr);
}
static t_stat fprint_ada(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "ADA");
return fprint_index(of, insn, addr);
}
static t_stat fprint_adm(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "ADM");
return fprint_index(of, insn, addr);
}
static t_stat fprint_lam(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "LAM");
return fprint_index(of, insn, addr);
}
static t_stat fprint_mul(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "MUL");
return fprint_index(of, insn, addr);
}
static t_stat fprint_ldh(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "LDH");
return fprint_index(of, insn, addr);
}
static t_stat fprint_sth(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "STH");
return fprint_index(of, insn, addr);
}
static t_stat fprint_shd(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "SHD");
return fprint_index(of, insn, addr);
}
static t_stat fprint_sae(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "SAE");
return fprint_index(of, insn, addr);
}
static t_stat fprint_sro(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "SRO");
return fprint_index(of, insn, addr);
}
static t_stat fprint_bcl(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "BCL");
return fprint_index(of, insn, addr);
}
static t_stat fprint_bse(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "BSE");
return fprint_index(of, insn, addr);
}
static t_stat fprint_bco(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "BCO");
return fprint_index(of, insn, addr);
}
static t_stat fprint_dsc(FILE *of, uint16 insn, uint16 addr)
{
fprintf(of, "DSC");
return fprint_index(of, insn, addr);
}
static void fprint_add(FILE *of, uint16 insn)
{
fprintf(of, "ADD %04o", insn & 01777);
}
static void fprint_stc(FILE *of, uint16 insn)
{
fprintf(of, "STC %04o", insn & 01777);
}
static void fprint_jmp(FILE *of, uint16 insn)
{
fprintf(of, "JMP %04o", insn & 01777);
}
t_stat fprint_sym(FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw)
{
t_stat stat;
if ((sw & SWMASK ('M')) == 0)
return SCPE_ARG;
if ((stat = build_dev_tab()) != SCPE_OK)
return stat;
switch (*val & 07740) {
case 00000:
fprint_misc(of, *val);
break;
case 00040:
fprint_set(of, *val, addr);
return -1;
case 00100:
fprint_sam(of, *val);
break;
case 00140:
fprint_dis(of, *val, addr);
break;
case 00200:
fprint_xsk(of, *val, addr);
break;
case 00240:
fprint_rol(of, *val, addr);
break;
case 00300:
fprint_ror(of, *val, addr);
break;
case 00340:
fprint_scr(of, *val, addr);
break;
case 00400:
case 00440:
fprint_skip(of, *val);
break;
case 00500:
case 00540:
fprint_opr(of, *val);
break;
case 00600:
fprint_lmb(of, *val);
break;
case 00640:
fprint_umb(of, *val);
break;
case 00700:
case 00740:
fprint_tape(of, *val, addr);
break;
case 01000:
return fprint_lda(of, *val, addr);
case 01040:
return fprint_sta(of, *val, addr);
case 01100:
return fprint_ada(of, *val, addr);
case 01140:
return fprint_adm(of, *val, addr);
case 01200:
return fprint_lam(of, *val, addr);
case 01240:
return fprint_mul(of, *val, addr);
case 01300:
return fprint_ldh(of, *val, addr);
case 01340:
return fprint_sth(of, *val, addr);
case 01400:
return fprint_shd(of, *val, addr);
case 01440:
return fprint_sae(of, *val, addr);
case 01500:
return fprint_sro(of, *val, addr);
case 01540:
return fprint_bcl(of, *val, addr);
case 01600:
return fprint_bse(of, *val, addr);
case 01640:
return fprint_bco(of, *val, addr);
case 01740:
return fprint_dsc(of, *val, addr);
case 02000: case 02040: case 02100: case 02140: case 02200: case 02240: case 02300: case 02340:
case 02400: case 02440: case 02500: case 02540: case 02600: case 02640: case 02700: case 02740:
case 03000: case 03040: case 03100: case 03140: case 03200: case 03240: case 03300: case 03340:
case 03400: case 03440: case 03500: case 03540: case 03600: case 03640: case 03700: case 03740:
fprint_add(of, *val);
break;
case 04000: case 04040: case 04100: case 04140: case 04200: case 04240: case 04300: case 04340:
case 04400: case 04440: case 04500: case 04540: case 04600: case 04640: case 04700: case 04740:
case 05000: case 05040: case 05100: case 05140: case 05200: case 05240: case 05300: case 05340:
case 05400: case 05440: case 05500: case 05540: case 05600: case 05640: case 05700: case 05740:
fprint_stc(of, *val);
break;
case 06000: case 06040: case 06100: case 06140: case 06200: case 06240: case 06300: case 06340:
case 06400: case 06440: case 06500: case 06540: case 06600: case 06640: case 06700: case 06740:
case 07000: case 07040: case 07100: case 07140: case 07200: case 07240: case 07300: case 07340:
case 07400: case 07440: case 07500: case 07540: case 07600: case 07640: case 07700: case 07740:
fprint_jmp(of, *val);
break;
}
return SCPE_OK;
}
t_stat parse_sym(CONST char *cptr, t_addr addr, UNIT *uptr,
t_value *val, int32 sw)
{
t_stat stat;
*val = get_uint(cptr, 8, ~0, &stat);
if (stat != SCPE_OK)
return stat;
return 0;
}

439
linc/linc_tape.c Normal file
View File

@@ -0,0 +1,439 @@
#include "linc_defs.h"
#define POS u3
#define SPEED u4
#define ACC u5
#define OFFSET u6
#define C (*(uint16 *)cpu_reg[1].loc)
#define A (*(uint16 *)cpu_reg[2].loc)
#define S (*(uint16 *)cpu_reg[6].loc)
#define B (*(uint16 *)cpu_reg[7].loc)
#define paused (*(int *)cpu_reg[11].loc)
#define IBZ (*(int *)cpu_reg[12].loc)
#define ACC_START 3
#define ACC_REVERSE 6
#define ACC_STOP 1
#define MAX_SPEED (ACC_START * 625) /* 0.1s / 160µs */
#define IBZ_WORDS 5
#define DATA_WORDS 256
#define OTHER_WORDS 7
#define BLOCK_WORDS (IBZ_WORDS + DATA_WORDS + OTHER_WORDS)
#define START_POS (ACC_START * (625 + (625 * 625))/2)
#define MAX_BLOCKS 512
#define MAX_POS ((BLOCK_WORDS * MAX_BLOCKS + IBZ_WORDS) * MAX_SPEED)
#define RDC 0 /* read tape and check */
#define RCG 1 /* read tape group */
#define RDE 2 /* read tape */
#define MTB 3 /* move toward block */
#define WRC 4 /* write tape and check */
#define WCG 5 /* write tape group */
#define WRI 6 /* write tape */
#define CHK 7 /* check tape */
#define DBG 0001
#define DBG_SEEK 0002
#define DBG_READ 0004
#define DBG_WRITE 0010
#define DBG_POS 0020
static uint16 GROUP;
static int16 CURRENT_BLOCK;
static int16 WANTED_BLOCK;
static t_stat tape_svc(UNIT *uptr);
static t_stat tape_reset(DEVICE *dptr);
static t_stat tape_boot(int32 u, DEVICE *dptr);
static t_stat tape_attach(UNIT *uptr, CONST char *cptr);
static t_stat tape_detach(UNIT *uptr);
#define UNIT_FLAGS (UNIT_IDLE|UNIT_FIX|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE)
#define CAPACITY (MAX_BLOCKS * DATA_WORDS)
static UNIT tape_unit[] = {
{ UDATA(&tape_svc, UNIT_FLAGS, CAPACITY) },
{ UDATA(&tape_svc, UNIT_FLAGS, CAPACITY) },
{ UDATA(&tape_svc, UNIT_DIS, 0) },
{ UDATA(&tape_svc, UNIT_DIS, 0) },
{ UDATA(&tape_svc, UNIT_FLAGS, CAPACITY) },
{ UDATA(&tape_svc, UNIT_FLAGS, CAPACITY) }
};
static DEBTAB tape_deb[] = {
{ "DBG", DBG },
{ "SEEK", DBG_SEEK },
{ "READ", DBG_READ },
{ "WRITE", DBG_WRITE },
{ "POSITION", DBG_POS },
{ NULL, 0 }
};
DEVICE tape_dev = {
"TAPE", tape_unit, NULL, NULL,
6, 8, 12, 1, 8, 12,
NULL, NULL, &tape_reset,
&tape_boot, &tape_attach, &tape_detach,
NULL, DEV_DEBUG, 0, tape_deb,
NULL, NULL, NULL, NULL, NULL, NULL
};
void tape_op(void)
{
uint16 u = (C & 050) >> 3;
UNIT *uptr = &tape_unit[u];
if ((uptr->flags & UNIT_ATT) == 0)
return;
if (uptr->SPEED < 0) {
if ((C & 7) != MTB) {
sim_debug(DBG_SEEK, &tape_dev, "Reverse to forward\n");
uptr->ACC = ACC_REVERSE;
}
} else if (uptr->POS >= MAX_POS) {
sim_debug(DBG_SEEK, &tape_dev, "End zone; reverse\n");
uptr->ACC = ACC_REVERSE;
} else if (uptr->SPEED < MAX_SPEED || uptr->ACC < 0) {
sim_debug(DBG_SEEK, &tape_dev, "Speed up\n");
uptr->ACC = ACC_START;
}
if (!sim_is_active(uptr))
sim_activate_after(uptr, 1);
paused = 1;
A = 0;
WANTED_BLOCK = B & 0777;
switch (C & 7) {
case RDC: case RDE: case WRC: case WRI: case CHK:
S = 256 * (B >> 9);
GROUP = 0;
sim_debug(DBG, &tape_dev, "Single tranfer: S=%04o, BN=%03o\n",
S, WANTED_BLOCK);
break;
case RCG: case WCG:
S = 256 * (B & 7);
GROUP = B >> 9;
sim_debug(DBG, &tape_dev, "Group transfer: S=%04o, BN=%03o/%o\n",
S, WANTED_BLOCK, GROUP+1);
break;
case MTB:
sim_debug(DBG, &tape_dev, "Move towards block %03o\n", WANTED_BLOCK);
break;
}
}
static t_stat tape_seek(UNIT *uptr, t_addr block, t_addr offset)
{
offset = DATA_WORDS * block + offset;
offset *= 2;
if (sim_fseek(uptr->fileref, offset, SEEK_SET) == -1)
return SCPE_IOERR;
return SCPE_OK;
}
static uint16 read_word(UNIT *uptr, t_addr block, t_addr offset)
{
t_stat stat;
uint8 data[2];
uint16 word;
stat = tape_seek(uptr, block, offset);
if (stat != SCPE_OK)
;
if (sim_fread(data, 1, 2, uptr->fileref) != 2)
;
if (data[1] & 0xF0)
;
word = data[1];
word <<= 8;
word |= data[0];
return word;
}
static void write_word(UNIT *uptr, t_addr block, t_addr offset, uint16 word)
{
t_stat stat;
uint8 data[2];
stat = tape_seek(uptr, block, offset);
if (stat != SCPE_OK)
;
data[0] = word & 0xFF;
data[1] = word >> 8;
if (sim_fwrite(data, 1, 2, uptr->fileref) != 2)
;
}
/*
IBZ BN G block CS C C G BN IBZ
5 1 1 256 1 1 1 1 1 5
---------------------
263
--------------------------
268
start - 100 ms
stop - 300 ms
reverse - 100 ms
BN to BN at 60 ips - 43 ms
block length = 43 ms * 60 inch/s = 2.58 inch
per word - 160 µs
word length = 0.0096 inch
words per inch = 104
words per second = 6250
end zone to end zone - 23 s
tape length = 23 * 60 = 1380 inch = 115 feet
end zone length = 5 feet
*/
static void tape_done(UNIT *uptr)
{
sim_debug(DBG, &tape_dev, "Done with block\n");
switch (C & 7) {
case RDC: case RCG: case RDE: case CHK:
A = 07777;
break;
case WRI:
A = A ^ 07777;
A++;
A &= 07777;
break;
case MTB:
A = (WANTED_BLOCK + ~CURRENT_BLOCK);
A += A >> 12;
A &= 07777;
break;
}
switch (C & 7) {
case RDC:
if (A != 07777) {
sim_debug(DBG, &tape_dev, "Check failed; read again\n");
S &= ~0377;
} else {
sim_debug(DBG, &tape_dev, "Check passed\n");
paused = 0;
}
break;
case WRC:
sim_debug(DBG, &tape_dev, "Block written, go back and check\n");
// For now, done.
A = 07777;
paused = 0;
break;
case RCG: case WCG:
if (GROUP == 0) {
sim_debug(DBG, &tape_dev, "Done with group\n");
paused = 0;
} else {
sim_debug(DBG, &tape_dev, "Blocks left in group: %d\n", GROUP);
GROUP--;
}
WANTED_BLOCK = (WANTED_BLOCK + 1) & 0777;
break;
case RDE: case WRI:
sim_debug(DBG, &tape_dev, "Transfer done\n");
paused = 0;
break;
case MTB:
sim_debug(DBG, &tape_dev, "Move towards block done, result %04o\n", A);
paused = 0;
break;
case CHK:
sim_debug(DBG, &tape_dev, "Check done\n");
paused = 0;
break;
}
if (paused)
;
else if ((C & 020) == 0) {
sim_debug(DBG_SEEK, &tape_dev, "Instruction done, stop tape\n");
uptr->ACC = uptr->SPEED > 0 ? -ACC_STOP : ACC_STOP;
} else {
sim_debug(DBG_SEEK, &tape_dev, "Instruction done, keep moving\n");
}
}
static void tape_word(UNIT *uptr, uint16 block, uint16 offset)
{
switch (C & 7) {
case RDC: case RCG: case RDE: case CHK:
B = read_word(uptr, block, offset);
sim_debug(DBG_READ, &tape_dev,
"Read block %03o offset %03o data %04o address %04o\n",
block, offset, B, S);
if ((C & 7) != CHK)
M[S] = B;
break;
case WRC: case WCG: case WRI:
B = M[S];
sim_debug(DBG_WRITE, &tape_dev,
"Write block %03o offset %03o data %04o address %04o\n",
block, offset, B, S);
write_word(uptr, block, offset, B);
break;
}
S = (S+1) & 03777;
A += B;
A &= 07777;
}
static t_stat tape_svc(UNIT *uptr)
{
long pos, block, offset;
uptr->SPEED += uptr->ACC;
if (uptr->SPEED >= MAX_SPEED) {
uptr->SPEED = MAX_SPEED;
uptr->ACC = 0;
}
else if (uptr->SPEED <= -MAX_SPEED) {
uptr->SPEED = -MAX_SPEED;
uptr->ACC = 0;
} else if (uptr->SPEED == 0 && (uptr->ACC == ACC_STOP || uptr->ACC == -ACC_STOP))
uptr->ACC = 0;
uptr->POS += uptr->SPEED;
sim_debug(DBG_POS, &tape_dev, "Speed %d, position %d (block %03o)\n",
uptr->SPEED, uptr->POS, uptr->POS / MAX_SPEED / BLOCK_WORDS);
if (uptr->POS < 0 && uptr->ACC <= 0) {
sim_debug(DBG_SEEK, &tape_dev, "End zone; stop tape\n");
uptr->ACC = ACC_STOP;
} else if(uptr->POS >= MAX_POS && uptr->ACC >= 0) {
sim_debug(DBG_SEEK, &tape_dev, "End zone; stop tape\n");
uptr->ACC = -ACC_STOP;
}
if (uptr->SPEED != 0)
/* The tape takes 160 microseconds between words. This is
approximately 20 memory cycles, 8 microseconds each. */
sim_activate(uptr, 20);
pos = uptr->POS / MAX_SPEED;
if (pos < 0)
return SCPE_OK;
block = pos / BLOCK_WORDS;
offset = pos % BLOCK_WORDS;
if (block >= MAX_BLOCKS)
return SCPE_OK;
IBZ = offset < IBZ_WORDS;
if (IBZ)
sim_debug(DBG, &tape_dev, "Interblock zone\n");
if (uptr->SPEED > -MAX_SPEED && uptr->SPEED < MAX_SPEED)
return SCPE_OK;
if (!paused)
return SCPE_OK;
if (uptr->SPEED > 0) {
if (offset == 5) {
/* Forward block number. */
CURRENT_BLOCK = (uint16)(block + uptr->OFFSET);
sim_debug(DBG_SEEK, &tape_dev,
"Found block number %03o; looking for %03o\n",
CURRENT_BLOCK, WANTED_BLOCK);
if (CURRENT_BLOCK > WANTED_BLOCK) {
sim_debug(DBG_SEEK, &tape_dev, "Reverse to find lower block numbers\n");
uptr->ACC = -ACC_REVERSE;
}
if ((C & 7) == MTB)
tape_done(uptr);
/* Word 6 is a guard. */
} else if (offset >= 7 && offset < 263) {
if (CURRENT_BLOCK == WANTED_BLOCK)
tape_word(uptr, (uint16)block, (uint16)(offset - 7));
}
else if (offset == 263 && CURRENT_BLOCK == WANTED_BLOCK)
/* Checksum here. */
tape_done(uptr);
}
/* Word 264-265 are "C". */
/* Word 266 is a guard. */
else if (offset == 267 && uptr->SPEED < 0) {
/* Reverse block number. */
CURRENT_BLOCK = (uint16)(block + uptr->OFFSET);
sim_debug(DBG_SEEK, &tape_dev,
"Found reverse block number %03o; looking for %03o\n",
CURRENT_BLOCK, WANTED_BLOCK);
if (CURRENT_BLOCK <= WANTED_BLOCK) {
sim_debug(DBG_SEEK, &tape_dev, "Reverse to find higher block numbers\n");
uptr->ACC = ACC_REVERSE;
uptr->POS -= MAX_SPEED * BLOCK_WORDS;
}
if ((C & 7) == MTB)
tape_done(uptr);
}
return SCPE_OK;
}
static t_stat tape_reset(DEVICE *dptr)
{
return SCPE_OK;
}
static t_stat tape_boot(int32 unit_num, DEVICE *dptr)
{
if (unit_num >= 2 && unit_num <= 3)
return SCPE_ARG;
//LSW = 0701 + (unit_num << 3);
//RSW = (7 << 9) | 0300;
//P = 020;
return SCPE_OK;
}
static t_stat tape_attach(UNIT *uptr, CONST char *cptr)
{
t_stat stat;
t_offset size;
if (uptr - tape_unit >= 2 && uptr - tape_unit <= 3)
return SCPE_ARG;
stat = attach_unit(uptr, cptr);
if (stat != SCPE_OK)
return stat;
size = sim_fsize(uptr->fileref);
if (size == MAX_BLOCKS * DATA_WORDS * 2)
uptr->OFFSET = 0; /* Plain image. */
else if ((size % (2 * DATA_WORDS)) == 6) {
/* Extended image with additional meta data. */
uint16 metadata = (uint16)(size / (2 * DATA_WORDS));
uint16 block_size = read_word(uptr, metadata, 0);
int16 forward_offset = (int16)read_word(uptr, metadata, 1);
int16 reverse_offset = (int16)read_word(uptr, metadata, 2);
sim_debug(DBG, &tape_dev,
"Extended image with block size %o, block offset %d/%d\r\n",
block_size, forward_offset, reverse_offset);
if (block_size != DATA_WORDS)
return SCPE_FMT;
if (forward_offset != reverse_offset)
return SCPE_FMT;
uptr->OFFSET = forward_offset;
} else
return SCPE_FMT;
uptr->POS = -2 * START_POS;
uptr->SPEED = 0;
return SCPE_OK;
}
static t_stat tape_detach(UNIT *uptr)
{
if (uptr - tape_unit >= 2 && uptr - tape_unit <= 3)
return SCPE_ARG;
if ((uptr->flags & UNIT_ATT) == 0)
return SCPE_OK;
if (sim_is_active(uptr))
sim_cancel(uptr);
return detach_unit(uptr);
}

6
linc/linc_tty.c Normal file
View File

@@ -0,0 +1,6 @@
#include "linc_defs.h"
DEVICE tty_dev = {
"TTY", NULL, NULL, NULL,
0, 8, 12, 1, 8, 12,
};

View File

@@ -197,6 +197,10 @@ ifneq (3,${SIM_MAJOR})
ifneq (,$(findstring imlac,${MAKECMDGOALS}))
VIDEO_USEFUL = true
endif
# building the LINC needs video support
ifneq (,$(findstring linc,${MAKECMDGOALS}))
VIDEO_USEFUL = true
endif
# building the TT2500 needs video support
ifneq (,$(findstring tt2500,${MAKECMDGOALS}))
VIDEO_USEFUL = true
@@ -2013,7 +2017,15 @@ IMLAC = ${IMLACD}/imlac_sys.c ${IMLACD}/imlac_cpu.c \
${IMLACD}/imlac_dp.c ${IMLACD}/imlac_crt.c ${IMLACD}/imlac_kbd.c \
${IMLACD}/imlac_tty.c ${IMLACD}/imlac_pt.c ${IMLACD}/imlac_bel.c \
${DISPLAYL}
IMLAC_OPT = -I ${IMLACD} ${DISPLAY_OPT}
IMLAC_OPT = -I ${IMLACD} ${DISPLAY_OPT} ${AIO_CCDEFS}
LINCD = ${SIMHD}/linc
LINC = ${LINCD}/linc_sys.c ${LINCD}/linc_cpu.c \
${LINCD}/linc_crt.c ${LINCD}/linc_dpy.c ${LINCD}/linc_kbd.c \
${LINCD}/linc_tape.c ${LINCD}/linc_tty.c \
${DISPLAYL}
LINC_OPT = -I ${LINCD} ${DISPLAY_OPT} ${AIO_CCDEFS}
TT2500D = ${SIMHD}/tt2500
@@ -2021,7 +2033,7 @@ 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}
TT2500_OPT = -I ${TT2500D} ${DISPLAY_OPT}
TT2500_OPT = -I ${TT2500D} ${DISPLAY_OPT} ${AIO_CCDEFS}
PDP8D = ${SIMHD}/PDP8
@@ -2514,7 +2526,7 @@ ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 \
swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 intel-mds \
scelbi 3b2 3b2-700 i701 i704 i7010 i7070 i7080 i7090 \
sigma uc15 pdp10-ka pdp10-ki pdp10-kl pdp10-ks pdp6 i650 \
imlac tt2500 sel32
imlac linc tt2500 sel32
all : ${ALL}
@@ -2592,6 +2604,12 @@ $(BIN)imlac$(EXE) : ${IMLAC} ${SIM}
$(MAKEIT) OPTS="$(IMLAC_OPT)"
linc : $(BIN)linc$(EXE)
$(BIN)linc$(EXE) : ${LINC} ${SIM}
$(MAKEIT) OPTS="$(LINC_OPT)"
tt2500 : $(BIN)tt2500$(EXE)
$(BIN)tt2500$(EXE) : ${TT2500} ${SIM}