simh-testsetgenerator/PDP10/ka10_pipanel.c
2024-06-08 13:54:58 -04:00

763 lines
26 KiB
C

/* kx10_pipanel.c: PDP-10 PiDP10 front panel.
Copyright (c) 2022, Richard Cornwell
Based on code by Oscar Vermeulen
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
RICHARD CORNWELL 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 Richard Cornwell shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Richard Cornwell
*/
#if PIDP10
/* The following includes are ok since this code can only be run on a
* Rasberry PI under Linux.
*
* To Build this:
* make PIDP10=1 pdp10-ka
*/
#include <pthread.h> /* Needed for pthread */
#include <unistd.h> /* Needed for sleep/geteuid */
#include <sys/types.h>
#include <editline/readline.h>
#include "pinctrl/gpiolib.h"
#include "kx10_defs.h"
extern uint64 SW; /* Switch register */
extern t_addr AS; /* Address switches */
extern uint64 MB; /* Memory Bus register */
extern uint64 MI; /* Memory indicator register */
extern uint8 MI_flag; /* Memory indicator mode */
extern uint32 FLAGS; /* Flags register */
extern uint8 RUN; /* Run flag */
extern uint32 IR; /* Instruction register */
extern uint32 AC; /* Accumulator */
extern uint8 IX; /* Index register */
extern uint8 IND; /* Indirect flag */
extern t_addr AB; /* Memory address register */
extern t_addr PC; /* Program counter register */
#if KA | KI
extern int nxm_stop; /* Stop if non-existent memory access */
extern int adr_cond; /* Address stop condition */
#endif
extern uint8 IOB_PI; /* Pending Interrupt requests */
extern uint8 PIR; /* Current Interrupt requests */
extern uint8 PIH; /* Currently held interrupts */
extern uint8 PIE; /* Currently enabled interrupts */
extern int pi_enable; /* Interrupt system enabled */
extern uint8 prog_stop; /* Program stop */
extern uint8 examine_sw; /* Examine memory */
extern uint8 deposit_sw; /* Deposit memory */
extern uint8 sing_inst_sw;/* Execute single instruction */
extern uint8 xct_sw; /* Execute an instruction */
extern uint8 stop_sw; /* Stop the simulator */
extern uint8 MI_disable; /* Disable MI display */
extern int watch_stop; /* Memory Stop */
extern uint32 rdrin_dev; /* Read in device. */
extern uint8 MI_disable; /* Disable MI */
int repeat_sw; /* Repeat switch state */
int par_stop; /* Parity stop */
int pwr_off; /* Power off system */
int rep_rate; /* Rate of repeat function */
int rep_count; /* Count down to repeat trigger */
/* led row 0 */
#define MB_MASK0 RMASK /* 18-35 */
#define MB_V_0 0 /* right */
/* led row 1 */
#define MB_MASK1 LMASK /* 0-17 */
#define MB_V_1 18 /* right */
/* led row 2 */
#define AB_MASK2 RMASK /* 18-35 */
#define AB_V_2 0 /* right */
/* led row 3 */
#define IX_MASK3 017
#define IX_V_3 0 /* left */
#define IND_LAMP 020
#define AC_MASK3 017
#define AC_V_3 5 /* left */
#define IR_MASK3 0777 /* 0-9 */
#define IR_V_3 9 /* left */
/* led row 4 */
#define PC_MASK4 RMASK /* 18-35 */
#define PC_V_4 0 /* right */
/* led row 5 */
#define PI_IOB_MASK5 0177
#define PI_IOB_V_5 7 /* left */
#define PI_ENB_MASK5 0177
#define PI_ENB_V_5 0 /* left */
#define PROG_STOP_LAMP 0040000
#define USER_LAMP 0100000
#define MEM_STOP_LAMP 0200000
#define PWR_LAMP 0400000
/* led row 6 */
#define PI_REQ_MASK6 0177
#define PI_REQ_V_6 0 /* left */
#define PI_PRO_MASK6 0177
#define PI_PRO_V_6 7 /* left */
#define RUN_LAMP 0040000
#define PION_LAMP 0100000
#define PI_LAMP 0200000
#define MI_LAMP 0400000
/* switch row 0 */
#define SR_MASK0 RMASK
#define SR_V_0 0 /* Left */
/* switch row 1 */
#define SR_MASK1 LMASK
#define SR_V_1 18 /* Left */
/* Switch row 2 */
#define MA_SW_MASK3 RMASK
#define MA_SW_V_3 0 /* Left */
/* Switch row 3 */
#define EXAM_NEXT 000001 /* SW=0 */
#define EXAM_THIS 000002 /* SW=1 */
#define XCT_SW 000004 /* SW=2 Set xct_inst */
#define RESET_SW 000010 /* SW=3 Call reset */
#define STOP_SW 000020 /* SW=4 Set RUN = 0 */
#define CONT_SW 000040 /* SW=5 call sim_instr */
#define START_SW 000100 /* SW=6 Call reset then sim_instr */
#define READ_SW 000200 /* SW=7 Boot function */
#define DEP_NEXT 000400 /* SW=8 */
#define DEP_THIS 001000 /* SW=9 */
/* Switch row 4 */
#define ADR_BRK_SW 000001 /* Address Break */
#define ADR_STOP_SW 000002 /* Address stop */
#define WRITE_SW 000004 /* Write stop */
#define DATA_FETCH 000010 /* Data fetch stop */
#define INST_FETCH 000020 /* Instruct fetch stop */
#define REP_SW 000040 /* Repeat switch */
#define NXM_STOP 000100 /* set nxm_stop */
#define PAR_STOP 000200 /* Nop */
#define SING_CYCL 000400 /* Nop */
#define SING_INST 001000 /* set sing_inst */
int xrows[3] = { 4, 17, 27 };
int xIO = 22; /* GPIO 22: */
int cols[18] = { 21,20,16,12,7,8,25,24,23,18,10,9,11,5,6,13,19,26 };
long intervl = 50000;
struct {
int last_state; /* last state */
int state; /* Stable state */
int debounce; /* Debounce timer */
int changed; /* Switch changed state */
} switch_state[16];
void *blink(void *ptr); /* the real-time GPIO multiplexing process to start up */
pthread_t blink_thread;
int blink_thread_terminate = 0;
t_stat gpio_mux_thread_start()
{
int res;
res = pthread_create(&blink_thread, NULL, blink, &blink_thread_terminate);
if (res) {
return sim_messagef(SCPE_IERR,
"Error creating gpio_mux thread, return code %d\n", res);
}
sim_messagef(SCPE_OK, "Created blink_thread\n");
sleep(2); /* allow 2 sec for multiplex to start */
return SCPE_OK;
}
/*
* Debounce a momentary switch.
*/
static void debounce_sw(int state, int sw)
{
if (switch_state[sw].state == state) {
if (switch_state[sw].debounce != 0) {
switch_state[sw].debounce--;
} else {
if (switch_state[sw].last_state != switch_state[sw].state) {
switch_state[sw].changed = 1;
}
switch_state[sw].last_state = switch_state[sw].state;
}
} else {
switch_state[sw].debounce = 8;
switch_state[sw].changed = 0;
switch_state[sw].state = state;
}
}
static t_addr
read_sw()
{
int col, row, i;
t_uint64 sw;
t_addr new_as;
struct timespec spec;
gpio_set_drive(xIO, DRIVE_HIGH);
for (i = 0; i < 18; i++) {
gpio_set_dir(cols[i], DIR_INPUT);
}
spec.tv_sec = 0;
new_as = 0;
for (row=0; row<5; row++) {
/* Select row address */
for (i = 0; i < 3; i++) {
if ((row & (1 << i)) == 0) {
gpio_set_drive(xrows[i], DRIVE_LOW);
} else {
gpio_set_drive(xrows[i], DRIVE_HIGH);
}
}
spec.tv_nsec = intervl/10;
nanosleep(&spec, NULL);
sw = 0;
for (i = 0; i < 18; i++) {
if (gpio_get_level(cols[i])) {
sw |= 1 << i;
}
}
/* Map row to values */
switch (row) {
default:
case 0:
SW = (SW & SR_MASK1) | ((~sw << SR_V_0) & SR_MASK0);
break;
case 1:
SW = (SW & SR_MASK0) | ((~sw << SR_V_1) & SR_MASK1);
break;
case 2:
new_as = (t_addr)(~sw << MA_SW_V_3) & MA_SW_MASK3;
break;
case 3: /* Momentary switches */
for (col = 0; col < 10; col++) {
int state = (sw & (1 << col)) == 0;
debounce_sw(state, col);
}
break;
case 4:
#if KA | KI
adr_cond = 0;
adr_cond |= ((sw & INST_FETCH) == 0) ? ADR_IFETCH : 0;
adr_cond |= ((sw & DATA_FETCH) == 0) ? ADR_DFETCH : 0;
adr_cond |= ((sw & WRITE_SW) == 0) ? ADR_WRITE : 0;
adr_cond |= ((sw & ADR_STOP_SW) == 0) ? ADR_STOP : 0;
adr_cond |= ((sw & ADR_BRK_SW) == 0) ? ADR_BREAK : 0;
nxm_stop = (sw & NXM_STOP) == 0;
#endif
sing_inst_sw = ((sw & SING_INST) == 0) ||
((sw & SING_CYCL) == 0);
/* PAR_STOP handle special features */
par_stop = (sw & PAR_STOP) == 0;
/* SING_CYCL no function yet */
repeat_sw = (sw & REP_SW) == 0;
break;
}
}
return new_as;
}
void *blink(void *ptr)
{
int *terminate = (int *)ptr;
int col, row, i;
int num_gpios, ret;
uint32 leds;
t_addr new_as;
struct timespec spec;
struct sched_param sp;
spec.tv_sec = 0;
sp.sched_priority = 98; // maybe 99, 32, 31?
if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp))
fprintf(stderr, "warning: failed to set RT priority\n");
ret = gpiolib_init();
if (ret < 0) {
sim_messagef(SCPE_IERR, "Unable to initialize gpiolib- %d\n", ret);
return (void *)-1;
}
num_gpios = ret;
if (num_gpios == 0) {
sim_messagef(SCPE_IERR, "No GPIO chips found\n");
return (void *)-1;
}
ret = gpiolib_mmap();
if (ret) {
if (ret == EACCES && geteuid()) {
sim_messagef(SCPE_IERR, "Must be root\n");
} else {
sim_perror("Failed to mmap gpiolib");
}
return (void *)-1;
}
/* Initialize GPIO pins */
gpio_set_fsel(xIO, GPIO_FSEL_OUTPUT);
gpio_set_dir(xIO, DIR_OUTPUT);
gpio_set_drive(xIO, DRIVE_HIGH);
/* Row address pins */
for (row = 0; row < 3; row++) {
gpio_set_fsel(xrows[row], GPIO_FSEL_OUTPUT);
gpio_set_dir(xrows[row], DIR_OUTPUT);
gpio_set_drive(xrows[row], DRIVE_HIGH);
}
/* Columns */
for (col = 0; col < 10; col++) {
gpio_set_fsel(cols[col], GPIO_FSEL_INPUT);
gpio_set_pull(cols[col], PULL_UP);
}
/* Read initial value of switches */
new_as = read_sw();
sim_messagef(SCPE_OK, "PiDP-10 FP on\n");
/* start the actual multiplexing */
while (*terminate == 0)
{
/* Set to point to switchs while updating digits */
gpio_set_drive(xIO, DRIVE_HIGH);
/* Set direction of columns to output */
for (i = 0; i < 18; i++) {
gpio_set_dir(cols[i], DIR_OUTPUT);
}
/* Display each row */
for (row=0; row<7; row++) { /* 7 rows of LEDS get lit */
switch (row) {
default:
case 0:
leds = (((MI_flag)? MI : MB) & MB_MASK0) >> MB_V_0;
break;
case 1:
leds = (((MI_flag)? MI : MB) & MB_MASK1) >> MB_V_1;
break;
case 2:
if (par_stop) {
leds = rdrin_dev & 0777;
leds |= rep_rate << 12;
leds |= MI_disable << 10;
} else {
leds = (AB & AB_MASK2) >> AB_V_2;
}
break;
case 3:
leds = (IR & IR_MASK3) << IR_V_3;
leds |= (AC & AC_MASK3) << AC_V_3;
leds |= (IND) ? IND_LAMP : 0;
leds |= (IX & IX_MASK3) << IX_V_3;
break;
case 4:
leds = (PC & PC_MASK4) >> PC_V_4;
break;
case 5:
leds = PWR_LAMP;
leds |= (IOB_PI & PI_IOB_MASK5) << PI_IOB_V_5;
leds |= (PIE & PI_ENB_MASK5) << PI_ENB_V_5;
leds |= (FLAGS & USER) ? USER_LAMP : 0;
leds |= (prog_stop) ? PROG_STOP_LAMP: 0;
leds |= (watch_stop) ? MEM_STOP_LAMP: 0;
break;
case 6:
leds = (RUN) ? RUN_LAMP : 0;
leds |= (pi_enable) ? PION_LAMP : 0;
leds |= (PIR & PI_REQ_MASK6) << PI_REQ_V_6;
leds |= (PIH & PI_PRO_MASK6) << PI_PRO_V_6;
leds |= (MI_flag) ? PI_LAMP : MI_LAMP;
break;
}
/* Select correct row to display */
for (i = 0; i < 3; i++) {
if ((row & (1 << i)) == 0) {
gpio_set_drive(xrows[i], DRIVE_LOW);
} else {
gpio_set_drive(xrows[i], DRIVE_HIGH);
}
}
/* Update the leds for output */
for (i = 0; i < 18; i++) {
if ((leds & (1 << i)) == 0) {
gpio_set_drive(cols[i], DRIVE_HIGH);
} else {
gpio_set_drive(cols[i], DRIVE_LOW);
}
}
/* Select output */
gpio_set_drive(xIO, DRIVE_LOW);
spec.tv_nsec = intervl;
nanosleep(&spec, NULL);
/* Deselect output */
gpio_set_drive(xIO, DRIVE_HIGH);
spec.tv_nsec = intervl/10;
nanosleep(&spec, NULL);
}
/* Read in switches */
new_as = read_sw();
/* If running, check for switch changes */
if (par_stop) {
/* Process all momentary switches */
for (col = 0; col < 10; col++) {
if (switch_state[col].changed && switch_state[col].state) {
switch_state[col].changed = 0;
switch (col) {
case 1: /* Examine this */
rep_rate = (new_as >> 14) & 0xf;
break;
case 4: /* Stop function */
stop_sw = 1;
pwr_off = 1;
break;
case 5: /* Continue */
MI_disable = !MI_disable;
if (MI_disable)
MI_flag = 0;
break;
#if KA | KI
case 7: /* ReadIN */
rdrin_dev = 0774 & new_as;
break;
#endif
case 0: /* Examine next */
case 2: /* Execute function */
case 3: /* Reset function */
case 6: /* Start */
case 8: /* Deposit next */
case 9: /* Deposit this */
default:
break;
}
}
}
} else {
AS = new_as;
}
/* Check repeat count */
if (rep_count > 0 && --rep_count == 0) {
for (col = 0; col < 10; col++) {
switch_state[col].changed = switch_state[col].state;
}
}
/* Process switch changes if running */
if (RUN) {
for (col = 0; col < 10; col++) {
if (switch_state[col].changed && switch_state[col].state) {
/* If repeat switch set, trigger timer */
if (repeat_sw) {
rep_count = (rep_rate + 1) * 16;
}
switch (col) {
case 1: /* Examine this */
examine_sw = 1;
MI_flag = 0;
switch_state[col].changed = 0;
break;
case 0: /* Examine next */
case 5: /* Continue */
case 6: /* Start */
case 7: /* ReadIN */
case 8: /* Deposit next */
default:
switch_state[col].changed = 0;
break;
case 2: /* Execute function */
xct_sw = 1;
switch_state[col].changed = 0;
break;
case 3: /* Reset function */
stop_sw = 1;
break;
case 4: /* Stop function */
stop_sw = 1;
switch_state[col].changed = 0;
break;
case 9: /* Deposit this */
deposit_sw = 1;
MI_flag = 0;
switch_state[col].changed = 0;
break;
}
}
}
}
/* done with reading the switches,
* so start the next cycle of lighting up LEDs
*/
}
/* received terminate signal, close down */
gpio_set_drive(xIO, DRIVE_HIGH);
for (i = 0; i < 18; i++) {
gpio_set_dir(cols[i], DIR_INPUT);
}
for (row = 0; row < 3; row++) {
gpio_set_drive(xrows[row], DRIVE_HIGH);
}
}
volatile int input_wait;
static char *input_buffer = NULL;
/*
* Handler for EditLine package when line is complete.
*/
static void
read_line_handler(char *line)
{
if (line != NULL) {
input_buffer = line;
input_wait = 0;
add_history(line);
}
}
/*
* Process input from stdin or switches.
*/
static char *
vm_read(char *cptr, int32 sz, FILE *file)
{
struct timeval tv = {0,10000}; /* Wait for 10ms */
fd_set read_set;
int fd = fileno(file); /* What to wait on */
int col;
if (input_buffer != NULL)
free(input_buffer);
rl_callback_handler_install(sim_prompt, (rl_vcpfunc_t*) &read_line_handler);
input_wait = 1;
input_buffer = NULL;
while (input_wait) {
FD_ZERO(&read_set);
FD_SET(fd, &read_set);
tv.tv_sec = 0;
tv.tv_usec = 10000;
(void)select(fd+1, &read_set, NULL, NULL, &tv);
if (FD_ISSET(fd, &read_set)) {
rl_callback_read_char();
} else {
if (pwr_off) {
if ((input_buffer = (char *)malloc(20)) != 0) {
strcpy(input_buffer, "quit\r");
stop_sw = 1;
pwr_off = 0;
input_wait = 0;
}
break;
}
/* Process switches */
for (col = 0; col < 10; col++) {
if (switch_state[col].changed && switch_state[col].state) {
/* If repeat switch set, trigger timer */
if (repeat_sw) {
rep_count = (rep_rate + 1) * 16;
}
switch (col) {
case 0: /* Examine next */
AB++;
MB = (AB < 020) ? FM[AB] : M[AB];
MI_flag = 0;
break;
case 1: /* Examine this */
AB = AS;
MB = (AS < 020) ? FM[AS] : M[AS];
MI_flag = 0;
break;
case 2: /* Execute function */
if ((input_buffer = (char *)malloc(20)) != 0) {
strcpy(input_buffer, "step\r");
xct_sw = 1;
input_wait = 0;
}
break;
case 3: /* Reset function */
if ((input_buffer = (char *)malloc(20)) != 0) {
strcpy(input_buffer, "reset all\r");
input_wait = 0;
}
break;
case 4: /* Stop function */
break;
case 5: /* Continue */
if ((input_buffer = (char *)malloc(10)) != 0) {
strcpy(input_buffer,
(sing_inst_sw) ? "step\r" : "cont\r");
input_wait = 0;
}
break;
case 6: /* Start */
if ((input_buffer = (char *)malloc(20)) != 0) {
sprintf(input_buffer, "run %06o\r", AS);
input_wait = 0;
}
break;
#if KA | KI
case 7: /* ReadIN */
if ((input_buffer = (char *)malloc(20)) != 0) {
DEVICE *dptr;
int i;
/* Scan all devices to find match */
for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
DIB *dibp = (DIB *) dptr->ctxt;
if (dibp && !(dptr->flags & DEV_DIS) &&
(dibp->dev_num == (rdrin_dev & 0774))) {
if (dptr->numunits > 1)
sprintf(input_buffer, "boot %s0\r",
dptr->name);
else
sprintf(input_buffer, "boot %s\r",
dptr->name);
input_wait = 0;
break;
}
}
}
/* If we did not find a boot device, free command */
if (input_wait) {
free(input_buffer);
input_buffer = NULL;
sim_messagef(SCPE_OK, "Device %03o not found\n",
rdrin_dev);
}
break;
#endif
case 8: /* Deposit next */
AB++;
if (AB < 020) {
FM[AB] = SW;
MB = FM[AB];
} else {
M[AB] = SW;
MB = M[AB];
}
MI_flag = 0;
break;
case 9: /* Deposit this */
AB = AS;
if (AS < 020) {
FM[AS] = SW;
MB = FM[AS];
} else {
M[AS] = SW;
MB = M[AS];
}
MI_flag = 0;
break;
}
switch_state[col].changed = 0;
}
}
}
}
rl_callback_handler_remove();
return input_buffer;
}
static void
vm_post(t_bool from_scp)
{
}
/*
* Start panel thread, and install console read functions.
*/
t_stat pi_panel_start(void)
{
int terminate = 1;
int i,j;
t_stat r;
/* start up multiplexing thread */
r = gpio_mux_thread_start();
sim_vm_read = &vm_read;
sim_vm_post = &vm_post;
return r;
}
/*
* Stop display thread.
*/
void pi_panel_stop(void)
{
if (blink_thread_terminate == 0) {
blink_thread_terminate=1;
sim_vm_read = NULL;
sleep (2); /* allow threads to close down */
}
}
#endif