mirror of
https://github.com/holub/mame
synced 2025-05-21 21:29:15 +03:00
1987 lines
72 KiB
C
1987 lines
72 KiB
C
/*
|
|
|
|
Driver for a PDP1 emulator.
|
|
|
|
Digital Equipment Corporation
|
|
Brian Silverman (original Java Source)
|
|
Vadim Gerasimov (original Java Source)
|
|
Chris Salomon (MESS driver)
|
|
Raphael Nabet (MESS driver)
|
|
|
|
Initially, this was a conversion of a JAVA emulator
|
|
(although code has been edited extensively ever since).
|
|
I have tried contacting the author, but heard as yet nothing of him,
|
|
so I don't know if it all right with him, but after all -> he did
|
|
release the source, so hopefully everything will be fine (no his
|
|
name is not Marat).
|
|
|
|
Note: naturally I have no PDP1, I have never seen one, nor have I any
|
|
programs for it.
|
|
|
|
The first supported program was:
|
|
|
|
SPACEWAR!
|
|
|
|
The first Videogame EVER!
|
|
|
|
When I saw the java emulator, running that game I was quite intrigued to
|
|
include a driver for MESS.
|
|
I think the historical value of SPACEWAR! is enormous.
|
|
|
|
Two other programs are supported: Munching squares and LISP.
|
|
|
|
Added Debugging and Disassembler...
|
|
|
|
|
|
Also:
|
|
ftp://minnie.cs.adfa.oz.au/pub/PDP-11/Sims/Supnik_2.3/software/lispswre.tar.gz
|
|
Is a packet which includes the original LISP as source and
|
|
binary form plus a macro assembler for PDP1 programs.
|
|
|
|
For more documentation look at the source for the driver,
|
|
and the cpu/pdp1/pdp1.c file (information about the whereabouts of information
|
|
and the java source).
|
|
|
|
*/
|
|
|
|
#include "emu.h"
|
|
#include "cpu/pdp1/pdp1.h"
|
|
#include "includes/pdp1.h"
|
|
#include "video/crt.h"
|
|
|
|
/*
|
|
*
|
|
* The loading storing OS... is not emulated (I haven't a clue where to
|
|
* get programs for the machine)
|
|
*
|
|
*/
|
|
|
|
|
|
static ADDRESS_MAP_START(pdp1_map, AS_PROGRAM, 32, pdp1_state )
|
|
AM_RANGE(0x00000, 0x3ffff) AM_RAM
|
|
ADDRESS_MAP_END
|
|
|
|
static INPUT_PORTS_START( pdp1 )
|
|
PORT_START("SPACEWAR") /* 0: spacewar controllers */
|
|
PORT_BIT( ROTATE_LEFT_PLAYER1, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_NAME("Spin Left Player 1") PORT_CODE(KEYCODE_A) PORT_CODE(JOYCODE_X_LEFT_SWITCH)
|
|
PORT_BIT( ROTATE_RIGHT_PLAYER1, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_NAME("Spin Right Player 1") PORT_CODE(KEYCODE_S) PORT_CODE(JOYCODE_X_RIGHT_SWITCH)
|
|
PORT_BIT( THRUST_PLAYER1, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Thrust Player 1") PORT_CODE(KEYCODE_D) PORT_CODE(JOYCODE_BUTTON1)
|
|
PORT_BIT( FIRE_PLAYER1, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Fire Player 1") PORT_CODE(KEYCODE_F) PORT_CODE(JOYCODE_BUTTON2)
|
|
PORT_BIT( ROTATE_LEFT_PLAYER2, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_NAME("Spin Left Player 2") PORT_CODE(KEYCODE_LEFT) PORT_CODE(JOYCODE_X_LEFT_SWITCH ) PORT_PLAYER(2)
|
|
PORT_BIT( ROTATE_RIGHT_PLAYER2, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_NAME("Spin Right Player 2") PORT_CODE(KEYCODE_RIGHT) PORT_CODE(JOYCODE_X_RIGHT_SWITCH ) PORT_PLAYER(2)
|
|
PORT_BIT( THRUST_PLAYER2, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Thrust Player 2") PORT_CODE(KEYCODE_UP) PORT_CODE(JOYCODE_BUTTON1 ) PORT_PLAYER(2)
|
|
PORT_BIT( FIRE_PLAYER2, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Fire Player 2") PORT_CODE(KEYCODE_DOWN) PORT_CODE(JOYCODE_BUTTON2 ) PORT_PLAYER(2)
|
|
PORT_BIT( HSPACE_PLAYER1, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Hyperspace Player 1") PORT_CODE(KEYCODE_Z) PORT_CODE(JOYCODE_BUTTON3)
|
|
PORT_BIT( HSPACE_PLAYER2, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Hyperspace Player 2") PORT_CODE(KEYCODE_SLASH) PORT_CODE(JOYCODE_BUTTON3 ) PORT_PLAYER(2)
|
|
|
|
PORT_START("CSW") /* 1: various pdp1 operator control panel switches */
|
|
PORT_BIT(pdp1_control, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("control panel key") PORT_CODE(KEYCODE_LCONTROL)
|
|
PORT_BIT(pdp1_extend, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("extend") PORT_CODE(KEYCODE_TILDE)
|
|
PORT_BIT(pdp1_start_nobrk, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("start (sequence break disabled)") PORT_CODE(KEYCODE_U)
|
|
PORT_BIT(pdp1_start_brk, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("start (sequence break enabled)") PORT_CODE(KEYCODE_I)
|
|
PORT_BIT(pdp1_stop, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("stop") PORT_CODE(KEYCODE_O)
|
|
PORT_BIT(pdp1_continue, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("continue") PORT_CODE(KEYCODE_P)
|
|
PORT_BIT(pdp1_examine, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("examine") PORT_CODE(KEYCODE_OPENBRACE)
|
|
PORT_BIT(pdp1_deposit, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("deposit") PORT_CODE(KEYCODE_CLOSEBRACE)
|
|
PORT_BIT(pdp1_read_in, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("read in") PORT_CODE(KEYCODE_ENTER)
|
|
PORT_BIT(pdp1_reader, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("reader")
|
|
PORT_BIT(pdp1_tape_feed, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("tape feed")
|
|
PORT_BIT(pdp1_single_step, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("single step") PORT_CODE(KEYCODE_STOP)
|
|
PORT_BIT(pdp1_single_inst, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("single inst") PORT_CODE(KEYCODE_SLASH)
|
|
|
|
PORT_START("SENSE") /* 2: operator control panel sense switches */
|
|
PORT_DIPNAME( 040, 000, "Sense Switch 1") PORT_CODE(KEYCODE_1_PAD)
|
|
PORT_DIPSETTING( 000, DEF_STR( Off ) )
|
|
PORT_DIPSETTING( 040, DEF_STR( On ) )
|
|
PORT_DIPNAME( 020, 000, "Sense Switch 2") PORT_CODE(KEYCODE_2_PAD)
|
|
PORT_DIPSETTING( 000, DEF_STR( Off ) )
|
|
PORT_DIPSETTING( 020, DEF_STR( On ) )
|
|
PORT_DIPNAME( 010, 000, "Sense Switch 3") PORT_CODE(KEYCODE_3_PAD)
|
|
PORT_DIPSETTING( 000, DEF_STR( Off ) )
|
|
PORT_DIPSETTING( 010, DEF_STR( On ) )
|
|
PORT_DIPNAME( 004, 000, "Sense Switch 4") PORT_CODE(KEYCODE_4_PAD)
|
|
PORT_DIPSETTING( 000, DEF_STR( Off ) )
|
|
PORT_DIPSETTING( 004, DEF_STR( On ) )
|
|
PORT_DIPNAME( 002, 002, "Sense Switch 5") PORT_CODE(KEYCODE_5_PAD)
|
|
PORT_DIPSETTING( 000, DEF_STR( Off ) )
|
|
PORT_DIPSETTING( 002, DEF_STR( On ) )
|
|
PORT_DIPNAME( 001, 000, "Sense Switch 6") PORT_CODE(KEYCODE_6_PAD)
|
|
PORT_DIPSETTING( 000, DEF_STR( Off ) )
|
|
PORT_DIPSETTING( 001, DEF_STR( On ) )
|
|
|
|
PORT_START("TSTADD") /* 3: operator control panel test address switches */
|
|
PORT_BIT( 0100000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Extension Test Address Switch 3") PORT_CODE(KEYCODE_1)
|
|
PORT_BIT( 0040000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Extension Test Address Switch 4") PORT_CODE(KEYCODE_2)
|
|
PORT_BIT( 0020000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Extension Test Address Switch 5") PORT_CODE(KEYCODE_3)
|
|
PORT_BIT( 0010000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Extension Test Address Switch 6") PORT_CODE(KEYCODE_4)
|
|
PORT_BIT( 0004000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 7") PORT_CODE(KEYCODE_5)
|
|
PORT_BIT( 0002000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 8") PORT_CODE(KEYCODE_6)
|
|
PORT_BIT( 0001000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 9") PORT_CODE(KEYCODE_7)
|
|
PORT_BIT( 0000400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 10") PORT_CODE(KEYCODE_8)
|
|
PORT_BIT( 0000200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 11") PORT_CODE(KEYCODE_9)
|
|
PORT_BIT( 0000100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 12") PORT_CODE(KEYCODE_0)
|
|
PORT_BIT( 0000040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 13") PORT_CODE(KEYCODE_MINUS)
|
|
PORT_BIT( 0000020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 14") PORT_CODE(KEYCODE_EQUALS)
|
|
PORT_BIT( 0000010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 15") PORT_CODE(KEYCODE_Q)
|
|
PORT_BIT( 0000004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 16") PORT_CODE(KEYCODE_W)
|
|
PORT_BIT( 0000002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 17") PORT_CODE(KEYCODE_E)
|
|
PORT_BIT( 0000001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Address Switch 18") PORT_CODE(KEYCODE_R)
|
|
|
|
PORT_START("TWDMSB") /* 4: operator control panel test word switches MSB */
|
|
PORT_BIT( 0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 1") PORT_CODE(KEYCODE_A)
|
|
PORT_BIT( 0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 2") PORT_CODE(KEYCODE_S)
|
|
|
|
PORT_START("TWDLSB") /* 5: operator control panel test word switches LSB */
|
|
PORT_BIT( 0100000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 3") PORT_CODE(KEYCODE_D)
|
|
PORT_BIT( 0040000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 4") PORT_CODE(KEYCODE_F)
|
|
PORT_BIT( 0020000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 5") PORT_CODE(KEYCODE_G)
|
|
PORT_BIT( 0010000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 6") PORT_CODE(KEYCODE_H)
|
|
PORT_BIT( 0004000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 7") PORT_CODE(KEYCODE_J)
|
|
PORT_BIT( 0002000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 8") PORT_CODE(KEYCODE_K)
|
|
PORT_BIT( 0001000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 9") PORT_CODE(KEYCODE_L)
|
|
PORT_BIT( 0000400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 10") PORT_CODE(KEYCODE_COLON)
|
|
PORT_BIT( 0000200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 11") PORT_CODE(KEYCODE_QUOTE)
|
|
PORT_BIT( 0000100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 12") PORT_CODE(KEYCODE_BACKSLASH)
|
|
PORT_BIT( 0000040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 13") PORT_CODE(KEYCODE_Z)
|
|
PORT_BIT( 0000020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 14") PORT_CODE(KEYCODE_X)
|
|
PORT_BIT( 0000010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 15") PORT_CODE(KEYCODE_C)
|
|
PORT_BIT( 0000004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 16") PORT_CODE(KEYCODE_V)
|
|
PORT_BIT( 0000002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 17") PORT_CODE(KEYCODE_B)
|
|
PORT_BIT( 0000001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Test Word Switch 18") PORT_CODE(KEYCODE_N)
|
|
|
|
/*
|
|
Note that I can see 2 additional keys whose purpose is unknown to me.
|
|
The caps look like "MAR REL" for the leftmost one and "MAR SET" for
|
|
rightmost one: maybe they were used to set the margin (I don't have the
|
|
manual for the typewriter). */
|
|
|
|
PORT_START("TWR0") /* 6: typewriter codes 00-17 */
|
|
PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(Space)") PORT_CODE(KEYCODE_SPACE)
|
|
PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("1 \"") PORT_CODE(KEYCODE_1)
|
|
PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("2 '") PORT_CODE(KEYCODE_2)
|
|
PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("3 ~") PORT_CODE(KEYCODE_3)
|
|
PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("4 (implies)") PORT_CODE(KEYCODE_4)
|
|
PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("5 (or)") PORT_CODE(KEYCODE_5)
|
|
PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("6 (and)") PORT_CODE(KEYCODE_6)
|
|
PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("7 <") PORT_CODE(KEYCODE_7)
|
|
PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("8 >") PORT_CODE(KEYCODE_8)
|
|
PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("9 (up arrow)") PORT_CODE(KEYCODE_9)
|
|
|
|
PORT_START("TWR1") /* 7: typewriter codes 20-37 */
|
|
PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("0 (right arrow)") PORT_CODE(KEYCODE_0)
|
|
PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("/ ?") PORT_CODE(KEYCODE_SLASH)
|
|
PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
|
|
PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
|
|
PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
|
|
PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
|
|
PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
|
|
PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
|
|
PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
|
|
PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
|
|
PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(", =") PORT_CODE(KEYCODE_COMMA)
|
|
PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab Key") PORT_CODE(KEYCODE_TAB)
|
|
|
|
PORT_START("TWR2") /* 8: typewriter codes 40-57 */
|
|
PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(non-spacing middle dot) _") PORT_CODE(KEYCODE_QUOTE)
|
|
PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
|
|
PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)
|
|
PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)
|
|
PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
|
|
PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
|
|
PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)
|
|
PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
|
|
PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
|
|
PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
|
|
PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("- +") PORT_CODE(KEYCODE_COLON)
|
|
PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(") ]") PORT_CODE(KEYCODE_EQUALS)
|
|
PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("(non-spacing overstrike) |") PORT_CODE(KEYCODE_OPENBRACE)
|
|
PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("( [") PORT_CODE(KEYCODE_MINUS)
|
|
|
|
PORT_START("TWR3") /* 9: typewriter codes 60-77 */
|
|
PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
|
|
PORT_BIT(0x0004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
|
|
PORT_BIT(0x0008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
|
|
PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
|
|
PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
|
|
PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
|
|
PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
|
|
PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
|
|
PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)
|
|
PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Lower Case") PORT_CODE(KEYCODE_LSHIFT)
|
|
PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(". (multiply)") PORT_CODE(KEYCODE_STOP)
|
|
PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Upper case") PORT_CODE(KEYCODE_RSHIFT)
|
|
/* hack to support my macintosh which does not differentiate the Right Shift key */
|
|
/* PORT_BIT(0x1000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Upper case") PORT_CODE(KEYCODE_CAPSLOCK) */
|
|
PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Backspace") PORT_CODE(KEYCODE_BACKSPACE)
|
|
PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Return") PORT_CODE(KEYCODE_ENTER)
|
|
|
|
PORT_START("CFG") /* 10: pseudo-input port with config */
|
|
PORT_DIPNAME( 0x0003, 0x0002, "RAM size")
|
|
PORT_DIPSETTING( 0x0000, "4kw" )
|
|
PORT_DIPSETTING( 0x0001, "32kw")
|
|
PORT_DIPSETTING( 0x0002, "64kw")
|
|
PORT_DIPNAME( 0x0004, 0x0000, "Hardware multiply")
|
|
PORT_DIPSETTING( 0x0000, DEF_STR( Off ) )
|
|
PORT_DIPSETTING( 0x0004, DEF_STR( On ) )
|
|
PORT_DIPNAME( 0x0008, 0x0000, "Hardware divide")
|
|
PORT_DIPSETTING( 0x0000, DEF_STR( Off ) )
|
|
PORT_DIPSETTING( 0x0008, DEF_STR( On ) )
|
|
PORT_DIPNAME( 0x0010, 0x0000, "Type 20 sequence break system")
|
|
PORT_DIPSETTING( 0x0000, DEF_STR( Off ) )
|
|
PORT_DIPSETTING( 0x0010, DEF_STR( On ) )
|
|
PORT_DIPNAME( 0x0020, 0x0000, "Type 32 light pen") PORT_CODE(KEYCODE_ENTER_PAD)
|
|
PORT_DIPSETTING( 0x0000, DEF_STR( Off ) )
|
|
PORT_DIPSETTING( 0x0020, DEF_STR( On ) )
|
|
|
|
PORT_START("LIGHTPEN") /* 11: pseudo-input port with lightpen status */
|
|
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("select larger light pen tip") PORT_CODE(KEYCODE_PLUS_PAD)
|
|
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("select smaller light pen tip") PORT_CODE(KEYCODE_MINUS_PAD)
|
|
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("light pen down")
|
|
|
|
PORT_START("LIGHTX") /* 12: lightpen - X AXIS */
|
|
PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1) PORT_RESET
|
|
|
|
PORT_START("LIGHTY") /* 13: lightpen - Y AXIS */
|
|
PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1) PORT_RESET
|
|
INPUT_PORTS_END
|
|
|
|
|
|
static const gfx_layout fontlayout =
|
|
{
|
|
6, 8, /* 6*8 characters */
|
|
pdp1_charnum, /* 96+4 characters */
|
|
1, /* 1 bit per pixel */
|
|
{ 0 },
|
|
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* straightforward layout */
|
|
{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
|
|
8*8 /* every char takes 8 consecutive bytes */
|
|
};
|
|
|
|
|
|
/*
|
|
The static palette only includes the pens for the control panel and
|
|
the typewriter, as the CRT palette is generated dynamically.
|
|
|
|
The CRT palette defines various levels of intensity between white and
|
|
black. Grey levels follow an exponential law, so that decrementing the
|
|
color index periodically will simulate the remanence of a cathode ray tube.
|
|
*/
|
|
static const UINT8 pdp1_colors[] =
|
|
{
|
|
0x00,0x00,0x00, /* black */
|
|
0xFF,0xFF,0xFF, /* white */
|
|
0x00,0xFF,0x00, /* green */
|
|
0x00,0x40,0x00, /* dark green */
|
|
0xFF,0x00,0x00, /* red */
|
|
0x80,0x80,0x80 /* light gray */
|
|
};
|
|
|
|
static const UINT8 pdp1_palette[] =
|
|
{
|
|
pen_panel_bg, pen_panel_caption,
|
|
pen_typewriter_bg, pen_black,
|
|
pen_typewriter_bg, pen_red
|
|
};
|
|
|
|
static const UINT8 total_colors_needed = pen_crt_num_levels + sizeof(pdp1_colors) / 3;
|
|
|
|
static GFXDECODE_START( pdp1 )
|
|
GFXDECODE_ENTRY( "gfx1", 0, fontlayout, pen_crt_num_levels + sizeof(pdp1_colors) / 3, 3 )
|
|
GFXDECODE_END
|
|
|
|
/* Initialise the palette */
|
|
void pdp1_state::palette_init()
|
|
{
|
|
/* rgb components for the two color emissions */
|
|
const double r1 = .1, g1 = .1, b1 = .924, r2 = .7, g2 = .7, b2 = .076;
|
|
/* half period in seconds for the two color emissions */
|
|
const double half_period_1 = .05, half_period_2 = .20;
|
|
/* refresh period in seconds */
|
|
const double update_period = 1./refresh_rate;
|
|
double decay_1, decay_2;
|
|
double cur_level_1, cur_level_2;
|
|
UINT8 i, r, g, b;
|
|
|
|
machine().colortable = colortable_alloc(machine(), total_colors_needed);
|
|
|
|
/* initialize CRT palette */
|
|
|
|
/* compute the decay factor per refresh frame */
|
|
decay_1 = pow(.5, update_period / half_period_1);
|
|
decay_2 = pow(.5, update_period / half_period_2);
|
|
|
|
cur_level_1 = cur_level_2 = 255.; /* start with maximum level */
|
|
|
|
for (i=pen_crt_max_intensity; i>0; i--)
|
|
{
|
|
/* compute the current color */
|
|
r = (int) ((r1*cur_level_1 + r2*cur_level_2) + .5);
|
|
g = (int) ((g1*cur_level_1 + g2*cur_level_2) + .5);
|
|
b = (int) ((b1*cur_level_1 + b2*cur_level_2) + .5);
|
|
/* write color in palette */
|
|
colortable_palette_set_color(machine().colortable, i, MAKE_RGB(r, g, b));
|
|
/* apply decay for next iteration */
|
|
cur_level_1 *= decay_1;
|
|
cur_level_2 *= decay_2;
|
|
}
|
|
|
|
colortable_palette_set_color(machine().colortable, 0, MAKE_RGB(0, 0, 0));
|
|
|
|
/* load static palette */
|
|
for ( i = 0; i < 6; i++ )
|
|
{
|
|
r = pdp1_colors[i*3]; g = pdp1_colors[i*3+1]; b = pdp1_colors[i*3+2];
|
|
colortable_palette_set_color(machine().colortable, pen_crt_num_levels + i, MAKE_RGB(r, g, b));
|
|
}
|
|
|
|
/* copy colortable to palette */
|
|
for( i = 0; i < total_colors_needed; i++ )
|
|
colortable_entry_set_value(machine().colortable, i, i);
|
|
|
|
/* set up palette for text */
|
|
for( i = 0; i < 6; i++ )
|
|
colortable_entry_set_value(machine().colortable, total_colors_needed + i, pdp1_palette[i]);
|
|
}
|
|
|
|
|
|
static const crt_interface pdp1_crt_interface =
|
|
{
|
|
pen_crt_num_levels,
|
|
crt_window_offset_x, crt_window_offset_y,
|
|
crt_window_width, crt_window_height
|
|
};
|
|
|
|
|
|
/*
|
|
pdp1 machine code
|
|
|
|
includes emulation for I/O devices (with the IOT opcode) and control panel functions
|
|
|
|
TODO:
|
|
* typewriter out should overwrite the typewriter buffer
|
|
* improve emulation of the internals of tape reader?
|
|
* improve puncher timing?
|
|
*/
|
|
|
|
|
|
/* This flag makes the emulated pdp-1 trigger a sequence break request when a character has been
|
|
typed on the typewriter keyboard. It is useful in order to test sequence break, but we need
|
|
to emulate a connection box in which we can connect each wire to any interrupt line. Also,
|
|
we need to determine the exact relationship between the status register and the sequence break
|
|
system (both standard and type 20). */
|
|
#define USE_SBS 0
|
|
|
|
#define LOG_IOT 0
|
|
#define LOG_IOT_EXTRA 0
|
|
|
|
/* IOT completion may take much more than the 5us instruction time. A possible programming
|
|
error would be to execute an IOT before the last IOT of the same kind is over: such a thing
|
|
would confuse the targeted IO device, which might ignore the latter IOT, execute either
|
|
or both IOT incompletely, or screw up completely. I insist that such an error can be caused
|
|
by a pdp-1 programming error, even if there is no emulator error. */
|
|
#define LOG_IOT_OVERLAP 0
|
|
|
|
|
|
static int tape_read(pdp1_state *state, UINT8 *reply);
|
|
|
|
|
|
|
|
|
|
static void pdp1_tape_read_binary(device_t *device);
|
|
static void pdp1_io_sc_callback(device_t *device);
|
|
|
|
static void iot_rpa(device_t *device, int op2, int nac, int mb, int *io, int ac);
|
|
static void iot_rpb(device_t *device, int op2, int nac, int mb, int *io, int ac);
|
|
static void iot_rrb(device_t *device, int op2, int nac, int mb, int *io, int ac);
|
|
static void iot_ppa(device_t *device, int op2, int nac, int mb, int *io, int ac);
|
|
static void iot_ppb(device_t *device, int op2, int nac, int mb, int *io, int ac);
|
|
|
|
static void iot_tyo(device_t *device, int op2, int nac, int mb, int *io, int ac);
|
|
static void iot_tyi(device_t *device, int op2, int nac, int mb, int *io, int ac);
|
|
|
|
static void iot_dpy(device_t *device, int op2, int nac, int mb, int *io, int ac);
|
|
|
|
static void iot_dia(device_t *device, int op2, int nac, int mb, int *io, int ac);
|
|
static void iot_dba(device_t *device, int op2, int nac, int mb, int *io, int ac);
|
|
static void iot_dcc(device_t *device, int op2, int nac, int mb, int *io, int ac);
|
|
static void iot_dra(device_t *device, int op2, int nac, int mb, int *io, int ac);
|
|
|
|
static void iot_011(device_t *device, int op2, int nac, int mb, int *io, int ac);
|
|
|
|
static void iot_cks(device_t *device, int op2, int nac, int mb, int *io, int ac);
|
|
|
|
|
|
/*
|
|
devices which are known to generate a completion pulse (source: maintainance manual 9-??,
|
|
and 9-20, 9-21):
|
|
emulated:
|
|
* perforated tape reader
|
|
* perforated tape punch
|
|
* typewriter output
|
|
* CRT display
|
|
unemulated:
|
|
* card punch (pac: 43)
|
|
* line printer (lpr, lsp, but NOT lfb)
|
|
|
|
This list should probably include additional optional devices (card reader comes to mind).
|
|
*/
|
|
|
|
/* IO status word */
|
|
|
|
/* defines for io_status bits */
|
|
enum
|
|
{
|
|
io_st_pen = 0400000, /* light pen: light has hit the pen */
|
|
io_st_ptr = 0200000, /* perforated tape reader: reader buffer full */
|
|
io_st_tyo = 0100000, /* typewriter out: device ready */
|
|
io_st_tyi = 0040000, /* typewriter in: new character in buffer */
|
|
io_st_ptp = 0020000 /* perforated tape punch: device ready */
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* crt display timer */
|
|
|
|
/* light pen config */
|
|
|
|
|
|
|
|
#define PARALLEL_DRUM_WORD_TIME attotime::from_nsec(8500)
|
|
#define PARALLEL_DRUM_ROTATION_TIME attotime::from_nsec(8500*4096)
|
|
|
|
|
|
static pdp1_reset_param_t pdp1_reset_param =
|
|
{
|
|
{ /* external iot handlers. NULL means that the iot is unimplemented, unless there are
|
|
parentheses around the iot name, in which case the iot is internal to the cpu core. */
|
|
/* I put a ? when the source is the handbook, since a) I have used the maintainance manual
|
|
as the primary source (as it goes more into details) b) the handbook and the maintainance
|
|
manual occasionnally contradict each other. */
|
|
/* dia, dba, dcc, dra are documented in MIT PDP-1 COMPUTER MODIFICATION
|
|
BULLETIN no. 2 (drumInstrWriteup.bin/drumInstrWriteup.txt), and are
|
|
similar to IOT documented in Parallel Drum Type 23 Instruction Manual. */
|
|
/* (iot) rpa rpb tyo tyi ppa ppb dpy */
|
|
NULL, iot_rpa, iot_rpb, iot_tyo, iot_tyi, iot_ppa, iot_ppb, iot_dpy,
|
|
/* spacewar */
|
|
NULL, iot_011, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
/* lag glf?/jsp? gpl?/gpr?/gcf? */
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
/* rrb rcb? rcc? cks mcs mes mel */
|
|
iot_rrb, NULL, NULL, iot_cks, NULL, NULL, NULL, NULL,
|
|
/* cad? rac? rbc? pac lpr/lfb/lsp swc/sci/sdf?/shr? scv? */
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
/* (dsc) (asc) (isb) (cac) (lsm) (esm) (cbs) */
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
/* icv? dia dba dcc dra mri|rlc? mrf/inr?/ccr? */
|
|
NULL, iot_dia, iot_dba, iot_dcc, iot_dra, NULL, NULL, NULL,
|
|
/* mcb|dur? mwc|mtf? mrc|sfc?... msm|cgo? (eem/lem) mic muf */
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
},
|
|
pdp1_tape_read_binary,
|
|
pdp1_io_sc_callback,
|
|
0, /* extend mode support defined in input ports and pdp1_init_machine */
|
|
0, /* hardware multiply/divide support defined in input ports and pdp1_init_machine */
|
|
0 /* type 20 sequence break system support defined in input ports and pdp1_init_machine */
|
|
};
|
|
|
|
void pdp1_state::machine_reset()
|
|
{
|
|
int cfg;
|
|
|
|
cfg = machine().root_device().ioport("CFG")->read();
|
|
pdp1_reset_param.extend_support = (cfg >> pdp1_config_extend_bit) & pdp1_config_extend_mask;
|
|
pdp1_reset_param.hw_mul_div = (cfg >> pdp1_config_hw_mul_div_bit) & pdp1_config_hw_mul_div_mask;
|
|
pdp1_reset_param.type_20_sbs = (cfg >> pdp1_config_type_20_sbs_bit) & pdp1_config_type_20_sbs_mask;
|
|
|
|
/* reset device state */
|
|
m_tape_reader.rcl = m_tape_reader.rc = 0;
|
|
m_io_status = io_st_tyo | io_st_ptp;
|
|
m_lightpen.active = m_lightpen.down = 0;
|
|
m_lightpen.x = m_lightpen.y = 0;
|
|
m_lightpen.radius = 10; /* ??? */
|
|
pdp1_update_lightpen_state(machine(), &m_lightpen);
|
|
}
|
|
|
|
|
|
void pdp1_state::pdp1_machine_stop()
|
|
{
|
|
/* the core will take care of freeing the timers, BUT we must set the variables
|
|
to NULL if we don't want to risk confusing the tape image init function */
|
|
m_tape_reader.timer = m_tape_puncher.timer = m_typewriter.tyo_timer = m_dpy_timer = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
driver init function
|
|
|
|
Set up the pdp1_memory pointer, and generate font data.
|
|
*/
|
|
void pdp1_state::machine_start()
|
|
{
|
|
UINT8 *dst;
|
|
|
|
static const unsigned char fontdata6x8[pdp1_fontdata_size] =
|
|
{ /* ASCII characters */
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,
|
|
0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0xf8,0x50,0xf8,0x50,0x00,0x00,
|
|
0x20,0x70,0xc0,0x70,0x18,0xf0,0x20,0x00,0x40,0xa4,0x48,0x10,0x20,0x48,0x94,0x08,
|
|
0x60,0x90,0xa0,0x40,0xa8,0x90,0x68,0x00,0x10,0x20,0x40,0x00,0x00,0x00,0x00,0x00,
|
|
0x20,0x40,0x40,0x40,0x40,0x40,0x20,0x00,0x10,0x08,0x08,0x08,0x08,0x08,0x10,0x00,
|
|
0x20,0xa8,0x70,0xf8,0x70,0xa8,0x20,0x00,0x00,0x20,0x20,0xf8,0x20,0x20,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x60,0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,
|
|
0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x10,0x30,0x10,0x10,0x10,0x10,0x10,0x00,
|
|
0x70,0x88,0x08,0x10,0x20,0x40,0xf8,0x00,0x70,0x88,0x08,0x30,0x08,0x88,0x70,0x00,
|
|
0x10,0x30,0x50,0x90,0xf8,0x10,0x10,0x00,0xf8,0x80,0xf0,0x08,0x08,0x88,0x70,0x00,
|
|
0x70,0x80,0xf0,0x88,0x88,0x88,0x70,0x00,0xf8,0x08,0x08,0x10,0x20,0x20,0x20,0x00,
|
|
0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,0x70,0x88,0x88,0x88,0x78,0x08,0x70,0x00,
|
|
0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x00,0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x60,
|
|
0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x00,0x00,0x00,0xf8,0x00,0xf8,0x00,0x00,0x00,
|
|
0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x70,0x88,0x08,0x10,0x20,0x00,0x20,0x00,
|
|
0x70,0x88,0xb8,0xa8,0xb8,0x80,0x70,0x00,0x70,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,
|
|
0xf0,0x88,0x88,0xf0,0x88,0x88,0xf0,0x00,0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00,
|
|
0xf0,0x88,0x88,0x88,0x88,0x88,0xf0,0x00,0xf8,0x80,0x80,0xf0,0x80,0x80,0xf8,0x00,
|
|
0xf8,0x80,0x80,0xf0,0x80,0x80,0x80,0x00,0x70,0x88,0x80,0x98,0x88,0x88,0x70,0x00,
|
|
0x88,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,
|
|
0x08,0x08,0x08,0x08,0x88,0x88,0x70,0x00,0x88,0x90,0xa0,0xc0,0xa0,0x90,0x88,0x00,
|
|
0x80,0x80,0x80,0x80,0x80,0x80,0xf8,0x00,0x88,0xd8,0xa8,0x88,0x88,0x88,0x88,0x00,
|
|
0x88,0xc8,0xa8,0x98,0x88,0x88,0x88,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,
|
|
0xf0,0x88,0x88,0xf0,0x80,0x80,0x80,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x08,
|
|
0xf0,0x88,0x88,0xf0,0x88,0x88,0x88,0x00,0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00,
|
|
0xf8,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,
|
|
0x88,0x88,0x88,0x88,0x88,0x50,0x20,0x00,0x88,0x88,0x88,0x88,0xa8,0xd8,0x88,0x00,
|
|
0x88,0x50,0x20,0x20,0x20,0x50,0x88,0x00,0x88,0x88,0x88,0x50,0x20,0x20,0x20,0x00,
|
|
0xf8,0x08,0x10,0x20,0x40,0x80,0xf8,0x00,0x30,0x20,0x20,0x20,0x20,0x20,0x30,0x00,
|
|
0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x30,0x10,0x10,0x10,0x10,0x10,0x30,0x00,
|
|
0x20,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,
|
|
0x40,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x08,0x78,0x88,0x78,0x00,
|
|
0x80,0x80,0xf0,0x88,0x88,0x88,0xf0,0x00,0x00,0x00,0x70,0x88,0x80,0x80,0x78,0x00,
|
|
0x08,0x08,0x78,0x88,0x88,0x88,0x78,0x00,0x00,0x00,0x70,0x88,0xf8,0x80,0x78,0x00,
|
|
0x18,0x20,0x70,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x70,
|
|
0x80,0x80,0xf0,0x88,0x88,0x88,0x88,0x00,0x20,0x00,0x20,0x20,0x20,0x20,0x20,0x00,
|
|
0x20,0x00,0x20,0x20,0x20,0x20,0x20,0xc0,0x80,0x80,0x90,0xa0,0xe0,0x90,0x88,0x00,
|
|
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xf0,0xa8,0xa8,0xa8,0xa8,0x00,
|
|
0x00,0x00,0xb0,0xc8,0x88,0x88,0x88,0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00,
|
|
0x00,0x00,0xf0,0x88,0x88,0xf0,0x80,0x80,0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x08,
|
|
0x00,0x00,0xb0,0xc8,0x80,0x80,0x80,0x00,0x00,0x00,0x78,0x80,0x70,0x08,0xf0,0x00,
|
|
0x20,0x20,0x70,0x20,0x20,0x20,0x18,0x00,0x00,0x00,0x88,0x88,0x88,0x98,0x68,0x00,
|
|
0x00,0x00,0x88,0x88,0x88,0x50,0x20,0x00,0x00,0x00,0xa8,0xa8,0xa8,0xa8,0x50,0x00,
|
|
0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x88,0x88,0x88,0x78,0x08,0x70,
|
|
0x00,0x00,0xf8,0x10,0x20,0x40,0xf8,0x00,0x08,0x10,0x10,0x20,0x10,0x10,0x08,0x00,
|
|
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x40,0x20,0x20,0x10,0x20,0x20,0x40,0x00,
|
|
0x00,0x68,0xb0,0x00,0x00,0x00,0x00,0x00,0x20,0x50,0x20,0x50,0xa8,0x50,0x00,0x00,
|
|
|
|
/* non-spacing middle dot */
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x20,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
/* non-spacing overstrike */
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0xfc,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
/* implies */
|
|
0x00,
|
|
0x00,
|
|
0x70,
|
|
0x08,
|
|
0x08,
|
|
0x08,
|
|
0x70,
|
|
0x00,
|
|
/* or */
|
|
0x88,
|
|
0x88,
|
|
0x88,
|
|
0x50,
|
|
0x50,
|
|
0x50,
|
|
0x20,
|
|
0x00,
|
|
/* and */
|
|
0x20,
|
|
0x50,
|
|
0x50,
|
|
0x50,
|
|
0x88,
|
|
0x88,
|
|
0x88,
|
|
0x00,
|
|
/* up arrow */
|
|
0x20,
|
|
0x70,
|
|
0xa8,
|
|
0x20,
|
|
0x20,
|
|
0x20,
|
|
0x20,
|
|
0x00,
|
|
/* right arrow */
|
|
0x00,
|
|
0x20,
|
|
0x10,
|
|
0xf8,
|
|
0x10,
|
|
0x20,
|
|
0x00,
|
|
0x00,
|
|
/* multiply */
|
|
0x00,
|
|
0x88,
|
|
0x50,
|
|
0x20,
|
|
0x50,
|
|
0x88,
|
|
0x00,
|
|
0x00,
|
|
};
|
|
|
|
/* set up our font */
|
|
dst = machine().root_device().memregion("gfx1")->base();
|
|
memcpy(dst, fontdata6x8, pdp1_fontdata_size);
|
|
|
|
machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(pdp1_state::pdp1_machine_stop),this));
|
|
|
|
m_tape_reader.timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(pdp1_state::reader_callback),this));
|
|
m_tape_puncher.timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(pdp1_state::puncher_callback),this));
|
|
m_typewriter.tyo_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(pdp1_state::tyo_callback),this));
|
|
m_dpy_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(pdp1_state::dpy_callback),this));
|
|
m_tape_reader.timer->adjust(attotime::zero, 0, attotime::from_hz(2500));
|
|
m_tape_reader.timer->enable(0);
|
|
}
|
|
|
|
|
|
/*
|
|
perforated tape handling
|
|
*/
|
|
|
|
class pdp1_readtape_image_device : public device_t,
|
|
public device_image_interface
|
|
{
|
|
public:
|
|
// construction/destruction
|
|
pdp1_readtape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
|
|
|
// image-level overrides
|
|
virtual iodevice_t image_type() const { return IO_PUNCHTAPE; }
|
|
|
|
virtual bool is_readable() const { return 1; }
|
|
virtual bool is_writeable() const { return 0; }
|
|
virtual bool is_creatable() const { return 0; }
|
|
virtual bool must_be_loaded() const { return 0; }
|
|
virtual bool is_reset_on_load() const { return 0; }
|
|
virtual const char *image_interface() const { return NULL; }
|
|
virtual const char *file_extensions() const { return "tap,rim"; }
|
|
virtual const option_guide *create_option_guide() const { return NULL; }
|
|
|
|
virtual bool call_load();
|
|
virtual void call_unload();
|
|
protected:
|
|
// device-level overrides
|
|
virtual void device_config_complete() { update_names(); }
|
|
virtual void device_start() { }
|
|
};
|
|
|
|
const device_type PDP1_READTAPE = &device_creator<pdp1_readtape_image_device>;
|
|
|
|
pdp1_readtape_image_device::pdp1_readtape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
|
: device_t(mconfig, PDP1_READTAPE, "PDP1 Tape Reader", tag, owner, clock),
|
|
device_image_interface(mconfig, *this)
|
|
{
|
|
}
|
|
|
|
class pdp1_punchtape_image_device : public device_t,
|
|
public device_image_interface
|
|
{
|
|
public:
|
|
// construction/destruction
|
|
pdp1_punchtape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
|
|
|
// image-level overrides
|
|
virtual iodevice_t image_type() const { return IO_PUNCHTAPE; }
|
|
|
|
virtual bool is_readable() const { return 0; }
|
|
virtual bool is_writeable() const { return 1; }
|
|
virtual bool is_creatable() const { return 1; }
|
|
virtual bool must_be_loaded() const { return 0; }
|
|
virtual bool is_reset_on_load() const { return 0; }
|
|
virtual const char *image_interface() const { return NULL; }
|
|
virtual const char *file_extensions() const { return "tap,rim"; }
|
|
virtual const option_guide *create_option_guide() const { return NULL; }
|
|
|
|
virtual bool call_load();
|
|
virtual void call_unload();
|
|
protected:
|
|
// device-level overrides
|
|
virtual void device_config_complete() { update_names(); }
|
|
virtual void device_start() { }
|
|
};
|
|
|
|
const device_type PDP1_PUNCHTAPE = &device_creator<pdp1_punchtape_image_device>;
|
|
|
|
pdp1_punchtape_image_device::pdp1_punchtape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
|
: device_t(mconfig, PDP1_PUNCHTAPE, "PDP1 Tape Puncher", tag, owner, clock),
|
|
device_image_interface(mconfig, *this)
|
|
{
|
|
}
|
|
|
|
|
|
class pdp1_printer_image_device : public device_t,
|
|
public device_image_interface
|
|
{
|
|
public:
|
|
// construction/destruction
|
|
pdp1_printer_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
|
|
|
// image-level overrides
|
|
virtual iodevice_t image_type() const { return IO_PRINTER; }
|
|
|
|
virtual bool is_readable() const { return 0; }
|
|
virtual bool is_writeable() const { return 1; }
|
|
virtual bool is_creatable() const { return 1; }
|
|
virtual bool must_be_loaded() const { return 0; }
|
|
virtual bool is_reset_on_load() const { return 0; }
|
|
virtual const char *image_interface() const { return NULL; }
|
|
virtual const char *file_extensions() const { return "typ"; }
|
|
virtual const option_guide *create_option_guide() const { return NULL; }
|
|
|
|
virtual bool call_load();
|
|
virtual void call_unload();
|
|
protected:
|
|
// device-level overrides
|
|
virtual void device_config_complete() { update_names(); }
|
|
virtual void device_start() { }
|
|
};
|
|
|
|
const device_type PDP1_PRINTER = &device_creator<pdp1_printer_image_device>;
|
|
|
|
pdp1_printer_image_device::pdp1_printer_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
|
: device_t(mconfig, PDP1_PRINTER, "PDP1 Typewriter", tag, owner, clock),
|
|
device_image_interface(mconfig, *this)
|
|
{
|
|
}
|
|
|
|
class pdp1_cylinder_image_device : public device_t,
|
|
public device_image_interface
|
|
{
|
|
public:
|
|
// construction/destruction
|
|
pdp1_cylinder_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
|
|
|
// image-level overrides
|
|
virtual iodevice_t image_type() const { return IO_CYLINDER; }
|
|
|
|
virtual bool is_readable() const { return 1; }
|
|
virtual bool is_writeable() const { return 1; }
|
|
virtual bool is_creatable() const { return 1; }
|
|
virtual bool must_be_loaded() const { return 0; }
|
|
virtual bool is_reset_on_load() const { return 0; }
|
|
virtual const char *image_interface() const { return NULL; }
|
|
virtual const char *file_extensions() const { return "drm"; }
|
|
virtual const option_guide *create_option_guide() const { return NULL; }
|
|
|
|
virtual bool call_load();
|
|
virtual void call_unload();
|
|
protected:
|
|
// device-level overrides
|
|
virtual void device_config_complete() { update_names(); }
|
|
virtual void device_start() { }
|
|
};
|
|
|
|
const device_type PDP1_CYLINDER = &device_creator<pdp1_cylinder_image_device>;
|
|
|
|
pdp1_cylinder_image_device::pdp1_cylinder_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
|
: device_t(mconfig, PDP1_CYLINDER, "PDP1 Cylinder", tag, owner, clock),
|
|
device_image_interface(mconfig, *this)
|
|
{
|
|
}
|
|
|
|
/*
|
|
Open a perforated tape image
|
|
*/
|
|
bool pdp1_readtape_image_device::call_load()
|
|
{
|
|
pdp1_state *state = machine().driver_data<pdp1_state>();
|
|
|
|
/* reader unit */
|
|
state->m_tape_reader.fd = this;
|
|
|
|
/* start motor */
|
|
state->m_tape_reader.motor_on = 1;
|
|
|
|
/* restart reader IO when necessary */
|
|
/* note that this function may be called before pdp1_init_machine, therefore
|
|
before state->m_tape_reader.timer is allocated. It does not matter, as the clutch is never
|
|
down at power-up, but we must not call timer_enable with a NULL parameter! */
|
|
|
|
if (state->m_tape_reader.timer)
|
|
{
|
|
if (state->m_tape_reader.motor_on && state->m_tape_reader.rcl)
|
|
{
|
|
state->m_tape_reader.timer->enable(1);
|
|
}
|
|
else
|
|
{
|
|
state->m_tape_reader.timer->enable(0);
|
|
}
|
|
}
|
|
|
|
return IMAGE_INIT_PASS;
|
|
}
|
|
|
|
void pdp1_readtape_image_device::call_unload()
|
|
{
|
|
pdp1_state *state = machine().driver_data<pdp1_state>();
|
|
|
|
/* reader unit */
|
|
state->m_tape_reader.fd = NULL;
|
|
|
|
/* stop motor */
|
|
state->m_tape_reader.motor_on = 0;
|
|
|
|
if (state->m_tape_reader.timer)
|
|
state->m_tape_reader.timer->enable(0);
|
|
}
|
|
|
|
/*
|
|
Read a byte from perforated tape
|
|
*/
|
|
static int tape_read(pdp1_state *state, UINT8 *reply)
|
|
{
|
|
if (state->m_tape_reader.fd && (state->m_tape_reader.fd->fread(reply, 1) == 1))
|
|
return 0; /* unit OK */
|
|
else
|
|
return 1; /* unit not ready */
|
|
}
|
|
|
|
|
|
/*
|
|
common code for tape read commands (RPA, RPB, and read-in mode)
|
|
*/
|
|
static void begin_tape_read(pdp1_state *state, int binary, int nac)
|
|
{
|
|
state->m_tape_reader.rb = 0;
|
|
state->m_tape_reader.rcl = 1;
|
|
state->m_tape_reader.rc = (binary) ? 1 : 3;
|
|
state->m_tape_reader.rby = (binary) ? 1 : 0;
|
|
state->m_tape_reader.rcp = nac;
|
|
|
|
if (LOG_IOT_OVERLAP)
|
|
{
|
|
if (state->m_tape_reader.timer->enable(0))
|
|
logerror("Error: overlapped perforated tape reads (Read-in mode, RPA/RPB instruction)\n");
|
|
}
|
|
/* set up delay if tape is advancing */
|
|
if (state->m_tape_reader.motor_on && state->m_tape_reader.rcl)
|
|
{
|
|
/* delay is approximately 1/400s */
|
|
state->m_tape_reader.timer->enable(1);
|
|
}
|
|
else
|
|
{
|
|
state->m_tape_reader.timer->enable(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
timer callback to simulate reader IO
|
|
*/
|
|
TIMER_CALLBACK_MEMBER(pdp1_state::reader_callback)
|
|
{
|
|
int not_ready;
|
|
UINT8 data;
|
|
|
|
if (m_tape_reader.rc)
|
|
{
|
|
not_ready = tape_read(this, & data);
|
|
if (not_ready)
|
|
{
|
|
m_tape_reader.motor_on = 0; /* let us stop the motor */
|
|
}
|
|
else
|
|
{
|
|
if ((! m_tape_reader.rby) || (data & 0200))
|
|
{
|
|
m_tape_reader.rb |= (m_tape_reader.rby) ? (data & 077) : data;
|
|
|
|
if (m_tape_reader.rc != 3)
|
|
{
|
|
m_tape_reader.rb <<= 6;
|
|
}
|
|
|
|
m_tape_reader.rc = (m_tape_reader.rc+1) & 3;
|
|
|
|
if (m_tape_reader.rc == 0)
|
|
{ /* IO complete */
|
|
m_tape_reader.rcl = 0;
|
|
if (m_tape_reader.rcp)
|
|
{
|
|
machine().device("maincpu")->state().set_state_int(PDP1_IO, m_tape_reader.rb); /* transfer reader buffer to IO */
|
|
pdp1_pulse_iot_done(machine().device("maincpu"));
|
|
}
|
|
else
|
|
m_io_status |= io_st_ptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_tape_reader.motor_on && m_tape_reader.rcl)
|
|
{
|
|
m_tape_reader.timer->enable(1);
|
|
}
|
|
else
|
|
{
|
|
m_tape_reader.timer->enable(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Initiate read of a 18-bit word in binary format from tape (used in read-in mode)
|
|
*/
|
|
void pdp1_tape_read_binary(device_t *device)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
begin_tape_read(state, 1, 1);
|
|
}
|
|
|
|
/*
|
|
perforated tape reader iot callbacks
|
|
*/
|
|
|
|
/*
|
|
rpa iot callback
|
|
|
|
op2: iot command operation (equivalent to mb & 077)
|
|
nac: nead a completion pulse
|
|
mb: contents of the MB register
|
|
io: pointer on the IO register
|
|
ac: contents of the AC register
|
|
*/
|
|
/*
|
|
* Read Perforated Tape, Alphanumeric
|
|
* rpa Address 0001
|
|
*
|
|
* This instruction reads one line of tape (all eight Channels) and transfers the resulting 8-bit code to
|
|
* the Reader Buffer. If bits 5 and 6 of the rpa function are both zero (720001), the contents of the
|
|
* Reader Buffer must be transferred to the IO Register by executing the rrb instruction. When the Reader
|
|
* Buffer has information ready to be transferred to the IO Register, Status Register Bit 1 is set to
|
|
* one. If bits 5 and 6 are different (730001 or 724001) the 8-bit code read from tape is automatically
|
|
* transferred to the IO Register via the Reader Buffer and appears as follows:
|
|
*
|
|
* IO Bits 10 11 12 13 14 15 16 17
|
|
* TAPE CHANNELS 8 7 6 5 4 3 2 1
|
|
*/
|
|
static void iot_rpa(device_t *device, int op2, int nac, int mb, int *io, int ac)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
if (LOG_IOT_EXTRA)
|
|
logerror("Warning, RPA instruction not fully emulated: mb=0%06o, (%s)\n", (unsigned) mb, device->machine().describe_context());
|
|
|
|
begin_tape_read(state, 0, nac);
|
|
}
|
|
|
|
/*
|
|
rpb iot callback
|
|
*/
|
|
/*
|
|
* Read Perforated Tape, Binary rpb
|
|
* Address 0002
|
|
*
|
|
* The instruction reads three lines of tape (six Channels per line) and assembles
|
|
* the resulting 18-bit word in the Reader Buffer. For a line to be recognized by
|
|
* this instruction Channel 8 must be punched (lines with Channel 8 not punched will
|
|
* be skipped over). Channel 7 is ignored. The instruction sub 5137, for example,
|
|
* appears on tape and is assembled by rpb as follows:
|
|
*
|
|
* Channel 8 7 6 5 4 | 3 2 1
|
|
* Line 1 X X | X
|
|
* Line 2 X X X | X
|
|
* Line 3 X X X | X X X
|
|
* Reader Buffer 100 010 101 001 011 111
|
|
*
|
|
* (Vertical dashed line indicates sprocket holes and the symbols "X" indicate holes
|
|
* punched in tape).
|
|
*
|
|
* If bits 5 and 6 of the rpb instruction are both zero (720002), the contents of
|
|
* the Reader Buffer must be transferred to the IO Register by executing
|
|
* a rrb instruction. When the Reader Buffer has information ready to be transferred
|
|
* to the IO Register, Status Register Bit 1 is set to one. If bits 5 and 6 are
|
|
* different (730002 or 724002) the 18-bit word read from tape is automatically
|
|
* transferred to the IO Register via the Reader Buffer.
|
|
*/
|
|
static void iot_rpb(device_t *device, int op2, int nac, int mb, int *io, int ac)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
if (LOG_IOT_EXTRA)
|
|
logerror("Warning, RPB instruction not fully emulated: mb=0%06o, (%s)\n", (unsigned) mb, device->machine().describe_context());
|
|
|
|
begin_tape_read(state, 1, nac);
|
|
}
|
|
|
|
/*
|
|
rrb iot callback
|
|
*/
|
|
static void iot_rrb(device_t *device, int op2, int nac, int mb, int *io, int ac)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
if (LOG_IOT_EXTRA)
|
|
logerror("RRB instruction: mb=0%06o, (%s)\n", (unsigned) mb, device->machine().describe_context());
|
|
|
|
*io = state->m_tape_reader.rb;
|
|
state->m_io_status &= ~io_st_ptr;
|
|
}
|
|
|
|
|
|
bool pdp1_punchtape_image_device::call_load()
|
|
{
|
|
pdp1_state *state = machine().driver_data<pdp1_state>();
|
|
|
|
/* punch unit */
|
|
state->m_tape_puncher.fd = this;
|
|
|
|
return IMAGE_INIT_PASS;
|
|
}
|
|
|
|
void pdp1_punchtape_image_device::call_unload()
|
|
{
|
|
pdp1_state *state = machine().driver_data<pdp1_state>();
|
|
|
|
/* punch unit */
|
|
state->m_tape_puncher.fd = NULL;
|
|
}
|
|
|
|
/*
|
|
Write a byte to perforated tape
|
|
*/
|
|
static void tape_write(pdp1_state *state, UINT8 data)
|
|
{
|
|
if (state->m_tape_puncher.fd)
|
|
state->m_tape_puncher.fd->fwrite(& data, 1);
|
|
}
|
|
|
|
/*
|
|
timer callback to generate punch completion pulse
|
|
*/
|
|
TIMER_CALLBACK_MEMBER(pdp1_state::puncher_callback)
|
|
{
|
|
int nac = param;
|
|
m_io_status |= io_st_ptp;
|
|
if (nac)
|
|
{
|
|
pdp1_pulse_iot_done(machine().device("maincpu"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
perforated tape punch iot callbacks
|
|
*/
|
|
|
|
/*
|
|
ppa iot callback
|
|
*/
|
|
/*
|
|
* Punch Perforated Tape, Alphanumeric
|
|
* ppa Address 0005
|
|
*
|
|
* For each In-Out Transfer instruction one line of tape is punched. In-Out Register
|
|
* Bit 17 conditions Hole 1. Bit 16 conditions Hole 2, etc. Bit 10 conditions Hole 8
|
|
*/
|
|
static void iot_ppa(device_t *device, int op2, int nac, int mb, int *io, int ac)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
if (LOG_IOT_EXTRA)
|
|
logerror("PPA instruction: mb=0%06o, (%s)\n", (unsigned) mb, device->machine().describe_context());
|
|
|
|
tape_write(state, *io & 0377);
|
|
state->m_io_status &= ~io_st_ptp;
|
|
/* delay is approximately 1/63.3 second */
|
|
if (LOG_IOT_OVERLAP)
|
|
{
|
|
if (state->m_tape_puncher.timer->enable(0))
|
|
logerror("Error: overlapped PPA/PPB instructions: mb=0%06o, (%s)\n", (unsigned) mb, device->machine().describe_context());
|
|
}
|
|
|
|
state->m_tape_puncher.timer->adjust(attotime::from_usec(15800), nac);
|
|
}
|
|
|
|
/*
|
|
ppb iot callback
|
|
*/
|
|
/*
|
|
* Punch Perforated Tape, Binary
|
|
* ppb Addres 0006
|
|
*
|
|
* For each In-Out Transfer instruction one line of tape is punched. In-Out Register
|
|
* Bit 5 conditions Hole 1. Bit 4 conditions Hole 2, etc. Bit 0 conditions Hole 6.
|
|
* Hole 7 is left blank. Hole 8 is always punched in this mode.
|
|
*/
|
|
static void iot_ppb(device_t *device, int op2, int nac, int mb, int *io, int ac)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
if (LOG_IOT_EXTRA)
|
|
logerror("PPB instruction: mb=0%06o, (%s)\n", (unsigned) mb, device->machine().describe_context());
|
|
|
|
tape_write(state, (*io >> 12) | 0200);
|
|
state->m_io_status &= ~io_st_ptp;
|
|
/* delay is approximately 1/63.3 second */
|
|
if (LOG_IOT_OVERLAP)
|
|
{
|
|
if (state->m_tape_puncher.timer->enable(0))
|
|
logerror("Error: overlapped PPA/PPB instructions: mb=0%06o, (%s)\n", (unsigned) mb, device->machine().describe_context());
|
|
}
|
|
state->m_tape_puncher.timer->adjust(attotime::from_usec(15800), nac);
|
|
}
|
|
|
|
|
|
/*
|
|
Typewriter handling
|
|
|
|
The alphanumeric on-line typewriter is a standard device on pdp-1: it
|
|
can both handle keyboard input and print output text.
|
|
*/
|
|
|
|
/*
|
|
Open a file for typewriter output
|
|
*/
|
|
bool pdp1_printer_image_device::call_load()
|
|
{
|
|
pdp1_state *state = machine().driver_data<pdp1_state>();
|
|
/* open file */
|
|
state->m_typewriter.fd = this;
|
|
|
|
state->m_io_status |= io_st_tyo;
|
|
|
|
return IMAGE_INIT_PASS;
|
|
}
|
|
|
|
void pdp1_printer_image_device::call_unload()
|
|
{
|
|
pdp1_state *state = machine().driver_data<pdp1_state>();
|
|
state->m_typewriter.fd = NULL;
|
|
}
|
|
|
|
/*
|
|
Write a character to typewriter
|
|
*/
|
|
static void typewriter_out(running_machine &machine, UINT8 data)
|
|
{
|
|
pdp1_state *state = machine.driver_data<pdp1_state>();
|
|
if (LOG_IOT_EXTRA)
|
|
logerror("typewriter output %o\n", data);
|
|
|
|
pdp1_typewriter_drawchar(machine, data);
|
|
if (state->m_typewriter.fd)
|
|
#if 1
|
|
state->m_typewriter.fd->fwrite(& data, 1);
|
|
#else
|
|
{
|
|
static const char ascii_table[2][64] =
|
|
{ /* n-s = non-spacing */
|
|
{ /* lower case */
|
|
' ', '1', '2', '3',
|
|
'4', '5', '6', '7',
|
|
'8', '9', '*', '*',
|
|
'*', '*', '*', '*',
|
|
'0', '/', 's', 't',
|
|
'u', 'v', 'w', 'x',
|
|
'y', 'z', '*', ',',
|
|
'*',/*black*/ '*',/*red*/ '*',/*Tab*/ '*',
|
|
'\200',/*n-s middle dot*/'j', 'k', 'l',
|
|
'm', 'n', 'o', 'p',
|
|
'q', 'r', '*', '*',
|
|
'-', ')', '\201',/*n-s overstrike*/'(',
|
|
'*', 'a', 'b', 'c',
|
|
'd', 'e', 'f', 'g',
|
|
'h', 'i', '*',/*Lower Case*/ '.',
|
|
'*',/*Upper Case*/ '*',/*Backspace*/ '*', '*'/*Carriage Return*/
|
|
},
|
|
{ /* upper case */
|
|
' ', '"', '\'', '~',
|
|
'\202',/*implies*/ '\203',/*or*/ '\204',/*and*/ '<',
|
|
'>', '\205',/*up arrow*/ '*', '*',
|
|
'*', '*', '*', '*',
|
|
'\206',/*right arrow*/'?', 'S', 'T',
|
|
'U', 'V', 'W', 'X',
|
|
'Y', 'Z', '*', '=',
|
|
'*',/*black*/ '*',/*red*/ '\t',/*Tab*/ '*',
|
|
'_',/*n-s???*/ 'J', 'K', 'L',
|
|
'M', 'N', 'O', 'P',
|
|
'Q', 'R', '*', '*',
|
|
'+', ']', '|',/*n-s???*/ '[',
|
|
'*', 'A', 'B', 'C',
|
|
'D', 'E', 'F', 'G',
|
|
'H', 'I', '*',/*Lower Case*/ '\207',/*multiply*/
|
|
'*',/*Upper Case*/ '\b',/*Backspace*/ '*', '*'/*Carriage Return*/
|
|
}
|
|
};
|
|
|
|
data &= 0x3f;
|
|
|
|
switch (data)
|
|
{
|
|
case 034:
|
|
/* Black: ignore */
|
|
//color = color_typewriter_black;
|
|
{
|
|
static const char black[5] = { '\033', '[', '3', '0', 'm' };
|
|
image_fwrite(state->m_typewriter.fd, black, sizeof(black));
|
|
}
|
|
break;
|
|
|
|
case 035:
|
|
/* Red: ignore */
|
|
//color = color_typewriter_red;
|
|
{
|
|
static const char red[5] = { '\033', '[', '3', '1', 'm' };
|
|
image_fwrite(state->m_typewriter.fd, red, sizeof(red));
|
|
}
|
|
break;
|
|
|
|
case 072:
|
|
/* Lower case */
|
|
state->m_case_shift = 0;
|
|
break;
|
|
|
|
case 074:
|
|
/* Upper case */
|
|
state->m_case_shift = 1;
|
|
break;
|
|
|
|
case 077:
|
|
/* Carriage Return */
|
|
{
|
|
static const char line_end[2] = { '\r', '\n' };
|
|
image_fwrite(state->m_typewriter.fd, line_end, sizeof(line_end));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* Any printable character... */
|
|
|
|
if ((data != 040) && (data != 056)) /* 040 and 056 are non-spacing characters: don't try to print right now */
|
|
/* print character (lookup ASCII equivalent in table) */
|
|
image_fwrite(state->m_typewriter.fd, & ascii_table[state->m_case_shift][data], 1);
|
|
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
timer callback to generate typewriter completion pulse
|
|
*/
|
|
TIMER_CALLBACK_MEMBER(pdp1_state::tyo_callback)
|
|
{
|
|
int nac = param;
|
|
m_io_status |= io_st_tyo;
|
|
if (nac)
|
|
{
|
|
pdp1_pulse_iot_done(machine().device("maincpu"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
tyo iot callback
|
|
*/
|
|
static void iot_tyo(device_t *device, int op2, int nac, int mb, int *io, int ac)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
int ch, delay;
|
|
|
|
if (LOG_IOT_EXTRA)
|
|
logerror("Warning, TYO instruction not fully emulated: mb=0%06o, (%s)\n", (unsigned) mb, device->machine().describe_context());
|
|
|
|
ch = (*io) & 077;
|
|
|
|
typewriter_out(device->machine(), ch);
|
|
state->m_io_status &= ~io_st_tyo;
|
|
|
|
/* compute completion delay (source: maintainance manual 9-12, 9-13 and 9-14) */
|
|
switch (ch)
|
|
{
|
|
case 072: /* lower-case */
|
|
case 074: /* upper-case */
|
|
case 034: /* black */
|
|
case 035: /* red */
|
|
delay = 175; /* approximately 175ms (?) */
|
|
break;
|
|
|
|
case 077: /* carriage return */
|
|
delay = 205; /* approximately 205ms (?) */
|
|
break;
|
|
|
|
default:
|
|
delay = 105; /* approximately 105ms */
|
|
break;
|
|
}
|
|
if (LOG_IOT_OVERLAP)
|
|
{
|
|
if (state->m_typewriter.tyo_timer->enable(0))
|
|
logerror("Error: overlapped TYO instruction: mb=0%06o, (%s)\n", (unsigned) mb, device->machine().describe_context());
|
|
}
|
|
|
|
state->m_typewriter.tyo_timer->adjust(attotime::from_msec(delay), nac);
|
|
}
|
|
|
|
/*
|
|
tyi iot callback
|
|
*/
|
|
/* When a typewriter key is struck, the code for the struck key is placed in the
|
|
* typewriter buffer, Program Flag 1 is set, and the type-in status bit is set to
|
|
* one. A program designed to accept typed-in data would periodically check
|
|
* Program Flag 1, and if found to be set, an In-Out Transfer Instruction with
|
|
* address 4 could be executed for the information to be transferred to the
|
|
* In-Out Register. This In-Out Transfer should not use the optional in-out wait.
|
|
* The information contained in the typewriter buffer is then transferred to the
|
|
* right six bits of the In-Out Register. The tyi instruction automatically
|
|
* clears the In-Out Register before transferring the information and also clears
|
|
* the type-in status bit.
|
|
*/
|
|
static void iot_tyi(device_t *device, int op2, int nac, int mb, int *io, int ac)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
if (LOG_IOT_EXTRA)
|
|
logerror("Warning, TYI instruction not fully emulated: mb=0%06o, (%s)\n", (unsigned) mb, device->machine().describe_context());
|
|
|
|
*io = state->m_typewriter.tb;
|
|
if (! (state->m_io_status & io_st_tyi))
|
|
*io |= 0100;
|
|
else
|
|
{
|
|
state->m_io_status &= ~io_st_tyi;
|
|
if (USE_SBS)
|
|
device->machine().device("maincpu")->execute().set_input_line_and_vector(0, CLEAR_LINE, 0); /* interrupt it, baby */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* PRECISION CRT DISPLAY (TYPE 30)
|
|
*
|
|
* This sixteen-inch cathode ray tube display is intended to be used as an on-line output device for the
|
|
* PDP-1. It is useful for high speed presentation of graphs, diagrams, drawings, and alphanumerical
|
|
* information. The unit is solid state, self-powered and completely buffered. It has magnetic focus and
|
|
* deflection.
|
|
*
|
|
* Display characteristics are as follows:
|
|
*
|
|
* Random point plotting
|
|
* Accuracy of points +/-3 per cent of raster size
|
|
* Raster size 9.25 by 9.25 inches
|
|
* 1024 by 1024 addressable locations
|
|
* Fixed origin at center of CRT
|
|
* Ones complement binary arithmetic
|
|
* Plots 20,000 points per second
|
|
*
|
|
* Resolution is such that 512 points along each axis are discernable on the face of the tube.
|
|
*
|
|
* One instruction is added to the PDP-1 with the installation of this display:
|
|
*
|
|
* Display One Point On CRT
|
|
* dpy Address 0007
|
|
*
|
|
* This instruction clears the light pen status bit and displays one point using bits 0 through 9 of the
|
|
* AC to represent the (signed) X coordinate of the point and bits 0 through 9 of the IO as the (signed)
|
|
* Y coordinate.
|
|
*
|
|
* Many variations of the Type 30 Display are available. Some of these include hardware for line and
|
|
* curve generation.
|
|
*/
|
|
|
|
|
|
/*
|
|
timer callback to generate crt completion pulse
|
|
*/
|
|
TIMER_CALLBACK_MEMBER(pdp1_state::dpy_callback)
|
|
{
|
|
pdp1_pulse_iot_done(machine().device("maincpu"));
|
|
}
|
|
|
|
|
|
/*
|
|
dpy iot callback
|
|
|
|
Light on one pixel on CRT
|
|
*/
|
|
static void iot_dpy(device_t *device, int op2, int nac, int mb, int *io, int ac)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
int x;
|
|
int y;
|
|
|
|
|
|
x = ((ac+0400000) & 0777777) >> 8;
|
|
y = (((*io)+0400000) & 0777777) >> 8;
|
|
pdp1_plot(device->machine(), x, y);
|
|
|
|
/* light pen 32 support */
|
|
state->m_io_status &= ~io_st_pen;
|
|
|
|
if (state->m_lightpen.down && ((x-state->m_lightpen.x)*(x-state->m_lightpen.x)+(y-state->m_lightpen.y)*(y-state->m_lightpen.y) < state->m_lightpen.radius*state->m_lightpen.radius))
|
|
{
|
|
state->m_io_status |= io_st_pen;
|
|
|
|
device->machine().device("maincpu")->state().set_state_int(PDP1_PF3, 1);
|
|
}
|
|
|
|
if (nac)
|
|
{
|
|
/* 50us delay */
|
|
if (LOG_IOT_OVERLAP)
|
|
{
|
|
/* note that overlap detection is incomplete: it will only work if both DPY
|
|
instructions require a completion pulse */
|
|
if (state->m_dpy_timer->enable(0))
|
|
logerror("Error: overlapped DPY instruction: mb=0%06o, (%s)\n", (unsigned) mb, device->machine().describe_context());
|
|
}
|
|
state->m_dpy_timer->adjust(attotime::from_usec(50));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
MIT parallel drum (variant of type 23)
|
|
*/
|
|
|
|
static void parallel_drum_set_il(pdp1_state *state, int il)
|
|
{
|
|
attotime il_phase;
|
|
|
|
state->m_parallel_drum.il = il;
|
|
|
|
il_phase = ((PARALLEL_DRUM_WORD_TIME * il) - state->m_parallel_drum.rotation_timer->elapsed());
|
|
if (il_phase < attotime::zero)
|
|
il_phase = il_phase + PARALLEL_DRUM_ROTATION_TIME;
|
|
state->m_parallel_drum.il_timer->adjust(il_phase, 0, PARALLEL_DRUM_ROTATION_TIME);
|
|
}
|
|
|
|
#ifdef UNUSED_FUNCTION
|
|
TIMER_CALLBACK_MEMBER(pdp1_state::il_timer_callback)
|
|
{
|
|
if (m_parallel_drum.dba)
|
|
{
|
|
/* set break request and status bit 5 */
|
|
/* ... */
|
|
}
|
|
}
|
|
|
|
static void parallel_drum_init(pdp1_state *state)
|
|
{
|
|
state->m_parallel_drum.rotation_timer = machine.scheduler().timer_alloc();
|
|
state->m_parallel_drum.rotation_timer->adjust(PARALLEL_DRUM_ROTATION_TIME, 0, PARALLEL_DRUM_ROTATION_TIME);
|
|
|
|
state->m_parallel_drum.il_timer = machine.scheduler().timer_alloc(timer_expired_delegate(FUNC(pdp1_state::il_timer_callback),this));
|
|
parallel_drum_set_il(0);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Open a file for drum
|
|
*/
|
|
bool pdp1_cylinder_image_device::call_load()
|
|
{
|
|
pdp1_state *state = machine().driver_data<pdp1_state>();
|
|
/* open file */
|
|
state->m_parallel_drum.fd = this;
|
|
|
|
return IMAGE_INIT_PASS;
|
|
}
|
|
|
|
void pdp1_cylinder_image_device::call_unload()
|
|
{
|
|
pdp1_state *state = machine().driver_data<pdp1_state>();
|
|
state->m_parallel_drum.fd = NULL;
|
|
}
|
|
|
|
static void iot_dia(device_t *device, int op2, int nac, int mb, int *io, int ac)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
state->m_parallel_drum.wfb = ((*io) & 0370000) >> 12;
|
|
parallel_drum_set_il(state, (*io) & 0007777);
|
|
|
|
state->m_parallel_drum.dba = 0; /* right? */
|
|
}
|
|
|
|
static void iot_dba(device_t *device, int op2, int nac, int mb, int *io, int ac)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
state->m_parallel_drum.wfb = ((*io) & 0370000) >> 12;
|
|
parallel_drum_set_il(state, (*io) & 0007777);
|
|
|
|
state->m_parallel_drum.dba = 1;
|
|
}
|
|
|
|
/*
|
|
Read a word from drum
|
|
*/
|
|
static UINT32 drum_read(pdp1_state *state, int field, int position)
|
|
{
|
|
int offset = (field*4096+position)*3;
|
|
UINT8 buf[3];
|
|
|
|
if (state->m_parallel_drum.fd && (!state->m_parallel_drum.fd->fseek(offset, SEEK_SET)) && (state->m_parallel_drum.fd->fread( buf, 3) == 3))
|
|
return ((buf[0] << 16) | (buf[1] << 8) | buf[2]) & 0777777;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Write a word to drum
|
|
*/
|
|
static void drum_write(pdp1_state *state, int field, int position, UINT32 data)
|
|
{
|
|
int offset = (field*4096+position)*3;
|
|
UINT8 buf[3];
|
|
|
|
if (state->m_parallel_drum.fd)
|
|
{
|
|
buf[0] = data >> 16;
|
|
buf[1] = data >> 8;
|
|
buf[2] = data;
|
|
|
|
state->m_parallel_drum.fd->fseek(offset, SEEK_SET);
|
|
state->m_parallel_drum.fd->fwrite( buf, 3);
|
|
}
|
|
}
|
|
|
|
static void iot_dcc(device_t *device, int op2, int nac, int mb, int *io, int ac)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
attotime delay;
|
|
int dc;
|
|
|
|
state->m_parallel_drum.rfb = ((*io) & 0370000) >> 12;
|
|
state->m_parallel_drum.wc = - ((*io) & 0007777);
|
|
|
|
state->m_parallel_drum.wcl = ac & 0177777/*0007777???*/;
|
|
|
|
state->m_parallel_drum.dba = 0; /* right? */
|
|
/* clear status bit 5... */
|
|
|
|
/* do transfer */
|
|
delay = state->m_parallel_drum.il_timer->remaining();
|
|
dc = state->m_parallel_drum.il;
|
|
do
|
|
{
|
|
if ((state->m_parallel_drum.wfb >= 1) && (state->m_parallel_drum.wfb <= 22))
|
|
{
|
|
drum_write(state, state->m_parallel_drum.wfb-1, dc, (signed)device->machine().device("maincpu")->memory().space(AS_PROGRAM).read_dword(state->m_parallel_drum.wcl<<2));
|
|
}
|
|
|
|
if ((state->m_parallel_drum.rfb >= 1) && (state->m_parallel_drum.rfb <= 22))
|
|
{
|
|
device->machine().device("maincpu")->memory().space(AS_PROGRAM).write_dword(state->m_parallel_drum.wcl<<2, drum_read(state, state->m_parallel_drum.rfb-1, dc));
|
|
}
|
|
|
|
state->m_parallel_drum.wc = (state->m_parallel_drum.wc+1) & 07777;
|
|
state->m_parallel_drum.wcl = (state->m_parallel_drum.wcl+1) & 0177777/*0007777???*/;
|
|
dc = (dc+1) & 07777;
|
|
if (state->m_parallel_drum.wc)
|
|
delay = delay + PARALLEL_DRUM_WORD_TIME;
|
|
} while (state->m_parallel_drum.wc);
|
|
device->machine().device("maincpu")->execute().adjust_icount(-device->machine().device<cpu_device>("maincpu")->attotime_to_cycles(delay));
|
|
/* if no error, skip */
|
|
device->machine().device("maincpu")->state().set_state_int(PDP1_PC, device->machine().device("maincpu")->state().state_int(PDP1_PC)+1);
|
|
}
|
|
|
|
static void iot_dra(device_t *device, int op2, int nac, int mb, int *io, int ac)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
(*io) = (state->m_parallel_drum.rotation_timer->elapsed() *
|
|
(ATTOSECONDS_PER_SECOND / (PARALLEL_DRUM_WORD_TIME.as_attoseconds()))).seconds & 0007777;
|
|
|
|
/* set parity error and timing error... */
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
iot 11 callback
|
|
|
|
Read state of Spacewar! controllers
|
|
|
|
Not documented, except a few comments in the Spacewar! source code:
|
|
it should leave the control word in the io as follows.
|
|
high order 4 bits, rotate ccw, rotate cw, (both mean hyperspace)
|
|
fire rocket, and fire torpedo. low order 4 bits, same for
|
|
other ship. routine is entered by jsp cwg.
|
|
*/
|
|
static void iot_011(device_t *device, int op2, int nac, int mb, int *io, int ac)
|
|
{
|
|
int key_state = device->machine().root_device().ioport("SPACEWAR")->read();
|
|
int reply;
|
|
|
|
|
|
reply = 0;
|
|
|
|
if (key_state & ROTATE_RIGHT_PLAYER2)
|
|
reply |= 0400000;
|
|
if (key_state & ROTATE_LEFT_PLAYER2)
|
|
reply |= 0200000;
|
|
if (key_state & THRUST_PLAYER2)
|
|
reply |= 0100000;
|
|
if (key_state & FIRE_PLAYER2)
|
|
reply |= 0040000;
|
|
if (key_state & HSPACE_PLAYER2)
|
|
reply |= 0600000;
|
|
|
|
if (key_state & ROTATE_RIGHT_PLAYER1)
|
|
reply |= 010;
|
|
if (key_state & ROTATE_LEFT_PLAYER1)
|
|
reply |= 004;
|
|
if (key_state & THRUST_PLAYER1)
|
|
reply |= 002;
|
|
if (key_state & FIRE_PLAYER1)
|
|
reply |= 001;
|
|
if (key_state & HSPACE_PLAYER1)
|
|
reply |= 014;
|
|
|
|
*io = reply;
|
|
}
|
|
|
|
|
|
/*
|
|
cks iot callback
|
|
|
|
check IO status
|
|
*/
|
|
static void iot_cks(device_t *device, int op2, int nac, int mb, int *io, int ac)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
if (LOG_IOT_EXTRA)
|
|
logerror("CKS instruction: mb=0%06o, (%s)\n", (unsigned) mb, device->machine().describe_context());
|
|
|
|
*io = state->m_io_status;
|
|
}
|
|
|
|
|
|
/*
|
|
callback which is called when Start Clear is pulsed
|
|
|
|
IO devices should reset
|
|
*/
|
|
void pdp1_io_sc_callback(device_t *device)
|
|
{
|
|
pdp1_state *state = device->machine().driver_data<pdp1_state>();
|
|
state->m_tape_reader.rcl = state->m_tape_reader.rc = 0;
|
|
if (state->m_tape_reader.timer)
|
|
state->m_tape_reader.timer->enable(0);
|
|
|
|
if (state->m_tape_puncher.timer)
|
|
state->m_tape_puncher.timer->enable(0);
|
|
|
|
if (state->m_typewriter.tyo_timer)
|
|
state->m_typewriter.tyo_timer->enable(0);
|
|
|
|
if (state->m_dpy_timer)
|
|
state->m_dpy_timer->enable(0);
|
|
|
|
state->m_io_status = io_st_tyo | io_st_ptp;
|
|
}
|
|
|
|
|
|
/*
|
|
typewriter keyboard handler
|
|
*/
|
|
static void pdp1_keyboard(running_machine &machine)
|
|
{
|
|
pdp1_state *state = machine.driver_data<pdp1_state>();
|
|
int i;
|
|
int j;
|
|
|
|
int typewriter_keys[4];
|
|
|
|
int typewriter_transitions;
|
|
static const char *const twrnames[] = { "TWR0", "TWR1", "TWR2", "TWR3" };
|
|
|
|
|
|
for (i=0; i<4; i++)
|
|
{
|
|
typewriter_keys[i] = machine.root_device().ioport(twrnames[i])->read();
|
|
}
|
|
|
|
for (i=0; i<4; i++)
|
|
{
|
|
typewriter_transitions = typewriter_keys[i] & (~ state->m_old_typewriter_keys[i]);
|
|
if (typewriter_transitions)
|
|
{
|
|
for (j=0; (((typewriter_transitions >> j) & 1) == 0) /*&& (j<16)*/; j++)
|
|
;
|
|
state->m_typewriter.tb = (i << 4) + j;
|
|
state->m_io_status |= io_st_tyi;
|
|
#if USE_SBS
|
|
machine.device("maincpu")->execute().set_input_line_and_vector(0, ASSERT_LINE, 0); /* interrupt it, baby */
|
|
#endif
|
|
machine.device("maincpu")->state().set_state_int(PDP1_PF1, 1);
|
|
pdp1_typewriter_drawchar(machine, state->m_typewriter.tb); /* we want to echo input */
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i=0; i<4; i++)
|
|
state->m_old_typewriter_keys[i] = typewriter_keys[i];
|
|
}
|
|
|
|
static void pdp1_lightpen(running_machine &machine)
|
|
{
|
|
pdp1_state *state = machine.driver_data<pdp1_state>();
|
|
int x_delta, y_delta;
|
|
int current_state;
|
|
|
|
state->m_lightpen.active = (machine.root_device().ioport("CFG")->read() >> pdp1_config_lightpen_bit) & pdp1_config_lightpen_mask;
|
|
|
|
current_state = machine.root_device().ioport("LIGHTPEN")->read();
|
|
|
|
/* update pen down state */
|
|
state->m_lightpen.down = state->m_lightpen.active && (current_state & pdp1_lightpen_down);
|
|
|
|
/* update size of pen tip hole */
|
|
if ((current_state & ~state->m_old_lightpen) & pdp1_lightpen_smaller)
|
|
{
|
|
state->m_lightpen.radius --;
|
|
if (state->m_lightpen.radius < 0)
|
|
state->m_lightpen.radius = 0;
|
|
}
|
|
if ((current_state & ~state->m_old_lightpen) & pdp1_lightpen_larger)
|
|
{
|
|
state->m_lightpen.radius ++;
|
|
if (state->m_lightpen.radius > 32)
|
|
state->m_lightpen.radius = 32;
|
|
}
|
|
|
|
state->m_old_lightpen = current_state;
|
|
|
|
/* update pen position */
|
|
x_delta = machine.root_device().ioport("LIGHTX")->read();
|
|
y_delta = machine.root_device().ioport("LIGHTY")->read();
|
|
|
|
if (x_delta >= 0x80)
|
|
x_delta -= 0x100;
|
|
if (y_delta >= 0x80)
|
|
y_delta -= 256;
|
|
|
|
state->m_lightpen.x += x_delta;
|
|
state->m_lightpen.y += y_delta;
|
|
|
|
if (state->m_lightpen.x < 0)
|
|
state->m_lightpen.x = 0;
|
|
if (state->m_lightpen.x > 1023)
|
|
state->m_lightpen.x = 1023;
|
|
if (state->m_lightpen.y < 0)
|
|
state->m_lightpen.y = 0;
|
|
if (state->m_lightpen.y > 1023)
|
|
state->m_lightpen.y = 1023;
|
|
|
|
pdp1_update_lightpen_state(machine, &state->m_lightpen);
|
|
}
|
|
|
|
/*
|
|
Not a real interrupt - just handle keyboard input
|
|
*/
|
|
INTERRUPT_GEN_MEMBER(pdp1_state::pdp1_interrupt)
|
|
{
|
|
int control_keys;
|
|
int tw_keys;
|
|
int ta_keys;
|
|
|
|
int control_transitions;
|
|
int tw_transitions;
|
|
int ta_transitions;
|
|
|
|
|
|
device.state().set_state_int(PDP1_SS, machine().root_device().ioport("SENSE")->read());
|
|
|
|
/* read new state of control keys */
|
|
control_keys = ioport("CSW")->read();
|
|
|
|
if (control_keys & pdp1_control)
|
|
{
|
|
/* compute transitions */
|
|
control_transitions = control_keys & (~ m_old_control_keys);
|
|
|
|
if (control_transitions & pdp1_extend)
|
|
{
|
|
device.state().set_state_int(PDP1_EXTEND_SW, ! device.state().state_int(PDP1_EXTEND_SW));
|
|
}
|
|
if (control_transitions & pdp1_start_nobrk)
|
|
{
|
|
pdp1_pulse_start_clear(&device); /* pulse Start Clear line */
|
|
device.state().set_state_int(PDP1_EXD, device.state().state_int(PDP1_EXTEND_SW));
|
|
device.state().set_state_int(PDP1_SBM, (UINT64)0);
|
|
device.state().set_state_int(PDP1_OV, (UINT64)0);
|
|
device.state().set_state_int(PDP1_PC, device.state().state_int(PDP1_TA));
|
|
device.state().set_state_int(PDP1_RUN, 1);
|
|
}
|
|
if (control_transitions & pdp1_start_brk)
|
|
{
|
|
pdp1_pulse_start_clear(&device); /* pulse Start Clear line */
|
|
device.state().set_state_int(PDP1_EXD, device.state().state_int(PDP1_EXTEND_SW));
|
|
device.state().set_state_int(PDP1_SBM, 1);
|
|
device.state().set_state_int(PDP1_OV, (UINT64)0);
|
|
device.state().set_state_int(PDP1_PC, device.state().state_int(PDP1_TA));
|
|
device.state().set_state_int(PDP1_RUN, 1);
|
|
}
|
|
if (control_transitions & pdp1_stop)
|
|
{
|
|
device.state().set_state_int(PDP1_RUN, (UINT64)0);
|
|
device.state().set_state_int(PDP1_RIM, (UINT64)0); /* bug : we stop after reading an even-numbered word
|
|
(i.e. data), whereas a real pdp-1 stops after reading
|
|
an odd-numbered word (i.e. dio instruciton) */
|
|
}
|
|
if (control_transitions & pdp1_continue)
|
|
{
|
|
device.state().set_state_int(PDP1_RUN, 1);
|
|
}
|
|
if (control_transitions & pdp1_examine)
|
|
{
|
|
pdp1_pulse_start_clear(&device); /* pulse Start Clear line */
|
|
device.state().set_state_int(PDP1_PC, device.state().state_int(PDP1_TA));
|
|
device.state().set_state_int(PDP1_MA, device.state().state_int(PDP1_PC));
|
|
device.state().set_state_int(PDP1_IR, LAC); /* this instruction is actually executed */
|
|
|
|
device.state().set_state_int(PDP1_MB, (signed)device.memory().space(AS_PROGRAM).read_dword(PDP1_MA<<2));
|
|
device.state().set_state_int(PDP1_AC, device.state().state_int(PDP1_MB));
|
|
}
|
|
if (control_transitions & pdp1_deposit)
|
|
{
|
|
pdp1_pulse_start_clear(&device); /* pulse Start Clear line */
|
|
device.state().set_state_int(PDP1_PC, device.state().state_int(PDP1_TA));
|
|
device.state().set_state_int(PDP1_MA, device.state().state_int(PDP1_PC));
|
|
device.state().set_state_int(PDP1_AC, device.state().state_int(PDP1_TW));
|
|
device.state().set_state_int(PDP1_IR, DAC); /* this instruction is actually executed */
|
|
|
|
device.state().set_state_int(PDP1_MB, device.state().state_int(PDP1_AC));
|
|
device.memory().space(AS_PROGRAM).write_dword(device.state().state_int(PDP1_MA)<<2, device.state().state_int(PDP1_MB));
|
|
}
|
|
if (control_transitions & pdp1_read_in)
|
|
{ /* set cpu to read instructions from perforated tape */
|
|
pdp1_pulse_start_clear(&device); /* pulse Start Clear line */
|
|
device.state().set_state_int(PDP1_PC, ( device.state().state_int(PDP1_TA) & 0170000)
|
|
| (device.state().state_int(PDP1_PC) & 0007777)); /* transfer ETA to EPC */
|
|
/*machine.device("maincpu")->state().set_state_int(PDP1_MA, machine.device("maincpu")->state().state_int(PDP1_PC));*/
|
|
device.state().set_state_int(PDP1_EXD, device.state().state_int(PDP1_EXTEND_SW));
|
|
device.state().set_state_int(PDP1_OV, (UINT64)0); /* right??? */
|
|
device.state().set_state_int(PDP1_RUN, (UINT64)0);
|
|
device.state().set_state_int(PDP1_RIM, 1);
|
|
}
|
|
if (control_transitions & pdp1_reader)
|
|
{
|
|
}
|
|
if (control_transitions & pdp1_tape_feed)
|
|
{
|
|
}
|
|
if (control_transitions & pdp1_single_step)
|
|
{
|
|
device.state().set_state_int(PDP1_SNGL_STEP, ! device.state().state_int(PDP1_SNGL_STEP));
|
|
}
|
|
if (control_transitions & pdp1_single_inst)
|
|
{
|
|
device.state().set_state_int(PDP1_SNGL_INST, ! device.state().state_int(PDP1_SNGL_INST));
|
|
}
|
|
|
|
/* remember new state of control keys */
|
|
m_old_control_keys = control_keys;
|
|
|
|
|
|
/* handle test word keys */
|
|
tw_keys = (machine().root_device().ioport("TWDMSB")->read() << 16) | machine().root_device().ioport("TWDLSB")->read();
|
|
|
|
/* compute transitions */
|
|
tw_transitions = tw_keys & (~ m_old_tw_keys);
|
|
|
|
if (tw_transitions)
|
|
device.state().set_state_int(PDP1_TW, device.state().state_int(PDP1_TW) ^ tw_transitions);
|
|
|
|
/* remember new state of test word keys */
|
|
m_old_tw_keys = tw_keys;
|
|
|
|
|
|
/* handle address keys */
|
|
ta_keys = machine().root_device().ioport("TSTADD")->read();
|
|
|
|
/* compute transitions */
|
|
ta_transitions = ta_keys & (~ m_old_ta_keys);
|
|
|
|
if (ta_transitions)
|
|
device.state().set_state_int(PDP1_TA, device.state().state_int(PDP1_TA) ^ ta_transitions);
|
|
|
|
/* remember new state of test word keys */
|
|
m_old_ta_keys = ta_keys;
|
|
|
|
}
|
|
else
|
|
{
|
|
m_old_control_keys = 0;
|
|
m_old_tw_keys = 0;
|
|
m_old_ta_keys = 0;
|
|
|
|
pdp1_keyboard(machine());
|
|
}
|
|
|
|
pdp1_lightpen(machine());
|
|
}
|
|
|
|
|
|
static MACHINE_CONFIG_START( pdp1, pdp1_state )
|
|
|
|
/* basic machine hardware */
|
|
/* PDP1 CPU @ 200 kHz (no master clock, but the instruction and memory rate is 200 kHz) */
|
|
MCFG_CPU_ADD("maincpu", PDP1, 1000000/*the CPU core uses microsecond counts*/)
|
|
MCFG_CPU_CONFIG(pdp1_reset_param)
|
|
MCFG_CPU_PROGRAM_MAP(pdp1_map)
|
|
MCFG_CPU_VBLANK_INT_DRIVER("screen", pdp1_state, pdp1_interrupt) /* dummy interrupt: handles input */
|
|
|
|
|
|
/* video hardware (includes the control panel and typewriter output) */
|
|
MCFG_SCREEN_ADD("screen", RASTER)
|
|
MCFG_SCREEN_REFRESH_RATE(refresh_rate)
|
|
MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) /* not accurate */
|
|
MCFG_SCREEN_SIZE(virtual_width, virtual_height)
|
|
MCFG_SCREEN_VISIBLE_AREA(0, virtual_width-1, 0, virtual_height-1)
|
|
MCFG_SCREEN_UPDATE_DRIVER(pdp1_state, screen_update_pdp1)
|
|
MCFG_SCREEN_VBLANK_DRIVER(pdp1_state, screen_eof_pdp1)
|
|
|
|
MCFG_CRT_ADD( "crt", pdp1_crt_interface )
|
|
MCFG_DEVICE_ADD("readt", PDP1_READTAPE, 0)
|
|
MCFG_DEVICE_ADD("punch", PDP1_PUNCHTAPE, 0)
|
|
MCFG_DEVICE_ADD("typewriter", PDP1_PRINTER, 0)
|
|
MCFG_DEVICE_ADD("drum", PDP1_CYLINDER, 0)
|
|
|
|
MCFG_GFXDECODE(pdp1)
|
|
MCFG_PALETTE_LENGTH(pen_crt_num_levels + sizeof(pdp1_colors) / 3 + sizeof(pdp1_palette))
|
|
|
|
MACHINE_CONFIG_END
|
|
|
|
/*
|
|
pdp1 can address up to 65336 18 bit words when extended (4096 otherwise).
|
|
*/
|
|
ROM_START(pdp1)
|
|
ROM_REGION(pdp1_fontdata_size, "gfx1", ROMREGION_ERASEFF)
|
|
/* space filled with our font */
|
|
ROM_END
|
|
|
|
/***************************************************************************
|
|
|
|
Game driver(s)
|
|
|
|
***************************************************************************/
|
|
|
|
/* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME */
|
|
COMP( 1961, pdp1, 0, 0, pdp1, pdp1, driver_device, 0, "Digital Equipment Corporation", "PDP-1" , GAME_NO_SOUND_HW | GAME_NOT_WORKING)
|