simh-testsetgenerator/PDP11/pdp11_tv.c
Mark Pizzolato decbe5b76b Various simulators: Set line endings to CRLF for consistency, remove stray tabs
Project standard source code has tabs converted to spaces and CRLF line
endings.

Other text files have CRLF line endings.
2023-03-19 16:51:27 -04:00

628 lines
17 KiB
C

#ifdef USE_DISPLAY
/* pdp11_tv.c: TV, Logo TV raster display
Copyright (c) 2022, 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
THE AUTHORS 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 names of the authors shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the authors.
*/
#include "pdp11_defs.h"
#include "sim_video.h"
t_stat tv_rd(int32 *data, int32 PA, int32 access);
t_stat tv_wr(int32 data, int32 PA, int32 access);
t_stat tv_svc(UNIT *uptr);
t_stat tv_reset(DEVICE *dptr);
const char *tv_description (DEVICE *dptr);
#define TV_WINDOWS 2
#define TV_WIDTH 576 /* Display width. */
#define TV_HEIGHT 454 /* Display height. */
#define TV_PIXELS (TV_WIDTH * TV_HEIGHT) /* Total number of pixels. */
#define TV_KEYS 16 /* Max keys in buffer. */
static uint32 tv_surface[TV_WINDOWS][TV_PIXELS];
static uint32 tv_palette[TV_WINDOWS][2];
static VID_DISPLAY *tv_vptr[TV_WINDOWS];
static char tv_updated[TV_WINDOWS];
static int tv_keys = 0;
static uint16 tv_key[TV_KEYS]; /* Buffer for LKBB. */
static uint16 COLORD;
static uint16 VIDSW; /* Video switch register. */
static uint16 COLORA;
static uint8 tv_source[256];
static uint8 tv_display[256];
static uint16 TVINCR;
static uint16 TVSEL; /* Frame buffer select. */
static uint16 TVRADR;
static uint16 tvdata;
static uint16 TVWC;
static uint16 TVDADR;
static uint16 TVSHR;
static uint16 TVMSK;
static uint16 TVDWIN;
static uint16 TVRWIN;
static uint16 TVCNSL[64];
#define FB (16*1024)
static uint16 RAM[64*FB];
#define BASE ((TVSEL & 077) * FB)
/* VIDSW bits. */
// Low byte is the console number.
// High byte is the video source.
/* TVINCR bits. */
#define TVINC 0077 /* Mask for the increment. */
/* TVSEL bits. */
#define TVRCNS 0077 /* The console number mask. */
#define TVNSH 0000 /* No shift write mode. */
#define TVIOR 0100 /* The inclusive or mode. */
#define TVXOR 0200 /* The xor mode. */
#define TVMOV 0300 /* The move function. */
/* TVSHR bits. */
#define TVSHCN 0017 /* The shift count. */
/* TVCNSL bits. */
#define SCROLL 007777 /* Scroll pointer. */
#define REVSCR 010000 /* Reverse video. */
#define IOLN_TV 064
DIB tv_dib = {
IOBA_AUTO, IOLN_TV, &tv_rd, &tv_wr,
0, 0, 0, {NULL}, IOLN_TV
};
UNIT tv_unit = {
UDATA (&tv_svc, UNIT_IDLE, 0), 0
};
REG tv_reg[] = {
{ ORDATAD(LKBB, tv_key[0], 16, "Lebel keyboard interface") },
{ ORDATAD(COLORA, COLORA, 16, "Color map address") },
{ ORDATAD(VIDSW, VIDSW, 16, "Video switch") },
{ ORDATAD(COLORD, COLORD, 16, "Color map data") },
{ ORDATAD(TVINCR, TVINCR, 16, "Increment") },
{ ORDATAD(TVSEL, TVSEL, 16, "Console select") },
{ ORDATAD(TVRADR, TVRADR, 16, "Regular transfer address") },
{ ORDATAD(TVWC, TVWC, 16, "Word count") },
{ ORDATAD(TVDADR, TVDADR, 16, "Disk transfer address") },
{ ORDATAD(TVSHR, TVSHR, 16, "Shift register") },
{ ORDATAD(TVMSK, TVMSK, 16, "Mask") },
{ ORDATAD(TVDWIN, TVDWIN, 16, "Disk data window") },
{ ORDATAD(TVRWIN, TVRWIN, 16, "Regular data window") },
{ ORDATAD(TVCNSL, TVCNSL[0], 16, "Console status") },
{ NULL }
};
MTAB tv_mod[] = {
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 020, "ADDRESS", "ADDRESS",
&set_addr, &show_addr, NULL, "Bus address" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "VECTOR", "VECTOR",
&set_vec, &show_vec, NULL, "Interrupt vector" },
{ MTAB_XTD|MTAB_VDV, 0, NULL, "AUTOCONFIGURE",
&set_addr_flt, NULL, NULL, "Enable autoconfiguration of address & vector" },
{ 0 } };
#define DBG_IO 0001
#define DBG_VID 0002
#define DBG_KEY 0004
DEBTAB tv_deb[] = {
{ "IO", DBG_IO, "IO page" },
{ "VID", DBG_VID, "video" },
{ "KEY", DBG_KEY, "keyboard" },
{ NULL, 0 }
};
DEVICE tv_dev = {
"TV", &tv_unit, tv_reg, tv_mod,
1, 8, 16, 1, 8, 16,
NULL, NULL, &tv_reset,
NULL, NULL, NULL,
&tv_dib, DEV_DIS | DEV_DISABLE | DEV_UBUS | DEV_DEBUG,
0, tv_deb, NULL, NULL, NULL, NULL, NULL,
&tv_description
};
static void
render_word (uint8 buffer, uint16 address)
{
uint8 display = tv_display[buffer];
uint16 data;
int i;
if (display >= TV_WINDOWS)
return;
buffer &= 077;
address /= 2;
address += (TVCNSL[buffer] & SCROLL) << 2;
address &= FB-1;
data = RAM[FB * buffer + address];
for (i = 0; i < 16; i++) {
int pixel = (data & (0100000 >> i)) ? 0 : 1;
if (TVCNSL[buffer] & REVSCR)
pixel ^= 1;
tv_surface[display][address * 16 + i] =
tv_palette[display][pixel];
}
}
static void
render_display (uint16 display)
{
uint8 buffer = tv_source[display];
int i;
if (display >= TV_WINDOWS)
return;
sim_debug (DBG_VID, &tv_dev, "Render display %d buffer %d\n",
display, buffer);
for (i = 0; i < (TV_PIXELS / 8); i += 2)
render_word (buffer, i);
tv_updated[display] = 1;
}
static void
tv_alu (void)
{
uint16 data = RAM[BASE + (TVRADR/2)];
uint16 data2 = tvdata;
int sh = (TVSHR & TVSHCN);
if (TVSEL & TVMOV)
data2 = (data2 << sh) | (data2 >> (16 - sh));
data2 &= ~TVMSK;
switch (TVSEL & TVMOV) {
case TVIOR: data |= data2; break;
case TVXOR: data ^= data2; break;
case TVNSH:
case TVMOV: data = (data & TVMSK) | data2; break;
}
RAM[BASE + (TVRADR/2)] = data;
render_word (TVSEL & 077, TVRADR);
tv_updated[tv_display[TVSEL & 077]] = 1;
TVRADR += 2 * (TVINCR & TVINC);
}
static void
tv_loop (void)
{
while (TVWC != 0) {
tv_alu ();
TVWC++;
}
}
t_stat
tv_rd(int32 *data, int32 PA, int32 access)
{
t_stat stat = SCPE_OK;
*data = 0;
switch (PA & 077) {
case 000:
if (tv_keys > 0)
*data = tv_key[--tv_keys];
else
*data = 0;
sim_debug (DBG_IO, &tv_dev, "READ LKBB %06o\n", *data);
break;
case 002:
*data = COLORD;
sim_debug (DBG_IO, &tv_dev, "READ COLORD %06o\n", *data);
break;
case 004:
*data = VIDSW;
sim_debug (DBG_IO, &tv_dev, "READ VIDSW %06o\n", *data);
break;
case 006:
*data = COLORA;
sim_debug (DBG_IO, &tv_dev, "READ COLORA %06o\n", *data);
break;
case 040:
*data = TVINCR;
sim_debug (DBG_IO, &tv_dev, "READ TVINCR %06o\n", *data);
break;
case 042:
*data = TVSEL;
sim_debug (DBG_IO, &tv_dev, "READ TVSEL %06o\n", *data);
break;
case 044:
*data = TVRADR;
sim_debug (DBG_IO, &tv_dev, "READ TVRADR %06o\n", *data);
break;
case 046:
*data = TVWC;
sim_debug (DBG_IO, &tv_dev, "READ TVWC %06o\n", *data);
break;
case 050:
*data = TVDADR;
sim_debug (DBG_IO, &tv_dev, "READ TVDADR %06o\n", *data);
break;
case 052:
*data = TVSHR;
sim_debug (DBG_IO, &tv_dev, "READ TVSHR %06o\n", *data);
break;
case 053:
*data = TVSHR >> 8;
sim_debug (DBG_IO, &tv_dev, "READ TVSHR+1 %06o\n", *data);
break;
case 054:
*data = TVMSK;
sim_debug (DBG_IO, &tv_dev, "READ TVMSK %06o\n", *data);
break;
case 056:
*data = TVDWIN;
sim_debug (DBG_IO, &tv_dev, "READ TVDWIN %06o\n", *data);
break;
case 060:
*data = RAM[BASE + (TVRADR/2)];
sim_debug (DBG_IO, &tv_dev, "READ TVRWIN[%06o] %06o\n",
TVRADR, *data);
break;
case 062:
*data = TVCNSL[TVSEL & 077];
sim_debug (DBG_IO, &tv_dev, "READ TVCNSL %06o\n", *data);
break;
default:
sim_debug (DBG_IO, &tv_dev, "READ %06o\n", PA);
break;
}
return stat;
}
t_stat
tv_wr(int32 data, int32 PA, int32 access)
{
t_stat stat = SCPE_OK;
uint16 x;
const char *a = "WRITE";
if (access == WRITEB)
a = "WRITEB";
switch (PA & 077) {
case 000:
sim_debug (DBG_IO, &tv_dev, "%s LKBB %06o\n", a, data);
break;
case 002:
sim_debug (DBG_IO, &tv_dev, "%s COLORD %06o\n", a, data);
COLORD = data;
break;
case 004:
sim_debug (DBG_IO, &tv_dev, "%s VIDSW %06o\n", a, data);
tv_source[data >> 8] = data & 0377;
tv_display[data & 0377] = data >> 8;
render_display (data >> 8);
break;
case 006:
sim_debug (DBG_IO, &tv_dev, "%s COLORA %06o\n", a, data);
COLORA = data;
break;
case 040:
sim_debug (DBG_IO, &tv_dev, "%s TVINCR %06o\n", a, data);
TVINCR = data;
break;
case 042:
sim_debug (DBG_IO, &tv_dev, "%s TVSEL %06o\n", a, data);
if (access == WRITEB) {
data &= 0377;
data |= TVSEL & 0177400;
}
TVSEL = data;
break;
case 044:
sim_debug (DBG_IO, &tv_dev, "%s TVRADR %06o\n", a, data);
TVRADR = data;
break;
case 046:
sim_debug (DBG_IO, &tv_dev, "%s TVWC %06o\n", a, data);
TVWC = data;
tv_loop ();
break;
case 050:
sim_debug (DBG_IO, &tv_dev, "%s TVDADR %06o\n", a, data);
TVDADR = data;
break;
case 052:
sim_debug (DBG_IO, &tv_dev, "%s TVSHR %06o\n", a, data);
TVSHR = data;
break;
case 053:
sim_debug (DBG_IO, &tv_dev, "%s TVSHR+1 %06o\n", a, data);
TVSHR &= 0377;
TVSHR |= (data & 0377) << 8;
break;
case 054:
sim_debug (DBG_IO, &tv_dev, "%s TVMSK %06o\n", a, data);
TVMSK = data;
break;
case 056:
sim_debug (DBG_IO, &tv_dev, "%s TVDWIN %06o\n", a, data);
TVDWIN = data;
break;
case 060:
sim_debug (DBG_IO, &tv_dev, "%s TVRWIN[%06o] %06o\n",
a, TVRADR, data);
tvdata = data;
tv_alu ();
break;
case 062:
sim_debug (DBG_IO, &tv_dev, "%s TVCNSL %06o\n", a, data);
x = TVCNSL[TVSEL & 077];
TVCNSL[TVSEL & 077] = data;
if (x ^ data)
render_display (tv_display[TVSEL & 077]);
break;
default:
sim_debug (DBG_IO, &tv_dev, "%s %06o %06o\n", a, PA, data);
break;
}
return stat;
}
static void
toggle_fullscreen (VID_DISPLAY *vptr)
{
vid_set_fullscreen_window (vptr, !vid_is_fullscreen_window (vptr));
}
#define KEY(NORMAL, SHIFTED) (shifted ? (SHIFTED) : (NORMAL))
#define CTL(NORMAL, SHIFTED, CTLED) \
(control ? (CTLED) : KEY(NORMAL, SHIFTED))
static uint16
translate_key (SIM_KEY_EVENT *ev)
{
static int shifted = 0;
static int control = 0;
if (ev->state == SIM_KEYPRESS_UP) {
switch (ev->key) {
case SIM_KEY_F11:
toggle_fullscreen (ev->vptr);
break;
case SIM_KEY_SHIFT_L:
case SIM_KEY_SHIFT_R:
shifted = 0;
break;
case SIM_KEY_CTRL_L:
case SIM_KEY_CTRL_R:
case SIM_KEY_CAPS_LOCK:
control = 0;
break;
}
return 0;
}
switch (ev->key) {
case SIM_KEY_SHIFT_L:
case SIM_KEY_SHIFT_R:
shifted = 1;
return 0;
case SIM_KEY_CTRL_L:
case SIM_KEY_CTRL_R:
case SIM_KEY_CAPS_LOCK:
control = 1;
return 0;
case SIM_KEY_0:
return KEY (0060, 0004);
case SIM_KEY_1:
return KEY (0061, 0041);
case SIM_KEY_2:
return KEY (0062, 0052);
case SIM_KEY_3:
return KEY (0063, 0043);
case SIM_KEY_4:
return KEY (0064, 0044);
case SIM_KEY_5:
return KEY (0065, 0045);
case SIM_KEY_6:
return CTL (0066, 0137, 0037);
case SIM_KEY_7:
return KEY (0067, 0136);
case SIM_KEY_8:
return KEY (0070, 0030);
case SIM_KEY_9:
return KEY (0071, 0003);
case SIM_KEY_A:
return CTL (0141, 0101, 0000);
case SIM_KEY_B:
return CTL (0142, 0102, 0000);
case SIM_KEY_C:
return CTL (0143, 0103, 0000);
case SIM_KEY_D:
return CTL (0144, 0104, 0002);
case SIM_KEY_E:
return CTL (0145, 0105, 0000);
case SIM_KEY_F:
return CTL (0146, 0106, 0014);
case SIM_KEY_G:
return CTL (0147, 0107, 0034);
case SIM_KEY_H:
return CTL (0150, 0110, 0000);
case SIM_KEY_I:
return CTL (0151, 0111, 0012);
case SIM_KEY_J:
return CTL (0152, 0112, 0007);
case SIM_KEY_K:
return CTL (0153, 0113, 0000);
case SIM_KEY_L:
return CTL (0154, 0114, 0013);
case SIM_KEY_M:
return CTL (0155, 0115, 0015);
case SIM_KEY_N:
return CTL (0156, 0116, 0000);
case SIM_KEY_O:
return CTL (0157, 0117, 0000);
case SIM_KEY_P:
return CTL (0160, 0120, 0000);
case SIM_KEY_Q:
return CTL (0161, 0121, 0000);
case SIM_KEY_R:
return CTL (0162, 0122, 0000);
case SIM_KEY_S:
return CTL (0163, 0123, 0000);
case SIM_KEY_T:
return CTL (0164, 0124, 0000);
case SIM_KEY_U:
return CTL (0165, 0125, 0177);
case SIM_KEY_V:
return CTL (0166, 0126, 0000);
case SIM_KEY_W:
return CTL (0167, 0127, 0000);
case SIM_KEY_X:
return CTL (0170, 0130, 0000);
case SIM_KEY_Y:
return CTL (0171, 0131, 0000);
case SIM_KEY_Z:
return CTL (0172, 0132, 0000);
case SIM_KEY_BACKQUOTE:
return KEY (0050, 0051);
case SIM_KEY_MINUS:
return CTL (0020, 0033, 005);
case SIM_KEY_EQUALS:
return KEY (0055, 0001);
case SIM_KEY_LEFT_BRACKET:
return CTL (0133, 0000, 0011);
case SIM_KEY_RIGHT_BRACKET:
return CTL (0134, 0000, 0036);
case SIM_KEY_SEMICOLON:
return KEY (0042, 0047);
case SIM_KEY_SINGLE_QUOTE:
return KEY (0046, 0073);
case SIM_KEY_BACKSLASH:
case SIM_KEY_LEFT_BACKSLASH:
return CTL (0032, 0176, 0035);
case SIM_KEY_COMMA:
return KEY (0054, 0075);
case SIM_KEY_PERIOD:
return KEY (0056, 0140);
case SIM_KEY_SLASH:
return KEY (0027, 0057);
case SIM_KEY_ESC:
case SIM_KEY_F1:
return 011;
case SIM_KEY_BACKSPACE:
case SIM_KEY_DELETE:
return 031;
case SIM_KEY_TAB:
return 012;
case SIM_KEY_ENTER:
return 015;
case SIM_KEY_SPACE:
return 040;
default:
return 0;
}
}
static uint16
keyboard_number (VID_DISPLAY *vptr, uint16 code)
{
int i;
for (i = 0; i < TV_WINDOWS; i++) {
if (vptr == tv_vptr[i])
return code | (i << 8);
}
return 0;
}
t_stat tv_svc(UNIT *uptr)
{
SIM_KEY_EVENT ev;
uint16 key;
int i;
for (i = 0; i < TV_WINDOWS; i++) {
if (!tv_updated[i])
continue;
vid_draw_window (tv_vptr[i], 0, 0, TV_WIDTH, TV_HEIGHT, tv_surface[i]);
vid_refresh_window (tv_vptr[i]);
tv_updated[i] = 0;
sim_debug (DBG_VID, &tv_dev, "Display %d refreshed.\n", i);
}
while (vid_poll_kb (&ev) == SCPE_OK) {
key = translate_key (&ev);
if (key != 0)
key = keyboard_number (ev.vptr, key);
sim_debug (DBG_KEY, &tv_dev, "Keyboard %06o.\n", key);
if (tv_keys < TV_KEYS)
tv_key[tv_keys++] = key;
}
sim_activate_after (&tv_unit, 10000);
return SCPE_OK;
}
t_stat tv_reset(DEVICE *dptr)
{
t_stat r;
int i;
if (dptr->flags & DEV_DIS || sim_switches & SWMASK('P')) {
for (i = 0; i < TV_WINDOWS; i++) {
if (tv_vptr[i] != NULL)
vid_close_window (tv_vptr[i]);
}
memset (tv_vptr, 0, sizeof tv_vptr);
memset (tv_palette, 0, sizeof tv_palette);
memset (tv_updated, 0, sizeof tv_updated);
sim_cancel (&tv_unit);
return SCPE_OK;
}
for (i = 0; i < TV_WINDOWS; i++) {
if (tv_vptr[i] == NULL) {
char title[40];
snprintf (title, sizeof title, "Display %d", i);
r = vid_open_window (&tv_vptr[i], &tv_dev, title, TV_WIDTH, TV_HEIGHT, 0);
if (r != SCPE_OK)
return r;
tv_palette[i][0] = vid_map_rgb_window (tv_vptr[i], 0x00, 0x00, 0x00);
tv_palette[i][1] = vid_map_rgb_window (tv_vptr[i], 0x00, 0xFF, 0x30);
memset (TVCNSL, 0, sizeof TVCNSL);
memset (tv_source, 0, sizeof tv_source);
memset (tv_display, 0, sizeof tv_display);
render_display (i);
}
}
tv_keys = 0;
sim_activate (&tv_unit, 1);
return SCPE_OK;
}
const char *tv_description (DEVICE *dptr)
{
return "Raster display controller for MIT Logo PDP-11/45";
}
#else /* USE_DISPLAY not defined */
char pdp11_tv_unused; /* sometimes empty object modules cause problems */
#endif /* USE_DISPLAY not defined */