1
0
mirror of https://github.com/PDP-10/klh10.git synced 2026-02-11 02:31:23 +00:00
Files
PDP-10.klh10/src/dvlites.c
2015-07-18 02:18:05 +02:00

331 lines
11 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* DVLITES.C - Driver for Spare Time Gizmos Panda Display lights
*/
/* Copyright 2005 Mark Crispin
** All Rights Reserved
**
** This file is part of the KLH10 Distribution. Use, modification, and
** re-distribution is permitted subject to the terms in the file
** named "LICENSE", which contains the full text of the legal notices
** and should always accompany this Distribution.
**
** This program is provide "AS IS" with NO WARRANTY OF ANY KIND.
**
** This notice (including the copyright and warranty disclaimer)
** must be included in all copies or derivations of this software.
**
**
** This file is derived from PandaDisplayTest, which is
** Copyright 2004 by Spare Time Gizmos
** and is used by permission.
*/
#include "klh10.h"
#if !KLH10_DEV_LITES && CENV_SYS_DECOSF
/* Stupid gubbish needed to prevent OSF/1 AXP compiler from
** halting merely because compiled file is empty!
*/
static int decosfcclossage;
#endif
#if KLH10_DEV_LITES /* Moby conditional for entire file */
#include <sys/io.h>
#include "dvlites.h"
/* Internal function prototypes */
static void lites_wreg (unsigned char reg,unsigned char data);
#define DISABLE 0 /* convenient names */
#define ENABLE 1
/*
* DESCRIPTION:
*
* To a programmer, the Panda Display appears as a file of eight byte wide
* registers. The PC can write a register by sending the desired data to the
* parallel port data bit, selecting the desired register address with the
* INIT, AUTO LF, and SELECT IN printer status signals, and the clocking the
* printer STROBE signal. The Panda Display registers are write only - there's
* no way for the PC to ever read back from the display. In this file, the
* WriteRegister() method takes care of all the mechanics of writing a byte
* to an individual register.
*
* The first five registers, numbers 0..4, correspond to the forty data LEDs.
* Register 0 is the left most block of eight LEDs, and register 5 the right
* most. The MSB of each byte is the left most LED of the block and the LSB
* the right - pretty much what you'd expect. This isn't rocket science!
*
* Register 5 controls the four status LEDs. Bit 7 (the MSB) controls LED1,
* bit 6 LED2, bit 5 LED3 and finally, bit 4 controls LED4. The lower four
* bits of this register are currently unused.
*
* Register 6 is the display enable register - writing a 1 to the LSB of this
* register turns on the main forty data LEDs, and writing a 0 turns them off.
* The powerup state of this register (nor any of the others, for that matter)
* is not guaranteed, so the software should always initialize this register.
* Currently only the LSB of register 6 is used - the remaining 7 bits are
* ignored and should be zero. The display enable flag affects ONLY the main
* data LEDs - it has no effect on the four status LEDs. Disabling the display
* has no effect on the data currently stored in registers 0..4 nor does it
* prevent the software from loading new data in these registers; it simply
* turns off the LED drivers. Finally, note that this register is NOT affected
* by the unit selection (see register 7) - any writes to register 6 will
* change _all_ displays in the system at the same time.
*
* It's possible (if you're a really rabid blinking lights kinda person) to
* have up to four Panda Displays daisy chained on the same parallel port.
* Each display has a unique two bit address assigned to it by jumpers JP1/2,
* and register 7 is used to select the active display unit. The software
* should write a two bit unit select code to bits 6 and 7 (the LSBs) of this
* register and the display with the matching jumper settings will be selected.
*
* JP2 JP1 Unit
* --------- --------- ----
* removed removed 0
* removed installed 1
* installed removed 2
* installed installed 3
*
* Only the selected Panda Display will respond to writing registers 0..5.
* All unselected displays will ignore any writes to these registers. Registers
* 6 and 7 are always enabled regardless of the unit selected and any writes to
* these registers will affect all displays simultaneously. Finally, remember
* that this register doesn't have any guaranteed state after power up, so the
* software should always select a display as the first step.
*/
#define UNIT_PGM 0 /* unit 0: program data lights */
#define UNIX_xxx 1 /* unit 1: reserved for future assignment */
#define UNIX_yyy 2 /* unit 2: reserved for future assignment */
#define UNIX_zzz 3 /* unit 3: reserved for future assignment */
#define UNIT_MAX 3 /* maximum unit number */
/* Port address offsets */
#define PORT_DATA 0 /* offset of the data port from the base adr */
#define PORT_STATUS 1 /* " " " status " " " " " */
#define PORT_CONTROL 2 /* " " " control " " " " " */
#define PORT_MAX 3 /* number of ports used by the lights */
/* Control port definitions */
/*
* To send data to the display, the PC should first send the data byte to
* the DATA port and then generate pulse on the STROBE bit in the CONTROL
* port. The display latches all data from the PC on the _rising_ edge of
* the STROBE L signal, however remember that the parallel port hardware
* inverts this signal so that a rising edge is actually generated by the
* 1 -> 0 transition of the STROBE bit in the CONTROL register.
*/
#define STROBE 0x01
/*
* The Panda display has three "address" inputs that are used to select
* an internal register and/or a special function code. These three address
* inputs are driven by the parallel port's AUTO LF, INIT and SELECT outputs,
* which are all controlled by three bits in the CONTROL register. The AUTO
* LF and SELECT bits are actually inverted by the PC parallel port hardware
* (but INIT is not!), however the Panda board takes care of this for us and
* and they all look "normal" to us...
*/
#define RS0 0x02 /* AUTO LF */
#define RS1 0x04 /* INPUT */
#define RS2 0x08 /* SELECT (IN) */
/*
* This table defines the CONTROL port bits corresponding to each of the
* eight register addresses. It's done in this rather round-about way so that
* we don't have to depend on the register select bits being consecutive or
* even in order!
*/
static const unsigned char control[8] = {
0, RS0, RS1 , RS1|RS0,
RS2 , RS2 |RS0, RS2|RS1 , RS2|RS1|RS0
};
/* Mnemonics for the Panda Display register addresses... */
#define REG_DATA_MSB 0 /* left most 8 data LEDs */
#define REG_DATA_1 1 /* second from the left */
#define REG_DATA_2 2 /* center */
#define REG_DATA_3 3 /* second from the right */
#define REG_DATA_LSB 4 /* right most 8 data LEDs */
#define REG_STATUS 5 /* status LEDs */
#define REG_ENABLE 6 /* display enable register */
#define REG_SELECT 7 /* unit select register */
#define REG_MAX 7 /* maximum register */
/* Static data */
static int port = 0; /* parallel port, normally LPT1 (0x378) */
static int unit = -1; /* currently selected unit */
/* One-time initialization
* Accepts: port base register
* Returns: T if success, NIL if failure
*/
int lites_init (unsigned int prt)
{
int ret;
unsigned int i;
/* enable access to the port */
if (ret = !ioperm (prt,PORT_MAX,ENABLE)) {
port = prt; /* access granted, note port */
outb (0,port+PORT_CONTROL); /* initialize the displays */
for (i = 0; i <= UNIT_MAX; --i) {
lites_setdisplay (i); /* select unit */
lites_setenable (ENABLE); /* enable the display */
}
}
return ret;
}
/* Set a display unit
* Accepts: unit number (0 - 3)
*
* Unfortunately, the Panda display is a write only device, so there's no
* way to know how many units are attached (or even if there are any
* attached at all, for that matter!)...
*/
void lites_setdisplay (unsigned int unt)
{
if ((unt <= UNIT_MAX) && (unit != unt)) lites_wreg (REG_SELECT,unit = unt);
}
/* Set the enable for the main data LEDs on the current display
* Accepts: enable or disable
*
* Note that this has no effect on the status LEDs, nor does it change the
* data actually stored in registers 0..4 - it simply turns off the LED
* drivers...
*/
void lites_setenable (short enable)
{
lites_wreg (REG_ENABLE,enable ? ENABLE : DISABLE);
}
/* Display data lights
* Accepts: 5 bytes of data
*
* Send to the main data LEDs. Remember that register 0 is the left most
* (the MSBs) and register 4 the right most.
*/
void lites_data (unsigned char data[5])
{
lites_wreg (REG_DATA_MSB,data[0]);
lites_wreg (REG_DATA_1,data[1]);
lites_wreg (REG_DATA_2,data[2]);
lites_wreg (REG_DATA_3,data[3]);
lites_wreg (REG_DATA_LSB,data[4]);
}
/* Display status lights
* Accepts: LED status
*
* Since these all occupy the same register, there's no way to set them
* individually...
*/
void lites_status (unsigned char data)
{
lites_wreg (REG_STATUS,data & STATUS_LEDS);
}
/* Write byte to the specified register
* Accepts: register
* data byte
*
* It's not safe to change the register select bits and the STROBE at
* the same time. That's because there's no guarantee that the bits will
* all change simultaneously at the other end of the cable (because of
* variations in propagation delay, slew rate, ringing, etc) and any skew
* between the strobe and the address bits would cause glitches on the
* register clocks. The only safe thing is to set the register address
* and then pulse the STROBE while holding the register constant.
*/
static void lites_wreg (unsigned char reg,unsigned char data)
{
/* if port initted and register valid */
if (port && (reg <= REG_MAX)) {
/* send the data */
outb (data,port + PORT_DATA);
/* select the register */
outb (control[reg],port + PORT_CONTROL);
/* pulse strobe */
outb (control[reg] | STROBE,port + PORT_CONTROL);
outb (control[reg],port + PORT_CONTROL);
}
}
/* Routines specific to the primary display lights */
static unsigned char byte0 = 0; /* save of aux bits and high 4 pgm lites */
/* Send 36-bit program data to the program data lights
* Accepts: lh
* rh
*/
void lights_pgmlites (unsigned long lh,unsigned long rh)
{
unsigned char data[5];
lites_setdisplay (UNIT_PGM); /* select program display lights unit */
/* calculate MSB with aux bits */
byte0 = data[0] = ((lh >> 14) & 0xf) | (byte0 & 0xe0);
data[1] = (lh >> 6) & 0xff; /* calculate other bytes */
data[2] = ((lh << 2) | (rh >> 16)) & 0xff;
data[3] = (rh >> 8) & 0xff;
data[4] = rh & 0xff;
lites_data (data); /* send data */
}
/* Send 3-bit auxillary data to the program data lights
*/
void lights_pgmaux (unsigned char aux)
{
lites_setdisplay (UNIT_PGM); /* select program display lights unit */
/* rewrite just the MSB */
lites_wreg (REG_DATA_MSB,byte0 = (byte0 & 0xf) | ((aux & 0x7) << 5));
}
/* System heartbeat
*/
void lights_status (char cpu,char disk,char tape,char net,char push)
{
static unsigned char state = 0;
state |= (cpu ? STATUS_LED1 : 0) | (disk ? STATUS_LED2 : 0) |
(tape ? STATUS_LED3 : 0) | (net ? STATUS_LED4 : 0);
if (push) { /* push the state? */
lites_status (state); /* yes, do so */
state = 0; /* clear state for next time */
}
}
#endif /* KLH10_DEV_LITES */