mirror of
https://github.com/simh/simh.git
synced 2026-02-28 17:20:20 +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:
committed by
Mark Pizzolato
parent
f8c367d75d
commit
68c9c5536f
@@ -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
135
linc/README.md
Normal 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
|
||||
557
linc/linc_cpu.c
557
linc/linc_cpu.c
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 ());
|
||||
}
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
190
linc/linc_kbd.c
190
linc/linc_kbd.c
@@ -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;
|
||||
}
|
||||
|
||||
169
linc/linc_sys.c
169
linc/linc_sys.c
@@ -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;
|
||||
}
|
||||
|
||||
121
linc/linc_tape.c
121
linc/linc_tape.c
@@ -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;
|
||||
|
||||
124
linc/linc_tty.c
124
linc/linc_tty.c
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cd linc/tests
|
||||
cd %~p0
|
||||
set crt disabled
|
||||
reset
|
||||
do classic-test.do
|
||||
|
||||
53
linc/tests/tty.do
Normal file
53
linc/tests/tty.do
Normal 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
53
linc/tests/tty.lap6
Normal 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
|
||||
Reference in New Issue
Block a user