mirror of
https://github.com/open-simh/simh.git
synced 2026-01-13 23:37:13 +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:
parent
1740ac10fa
commit
fd3a79ebb5
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -36,7 +36,7 @@ jobs:
|
||||
- microvax2 vax730 vax750 vax780 vax8200 vax8600 microvax2000 infoserver100 infoserver150vxt microvax3100 microvax3100e vaxstation3100m30 vaxstation3100m38
|
||||
- microvax3100m80 vaxstation4000vlc infoserver1000 nova eclipse hp2100 hp3000 i1401 i1620 s3 altair altairz80 gri i7094
|
||||
- id16 id32 sds lgp h316 cdc1700 swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 sage pdq3 alpha
|
||||
- besm6 imlac tt2500 microvax3900 microvax1 rtvax1000 vaxstation3100m76 vaxstation4000m60
|
||||
- besm6 imlac linc tt2500 microvax3900 microvax1 rtvax1000 vaxstation3100m76 vaxstation4000m60
|
||||
- scelbi 3b2 i701 i704 i7010 i7070 i7080 i7090 sigma uc15 i650 sel32 intel-mds ibm1130
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@ -34,6 +34,7 @@ set(KA10D "${CMAKE_SOURCE_DIR}/PDP10")
|
||||
set(KI10D "${CMAKE_SOURCE_DIR}/PDP10")
|
||||
set(KL10D "${CMAKE_SOURCE_DIR}/PDP10")
|
||||
set(KS10D "${CMAKE_SOURCE_DIR}/PDP10")
|
||||
set(LINCD "${CMAKE_SOURCE_DIR}/linc")
|
||||
set(LGPD "${CMAKE_SOURCE_DIR}/LGP")
|
||||
set(ND100D "${CMAKE_SOURCE_DIR}/ND100")
|
||||
set(NOVAD "${CMAKE_SOURCE_DIR}/NOVA")
|
||||
@ -100,6 +101,7 @@ add_subdirectory(Ibm1130)
|
||||
add_subdirectory(Intel-Systems/Intel-MDS)
|
||||
add_subdirectory(Intel-Systems/scelbi)
|
||||
add_subdirectory(Interdata)
|
||||
add_subdirectory(linc)
|
||||
add_subdirectory(LGP)
|
||||
add_subdirectory(ND100)
|
||||
add_subdirectory(NOVA)
|
||||
|
||||
@ -131,6 +131,10 @@ struct color color_p31 = { p31, ELEMENTS(p31), 100000 };
|
||||
static struct phosphor p39[] = {{0.2, 1.0, 0.0, 0.5, 0.01}};
|
||||
struct color color_p39 = { p39, ELEMENTS(p39), 20000 };
|
||||
|
||||
/* orange phosphor for LINC */
|
||||
static struct phosphor p19[] = {{1.0, 0.7, 0.0, 0.1, 0.22}};
|
||||
struct color color_p19 = { p19, ELEMENTS(p19), 20000 };
|
||||
|
||||
static struct phosphor p40[] = {
|
||||
/* P40 blue-white spot with yellow-green decay (.045s to 10%?) */
|
||||
{0.4, 0.2, 0.924, 0.5, 0.0135},
|
||||
@ -253,6 +257,15 @@ static struct display displays[] = {
|
||||
*/
|
||||
{ DIS_III, "III Display", &color_p39, NULL, 1024, 1024 },
|
||||
|
||||
/*
|
||||
* LINC display
|
||||
* 512x511 addressable points.
|
||||
* The horizontal position is a 9-bit unsigned value, but the
|
||||
* vertical is a one's complement signed 9-bit value with
|
||||
* both +0 and -0 referring to the same position.
|
||||
*/
|
||||
{ DIS_LINC, "LINC Display", &color_p19, NULL, 512, 511 },
|
||||
|
||||
/*
|
||||
* Imlac display
|
||||
* 1024x1024 addressable points.
|
||||
|
||||
@ -47,6 +47,7 @@ enum display_type {
|
||||
DIS_VR17 = 17,
|
||||
DIS_VR20 = 20,
|
||||
DIS_TYPE30 = 30,
|
||||
DIS_LINC = 40,
|
||||
DIS_VR48 = 48,
|
||||
DIS_III = 111,
|
||||
DIS_TYPE340 = 340,
|
||||
|
||||
32
linc/CMakeLists.txt
Normal file
32
linc/CMakeLists.txt
Normal file
@ -0,0 +1,32 @@
|
||||
## LINC simulator
|
||||
##
|
||||
## This is an automagically generated file. Do NOT EDIT.
|
||||
## Any changes you make will be overwritten!!
|
||||
##
|
||||
## Make changes to the SIMH top-level makefile and then run the
|
||||
## "cmake/generate.py" script to regenerate these files.
|
||||
##
|
||||
## cd cmake; python -m generate --help
|
||||
##
|
||||
## ------------------------------------------------------------
|
||||
|
||||
if (HAVE_UNITY_FRAMEWORK AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/unit-tests/CMakeLists.txt")
|
||||
add_subdirectory(unit-tests)
|
||||
endif ()
|
||||
|
||||
add_simulator(linc
|
||||
SOURCES
|
||||
linc_cpu.c
|
||||
linc_crt.c
|
||||
linc_dpy.c
|
||||
linc_kbd.c
|
||||
linc_sys.c
|
||||
linc_tape.c
|
||||
linc_tty.c
|
||||
INCLUDES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
FEATURE_DISPLAY
|
||||
USES_AIO
|
||||
LABEL linc
|
||||
PKG_FAMILY default_family
|
||||
TEST linc)
|
||||
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
|
||||
1068
linc/linc_cpu.c
Normal file
1068
linc/linc_cpu.c
Normal file
File diff suppressed because it is too large
Load Diff
116
linc/linc_crt.c
Normal file
116
linc/linc_crt.c
Normal file
@ -0,0 +1,116 @@
|
||||
/* linc_crt.c: LINC CRT display
|
||||
|
||||
Copyright (c) 2025, Lars Brinkhoff
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
LARS BRINKHOFF BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of Lars Brinkhoff shall not be
|
||||
used in advertising or otherwise to promote the sale, use or other dealings
|
||||
in this Software without prior written authorization from Lars Brinkhoff.
|
||||
*/
|
||||
|
||||
#include "linc_defs.h"
|
||||
#include "sim_video.h"
|
||||
#include "display/display.h"
|
||||
|
||||
/* Function declaration. */
|
||||
static t_stat crt_svc (UNIT *uptr);
|
||||
static t_stat crt_reset (DEVICE *dptr);
|
||||
|
||||
static int crt_quit = FALSE;
|
||||
|
||||
/* Debug */
|
||||
#define DBG 0001
|
||||
|
||||
static UNIT crt_unit = {
|
||||
UDATA (&crt_svc, UNIT_IDLE, 0)
|
||||
};
|
||||
|
||||
static DEBTAB crt_deb[] = {
|
||||
{ "DBG", DBG },
|
||||
{ "VVID", SIM_VID_DBG_VIDEO },
|
||||
{ "KVID", SIM_VID_DBG_KEY },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
#ifdef USE_DISPLAY
|
||||
#define CRT_DIS 0
|
||||
#else
|
||||
#define CRT_DIS DEV_DIS
|
||||
#endif
|
||||
|
||||
DEVICE crt_dev = {
|
||||
"CRT", &crt_unit, NULL, NULL,
|
||||
1, 8, 12, 1, 8, 12,
|
||||
NULL, NULL, &crt_reset,
|
||||
NULL, NULL, NULL,
|
||||
NULL, DEV_DISABLE | DEV_DEBUG, 0, crt_deb,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
static t_stat
|
||||
crt_svc(UNIT *uptr)
|
||||
{
|
||||
#ifdef USE_DISPLAY
|
||||
display_age (100, 0);
|
||||
sim_activate_after (uptr, 100);
|
||||
if (crt_quit) {
|
||||
crt_quit = FALSE;
|
||||
return SCPE_STOP;
|
||||
}
|
||||
#endif
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
static void crt_quit_callback (void)
|
||||
{
|
||||
crt_quit = TRUE;
|
||||
}
|
||||
|
||||
static t_stat
|
||||
crt_reset (DEVICE *dptr)
|
||||
{
|
||||
#ifdef USE_DISPLAY
|
||||
if ((dptr->flags & DEV_DIS) != 0 || (sim_switches & SWMASK('P')) != 0) {
|
||||
display_close (dptr);
|
||||
sim_cancel (&crt_unit);
|
||||
} else {
|
||||
display_reset ();
|
||||
display_init (DIS_LINC, 1, dptr);
|
||||
vid_register_quit_callback (&crt_quit_callback);
|
||||
sim_activate_abs (&crt_unit, 0);
|
||||
}
|
||||
#endif
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
void
|
||||
crt_point (uint16 x, uint16 y)
|
||||
{
|
||||
sim_debug(DBG, &crt_dev, "Point %o,%o\n", x, y);
|
||||
#ifdef USE_DISPLAY
|
||||
if (crt_dev.flags & DEV_DIS)
|
||||
return;
|
||||
display_point(x, y, DISPLAY_INT_MAX, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void crt_toggle_fullscreen(void)
|
||||
{
|
||||
vid_set_fullscreen(!vid_is_fullscreen ());
|
||||
}
|
||||
73
linc/linc_defs.h
Normal file
73
linc/linc_defs.h
Normal file
@ -0,0 +1,73 @@
|
||||
/* linc_defs.h: LINC simulator definitions
|
||||
|
||||
Copyright (c) 2025, Lars Brinkhoff
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
LARS BRINKHOFF BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of Lars Brinkhoff shall not be
|
||||
used in advertising or otherwise to promote the sale, use or other dealings
|
||||
in this Software without prior written authorization from Lars Brinkhoff.
|
||||
|
||||
17-Sept-25 LB New simulator.
|
||||
*/
|
||||
|
||||
#ifndef LINC_DEFS_H_
|
||||
#define LINC_DEFS_H_ 0
|
||||
|
||||
#include "sim_defs.h"
|
||||
|
||||
#define STOP_HALT 1
|
||||
#define STOP_IBKPT 2
|
||||
#define STOP_RBKPT 3
|
||||
#define STOP_WBKPT 3
|
||||
|
||||
#define 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[];
|
||||
extern uint16 M[];
|
||||
|
||||
extern DEVICE cpu_dev;
|
||||
extern DEVICE crt_dev;
|
||||
extern DEVICE dpy_dev;
|
||||
extern DEVICE kbd_dev;
|
||||
extern DEVICE tape_dev;
|
||||
extern DEVICE tty_dev;
|
||||
|
||||
extern t_bool build_dev_tab(void);
|
||||
extern 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_ */
|
||||
40
linc/linc_dpy.c
Normal file
40
linc/linc_dpy.c
Normal file
@ -0,0 +1,40 @@
|
||||
#include "linc_defs.h"
|
||||
|
||||
/* Debug */
|
||||
#define DBG 0001
|
||||
|
||||
static DEBTAB dpy_deb[] = {
|
||||
{ "DBG", DBG },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
DEVICE dpy_dev = {
|
||||
"DPY", NULL, NULL, NULL,
|
||||
0, 8, 12, 1, 8, 12,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, DEV_DEBUG, 0, dpy_deb,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
void dpy_dis(uint16 h, uint16 x, uint16 y)
|
||||
{
|
||||
sim_debug(DBG, &dpy_dev, "DIS %u;%03o, A=%03o\n", h, x, y);
|
||||
/* Y coordinate +0 and -0 both refer to the same vertical position. */
|
||||
if (y < 256)
|
||||
y += 255;
|
||||
else
|
||||
y -= 256;
|
||||
crt_point(x, y);
|
||||
}
|
||||
|
||||
/* Called from display library to get data switches. */
|
||||
void
|
||||
cpu_get_switches (unsigned long *p1, unsigned long *p2)
|
||||
{
|
||||
}
|
||||
|
||||
/* Called from display library to set data switches. */
|
||||
void
|
||||
cpu_set_switches (unsigned long p1, unsigned long p2)
|
||||
{
|
||||
}
|
||||
267
linc/linc_kbd.c
Normal file
267
linc/linc_kbd.c
Normal file
@ -0,0 +1,267 @@
|
||||
#include "linc_defs.h"
|
||||
#include "sim_video.h"
|
||||
|
||||
/* Debug */
|
||||
#define DBG 0001
|
||||
|
||||
#define A (*(uint16 *)cpu_reg[2].loc)
|
||||
#define paused (*(int *)cpu_reg[11].loc)
|
||||
|
||||
static t_stat kbd_reset(DEVICE *dptr);
|
||||
|
||||
static DEBTAB kbd_deb[] = {
|
||||
{ "DBG", DBG },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
DEVICE kbd_dev = {
|
||||
"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
|
||||
};
|
||||
|
||||
/*
|
||||
CASE 0 1 2 3 4 5 6 7 8 9 DEL
|
||||
23 00 01 02 03 04 05 06 07 10 11 13
|
||||
|
||||
Q W E R T Y U I O P i=
|
||||
44 52 30 45 47 54 50 34 42 43 15
|
||||
|
||||
A S D F G H J K L +. -,
|
||||
24 46 27 31 32 33 35 36 37 10 17
|
||||
|
||||
#[ Z X C V B N M pu |⊟ META/EOL
|
||||
22 55 53 26 51 25 41 40 16 21 12
|
||||
|
||||
SPACE
|
||||
14
|
||||
*/
|
||||
|
||||
static int kbd_pressed = 0;
|
||||
static uint16 kbd_code;
|
||||
|
||||
int kbd_struck(void)
|
||||
{
|
||||
if (kbd_pressed)
|
||||
sim_debug(DBG, &kbd_dev, "KST\n");
|
||||
return kbd_pressed;
|
||||
}
|
||||
|
||||
uint16 kbd_key(uint16 wait)
|
||||
{
|
||||
if (kbd_pressed) {
|
||||
sim_debug(DBG, &kbd_dev, "KEY %02o\n", kbd_code);
|
||||
kbd_pressed = 0;
|
||||
return kbd_code;
|
||||
} else if (wait) {
|
||||
sim_debug(DBG, &kbd_dev, "KEY paused\n");
|
||||
paused = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kbd_convert(uint32 key)
|
||||
{
|
||||
switch (key) {
|
||||
case SIM_KEY_0: /* 0 Q */
|
||||
case SIM_KEY_BACKQUOTE:
|
||||
kbd_code = 000;
|
||||
break;
|
||||
case SIM_KEY_1: /* 1 R */
|
||||
kbd_code = 001;
|
||||
break;
|
||||
case SIM_KEY_2: /* 2 S */
|
||||
kbd_code = 002;
|
||||
break;
|
||||
case SIM_KEY_3: /* 3 T */
|
||||
kbd_code = 003;
|
||||
break;
|
||||
case SIM_KEY_4: /* 4 U */
|
||||
kbd_code = 004;
|
||||
break;
|
||||
case SIM_KEY_5: /* 5 V */
|
||||
kbd_code = 005;
|
||||
break;
|
||||
case SIM_KEY_6: /* 6 W */
|
||||
kbd_code = 006;
|
||||
break;
|
||||
case SIM_KEY_7: /* 7 X */
|
||||
kbd_code = 007;
|
||||
break;
|
||||
case SIM_KEY_8: /* 8 Y */
|
||||
kbd_code = 010;
|
||||
break;
|
||||
case SIM_KEY_9: /* 9 Z */
|
||||
kbd_code = 011;
|
||||
break;
|
||||
case SIM_KEY_ENTER:
|
||||
kbd_code = 012;
|
||||
break;
|
||||
case SIM_KEY_BACKSPACE:
|
||||
case SIM_KEY_DELETE:
|
||||
kbd_code = 013;
|
||||
break;
|
||||
case SIM_KEY_SPACE: /* Space ? */
|
||||
case SIM_KEY_SLASH:
|
||||
kbd_code = 014;
|
||||
break;
|
||||
case SIM_KEY_EQUALS: /* i = */
|
||||
kbd_code = 015;
|
||||
break;
|
||||
case SIM_KEY_F1: /* p u */
|
||||
kbd_code = 016;
|
||||
break;
|
||||
case SIM_KEY_MINUS: /* - , */
|
||||
case SIM_KEY_COMMA:
|
||||
kbd_code = 017;
|
||||
break;
|
||||
case SIM_KEY_PERIOD: /* + . */
|
||||
kbd_code = 020;
|
||||
break;
|
||||
case SIM_KEY_BACKSLASH: /* | ⊟ */
|
||||
kbd_code = 021;
|
||||
break;
|
||||
case SIM_KEY_LEFT_BRACKET: /* # [ */
|
||||
case SIM_KEY_LEFT_BACKSLASH:
|
||||
kbd_code = 022;
|
||||
break;
|
||||
case SIM_KEY_SHIFT_L: /* CASE _ */
|
||||
case SIM_KEY_SHIFT_R:
|
||||
kbd_code = 023;
|
||||
break;
|
||||
case SIM_KEY_A: /* A " */
|
||||
case SIM_KEY_SINGLE_QUOTE:
|
||||
kbd_code = 024;
|
||||
break;
|
||||
case SIM_KEY_B: /* B „ */
|
||||
kbd_code = 025;
|
||||
break;
|
||||
case SIM_KEY_C: /* C < */
|
||||
kbd_code = 026;
|
||||
break;
|
||||
case SIM_KEY_D: /* D > */
|
||||
kbd_code = 027;
|
||||
break;
|
||||
case SIM_KEY_E: /* E ] */
|
||||
case SIM_KEY_RIGHT_BRACKET:
|
||||
kbd_code = 030;
|
||||
break;
|
||||
case SIM_KEY_F: /* F ˣ */
|
||||
kbd_code = 031;
|
||||
break;
|
||||
case SIM_KEY_G: /* G : */
|
||||
case SIM_KEY_SEMICOLON:
|
||||
kbd_code = 032;
|
||||
break;
|
||||
case SIM_KEY_H: /* H */
|
||||
kbd_code = 033;
|
||||
break;
|
||||
case SIM_KEY_I: /* I */
|
||||
kbd_code = 034;
|
||||
break;
|
||||
case SIM_KEY_J: /* J */
|
||||
kbd_code = 035;
|
||||
break;
|
||||
case SIM_KEY_K: /* K */
|
||||
kbd_code = 036;
|
||||
break;
|
||||
case SIM_KEY_L: /* L */
|
||||
kbd_code = 037;
|
||||
break;
|
||||
case SIM_KEY_M: /* M */
|
||||
kbd_code = 040;
|
||||
break;
|
||||
case SIM_KEY_N: /* N */
|
||||
kbd_code = 041;
|
||||
break;
|
||||
case SIM_KEY_O: /* O */
|
||||
kbd_code = 042;
|
||||
break;
|
||||
case SIM_KEY_P: /* P */
|
||||
kbd_code = 043;
|
||||
break;
|
||||
case SIM_KEY_Q: /* Q */
|
||||
kbd_code = 044;
|
||||
break;
|
||||
case SIM_KEY_R: /* R */
|
||||
kbd_code = 045;
|
||||
break;
|
||||
case SIM_KEY_S: /* S */
|
||||
kbd_code = 046;
|
||||
break;
|
||||
case SIM_KEY_T: /* T */
|
||||
kbd_code = 047;
|
||||
break;
|
||||
case SIM_KEY_U: /* U */
|
||||
kbd_code = 050;
|
||||
break;
|
||||
case SIM_KEY_V: /* V */
|
||||
kbd_code = 051;
|
||||
break;
|
||||
case SIM_KEY_W: /* W */
|
||||
kbd_code = 052;
|
||||
break;
|
||||
case SIM_KEY_X: /* X */
|
||||
kbd_code = 053;
|
||||
break;
|
||||
case SIM_KEY_Y: /* Y */
|
||||
kbd_code = 054;
|
||||
break;
|
||||
case SIM_KEY_Z: /* Z */
|
||||
kbd_code = 055;
|
||||
break;
|
||||
case SIM_KEY_ALT_L: /* META? */
|
||||
case SIM_KEY_ALT_R:
|
||||
kbd_code = 056;
|
||||
break;
|
||||
// → 57
|
||||
// ? 60
|
||||
// = 61
|
||||
// u 62
|
||||
// , 63
|
||||
// . 64
|
||||
// ⊟ 65
|
||||
// [ 66
|
||||
// _ 67
|
||||
// " 70
|
||||
// „ 71
|
||||
// < 72
|
||||
// > 73
|
||||
// ] 74
|
||||
// ˣ 75
|
||||
// : 76
|
||||
// ʸ 77
|
||||
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);
|
||||
if (paused)
|
||||
A = kbd_code;
|
||||
else
|
||||
kbd_pressed = 1;
|
||||
paused = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
kbd_event(SIM_KEY_EVENT *ev)
|
||||
{
|
||||
if (ev->state == SIM_KEYPRESS_DOWN)
|
||||
kbd_convert(ev->key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static t_stat
|
||||
kbd_reset(DEVICE *dptr)
|
||||
{
|
||||
#ifdef USE_DISPLAY
|
||||
vid_display_kb_event_process = kbd_event;
|
||||
#endif
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
696
linc/linc_sys.c
Normal file
696
linc/linc_sys.c
Normal file
@ -0,0 +1,696 @@
|
||||
/* linc_sys.c: LINC simulator interface
|
||||
|
||||
Copyright (c) 2025, Lars Brinkhoff
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
LARS BRINKHOFF BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of Lars Brinkhoff shall not be
|
||||
used in advertising or otherwise to promote the sale, use or other dealings
|
||||
in this Software without prior written authorization from Lars Brinkhoff.
|
||||
|
||||
17-Sept-25 LB New simulator.
|
||||
*/
|
||||
|
||||
#include "linc_defs.h"
|
||||
|
||||
int32 sim_emax = 1;
|
||||
char sim_name[] = "LINC";
|
||||
|
||||
uint16 M[MEMSIZE];
|
||||
REG *sim_PC = &cpu_reg[0];
|
||||
|
||||
DEVICE *sim_devices[] = {
|
||||
&cpu_dev,
|
||||
&crt_dev,
|
||||
&dpy_dev,
|
||||
&kbd_dev,
|
||||
&tape_dev,
|
||||
&tty_dev,
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *sim_stop_messages[SCPE_BASE] = {
|
||||
"Unknown error",
|
||||
"HALT instruction",
|
||||
"Breakpoint",
|
||||
"Read Breakpoint",
|
||||
"Write Breakpoint"
|
||||
};
|
||||
|
||||
static t_stat
|
||||
get_binary_word(FILE *fileref, uint16 *x)
|
||||
{
|
||||
uint16 y;
|
||||
int c = Fgetc(fileref);
|
||||
if (c == EOF)
|
||||
return SCPE_EOF;
|
||||
y = c & 0xFF;
|
||||
c = Fgetc(fileref);
|
||||
if (c == EOF)
|
||||
return SCPE_IOERR;
|
||||
if (c & 0xF0)
|
||||
return SCPE_FMT;
|
||||
*x = y | (c << 8);
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
static t_stat
|
||||
get_octal_word(FILE *fileref, uint16 *x)
|
||||
{
|
||||
uint16 y, i;
|
||||
int c;
|
||||
for (i = 0;;) {
|
||||
c = Fgetc(fileref);
|
||||
if (c == EOF)
|
||||
return SCPE_EOF;
|
||||
if (c >= '0' && c <= '9') {
|
||||
y = c - '0';
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (; i < 4;) {
|
||||
c = Fgetc(fileref);
|
||||
if (c == EOF)
|
||||
return SCPE_IOERR;
|
||||
if (c < '0' || c > '9')
|
||||
break;
|
||||
y <<= 3;
|
||||
y |= c - '0';
|
||||
i++;
|
||||
}
|
||||
|
||||
*x = y;
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat
|
||||
sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag)
|
||||
{
|
||||
t_stat (*get_word)(FILE *fileref, uint16 *x) = get_binary_word;
|
||||
t_addr addr, length = MEMSIZE, start = 0, end;
|
||||
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 !=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;
|
||||
}
|
||||
|
||||
end = start + length;
|
||||
if (end > MEMSIZE)
|
||||
end = MEMSIZE;
|
||||
|
||||
sim_fseek(fileref, offset, SEEK_SET);
|
||||
|
||||
for (addr = start; addr < end; addr++) {
|
||||
uint16 x;
|
||||
t_stat stat = get_word(fileref, &x);
|
||||
if (stat == SCPE_EOF)
|
||||
return SCPE_OK;
|
||||
if (stat != SCPE_OK)
|
||||
return stat;
|
||||
M[addr] = x;
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_bool build_dev_tab(void)
|
||||
{
|
||||
DEVICE *dev;
|
||||
int i;
|
||||
|
||||
for (i = 0; (dev = sim_devices[i]) != NULL; i++) {
|
||||
;
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
static t_stat fprint_next(FILE *of, uint16 addr)
|
||||
{
|
||||
fprintf(of, "\n");
|
||||
fprint_val(of, ++addr & XMASK, 8, 10, PV_LEFT);
|
||||
fprintf(of, ":\t%04o", M[addr]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void fprint_misc(FILE *of, uint16 insn)
|
||||
{
|
||||
switch (insn) {
|
||||
case 00000:
|
||||
fprintf(of, "HLT");
|
||||
break;
|
||||
case 00005:
|
||||
fprintf(of, "ZTA");
|
||||
break;
|
||||
case 00010:
|
||||
fprintf(of, "ENI");
|
||||
break;
|
||||
case 00011:
|
||||
fprintf(of, "CLR");
|
||||
break;
|
||||
case 00012:
|
||||
fprintf(of, "DIN");
|
||||
break;
|
||||
case 00014:
|
||||
fprintf(of, "ATR");
|
||||
break;
|
||||
case 00015:
|
||||
fprintf(of, "RTA");
|
||||
break;
|
||||
case 00016:
|
||||
fprintf(of, "NOP");
|
||||
break;
|
||||
case 00017:
|
||||
fprintf(of, "COM");
|
||||
break;
|
||||
default:
|
||||
fprintf(of, "MSC %o", insn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static t_stat fprint_index(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
if (insn & IMASK)
|
||||
fprintf(of, " i");
|
||||
if (insn & BMASK)
|
||||
fprintf(of, " %o", insn & BMASK);
|
||||
else
|
||||
return fprint_next(of, addr);
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
static void fprint_set(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "SET");
|
||||
fprint_index(of, insn, addr);
|
||||
fprint_next(of, addr);
|
||||
}
|
||||
|
||||
static void fprint_sam(FILE *of, uint16 insn)
|
||||
{
|
||||
fprintf(of, "SAM%s %o", insn & IMASK ? " i" : "", insn & BMASK);
|
||||
}
|
||||
|
||||
static t_stat fprint_dis(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "DIS");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_xsk(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "XSK");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_rol(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "ROL");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_ror(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "ROR");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_scr(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "SCR");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static void fprint_skip(FILE *of, uint16 insn)
|
||||
{
|
||||
char beta[3];
|
||||
switch (insn & 057) {
|
||||
case 000: case 001: case 002: case 003: case 004: case 005: case 006: case 007:
|
||||
case 010: case 011: case 012: case 013:
|
||||
fprintf(of, "SXL");
|
||||
snprintf(beta, sizeof beta, "%o", insn & BMASK);
|
||||
break;
|
||||
case 015:
|
||||
fprintf(of, "KST");
|
||||
break;
|
||||
case 040: case 041: case 042: case 043: case 044: case 045:
|
||||
fprintf(of, "SNS ");
|
||||
snprintf(beta, sizeof beta, "%o", insn & 7);
|
||||
break;
|
||||
case 046:
|
||||
fprintf(of, "PIN");
|
||||
break;
|
||||
case 050:
|
||||
fprintf(of, "AZE");
|
||||
break;
|
||||
case 051:
|
||||
fprintf(of, "APO");
|
||||
break;
|
||||
case 052:
|
||||
fprintf(of, "LZE");
|
||||
break;
|
||||
case 053:
|
||||
fprintf(of, "IBZ");
|
||||
break;
|
||||
case 054:
|
||||
fprintf(of, "OVF");
|
||||
break;
|
||||
case 055:
|
||||
fprintf(of, "ZZZ");
|
||||
break;
|
||||
default:
|
||||
fprintf(of, "%04o", insn);
|
||||
return;
|
||||
}
|
||||
if (insn & IMASK)
|
||||
fprintf(of, " i" );
|
||||
fprintf(of, " %s", beta);
|
||||
}
|
||||
|
||||
static void fprint_opr(FILE *of, uint16 insn)
|
||||
{
|
||||
switch (insn & 07757) {
|
||||
case 0500: case 0501: case 0502: case 0503: case 0504: case 0505: case 0506: case 0507:
|
||||
case 0510: case 0511: case 0512: case 0513:
|
||||
break;
|
||||
case 0535:
|
||||
case 0515:
|
||||
fprintf(of, "KBD ");
|
||||
break;
|
||||
case 0516:
|
||||
fprintf(of, "RSW ");
|
||||
break;
|
||||
case 0517:
|
||||
fprintf(of, "LSW ");
|
||||
break;
|
||||
default:
|
||||
fprintf(of, "%04o", insn);
|
||||
break;
|
||||
}
|
||||
if (insn & IMASK)
|
||||
fprintf(of, "i" );
|
||||
}
|
||||
|
||||
static void fprint_lmb(FILE *of, uint16 insn)
|
||||
{
|
||||
fprintf(of, "LMB ");
|
||||
}
|
||||
|
||||
static void fprint_umb(FILE *of, uint16 insn)
|
||||
{
|
||||
fprintf(of, "UMB ");
|
||||
}
|
||||
|
||||
static void fprint_tape(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
switch (insn & 0707) {
|
||||
case 0700:
|
||||
fprintf(of, "RDC");
|
||||
break;
|
||||
case 0701:
|
||||
fprintf(of, "RCG");
|
||||
break;
|
||||
case 0702:
|
||||
fprintf(of, "RDE");
|
||||
break;
|
||||
case 0703:
|
||||
fprintf(of, "MTB");
|
||||
break;
|
||||
case 0704:
|
||||
fprintf(of, "WRC");
|
||||
break;
|
||||
case 0705:
|
||||
fprintf(of, "WCG");
|
||||
break;
|
||||
case 0706:
|
||||
fprintf(of, "WRI");
|
||||
break;
|
||||
case 0707:
|
||||
fprintf(of, "CHK");
|
||||
break;
|
||||
}
|
||||
if (insn & IMASK)
|
||||
fprintf(of, " i");
|
||||
if (insn & UMASK)
|
||||
fprintf(of, " u");
|
||||
fprint_next(of, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_lda(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "LDA");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_sta(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "STA");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_ada(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "ADA");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_adm(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "ADM");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_lam(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "LAM");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_mul(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "MUL");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_ldh(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "LDH");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_sth(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "STH");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_shd(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "SHD");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_sae(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "SAE");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_sro(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "SRO");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_bcl(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "BCL");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_bse(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "BSE");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_bco(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "BCO");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static t_stat fprint_dsc(FILE *of, uint16 insn, uint16 addr)
|
||||
{
|
||||
fprintf(of, "DSC");
|
||||
return fprint_index(of, insn, addr);
|
||||
}
|
||||
|
||||
static void fprint_add(FILE *of, uint16 insn)
|
||||
{
|
||||
fprintf(of, "ADD %04o", insn & XMASK);
|
||||
}
|
||||
|
||||
static void fprint_stc(FILE *of, uint16 insn)
|
||||
{
|
||||
fprintf(of, "STC %04o", insn & XMASK);
|
||||
}
|
||||
|
||||
static void fprint_jmp(FILE *of, uint16 insn)
|
||||
{
|
||||
fprintf(of, "JMP %04o", insn & XMASK);
|
||||
}
|
||||
|
||||
t_stat fprint_sym(FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw)
|
||||
{
|
||||
t_stat stat;
|
||||
|
||||
if ((sw & SWMASK ('M')) == 0)
|
||||
return SCPE_ARG;
|
||||
|
||||
if ((stat = build_dev_tab()) != SCPE_OK)
|
||||
return stat;
|
||||
|
||||
switch (*val & 07740) {
|
||||
case 00000:
|
||||
fprint_misc(of, *val);
|
||||
break;
|
||||
case 00040:
|
||||
fprint_set(of, *val, addr);
|
||||
return -1;
|
||||
case 00100:
|
||||
fprint_sam(of, *val);
|
||||
break;
|
||||
case 00140:
|
||||
fprint_dis(of, *val, addr);
|
||||
break;
|
||||
case 00200:
|
||||
fprint_xsk(of, *val, addr);
|
||||
break;
|
||||
case 00240:
|
||||
fprint_rol(of, *val, addr);
|
||||
break;
|
||||
case 00300:
|
||||
fprint_ror(of, *val, addr);
|
||||
break;
|
||||
case 00340:
|
||||
fprint_scr(of, *val, addr);
|
||||
break;
|
||||
case 00400:
|
||||
case 00440:
|
||||
fprint_skip(of, *val);
|
||||
break;
|
||||
case 00500:
|
||||
case 00540:
|
||||
fprint_opr(of, *val);
|
||||
break;
|
||||
case 00600:
|
||||
fprint_lmb(of, *val);
|
||||
break;
|
||||
case 00640:
|
||||
fprint_umb(of, *val);
|
||||
break;
|
||||
case 00700:
|
||||
case 00740:
|
||||
fprint_tape(of, *val, addr);
|
||||
break;
|
||||
case 01000:
|
||||
return fprint_lda(of, *val, addr);
|
||||
case 01040:
|
||||
return fprint_sta(of, *val, addr);
|
||||
case 01100:
|
||||
return fprint_ada(of, *val, addr);
|
||||
case 01140:
|
||||
return fprint_adm(of, *val, addr);
|
||||
case 01200:
|
||||
return fprint_lam(of, *val, addr);
|
||||
case 01240:
|
||||
return fprint_mul(of, *val, addr);
|
||||
case 01300:
|
||||
return fprint_ldh(of, *val, addr);
|
||||
case 01340:
|
||||
return fprint_sth(of, *val, addr);
|
||||
case 01400:
|
||||
return fprint_shd(of, *val, addr);
|
||||
case 01440:
|
||||
return fprint_sae(of, *val, addr);
|
||||
case 01500:
|
||||
return fprint_sro(of, *val, addr);
|
||||
case 01540:
|
||||
return fprint_bcl(of, *val, addr);
|
||||
case 01600:
|
||||
return fprint_bse(of, *val, addr);
|
||||
case 01640:
|
||||
return fprint_bco(of, *val, addr);
|
||||
case 01740:
|
||||
return fprint_dsc(of, *val, addr);
|
||||
case 02000: case 02040: case 02100: case 02140: case 02200: case 02240: case 02300: case 02340:
|
||||
case 02400: case 02440: case 02500: case 02540: case 02600: case 02640: case 02700: case 02740:
|
||||
case 03000: case 03040: case 03100: case 03140: case 03200: case 03240: case 03300: case 03340:
|
||||
case 03400: case 03440: case 03500: case 03540: case 03600: case 03640: case 03700: case 03740:
|
||||
fprint_add(of, *val);
|
||||
break;
|
||||
case 04000: case 04040: case 04100: case 04140: case 04200: case 04240: case 04300: case 04340:
|
||||
case 04400: case 04440: case 04500: case 04540: case 04600: case 04640: case 04700: case 04740:
|
||||
case 05000: case 05040: case 05100: case 05140: case 05200: case 05240: case 05300: case 05340:
|
||||
case 05400: case 05440: case 05500: case 05540: case 05600: case 05640: case 05700: case 05740:
|
||||
fprint_stc(of, *val);
|
||||
break;
|
||||
case 06000: case 06040: case 06100: case 06140: case 06200: case 06240: case 06300: case 06340:
|
||||
case 06400: case 06440: case 06500: case 06540: case 06600: case 06640: case 06700: case 06740:
|
||||
case 07000: case 07040: case 07100: case 07140: case 07200: case 07240: case 07300: case 07340:
|
||||
case 07400: case 07440: case 07500: case 07540: case 07600: case 07640: case 07700: case 07740:
|
||||
fprint_jmp(of, *val);
|
||||
break;
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
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 (*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;
|
||||
}
|
||||
470
linc/linc_tape.c
Normal file
470
linc/linc_tape.c
Normal file
@ -0,0 +1,470 @@
|
||||
#include "linc_defs.h"
|
||||
|
||||
#define POS u3
|
||||
#define SPEED u4
|
||||
#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)
|
||||
|
||||
#define ACC_START 3
|
||||
#define ACC_REVERSE 6
|
||||
#define ACC_STOP 1
|
||||
#define MAX_SPEED (ACC_START * 625) /* 0.1s / 160µs */
|
||||
#define IBZ_WORDS 5
|
||||
#define DATA_WORDS 256
|
||||
#define OTHER_WORDS 7
|
||||
#define BLOCK_WORDS (IBZ_WORDS + DATA_WORDS + OTHER_WORDS)
|
||||
#define START_POS (ACC_START * (625 + (625 * 625))/2)
|
||||
#define MAX_BLOCKS 512
|
||||
#define MAX_POS ((BLOCK_WORDS * MAX_BLOCKS + IBZ_WORDS) * MAX_SPEED)
|
||||
|
||||
#define GOOD_CHECKSUM 07777
|
||||
|
||||
#define RDC 0 /* read tape and check */
|
||||
#define RCG 1 /* read tape group */
|
||||
#define RDE 2 /* read tape */
|
||||
#define MTB 3 /* move toward block */
|
||||
#define WRC 4 /* write tape and check */
|
||||
#define WCG 5 /* write tape group */
|
||||
#define WRI 6 /* write tape */
|
||||
#define CHK 7 /* check tape */
|
||||
|
||||
#define DBG 0001
|
||||
#define DBG_SEEK 0002
|
||||
#define DBG_READ 0004
|
||||
#define DBG_WRITE 0010
|
||||
#define DBG_POS 0020
|
||||
|
||||
static uint16 GROUP;
|
||||
static int16 CURRENT_BLOCK;
|
||||
static int16 WANTED_BLOCK;
|
||||
|
||||
static t_stat tape_svc(UNIT *uptr);
|
||||
static t_stat tape_reset(DEVICE *dptr);
|
||||
static t_stat tape_boot(int32 u, DEVICE *dptr);
|
||||
static t_stat tape_attach(UNIT *uptr, CONST char *cptr);
|
||||
static t_stat tape_detach(UNIT *uptr);
|
||||
|
||||
#define UNIT_FLAGS (UNIT_IDLE|UNIT_FIX|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE)
|
||||
#define CAPACITY (MAX_BLOCKS * DATA_WORDS)
|
||||
|
||||
static UNIT tape_unit[] = {
|
||||
{ UDATA(&tape_svc, UNIT_FLAGS, CAPACITY) },
|
||||
{ UDATA(&tape_svc, UNIT_FLAGS, CAPACITY) },
|
||||
{ UDATA(&tape_svc, UNIT_DIS, 0) },
|
||||
{ UDATA(&tape_svc, UNIT_DIS, 0) },
|
||||
{ UDATA(&tape_svc, UNIT_FLAGS, CAPACITY) },
|
||||
{ UDATA(&tape_svc, UNIT_FLAGS, CAPACITY) }
|
||||
};
|
||||
|
||||
static DEBTAB tape_deb[] = {
|
||||
{ "DBG", DBG },
|
||||
{ "SEEK", DBG_SEEK },
|
||||
{ "READ", DBG_READ },
|
||||
{ "WRITE", DBG_WRITE },
|
||||
{ "POSITION", DBG_POS },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
DEVICE tape_dev = {
|
||||
"TAPE", tape_unit, NULL, NULL,
|
||||
6, 8, 12, 1, 8, 12,
|
||||
NULL, NULL, &tape_reset,
|
||||
&tape_boot, &tape_attach, &tape_detach,
|
||||
NULL, DEV_DEBUG, 0, tape_deb,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
void tape_op(void)
|
||||
{
|
||||
uint16 u = (C & 050) >> 3;
|
||||
UNIT *uptr = &tape_unit[u];
|
||||
|
||||
if ((uptr->flags & UNIT_ATT) == 0)
|
||||
return;
|
||||
|
||||
if (uptr->SPEED < 0) {
|
||||
if ((C & 7) != MTB) {
|
||||
sim_debug(DBG_SEEK, &tape_dev, "Reverse to forward\n");
|
||||
uptr->ACC = ACC_REVERSE;
|
||||
}
|
||||
} else if (uptr->POS >= MAX_POS) {
|
||||
sim_debug(DBG_SEEK, &tape_dev, "End zone; reverse\n");
|
||||
uptr->ACC = ACC_REVERSE;
|
||||
} else if (uptr->SPEED < MAX_SPEED || uptr->ACC < 0) {
|
||||
sim_debug(DBG_SEEK, &tape_dev, "Speed up\n");
|
||||
uptr->ACC = ACC_START;
|
||||
}
|
||||
if (!sim_is_active(uptr))
|
||||
sim_activate(uptr, 20);
|
||||
paused = 1;
|
||||
A = 0;
|
||||
WANTED_BLOCK = B & TMASK;
|
||||
|
||||
switch (C & 7) {
|
||||
case RDC: case RDE: case WRC: case WRI: case CHK:
|
||||
S = 256 * (B >> 9);
|
||||
GROUP = 0;
|
||||
sim_debug(DBG, &tape_dev, "Single tranfer: S=%04o, BN=%03o\n",
|
||||
S, WANTED_BLOCK);
|
||||
break;
|
||||
case RCG: case WCG:
|
||||
S = 256 * (B & 7);
|
||||
GROUP = B >> 9;
|
||||
sim_debug(DBG, &tape_dev, "Group transfer: S=%04o, BN=%03o/%o\n",
|
||||
S, WANTED_BLOCK, GROUP+1);
|
||||
break;
|
||||
case MTB:
|
||||
sim_debug(DBG, &tape_dev, "Move towards block %03o\n", WANTED_BLOCK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static t_stat tape_seek(FILE *fileref, t_addr block, t_addr offset)
|
||||
{
|
||||
offset = DATA_WORDS * block + offset;
|
||||
offset *= 2;
|
||||
if (sim_fseek(fileref, offset, SEEK_SET) == -1)
|
||||
return SCPE_IOERR;
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
static uint16 read_word(FILE *fileref, t_addr block, t_addr offset)
|
||||
{
|
||||
t_stat stat;
|
||||
uint8 data[2];
|
||||
uint16 word;
|
||||
|
||||
stat = tape_seek(fileref, block, offset);
|
||||
if (stat != SCPE_OK)
|
||||
;
|
||||
if (sim_fread(data, 1, 2, fileref) != 2)
|
||||
;
|
||||
if (data[1] & 0xF0)
|
||||
;
|
||||
word = data[1];
|
||||
word <<= 8;
|
||||
word |= data[0];
|
||||
return word;
|
||||
}
|
||||
|
||||
static void write_word(FILE *fileref, t_addr block, t_addr offset, uint16 word)
|
||||
{
|
||||
t_stat stat;
|
||||
uint8 data[2];
|
||||
|
||||
stat = tape_seek(fileref, block, offset);
|
||||
if (stat != SCPE_OK)
|
||||
;
|
||||
data[0] = word & 0xFF;
|
||||
data[1] = word >> 8;
|
||||
if (sim_fwrite(data, 1, 2, fileref) != 2)
|
||||
;
|
||||
}
|
||||
|
||||
/*
|
||||
IBZ BN G block CS C C G BN IBZ
|
||||
5 1 1 256 1 1 1 1 1 5
|
||||
---------------------
|
||||
263
|
||||
--------------------------
|
||||
268
|
||||
|
||||
|
||||
start - 100 ms
|
||||
stop - 300 ms
|
||||
reverse - 100 ms
|
||||
BN to BN at 60 ips - 43 ms
|
||||
block length = 43 ms * 60 inch/s = 2.58 inch
|
||||
|
||||
per word - 160 µs
|
||||
word length = 0.0096 inch
|
||||
words per inch = 104
|
||||
words per second = 6250
|
||||
end zone to end zone - 23 s
|
||||
tape length = 23 * 60 = 1380 inch = 115 feet
|
||||
end zone length = 5 feet
|
||||
|
||||
*/
|
||||
|
||||
static void tape_done(UNIT *uptr)
|
||||
{
|
||||
sim_debug(DBG, &tape_dev, "Done with block\n");
|
||||
|
||||
switch (C & 7) {
|
||||
case RDC: case RCG: case RDE: case CHK:
|
||||
A = GOOD_CHECKSUM;
|
||||
break;
|
||||
case WRI:
|
||||
A = (A ^ 07777) + 1;
|
||||
A &= WMASK;
|
||||
break;
|
||||
case MTB:
|
||||
A = (WANTED_BLOCK + ~CURRENT_BLOCK);
|
||||
A += A >> 12;
|
||||
A &= WMASK;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (C & 7) {
|
||||
case RDC:
|
||||
if (A != GOOD_CHECKSUM) {
|
||||
sim_debug(DBG, &tape_dev, "Check failed; read again\n");
|
||||
S &= ~0377;
|
||||
} else {
|
||||
sim_debug(DBG, &tape_dev, "Check passed\n");
|
||||
paused = 0;
|
||||
}
|
||||
break;
|
||||
case WRC:
|
||||
sim_debug(DBG, &tape_dev, "Block written, go back and check\n");
|
||||
// For now, done.
|
||||
A = GOOD_CHECKSUM;
|
||||
paused = 0;
|
||||
break;
|
||||
case RCG: case WCG:
|
||||
if (GROUP == 0) {
|
||||
sim_debug(DBG, &tape_dev, "Done with group\n");
|
||||
paused = 0;
|
||||
} else {
|
||||
sim_debug(DBG, &tape_dev, "Blocks left in group: %d\n", GROUP);
|
||||
GROUP--;
|
||||
}
|
||||
WANTED_BLOCK = (WANTED_BLOCK + 1) & TMASK;
|
||||
break;
|
||||
case RDE: case WRI:
|
||||
sim_debug(DBG, &tape_dev, "Transfer done\n");
|
||||
paused = 0;
|
||||
break;
|
||||
case MTB:
|
||||
sim_debug(DBG, &tape_dev, "Move towards block done, result %04o\n", A);
|
||||
paused = 0;
|
||||
break;
|
||||
case CHK:
|
||||
sim_debug(DBG, &tape_dev, "Check done\n");
|
||||
paused = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (paused)
|
||||
;
|
||||
else if ((C & IMASK) == 0) {
|
||||
sim_debug(DBG_SEEK, &tape_dev, "Instruction done, stop tape\n");
|
||||
uptr->ACC = uptr->SPEED > 0 ? -ACC_STOP : ACC_STOP;
|
||||
} else {
|
||||
sim_debug(DBG_SEEK, &tape_dev, "Instruction done, keep moving\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void tape_word(UNIT *uptr, uint16 block, uint16 offset)
|
||||
{
|
||||
switch (C & 7) {
|
||||
case RDC: case RCG: case RDE: case CHK:
|
||||
B = read_word(uptr->fileref, block, offset);
|
||||
sim_debug(DBG_READ, &tape_dev,
|
||||
"Read block %03o offset %03o data %04o address %04o\n",
|
||||
block, offset, B, S);
|
||||
if ((C & 7) != CHK)
|
||||
M[S] = B;
|
||||
break;
|
||||
case WRC: case WCG: case WRI:
|
||||
B = M[S];
|
||||
sim_debug(DBG_WRITE, &tape_dev,
|
||||
"Write block %03o offset %03o data %04o address %04o\n",
|
||||
block, offset, B, S);
|
||||
write_word(uptr->fileref, block, offset, B);
|
||||
break;
|
||||
}
|
||||
S = (S+1) & AMASK;
|
||||
A += B;
|
||||
A &= WMASK;
|
||||
}
|
||||
|
||||
static t_stat tape_svc(UNIT *uptr)
|
||||
{
|
||||
long pos, block, offset;
|
||||
|
||||
uptr->SPEED += uptr->ACC;
|
||||
if (uptr->SPEED >= MAX_SPEED) {
|
||||
uptr->SPEED = MAX_SPEED;
|
||||
uptr->ACC = 0;
|
||||
}
|
||||
else if (uptr->SPEED <= -MAX_SPEED) {
|
||||
uptr->SPEED = -MAX_SPEED;
|
||||
uptr->ACC = 0;
|
||||
} else if (uptr->SPEED == 0 && (uptr->ACC == ACC_STOP || uptr->ACC == -ACC_STOP))
|
||||
uptr->ACC = 0;
|
||||
uptr->POS += uptr->SPEED;
|
||||
sim_debug(DBG_POS, &tape_dev, "Speed %d, position %d (block %03o)\n",
|
||||
uptr->SPEED, uptr->POS, uptr->POS / MAX_SPEED / BLOCK_WORDS);
|
||||
|
||||
if (uptr->POS < 0 && uptr->ACC <= 0) {
|
||||
sim_debug(DBG_SEEK, &tape_dev, "End zone; stop tape\n");
|
||||
uptr->ACC = ACC_STOP;
|
||||
} else if(uptr->POS >= MAX_POS && uptr->ACC >= 0) {
|
||||
sim_debug(DBG_SEEK, &tape_dev, "End zone; stop tape\n");
|
||||
uptr->ACC = -ACC_STOP;
|
||||
}
|
||||
|
||||
if (uptr->SPEED != 0)
|
||||
/* The tape takes 160 microseconds between words. This is
|
||||
approximately 20 memory cycles, 8 microseconds each. */
|
||||
sim_activate(uptr, 20);
|
||||
|
||||
pos = uptr->POS / MAX_SPEED;
|
||||
if (pos < 0)
|
||||
return SCPE_OK;
|
||||
|
||||
block = pos / BLOCK_WORDS;
|
||||
offset = pos % BLOCK_WORDS;
|
||||
if (block >= MAX_BLOCKS)
|
||||
return SCPE_OK;
|
||||
|
||||
IBZ = offset < IBZ_WORDS;
|
||||
if (IBZ)
|
||||
sim_debug(DBG, &tape_dev, "Interblock zone\n");
|
||||
|
||||
if (uptr->SPEED > -MAX_SPEED && uptr->SPEED < MAX_SPEED)
|
||||
return SCPE_OK;
|
||||
|
||||
if (!paused)
|
||||
return SCPE_OK;
|
||||
|
||||
if (uptr->SPEED > 0) {
|
||||
if (offset == 5) {
|
||||
/* Forward block number. */
|
||||
CURRENT_BLOCK = (uint16)(block + uptr->OFFSET);
|
||||
sim_debug(DBG_SEEK, &tape_dev,
|
||||
"Found block number %03o; looking for %03o\n",
|
||||
CURRENT_BLOCK, WANTED_BLOCK);
|
||||
if (CURRENT_BLOCK > WANTED_BLOCK) {
|
||||
sim_debug(DBG_SEEK, &tape_dev, "Reverse to find lower block numbers\n");
|
||||
uptr->ACC = -ACC_REVERSE;
|
||||
}
|
||||
if ((C & 7) == MTB)
|
||||
tape_done(uptr);
|
||||
/* Word 6 is a guard. */
|
||||
} else if (offset >= 7 && offset < 263) {
|
||||
if (CURRENT_BLOCK == WANTED_BLOCK)
|
||||
tape_word(uptr, (uint16)block, (uint16)(offset - 7));
|
||||
}
|
||||
else if (offset == 263 && CURRENT_BLOCK == WANTED_BLOCK)
|
||||
/* Checksum here. */
|
||||
tape_done(uptr);
|
||||
}
|
||||
/* Word 264-265 are "C". */
|
||||
/* Word 266 is a guard. */
|
||||
else if (offset == 267 && uptr->SPEED < 0) {
|
||||
/* Reverse block number. */
|
||||
CURRENT_BLOCK = (uint16)(block + uptr->OFFSET);
|
||||
sim_debug(DBG_SEEK, &tape_dev,
|
||||
"Found reverse block number %03o; looking for %03o\n",
|
||||
CURRENT_BLOCK, WANTED_BLOCK);
|
||||
if (CURRENT_BLOCK <= WANTED_BLOCK) {
|
||||
sim_debug(DBG_SEEK, &tape_dev, "Reverse to find higher block numbers\n");
|
||||
uptr->ACC = ACC_REVERSE;
|
||||
uptr->POS -= MAX_SPEED * BLOCK_WORDS;
|
||||
}
|
||||
if ((C & 7) == MTB)
|
||||
tape_done(uptr);
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
static t_stat tape_reset(DEVICE *dptr)
|
||||
{
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
static t_stat tape_boot(int32 unit_num, DEVICE *dptr)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
static t_stat tape_detach(UNIT *uptr)
|
||||
{
|
||||
if (uptr - tape_unit >= 2 && uptr - tape_unit <= 3)
|
||||
return SCPE_ARG;
|
||||
if ((uptr->flags & UNIT_ATT) == 0)
|
||||
return SCPE_OK;
|
||||
if (sim_is_active(uptr))
|
||||
sim_cancel(uptr);
|
||||
return detach_unit(uptr);
|
||||
}
|
||||
124
linc/linc_tty.c
Normal file
124
linc/linc_tty.c
Normal file
@ -0,0 +1,124 @@
|
||||
#include "linc_defs.h"
|
||||
|
||||
|
||||
/* 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);
|
||||
}
|
||||
22
makefile
22
makefile
@ -160,6 +160,10 @@ ifneq (3,${SIM_MAJOR})
|
||||
ifneq (,$(findstring imlac,${MAKECMDGOALS}))
|
||||
VIDEO_USEFUL = true
|
||||
endif
|
||||
# building the LINC needs video support
|
||||
ifneq (,$(findstring linc,${MAKECMDGOALS}))
|
||||
VIDEO_USEFUL = true
|
||||
endif
|
||||
# building the TT2500 needs video support
|
||||
ifneq (,$(findstring tt2500,${MAKECMDGOALS}))
|
||||
VIDEO_USEFUL = true
|
||||
@ -1720,6 +1724,13 @@ IMLAC = ${IMLACD}/imlac_sys.c ${IMLACD}/imlac_cpu.c \
|
||||
IMLAC_OPT = -I ${IMLACD} ${DISPLAY_OPT} ${AIO_CCDEFS}
|
||||
|
||||
|
||||
LINCD = ${SIMHD}/linc
|
||||
LINC = ${LINCD}/linc_cpu.c ${LINCD}/linc_crt.c ${LINCD}/linc_dpy.c \
|
||||
${LINCD}/linc_kbd.c ${LINCD}/linc_sys.c \
|
||||
${LINCD}/linc_tape.c ${LINCD}/linc_tty.c ${DISPLAYL}
|
||||
LINC_OPT = -I ${LINCD} ${DISPLAY_OPT} ${AIO_CCDEFS}
|
||||
|
||||
|
||||
STUBD = ${SIMHD}/stub
|
||||
STUB = ${STUBD}/stub_sys.c ${STUBD}/stub_cpu.c
|
||||
STUB_OPT = -I ${STUBD}
|
||||
@ -2219,7 +2230,7 @@ ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 \
|
||||
swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 intel-mds \
|
||||
scelbi 3b2 3b2-700 i701 i704 i7010 i7070 i7080 i7090 \
|
||||
sigma uc15 pdp10-ka pdp10-ki pdp10-kl pdp10-ks pdp6 i650 \
|
||||
imlac tt2500 sel32
|
||||
imlac linc tt2500 sel32
|
||||
|
||||
all : ${ALL}
|
||||
|
||||
@ -2318,6 +2329,15 @@ ifneq (,$(call find_test,${IMLAC},imlac))
|
||||
$@ $(call find_test,${IMLACD},imlac) ${TEST_ARG}
|
||||
endif
|
||||
|
||||
linc : ${BIN}linc${EXE}
|
||||
|
||||
${BIN}linc${EXE} : ${LINC} ${SIM}
|
||||
${MKDIRBIN}
|
||||
${CC} ${LINC} ${SIM} ${LINC_OPT} ${CC_OUTSPEC} ${LDFLAGS}
|
||||
ifneq (,$(call find_test,${LINC},imlac))
|
||||
$@ $(call find_test,${LINCD},linc) ${TEST_ARG}
|
||||
endif
|
||||
|
||||
stub : ${BIN}stub${EXE}
|
||||
|
||||
${BIN}stub${EXE} : ${STUB} ${SIM}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user