1
0
mirror of https://github.com/simh/simh.git synced 2026-03-01 09:31:16 +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 f8c367d75d
commit 68c9c5536f
13 changed files with 1070 additions and 551 deletions

View File

@@ -1,32 +0,0 @@
## 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)

135
linc/README.md Normal file
View File

@@ -0,0 +1,135 @@
# LINC emulator
This is an emulator for the classic LINC from 1965.
### Devices
- CPU - LINC processor. If throttled to 125k, approximately original speed.
- CRT - CRT display. It can be disabled to run headless.
- DPY - point-plotting display controller.
- KBD - keyboard. Input comes from typing in the CRT window.
- SAM - sampled analog inputs.
- TAPE - four tape drives.
- TTY - teletype.
### LOAD
Software can be loaded into memory using the `LOAD` command. It will
normally read a file with 16-bit little endian words; the most
significant four bits of each word should be zero. If the `-O` switch
is supplied, the input is a text file with octal numbers separated by
whitespace.
To start reading from some particular position in the input file, add
`OFFSET=` after the file name and supply a number in octal.
To specify where in the memory to put the data, add `START=` after the
file name; the default is 0. To specify how many words to load, use
`LENGTH=`; the default is to write until the end of memory.
A binary input file can be a tape image. A plain image is 512 blocks
of 256 words each, totalling 262144 bytes. If the `-E` switch is
used, there is a check for the extended image format that can have
empty guard blocks at the beginning and the end. The last three words
in the extended format specify the block size, first forward block
number, and first reverse block number.
To state which tape block to start reading, add `BLOCK=` after the
file name and supply an octal number in the range 0-777.
### DO
The SIMH `DO` command has been modified. With arguments, it will
execute a script file as normal. Without argument, it acts like the
DO button on the LINC control panel. This executes one instruction
from the left switches, with the right switches providing a second
word if needed.
### Tape drives
To mount a tape image *file* on drive *n*, type `ATTACH TAPE<n>
<file>`. The plain or extended image format will be detected
automatically. Tape drives are numbered 0, 1, 4, and 5.
The `BOOT TAPEn` command will act like entering a tape read command in
the switches and starting the computer. The default is to read eight
blocks starting at 300, and start from location 20. This is the
conventional way to run LAP6. You can also add `RDC=` or `RCG=` to
boot some particular blocks. `START=` can be used to specify the
start address; it defaults to 20.
### Keyboard
The keys `0-9`, `A-Z`, and `Space` are mapped to their corresponding
LINC keys. `Enter` is mapped to `EOL`, `Delete` and `Backspace` are
mapped to `DEL`, and `Shift` is mapped to `CASE`. To type an upper
case symbol on some key, press `CASE`, release it, and then type the
key. For convenience, `Alt` is mapped to `META`.
The remaining keys are mapped thusly:
- `F1` is mapped to the `pu` key.
- `=` is mapped to the `i=` key.
- `-` and `,` are mapped to the `-,` key.
- `.` is mapped to the `+. key.
- `\` is mapped to the `|⊟` key.
- `[` and `left backslash` are mapped to the `*[` key.
The remaining upper case symbols:
- `CASE A` - `"`.
- `CASE B` - `„`.
- `CASE C` - `<`.
- `CASE D` - `>`.
- `CASE E` - `]`.
- `CASE F` - `*`.
- `CASE G` - `:`.
- `CASE Space` - `?`.
### Teletype
The TTY device implmenents a teletype for printing output. When a
file is attached, it will receive text decoded at 110 baud from relay
output 0.
Some characters are translated by LAP6 from the LINC character to ASCII:
- `i` to `&`
- `p` to `'`
- `|` to `\`
- `u` to `%`
- `⊟` to `$`
- `_` to `@`
- `"` to `^`
- `„` to `;`
### CPU
Registers:
- P - Instruction location, 10 bits.
- C - Current instruction, 12 bits.
- S - Memory address, 12 bits - 11 for address, 1 for halfword select.
- B - Memory data buffer, 12 bits.
- A - Accumulator, 12 bits - one's complement.
- Z - Various, 12 bits.
- L - Link, 1 bit.
- OVF - Overflow, 1 bit.
- IBZ - Tape interblock zone, 1 bit.
- ENI - Interrupt enabled, 1 bit.
- PINFF - Pause Interrupt enabled, 1 bit.
Switches:
- LSW - Left switches, 12 bits.
- RSW - Right switches, 12 bits.
- SSW - Sense switches, 6 bits.
Inputs:
- INTREQ, Interrupt request, 1 bit.
- R - Relays, 6 bits.
- XL - External levels, 12 of 1 bit each.
- SAM - Sampled analog inputs, 16 of 8 bits each; one's complement.
### Documentation
Programming:
https://bitsavers.org/pdf/washingtonUniversity/linc/Programming_the_LINC_Second_Edition_Jan69.pdf
Using the LAP6 operating system, text editor, assembler, tape filing system:
https://bitsavers.org/pdf/washingtonUniversity/linc/LINC_Reference_Manuals/LINC_Vol_16_Section_3_LAP6_Handbook_May67.pdf

View File

@@ -28,19 +28,17 @@
/* Debug */
#define DBG_CPU 0001
#define DBG_CPU 0001
#define DBG_INT 0002
#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 INSN_ENI 00010
#define INSN_NOP 00016
#define INSN_OPR 00500
#define INSN_MTP 00700
#define INSN_JMP 06000
#define X(_X) ((_X) & XMASK)
#define C03 (C & BMASK)
/* CPU state. */
static uint16 P;
@@ -57,9 +55,10 @@ static uint16 XL[12];
static int paused;
static int IBZ;
static int OVF;
static int INTREQ;
static int ENI = 0;
static int PINFF;
static int interrupt;
static int interrupt_enable = 0;
static int DO = 0;
static t_stat stop_reason;
@@ -80,6 +79,8 @@ 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 t_stat linc_boot(int32 flag, CONST char *ptr);
static t_stat linc_do(int32 flag, CONST char *ptr);
static UNIT cpu_unit = { UDATA(NULL, UNIT_FIX + UNIT_BINK, MEMSIZE) };
@@ -90,16 +91,21 @@ REG cpu_reg[] = {
{ ORDATAD(L, L, 1, "Link") },
{ ORDATAD(Z, Z, 12, "?") },
{ ORDATAD(R, R, 6, "Relay Register") },
{ ORDATAD(S, S, 12, "Memroy Address") },
{ ORDATAD(S, S, 12, "Memory 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") },
{ FLDATAD(IBZ, IBZ, 1, "Interblock zone") },
{ FLDATAD(OVF, OVF, 1, "Overflow") },
{ FLDATAD(INTREQ, INTREQ, 1, "Interrupt") },
{ FLDATAD(ENI, ENI, 1, "Interrupt Enable") },
{ FLDATAD(PIN, PINFF, 1, "Pause Interrupt") },
{ BRDATAD(SAM, SAM, 8, 8, 16, "Sampled analog inputs") },
{ BRDATAD(XL, XL, 8, 1, 12, "External levels") },
{ NULL }
};
@@ -111,6 +117,7 @@ static MTAB cpu_mod[] = {
static DEBTAB cpu_deb[] = {
{ "CPU", DBG_CPU },
{ "INTERRUPT", DBG_INT },
{ NULL, 0 }
};
@@ -122,41 +129,86 @@ DEVICE cpu_dev = {
NULL, NULL, NULL, NULL, NULL, NULL
};
static void pcinc(int flag)
static CTAB linc_cmd[] = {
{ "BOOT", &linc_boot, 0,
"BOOT {unit} boot simulator\n"
"BOOT TAPE{n} RCG={blocks} boot tape from specified blocks\n", NULL, &run_cmd_message },
{ "DO", &linc_do, 0,
"DO {unit} boot simulator\n"
"DO execute instruction in LSW and RSW\n", NULL, NULL },
{ NULL }
};
static void cpu_ndxp(int flag)
{
if (flag)
P = X(P + 1);
}
static void memaddr(uint16 addr)
static void cpu_ndxc()
{
C = (C & ~BMASK) | ((C + 1) & BMASK);
}
static void cpu_set_S(uint16 addr)
{
S = addr & WMASK;
}
static void membuf(uint16 data)
static void cpu_set_B(uint16 data)
{
B = data & WMASK;
}
static void memrd(void)
static void cpu_4ndxb()
{
B = M[S & AMASK];
cpu_set_B(B + 4);
}
static void cpu_4ndxa()
{
A = (A + 4) & WMASK;
}
static void cpu_mem_read(void)
{
cpu_set_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)
static void cpu_mem_modify(void)
{
M[S & AMASK] = B;
if (sim_brk_summ && sim_brk_test(S & AMASK, SWMASK('W')))
stop_reason = STOP_WBKPT;
}
static void memwr(void)
static void cpu_mem_write(void)
{
sim_interval--;
memmd();
cpu_mem_modify();
}
static void cpu_insn_addr()
{
if (!DO) {
cpu_set_S(P);
cpu_ndxp(1);
}
}
static void cpu_insn_read()
{
if (!DO)
cpu_mem_read();
}
static void cpu_fetch()
{
cpu_insn_addr();
cpu_insn_read();
}
static int cpu_halfword(void)
@@ -181,30 +233,26 @@ static void cpu_index(void)
} else {
tmp = 1;
}
membuf((B & 06000) | X(B + tmp));
memmd();
cpu_set_B((B & 06000) | X(B + tmp));
cpu_mem_modify();
}
}
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);
uint16 a = C03;
if (a == 0) {
cpu_insn_addr();
if ((C & IMASK) == 0) {
cpu_insn_read();
cpu_set_S(B);
}
} else {
cpu_set_S(a);
cpu_mem_read();
cpu_index();
cpu_set_S(B);
}
}
static void
@@ -214,15 +262,23 @@ cpu_misc(void)
case 00000: //HLT
stop_reason = STOP_HALT;
break;
case 00002: //PDP
sim_debug(DBG_CPU, &cpu_dev, "This is not a PDP-12.\n");
break;
case 00005: //ZTA
A = Z >> 1;
break;
case 00010: //ENI
interrupt_enable = 1;
sim_debug(DBG_INT, &cpu_dev, "Interrupt enabled.\n");
ENI = 1;
break;
case 00011: //CLR
A = L = Z = 0;
break;
case 00012: //DIN
sim_debug(DBG_INT, &cpu_dev, "Interrupt disabled.\n");
ENI = 0;
break;
case 00013: //Write gate on.
break;
case 00014: //ATR
@@ -241,33 +297,31 @@ cpu_misc(void)
static void cpu_set(void)
{
memaddr(P);
memrd();
pcinc(1);
cpu_fetch();
if ((C & IMASK) == 0) {
memaddr(B);
memrd();
cpu_set_S(B);
cpu_mem_read();
}
memaddr(C & BMASK);
memwr();
cpu_set_S(C03);
cpu_mem_write();
}
static void cpu_sam(void)
{
// sample analog input C & BMASK
// sample analog input C03
// 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];
A = SAM[C03];
if (A & 0200) /* One's complement +/-177. */
A |= 07600;
A |= 07400;
}
static void cpu_dis(void)
{
memaddr(C & BMASK);
memrd();
cpu_set_S(C03);
cpu_mem_read();
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);
@@ -275,15 +329,16 @@ static void cpu_dis(void)
static void cpu_xsk(void)
{
memaddr(C & BMASK);
memrd();
cpu_set_S(C03);
cpu_mem_read();
cpu_index();
pcinc(X(B) == 01777);
cpu_ndxp(X(B) == 01777);
}
static void cpu_rol(void)
{
while (C & BMASK) {
C = (C & ~BMASK) | (~B & BMASK);
while (C03 != 017) {
if (C & IMASK) {
A = (A << 1) | L;
L = A >> 12;
@@ -291,13 +346,14 @@ static void cpu_rol(void)
A = (A << 1) | (A >> 11);
}
A &= WMASK;
C--;
cpu_ndxc();
}
}
static void cpu_ror(void)
{
while (C & BMASK) {
C = (C & ~BMASK) | (~B & BMASK);
while (C03 != 017) {
Z = (Z >> 1) | ((A & 1) << 11);
if (C & IMASK) {
A |= L << 12;
@@ -307,28 +363,29 @@ static void cpu_ror(void)
A = (A >> 1) | (A << 11);
A &= WMASK;
}
C--;
cpu_ndxc();
}
}
static void cpu_scr(void)
{
while (C & BMASK) {
C = (C & ~BMASK) | (~B & BMASK);
while (C03 != 017) {
Z = (Z >> 1) | ((A & 1) << 11);
if (C & IMASK)
L = A & 1;
A = (A & 04000) | (A >> 1);
C--;
cpu_ndxc();
}
}
int cpu_skip(void)
{
int flag = 0;
int flag;
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];
flag = XL[C03];
break;
case 015: //KST
flag = kbd_struck();
@@ -338,6 +395,8 @@ int cpu_skip(void)
break;
case 046: //PIN
flag = PINFF;
sim_debug(DBG_INT, &cpu_dev, "Pause interrupt enabled.\n");
PINFF = 0;
break;
case 050: //AZE
flag = (A == 0) || (A == WMASK);
@@ -369,9 +428,9 @@ int cpu_skip(void)
static void cpu_opr(void)
{
switch (C & BMASK) {
switch (C03) {
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:
case 010: case 011: case 012: case 013:
if (C & IMASK)
; //Pause.
break;
@@ -389,37 +448,39 @@ static void cpu_opr(void)
static void cpu_lmb(void)
{
//Instruction field.
/* Lower memory bank. */
sim_debug(DBG_CPU, &cpu_dev, "This is not micro-LINC 300.\n");
}
static void cpu_umb(void)
{
//Data field.
/* Upper memory bank. */
sim_debug(DBG_CPU, &cpu_dev, "This is not micro-LINC 300.\n");
}
static void cpu_tape(void)
{
memaddr(P);
memrd();
pcinc(1);
cpu_fetch();
tape_op();
}
static void cpu_lda(void)
{
memrd();
cpu_mem_read();
A = B;
}
static void cpu_sta(void)
{
membuf(A);
memwr();
cpu_set_B(A);
/* Do not write immediate value if executing out of switches. */
if (!DO || (C & IMASK) == 0)
cpu_mem_write();
}
static void cpu_ada(void)
{
memrd();
cpu_mem_read();
OVF = ~(A ^ B);
A += B;
A += A >> 12;
@@ -430,13 +491,13 @@ static void cpu_ada(void)
static void cpu_adm(void)
{
cpu_ada();
membuf(A);
memmd();
cpu_set_B(A);
cpu_mem_modify();
}
static void cpu_lam(void)
{
memrd();
cpu_mem_read();
A += L;
L = A >> 12;
A &= WMASK;
@@ -444,40 +505,43 @@ static void cpu_lam(void)
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;
cpu_set_B(A);
cpu_mem_modify();
}
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--;
uint32 factor, product;
cpu_mem_read();
C &= ~BMASK;
L = (A ^ B) >> 11;
if (A & HMASK)
A ^= WMASK;
if (B & HMASK)
B ^= WMASK;
Z = B;
cpu_set_B(A);
factor = B;
product = A = 0;
while (C03 < 12) {
if (Z & 1)
product += factor;
Z >>= 1;
factor <<= 1;
cpu_ndxc();
}
if (S & HMASK)
A = (product >> 11) & WMASK;
else if ((A ^ B) & HMASK)
A = 04000 | (product & 03777);
A = product >> 11;
else
A = product & 03777;
L = A >> 11;
if (L)
A ^= 07777;
}
static void cpu_ldh(void)
{
memrd();
cpu_mem_read();
if ((S & HMASK) == 0)
B >>= 6;
A = B & RMASK;
@@ -485,91 +549,90 @@ static void cpu_ldh(void)
static void cpu_sth(void)
{
memrd();
cpu_mem_read();
if (S & HMASK)
membuf((A & RMASK) | (B & LMASK));
cpu_set_B((A & RMASK) | (B & LMASK));
else
membuf((A << 6) | (B & RMASK));
memmd();
cpu_set_B((A << 6) | (B & RMASK));
cpu_mem_modify();
}
static void cpu_shd(void)
{
memrd();
cpu_mem_read();
if ((S & HMASK) == 0)
B >>= 6;
pcinc((A & RMASK) != (B & RMASK));
cpu_ndxp((A & RMASK) != (B & RMASK));
}
static void cpu_sae(void)
{
memrd();
pcinc(A == B);
cpu_mem_read();
cpu_ndxp(A == B);
}
static void cpu_sro(void)
{
memrd();
pcinc((B & 1) == 0);
membuf((B >> 1) | (B << 11));
memmd();
cpu_mem_read();
cpu_ndxp((B & 1) == 0);
cpu_set_B((B >> 1) | (B << 11));
cpu_mem_modify();
}
static void cpu_bcl(void)
{
memrd();
cpu_mem_read();
A &= ~B;
}
static void cpu_bse(void)
{
memrd();
cpu_mem_read();
A |= B;
}
static void cpu_bco(void)
{
memrd();
cpu_mem_read();
A ^= B;
}
static void cpu_dsc(void)
{
int i, j;
uint16 data;
cpu_mem_read();
Z = B;
memrd();
data = B;
// C used for counting.
memaddr(1);
memrd();
cpu_set_S(1);
cpu_mem_read();
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;
C &= ~BMASK;
while (C03 < 12) {
if (C03 == 0 || C03 == 6) {
A &= 07740;
cpu_4ndxb();
}
if (Z & 1)
dpy_dis(B >> 11, B & DMASK, A & DMASK);
Z >>= 1;
cpu_4ndxa();
cpu_ndxc();
}
memmd();
cpu_mem_write();
}
static void cpu_add(void)
{
memaddr(X(C));
cpu_set_S(X(C));
cpu_ada();
}
static void cpu_stc(void)
{
memaddr(X(C));
membuf(A);
cpu_set_S(X(C));
cpu_set_B(A);
A = 0;
memwr();
cpu_mem_write();
}
static void cpu_jmp(void)
@@ -577,9 +640,9 @@ static void cpu_jmp(void)
uint16 tmp = P;
P = X(C);
if (P != 0) {
membuf(06000 | tmp);
memaddr(0);
memwr();
cpu_set_B(INSN_JMP | tmp);
cpu_set_S(0);
cpu_mem_write();
}
}
@@ -587,9 +650,9 @@ static void
cpu_insn(void)
{
/* Cycle 0, or I. */
memaddr(P);
memrd();
C = B;
cpu_fetch();
if (!DO)
C = B;
/* Cycle 1, or X. */
if ((C & 07000) == 01000)
@@ -602,7 +665,6 @@ cpu_insn(void)
}
/* Cycle 2, or O. */
pcinc(1);
/* Cycle 3, or E. */
switch (C & 07740) {
@@ -632,7 +694,7 @@ cpu_insn(void)
break;
case 00400:
case 00440:
pcinc(cpu_skip());
cpu_ndxp(cpu_skip());
break;
case 00500:
case 00540:
@@ -721,14 +783,96 @@ cpu_insn(void)
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;
t_stat cpu_do(void)
{
t_stat stat;
DO = 1;
C = LSW;
cpu_set_B(RSW);
cpu_insn();
DO = 0;
sim_interval = 1;
/* Can not return from DO until the instruction is done,
i.e. not paused. */
while (paused) {
AIO_CHECK_EVENT;
if (sim_interval <= 0) {
if ((stat = sim_process_event()) != SCPE_OK)
return stat;
}
sim_interval--;
}
return SCPE_OK;
}
static int jmp_or_eni(void)
{
return (C & 06000) == INSN_JMP || (C == INSN_ENI);
}
static int mtp_or_opr(void)
{
return (C & 07700) == INSN_MTP || (C & 07700) == INSN_OPR;
}
static void cpu_interrupt(void)
{
if (!INTREQ)
return;
if (!ENI)
return;
sim_debug(DBG_INT, &cpu_dev, "Interrupt requested and enabled.\n");
if (jmp_or_eni()) {
sim_debug(DBG_INT, &cpu_dev, "Interrupt not taken after JMP or ENI.\n");
return;
}
if (paused) {
if (!mtp_or_opr()) {
sim_debug(DBG_INT, &cpu_dev, "Pause only interrupted for MTP or OPR.\n");
return;
}
if (PINFF)
return;
sim_debug(DBG_INT, &cpu_dev, "Pause interrupted.\n");
PINFF = 1;
paused = 0;
}
sim_debug(DBG_INT, &cpu_dev, "Interrupt taken.\n");
cpu_set_S(021);
cpu_mem_read();
C = B;
if (history) {
history[history_i].P = 07777;
history[history_i].C = C;
history[history_i].S = S;
}
ENI = 0; /* Except for OPR. */
if ((C & 06000) == INSN_JMP)
cpu_jmp();
else if ((C & 07700) == INSN_OPR) {
ENI = 1; /* OPR doesn't disable interrupts. */
cpu_opr();
} else if (C == INSN_NOP)
;
else
sim_debug(DBG_INT, &cpu_dev, "Invalid interrupt instruction.\n");
if (history) {
history[history_i].B = B;
history[history_i].A = A;
history_i = (history_i + 1) % history_m;
if (history_n < history_m)
history_n++;
}
}
@@ -739,8 +883,18 @@ t_stat sim_instr(void)
if ((stat = build_dev_tab()) != SCPE_OK)
return stat;
/* Stepping is based on sim_step, not sim_interval. The latter is
approximately memory cycles, not instructions. */
sim_cancel_step();
/* Because we check sim_step before cpu_insn. */
if (sim_step)
sim_step++;
stop_reason = 0;
paused = 0;
PINFF = 0;
ENI = 0;
for (;;) {
AIO_CHECK_EVENT;
@@ -752,19 +906,19 @@ t_stat sim_instr(void)
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
/* Can not return from a STEP until the instruction is done,
i.e. not paused. */
if (!paused && sim_step != 0) {
if (--sim_step == 0)
return SCPE_STEP;
}
if (paused)
sim_interval--;
else
cpu_insn();
if (sim_step != 0) {
if (--sim_step == 0)
return SCPE_STEP;
}
cpu_interrupt();
if (stop_reason)
return stop_reason;
@@ -828,36 +982,87 @@ cpu_show_hist(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
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,
if (history[j].P == 07777)
fprintf (st, "---- ");
else
fprintf (st, "%04o ", history[j].P);
fprintf (st, "%04o %04o %04o %04o %d ",
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);
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;
sim_vm_cmd = linc_cmd;
return SCPE_OK;
}
static t_stat linc_boot(int32 flag, CONST char *cptr)
{
char dev[CBUFSIZE], arg[CBUFSIZE];
char bbuf[CBUFSIZE], gbuf[CBUFSIZE];
t_value block;
t_stat stat;
/* Is it BOOT TAPE? */
cptr = get_glyph(cptr, dev, 0);
if (*dev == 0)
return SCPE_ARG;
if (strncmp(dev, "TAPE", 4) != 0)
return run_cmd(RU_BOOT, dev);
/* Yes. Is there an argument after? */
if (*cptr == 0)
return run_cmd(RU_BOOT, dev);
bbuf[0] = 0;
strcpy(gbuf, "20");
while (*cptr) {
cptr = get_glyph(cptr, arg, 0);
if (strncmp(arg, "RDC=", 4) == 0)
LSW = 0700, strcpy(bbuf, arg + 4);
else if (strncmp(arg, "RCG=", 4) == 0)
LSW = 0701, strcpy(bbuf, arg + 4);
else if (strncmp(arg, "START=", 6) == 0)
LSW = 0701, strcpy(gbuf, arg + 6);
else
return SCPE_ARG;
}
if (*bbuf == 0)
return SCPE_ARG;
/* It's a BOOT TAPE RDC= or RCG=, so start from switches. */
block = get_uint(bbuf, 8, ~0, &stat);
if (stat != SCPE_OK)
return stat;
RSW = block;
stat = cpu_do();
if (stat != SCPE_OK)
return stat;
return run_cmd(RU_GO, gbuf);
}
static t_stat linc_do(int32 flag, CONST char *cptr)
{
/* With arguments, regular DO to execute script. */
if (*cptr != 0)
return do_cmd(flag, cptr);
/* No arguments, push the DO button on the LINC control panel. */
return cpu_do();
}

View File

@@ -44,6 +44,7 @@ static UNIT crt_unit = {
static DEBTAB crt_deb[] = {
{ "DBG", DBG },
{ "VVID", SIM_VID_DBG_VIDEO },
{ "KVID", SIM_VID_DBG_KEY },
{ NULL, 0 }
};
@@ -58,7 +59,7 @@ DEVICE crt_dev = {
1, 8, 12, 1, 8, 12,
NULL, NULL, &crt_reset,
NULL, NULL, NULL,
NULL, DEV_DISABLE | DEV_DEBUG | DEV_DISPLAY, 0, crt_deb,
NULL, DEV_DISABLE | DEV_DEBUG, 0, crt_deb,
NULL, NULL, NULL, NULL, NULL, NULL
};
@@ -109,14 +110,7 @@ crt_point (uint16 x, uint16 y)
#endif
}
/* Hook called when CRT goes idle. */
void
crt_idle (void)
{
}
/* Display high voltage sync. */
void
crt_hvc (void)
void crt_toggle_fullscreen(void)
{
vid_set_fullscreen(!vid_is_fullscreen ());
}

View File

@@ -36,6 +36,18 @@
#define STOP_RBKPT 3
#define STOP_WBKPT 3
#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 TMASK 00777 /* Tape block. */
#define LMASK 07700 /* Left half word. */
#define RMASK 00077 /* Right half word; character. */
#define IMASK 00020 /* Index bit. */
#define UMASK 00010 /* Tape unit bit. */
#define BMASK 00017 /* Beta; index register. */
#define MEMSIZE 2048
extern REG cpu_reg[];
@@ -49,10 +61,13 @@ extern DEVICE tape_dev;
extern DEVICE tty_dev;
extern t_bool build_dev_tab(void);
extern t_stat cpu_do(void);
extern void dpy_dis(uint16 h, uint16 x, uint16 y);
extern void crt_point (uint16 x, uint16 y);
extern void crt_toggle_fullscreen(void);
extern uint16 kbd_key(uint16 wait);
extern int kbd_struck(void);
extern void tape_op(void);
extern t_stat tape_metadata(FILE *, uint16 *, int16 *, int16 *);
#endif /* LINC_DEFS_H_ */

View File

@@ -4,12 +4,10 @@
/* Debug */
#define DBG 0001
static t_stat kbd_svc(UNIT *uptr);
static t_stat kbd_reset(DEVICE *dptr);
#define A (*(uint16 *)cpu_reg[2].loc)
#define paused (*(int *)cpu_reg[11].loc)
static UNIT kbd_unit = {
UDATA(&kbd_svc, UNIT_IDLE, 0)
};
static t_stat kbd_reset(DEVICE *dptr);
static DEBTAB kbd_deb[] = {
{ "DBG", DBG },
@@ -17,8 +15,8 @@ static DEBTAB kbd_deb[] = {
};
DEVICE kbd_dev = {
"KBD", &kbd_unit, NULL, NULL,
1, 8, 12, 1, 8, 12,
"KBD", NULL, NULL, NULL,
0, 8, 12, 1, 8, 12,
NULL, NULL, &kbd_reset,
NULL, NULL, NULL, NULL, DEV_DEBUG, 0, kbd_deb,
NULL, NULL, NULL, NULL, NULL, NULL
@@ -44,170 +42,6 @@ CASE 0 1 2 3 4 5 6 7 8 9 DEL
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)
@@ -219,11 +53,11 @@ 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 {
} else if (wait) {
sim_debug(DBG, &kbd_dev, "KEY paused\n");
paused = 1;
}
return 0;
}
@@ -399,12 +233,19 @@ static void kbd_convert(uint32 key)
// ˣ 75
// : 76
// ʸ 77
case SIM_KEY_F11:
crt_toggle_fullscreen();
return;
default:
return;
}
sim_debug(DBG, &kbd_dev, "Key struck %s -> %02o\n",
vid_key_name(key), kbd_code);
kbd_pressed = 1;
if (paused)
A = kbd_code;
else
kbd_pressed = 1;
paused = 0;
}
static int
@@ -421,7 +262,6 @@ kbd_reset(DEVICE *dptr)
#ifdef USE_DISPLAY
vid_display_kb_event_process = kbd_event;
#endif
sim_activate_abs(&kbd_unit, 0);
return SCPE_OK;
}

View File

@@ -104,28 +104,35 @@ 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;
int16 forward_offset = 0, reverse_offset;
uint16 block_size;
long offset = 0;
t_stat stat;
if (sim_switches & SWMASK('E')) {
stat = tape_metadata(fileref, &block_size, &forward_offset, &reverse_offset);
if (stat != SCPE_OK)
return stat;
if (block_size != 256)
return SCPE_FMT;
}
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);
while (*cptr !=0) {
char gbuf[100];
cptr = get_glyph(cptr, gbuf, 0);
if (strncmp(gbuf, "START=", 6) == 0)
start = (t_addr)get_uint(gbuf + 6, 8, ~0, &stat);
else if (strncmp(gbuf, "OFFSET=", 7) == 0)
offset = 2 * (long)get_uint(gbuf + 7, 8, ~0, &stat);
else if (strncmp(gbuf, "BLOCK=", 6) == 0)
offset = 512 * ((long)get_uint(gbuf + 6, 8, ~0, &stat) - forward_offset);
else if (strncmp(gbuf, "LENGTH=", 7) == 0)
length = (t_addr)get_uint(gbuf + 7, 8, ~0, &stat);
else
return SCPE_ARG;
cptr = strchr(cptr, ' ');
if (cptr == NULL)
break;
while (*cptr == ' ')
cptr++;
}
end = start + length;
@@ -162,7 +169,7 @@ t_bool build_dev_tab(void)
static t_stat fprint_next(FILE *of, uint16 addr)
{
fprintf(of, "\n");
fprint_val(of, ++addr & 01777, 8, 10, PV_LEFT);
fprint_val(of, ++addr & XMASK, 8, 10, PV_LEFT);
fprintf(of, ":\t%04o", M[addr]);
return -1;
}
@@ -182,8 +189,8 @@ static void fprint_misc(FILE *of, uint16 insn)
case 00011:
fprintf(of, "CLR");
break;
case 00013:
fprintf(of, "%04o", insn);
case 00012:
fprintf(of, "DIN");
break;
case 00014:
fprintf(of, "ATR");
@@ -198,17 +205,17 @@ static void fprint_misc(FILE *of, uint16 insn)
fprintf(of, "COM");
break;
default:
fprintf(of, "%04o", insn);
fprintf(of, "MSC %o", insn);
break;
}
}
static t_stat fprint_index(FILE *of, uint16 insn, uint16 addr)
{
if (insn & 020)
if (insn & IMASK)
fprintf(of, " i");
if (insn & 017)
fprintf(of, " %o", insn & 017);
if (insn & BMASK)
fprintf(of, " %o", insn & BMASK);
else
return fprint_next(of, addr);
return SCPE_OK;
@@ -223,7 +230,7 @@ static void fprint_set(FILE *of, uint16 insn, uint16 addr)
static void fprint_sam(FILE *of, uint16 insn)
{
fprintf(of, "SAM ");
fprintf(of, "SAM%s %o", insn & IMASK ? " i" : "", insn & BMASK);
}
static t_stat fprint_dis(FILE *of, uint16 insn, uint16 addr)
@@ -263,7 +270,7 @@ static void fprint_skip(FILE *of, uint16 insn)
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);
snprintf(beta, sizeof beta, "%o", insn & BMASK);
break;
case 015:
fprintf(of, "KST");
@@ -297,7 +304,7 @@ static void fprint_skip(FILE *of, uint16 insn)
fprintf(of, "%04o", insn);
return;
}
if (insn & 020)
if (insn & IMASK)
fprintf(of, " i" );
fprintf(of, " %s", beta);
}
@@ -322,7 +329,7 @@ static void fprint_opr(FILE *of, uint16 insn)
fprintf(of, "%04o", insn);
break;
}
if (insn & 020)
if (insn & IMASK)
fprintf(of, "i" );
}
@@ -364,9 +371,9 @@ static void fprint_tape(FILE *of, uint16 insn, uint16 addr)
fprintf(of, "CHK");
break;
}
if (insn & 020)
if (insn & IMASK)
fprintf(of, " i");
if (insn & 010)
if (insn & UMASK)
fprintf(of, " u");
fprint_next(of, addr);
}
@@ -463,17 +470,17 @@ static t_stat fprint_dsc(FILE *of, uint16 insn, uint16 addr)
static void fprint_add(FILE *of, uint16 insn)
{
fprintf(of, "ADD %04o", insn & 01777);
fprintf(of, "ADD %04o", insn & XMASK);
}
static void fprint_stc(FILE *of, uint16 insn)
{
fprintf(of, "STC %04o", insn & 01777);
fprintf(of, "STC %04o", insn & XMASK);
}
static void fprint_jmp(FILE *of, uint16 insn)
{
fprintf(of, "JMP %04o", insn & 01777);
fprintf(of, "JMP %04o", insn & XMASK);
}
t_stat fprint_sym(FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw)
@@ -582,12 +589,108 @@ t_stat fprint_sym(FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw)
return SCPE_OK;
}
struct symbol {
const char *name;
uint16 value;
};
static const struct symbol symbols[] = {
{ "U", 00010 },
{ "I", 00020 },
{ "HLT", 00000 },
{ "ZTA", 00005 },
{ "CLR", 00011 },
{ "DIN", 00012 },
{ "ATR", 00014 },
{ "RTA", 00015 },
{ "NOP", 00016 },
{ "COM", 00017 },
{ "SET", 00040 },
{ "SAM", 00100 },
{ "DIS", 00140 },
{ "XSK", 00200 },
{ "ROL", 00240 },
{ "ROR", 00300 },
{ "SCR", 00340 },
{ "SXL", 00400 },
{ "KST", 00415 },
{ "SNS", 00440 },
{ "AZE", 00450 },
{ "APO", 00451 },
{ "LZE", 00452 },
{ "IBZ", 00453 },
{ "OVF", 00454 },
{ "ZZZ", 00455 },
{ "OPR", 00500 },
{ "KBD", 00515 },
{ "RSW", 00516 },
{ "LSW", 00517 },
{ "LMB", 00600 },
{ "UMB", 00640 },
{ "RDC", 00700 },
{ "RCG", 00701 },
{ "RDE", 00702 },
{ "MTB", 00703 },
{ "WRC", 00704 },
{ "WCG", 00705 },
{ "WRI", 00706 },
{ "CHK", 00707 },
{ "LDA", 01000 },
{ "STA", 01040 },
{ "ADA", 01100 },
{ "ADM", 01140 },
{ "LAM", 01200 },
{ "MUL", 01240 },
{ "LDH", 01300 },
{ "STH", 01340 },
{ "SHD", 01400 },
{ "SAE", 01440 },
{ "SRO", 01500 },
{ "BCL", 01540 },
{ "BSE", 01600 },
{ "BCO", 01640 },
{ "DSC", 01740 },
{ "ADD", 02000 },
{ "STC", 04000 },
{ "JMP", 06000 }
};
t_stat parse_sym(CONST char *cptr, t_addr addr, UNIT *uptr,
t_value *val, int32 sw)
{
char gbuf[CBUFSIZE];
t_value val2;
t_stat stat;
int i;
*val = get_uint(cptr, 8, ~0, &stat);
if (stat != SCPE_OK)
return stat;
return 0;
if (*val > 07777)
return SCPE_ARG;
if (stat == SCPE_OK)
return SCPE_OK;
if (*cptr == '-') {
stat = parse_sym(cptr + 1, addr, uptr, val, sw);
if (stat != SCPE_OK)
return stat;
*val ^= 07777;
if (stat == SCPE_OK)
return SCPE_OK;
}
cptr = get_glyph(cptr, gbuf, 0);
for (i = 0; i < sizeof symbols / sizeof symbols[0]; i++) {
if (strcmp(gbuf, symbols[i].name) != 0)
continue;
*val = symbols[i].value;
if (*cptr) {
stat = parse_sym(cptr, addr, uptr, &val2, sw);
if (stat != SCPE_OK)
return stat;
*val |= val2;
}
return SCPE_OK;
}
return SCPE_ARG;
}

View File

@@ -5,10 +5,13 @@
#define ACC u5
#define OFFSET u6
#define P (*(uint16 *)cpu_reg[0].loc)
#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 LSW (*(uint16 *)cpu_reg[8].loc)
#define RSW (*(uint16 *)cpu_reg[9].loc)
#define paused (*(int *)cpu_reg[11].loc)
#define IBZ (*(int *)cpu_reg[12].loc)
@@ -24,6 +27,8 @@
#define MAX_BLOCKS 512
#define MAX_POS ((BLOCK_WORDS * MAX_BLOCKS + IBZ_WORDS) * MAX_SPEED)
#define GOOD_CHECKSUM 07777
#define RDC 0 /* read tape and check */
#define RCG 1 /* read tape group */
#define RDE 2 /* read tape */
@@ -100,10 +105,10 @@ void tape_op(void)
uptr->ACC = ACC_START;
}
if (!sim_is_active(uptr))
sim_activate_after(uptr, 1);
sim_activate(uptr, 20);
paused = 1;
A = 0;
WANTED_BLOCK = B & 0777;
WANTED_BLOCK = B & TMASK;
switch (C & 7) {
case RDC: case RDE: case WRC: case WRI: case CHK:
@@ -124,25 +129,25 @@ void tape_op(void)
}
}
static t_stat tape_seek(UNIT *uptr, t_addr block, t_addr offset)
static t_stat tape_seek(FILE *fileref, t_addr block, t_addr offset)
{
offset = DATA_WORDS * block + offset;
offset *= 2;
if (sim_fseek(uptr->fileref, offset, SEEK_SET) == -1)
if (sim_fseek(fileref, offset, SEEK_SET) == -1)
return SCPE_IOERR;
return SCPE_OK;
}
static uint16 read_word(UNIT *uptr, t_addr block, t_addr offset)
static uint16 read_word(FILE *fileref, t_addr block, t_addr offset)
{
t_stat stat;
uint8 data[2];
uint16 word;
stat = tape_seek(uptr, block, offset);
stat = tape_seek(fileref, block, offset);
if (stat != SCPE_OK)
;
if (sim_fread(data, 1, 2, uptr->fileref) != 2)
if (sim_fread(data, 1, 2, fileref) != 2)
;
if (data[1] & 0xF0)
;
@@ -152,17 +157,17 @@ static uint16 read_word(UNIT *uptr, t_addr block, t_addr offset)
return word;
}
static void write_word(UNIT *uptr, t_addr block, t_addr offset, uint16 word)
static void write_word(FILE *fileref, t_addr block, t_addr offset, uint16 word)
{
t_stat stat;
uint8 data[2];
stat = tape_seek(uptr, block, offset);
stat = tape_seek(fileref, block, offset);
if (stat != SCPE_OK)
;
data[0] = word & 0xFF;
data[1] = word >> 8;
if (sim_fwrite(data, 1, 2, uptr->fileref) != 2)
if (sim_fwrite(data, 1, 2, fileref) != 2)
;
}
@@ -197,23 +202,22 @@ static void tape_done(UNIT *uptr)
switch (C & 7) {
case RDC: case RCG: case RDE: case CHK:
A = 07777;
A = GOOD_CHECKSUM;
break;
case WRI:
A = A ^ 07777;
A++;
A &= 07777;
A = (A ^ 07777) + 1;
A &= WMASK;
break;
case MTB:
A = (WANTED_BLOCK + ~CURRENT_BLOCK);
A += A >> 12;
A &= 07777;
A &= WMASK;
break;
}
switch (C & 7) {
case RDC:
if (A != 07777) {
if (A != GOOD_CHECKSUM) {
sim_debug(DBG, &tape_dev, "Check failed; read again\n");
S &= ~0377;
} else {
@@ -224,7 +228,7 @@ static void tape_done(UNIT *uptr)
case WRC:
sim_debug(DBG, &tape_dev, "Block written, go back and check\n");
// For now, done.
A = 07777;
A = GOOD_CHECKSUM;
paused = 0;
break;
case RCG: case WCG:
@@ -235,7 +239,7 @@ static void tape_done(UNIT *uptr)
sim_debug(DBG, &tape_dev, "Blocks left in group: %d\n", GROUP);
GROUP--;
}
WANTED_BLOCK = (WANTED_BLOCK + 1) & 0777;
WANTED_BLOCK = (WANTED_BLOCK + 1) & TMASK;
break;
case RDE: case WRI:
sim_debug(DBG, &tape_dev, "Transfer done\n");
@@ -253,7 +257,7 @@ static void tape_done(UNIT *uptr)
if (paused)
;
else if ((C & 020) == 0) {
else if ((C & IMASK) == 0) {
sim_debug(DBG_SEEK, &tape_dev, "Instruction done, stop tape\n");
uptr->ACC = uptr->SPEED > 0 ? -ACC_STOP : ACC_STOP;
} else {
@@ -265,7 +269,7 @@ 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);
B = read_word(uptr->fileref, block, offset);
sim_debug(DBG_READ, &tape_dev,
"Read block %03o offset %03o data %04o address %04o\n",
block, offset, B, S);
@@ -277,12 +281,12 @@ static void tape_word(UNIT *uptr, uint16 block, uint16 offset)
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);
write_word(uptr->fileref, block, offset, B);
break;
}
S = (S+1) & 03777;
S = (S+1) & AMASK;
A += B;
A &= 07777;
A &= WMASK;
}
static t_stat tape_svc(UNIT *uptr)
@@ -384,43 +388,70 @@ static t_stat tape_reset(DEVICE *dptr)
static t_stat tape_boot(int32 unit_num, DEVICE *dptr)
{
uint16 block = 0300;
uint16 blocks = 8;
uint16 quarter = 0;
t_stat stat;
if (unit_num >= 2 && unit_num <= 3)
return SCPE_ARG;
if (blocks == 0)
return SCPE_ARG;
if (blocks == 1)
LSW = RDC;
else
LSW = RCG, quarter = blocks - 1;
LSW |= 0700 | (unit_num << 3);
RSW = (quarter << 9) | block;
stat = cpu_do();
if (stat != SCPE_OK)
return stat;
P = 020;
return SCPE_OK;
}
//LSW = 0701 + (unit_num << 3);
//RSW = (7 << 9) | 0300;
//P = 020;
t_stat tape_metadata(FILE *fileref, uint16 *block_size, int16 *forward_offset, int16 *reverse_offset)
{
t_offset size = sim_fsize(fileref);
if (size == MAX_BLOCKS * DATA_WORDS * 2) {
/* Plain image. */
*block_size = DATA_WORDS;
*forward_offset = 0;
*reverse_offset = 0;
} else if ((size % (2 * DATA_WORDS)) == 6) {
/* Extended image with additional meta data. */
uint16 metadata = (uint16)(size / (2 * DATA_WORDS));
*block_size = read_word(fileref, metadata, 0);
*forward_offset = (int16)read_word(fileref, metadata, 1);
*reverse_offset = (int16)read_word(fileref, metadata, 2);
} else
return SCPE_FMT;
return SCPE_OK;
}
static t_stat tape_attach(UNIT *uptr, CONST char *cptr)
{
t_stat stat;
t_offset size;
uint16 block_size;
int16 forward_offset, reverse_offset;
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
stat = tape_metadata(uptr->fileref, &block_size, &forward_offset, &reverse_offset);
if (stat != SCPE_OK)
return stat;
sim_debug(DBG, &tape_dev,
"Tape 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;
uptr->POS = -2 * START_POS;
uptr->SPEED = 0;

View File

@@ -1,6 +1,124 @@
#include "linc_defs.h"
DEVICE tty_dev = {
"TTY", NULL, NULL, NULL,
0, 8, 12, 1, 8, 12,
/* Data bits, 110 baud rate. */
#define BIT_TIME 1120
/* Sample rate to find the start bit edge. */
#define START_TIME (BIT_TIME / 5)
/* After finding the edge, wait until the middle of the first data bit. */
#define FIRST_TIME (BIT_TIME + (BIT_TIME - START_TIME) / 2)
#define R (*(uint16 *)cpu_reg[5].loc)
/* Debug */
#define DBG 0001
#define DBG_BIT 0002
#define DATA u3 /* Character being assembled. */
#define STATE u4 /* 0 for start bit, 1 for stop bit, otherwise data. */
#define PREVIOUS u5 /* Previous level seen. */
/* When a start bit is found, the state is set to 10 and then
decremented for each bit that is processed. */
#define STATE_START 0
#define STATE_STOP 1
/* STATE_DATA 2-9 */
#define STATE_FIRST 10
/* Function declaration. */
static t_stat tty_svc(UNIT *uptr);
static t_stat tty_attach(UNIT *uptr, CONST char *cptr);
static t_stat tty_detach(UNIT *uptr);
static UNIT tty_unit = {
UDATA(&tty_svc, UNIT_IDLE | UNIT_ATTABLE, 0)
};
static DEBTAB tty_deb[] = {
{ "DBG", DBG },
{ "BIT", DBG_BIT },
{ NULL, 0 }
};
DEVICE tty_dev = {
"TTY", &tty_unit, NULL, NULL,
1, 8, 12, 1, 8, 12,
NULL, NULL, NULL,
NULL, &tty_attach, &tty_detach,
NULL, DEV_DISABLE | DEV_DEBUG, 0, tty_deb,
NULL, NULL, NULL, NULL, NULL, NULL
};
static void tty_output(UNIT *uptr)
{
uint8 ch = uptr->DATA;
sim_debug(DBG, &tty_dev, "Character %03o '%c'\n", ch, ch & 0177);
fputc(ch & 0177, uptr->fileref);
fflush(uptr->fileref);
}
static t_stat tty_svc(UNIT *uptr)
{
switch (uptr->STATE) {
case STATE_START:
if (uptr->PREVIOUS == 0 || (R & 1) == 1) {
/* Keep looking for start bit. */
uptr->PREVIOUS = R & 1;
sim_activate(uptr, START_TIME);
return SCPE_OK;
}
sim_debug(DBG_BIT, &tty_dev, "Start bit edge found.\n");
uptr->STATE = STATE_FIRST;
uptr->DATA = 0;
/* Wait until the middle of the first data bit. Since the edge
was just seen, this is a little longer than the time between
data bits. */
sim_activate(uptr, FIRST_TIME);
break;
default:
sim_debug(DBG_BIT, &tty_dev, "Data bit %d is %d\n",
STATE_FIRST - 1 - uptr->STATE, R & 1);
uptr->DATA >>= 1;
uptr->DATA |= (R & 1) << 7;
sim_activate(uptr, BIT_TIME);
break;
case STATE_STOP:
sim_debug(DBG_BIT, &tty_dev, "Stop bit is %d\n", R & 1);
if (R & 1)
tty_output(uptr);
else
sim_debug(DBG, &tty_dev, "Framing error.\n");
uptr->PREVIOUS = R & 1;
/* Look for next start bit. */
sim_activate(uptr, START_TIME);
break;
}
/* Decrease the state counter, first through the data bits, then
the stop bit, and finally the start bit. */
uptr->STATE--;
return SCPE_OK;
}
static t_stat tty_attach(UNIT *uptr, CONST char *cptr)
{
t_stat stat = attach_unit(uptr, cptr);
if (stat != SCPE_OK)
return stat;
uptr->STATE = 0;
uptr->PREVIOUS = 0;
sim_activate(uptr, 1);
return SCPE_OK;
}
static t_stat tty_detach(UNIT *uptr)
{
if ((uptr->flags & UNIT_ATT) == 0)
return SCPE_OK;
if (sim_is_active(uptr))
sim_cancel(uptr);
return detach_unit(uptr);
}

View File

@@ -1,106 +1,110 @@
cd %~p0
# The tests check writing to the tape, so use a copy.
copy classic-test.linc clobbered.linc
attach tape0 clobbered.linc
echo CONTRL
load classic-test.linc block=10 start=0 length=400
load -e classic-test.linc block=0 start=0 length=400
break 34
# Special treatment for this test, because it's supposed to halt.
echo *** Test: 70 - HLTTST ***
load classic-test.linc block=11 start=400 length=400
load -e classic-test.linc block=1 start=400 length=400
assert 400==70
go 401
assert P==402
continue
assert P==34
call test 1 SAETST 012 002
call test 2 BCLTST 013 003
call test 3 BSETST 014 004
call test 4 BCOTST 015 005
call test 5 ROTL1 016 006
call test 6 ROTL2 017 007
call test 7 ROTL3 020 010
call test 10 ROTL4 021 011
call test 11 ROTL5 022 012
call test 12 ROTR1 023 013
call test 13 ROTR2 024 014
call test 14 ROTR3 025 015
call test 15 ROTR4 026 016
call test 16 ROTR5 027 017
call test 17 CLRTST 030 020
call test 20 ADDONE 031 021
call test 21 COMT1 032 022
call test 22 SCRT1 033 023
call test 23 SCRT2 034 024
call test 24 SCRT3 035 025
call test 25 SCRT4 037 027
call test 26 ADDT1 041 031
call test 27 FADRT1 042 032
call test 30 FADRT2 043 033
call test 31 iBETA1 045 035
call test 32 iBETA2 046 036
call test 33 iBETA3 047 037
call test 34 iBETA4 050 040
call test 35 LDAT1 051 041
call test 36 STAT1 052 042
call test 37 ADMT1 053 043
call test 40 LAMT1 054 044
call test 41 MULT1 055 045
call test 42 SROT1 056 046
call test 43 SETT1 057 047
call test 44 SETT2 060 050
call test 45 XSKT1 061 051
call test 46 XSKT2 062 052
call test 47 AZET1 063 053
call test 50 APOT1 064 054
call test 51 LZET1 065 055
call test 52 HWCT1 066 056
call test 53 HWCT2 067 057
call test 54 HWCT3 070 060
call test 55 HWCT4 071 061
call test 56 HWCT5 072 062
call test 57 RANADD 073 063
call test 60 ATRT1 074 064
call test 61 IBZT1 075 065
call test 62 JMPUP 076 066
call test 63 JMPDWN 077 067
call test 64 TAPETS 100 070
call test 65 MTBTST 111 101
call test 66 DISTST 112 102
call test 67 DSCTST 113 103
call test 700 OVFT1 114 104
call test 701 ZTAT1 115 105
call test 702 ZCLR1 116 106
deposit SAM[0] 177
call test 703 ZCLRT2 117 107
;call test 704 ENIT1 120 110
deposit RSW 0300
deposit LSW 0700
deposit SSW 77
deposit XL[0] 1
deposit XL[1] 1
deposit XL[2] 1
deposit XL[3] 1
deposit XL[4] 1
deposit XL[5] 1
deposit XL[6] 1
deposit XL[7] 1
deposit XL[8] 1
deposit XL[9] 1
deposit SAM[0] 177
deposit XL[0] 1
deposit XL[1] 1
deposit XL[2] 1
deposit XL[3] 1
deposit XL[4] 1
deposit XL[5] 1
deposit XL[6] 1
deposit XL[7] 1
deposit XL[8] 1
deposit XL[9] 1
deposit XL[10] 1
deposit XL[11] 1
call test 71 MISCTS 121 111
call test 1 SAETST 002
call test 2 BCLTST 003
call test 3 BSETST 004
call test 4 BCOTST 005
call test 5 ROTL1 006
call test 6 ROTL2 007
call test 7 ROTL3 010
call test 10 ROTL4 011
call test 11 ROTL5 012
call test 12 ROTR1 013
call test 13 ROTR2 014
call test 14 ROTR3 015
call test 15 ROTR4 016
call test 16 ROTR5 017
call test 17 CLRTST 020
call test 20 ADDONE 021
call test 21 COMT1 022
call test 22 SCRT1 023
call test 23 SCRT2 024
call test 24 SCRT3 025
call test 25 SCRT4 027
call test 26 ADDT1 031
call test 27 FADRT1 032
call test 30 FADRT2 033
call test 31 iBETA1 035
call test 32 iBETA2 036
call test 33 iBETA3 037
call test 34 iBETA4 040
call test 35 LDAT1 041
call test 36 STAT1 042
call test 37 ADMT1 043
call test 40 LAMT1 044
call test 41 MULT1 045
call test 42 SROT1 046
call test 43 SETT1 047
call test 44 SETT2 050
call test 45 XSKT1 051
call test 46 XSKT2 052
call test 47 AZET1 053
call test 50 APOT1 054
call test 51 LZET1 055
call test 52 HWCT1 056
call test 53 HWCT2 057
call test 54 HWCT3 060
call test 55 HWCT4 061
call test 56 HWCT5 062
call test 57 RANADD 063
call test 60 ATRT1 064
call test 61 IBZT1 065
call test 62 JMPUP 066
call test 63 JMPDWN 067
call test 64 TAPETS 070
call test 65 MTBTST 101
call test 66 DISTST 102
call test 67 DSCTST 103
call test 700 OVFT1 104
call test 701 ZTAT1 105
call test 702 ZCLR1 106
call test 703 ZCLRT2 107
deposit INTREQ 1
call test 704 ENIT1 110
call test 71 MISCTS 111
echo DIAGNOSTICS PASSED
quit
:test
echo *** Test: %1 - %2 ***
load classic-test.linc block=%3 start=400 length=400
load -e classic-test.linc block=%3 start=400 length=400
assert 400==%1
deposit 21 1%4
deposit 21 1%3
go 401
assert P==34
return

View File

@@ -1,4 +1,4 @@
cd linc/tests
cd %~p0
set crt disabled
reset
do classic-test.do

53
linc/tests/tty.do Normal file
View File

@@ -0,0 +1,53 @@
SET CRT DISABLED
RESET
DELETE printer.txt
ATTACH TTY printer.txt
DEPOSIT 0020 RTA
DEPOSIT 0021 BSE i
DEPOSIT 0022 1
DEPOSIT 0023 ATR
DEPOSIT 0024 SET i 12
DEPOSIT 0025 -430
DEPOSIT 0026 XSK i 12
DEPOSIT 0027 JMP 26
DEPOSIT 0030 SET i 1
DEPOSIT 0031 57
DEPOSIT 0032 SET i 2
DEPOSIT 0033 -20
DEPOSIT 0034 LDA i 1
DEPOSIT 0035 STC 44
DEPOSIT 0036 RTA
DEPOSIT 0037 SET i 11
DEPOSIT 0040 -13
DEPOSIT 0041 BCL i
DEPOSIT 0042 1
DEPOSIT 0043 SRO i
DEPOSIT 0045 ADD 42
DEPOSIT 0046 ATR
DEPOSIT 0047 SET i 12
DEPOSIT 0050 -430
DEPOSIT 0051 XSK i 12
DEPOSIT 0052 JMP 51
DEPOSIT 0053 XSK i 11
DEPOSIT 0054 JMP 41
DEPOSIT 0055 XSK i 2
DEPOSIT 0056 JMP 34
DEPOSIT 0057 HLT
DEPOSIT 0060 3220
DEPOSIT 0061 3212
DEPOSIT 0062 3230
DEPOSIT 0063 3230
DEPOSIT 0064 3236
DEPOSIT 0065 3100
DEPOSIT 0066 3250
DEPOSIT 0067 3212
DEPOSIT 0070 3230
DEPOSIT 0071 3212
DEPOSIT 0072 3250
DEPOSIT 0073 3262
DEPOSIT 0074 3240
DEPOSIT 0075 3212
DEPOSIT 0076 3032
DEPOSIT 0077 3024
GO 20
QUIT

53
linc/tests/tty.lap6 Normal file
View File

@@ -0,0 +1,53 @@
@20
RTA
BSE i [MARK STATE
1
ATR
SET i 12 [GIVE TTY TIME
-430
XSK i 12
JMP p-1
SET i 1 [TEXT DATA
2A-1
SET i 2 [TEXT LENGTH
2A-2B
#1A LDA i 1 [GET NEXT CHARACTER
STC 1D
RTA
SET i 11 [OUTPUT 11 BITS
-13 [1 START, 8 DATA
#1B BCL i [AND 2 STOP
#1C 1
SRO i
#1D
ADD 1C
ATR [OUTPUT BIT TO RELAY
SET i 12
-430 [DELAY FOR 110 BAUD
XSK i 12
JMP p-1
XSK i 11
JMP 1B [NEXT BIT
XSK i 2
JMP 1A [NEXT CHARACTER
HLT
[ASCII TEXT WITH ONE 0 START
[BIT AND TWO 1 STOP BITS
#2A 3220 [H
3212 [E
3230 [L
3230 [L
3236 [O
3100 [
3250 [T
3212 [E
3230 [L
3212 [E
3250 [T
3262 [Y
3240 [P
3212 [E
3032 [CR
3024 [LF
#2B