UniBone
"UniBone" is a Linux-to-UNIBUS bridge, implemented with a BeagleBone Black micro Linux system.

UniBone can keep old PDP-11s running, by emulating devices and aiding in repair.
UniBone is very similar to its QBUS successor "QBone".
The articles here address different audiences: either they describe some aspects of UniBone for end users, or they explore technical details.
Dynamic discussion at the Google group.
UniBone - BlinkenBone Programmer's Reference
The BlinkenBone device has several registers and functions ... so we need one of these dry "Reference" pages here.
Parameters of device BLINKENBONE:
Name Short Value Access Info
--------------- ----- ------------- --------- ------------------------------------------------------------------------------------------
name name BLINKENBONE read only Unique identifier of device
type type blinkenbone_c read only Type
enabled en 1 read only device installed and ready to use?
emulation_speed es 1 writable 1 = original speed, > 1: faster
verbosity v 4 writable 1 = fatal, 2 = error, 3 = warning, 4 = info, 5 = debug
base_addr addr 760200 read only controller base address in IO page
slot sl 30 read only backplane slot #, interrupt priority within one level, 0 = next to CPU
intr_vector iv 310 read only interrupt vector address
intr_level il 6 read only interrupt bus request level: 4,5,6,7
panel_host ph bigfoot read only hostname of Blinkenlight server, the computer running the panel .. physical, Java or PiDP11
panel_addr pa 0 read only Address of panel in the Blinkenlight server
panel_config pc 123456 writable Custom CONFIG register
poll_period pp 50 writable Panel switches are polled every so many milliseconds. 0=disable.
update_period up 10 writable Panel lamps are updated every so many milliseconds. 0=disable.
PDP-11 register map
The BlinkenBone device appears in the PDP-11 I/O page as list of 16 bit registers. Position is given by parameter base_addr.
- First comes a fix block with registers for the device itself. "Fix" means: they are independed of the connected BlinkenBone panel.
- Then there's the list of "mapped panel control registers". These are the memory location which give you access to the panels input switches and output lamps. These are highly dependend of the connected panel.
A panel needs much space in the I/O page. Actual register count depends on the panel ... so choose a base address with enough "air" behind.
For example, the PDP-11/70 panel needs 29 words of I/O page. And the big "PDP-10 KI10" counts for 116 registers.
Default for base address is 760200, in the midst of the "floating" register space, which is defined to end at 763776.
On "enable", the BlinkenBone device dumps out its actual register layout, shown below for the PDP-11/70 panel.
Fixed BlinkenBone device registers in PDP-11 address space:
Addr/Bits Reg name Info
--------- -------- ----
760200 PANEL_ICS Command and Status Register for panel Inputs
<15> ERR Panel not connected, server error
<7> IEVNT Change Event on some input switches, may trigger INT
<6> IIE Interrupt Enable for Input
760202 PANEL_OCS Command and Status Register for panel Outputs
<15> ERR Panel not connected, server error
<7> OEVNT Periodic panel Output (lamps) update occured, may trigger INT
<6> OIE Interrupt Enable for Output
<1:0> OTSTMODE panel test mode: 0=normal,1=lamp test,2=full test,3=powerless
760204 PANEL_IPERIOD Interval for periodic panel input polling
<9:0> IPERIOD 1..1000 millisecs, 0=off, inits to parameter "poll_period_ms"
760206 PANEL_OPERIOD Interval for periodic panel output update
<9:0> OPERIOD 1..1000 millisecs, 0=off, inits to parameter "update_period_ms"
760210 PANEL_ICHGREG Addr of last changed mapped input switch register
760212 PANEL_CONFIG User defined bitpattern to tell PDP-11 panel config
Detect Panel Switch changes with the "Input" logic
Register PANEL_ICS (Input Command and Status) gives error and status for the input side of BlinkenBone device, which reads out the panels switches periodically. Flag layout is DEC-style.
Flag ERR indicates that we have no connection to the panel, so there are no switches to handle and no mapped input registers for the panel.
The switches are polled from the Blinkenlight server periodically for changes. Polling speed can be set any time with register PANEL_IPERIOD in units of milliseconds. Default value after reset is given by parameter poll_period. There's no need to change polling period from default value 50ms, unless you have very fast fingers.
Bit IEVNT is set if a change in one of the panel switch states is detected. IEVNT is reset on read out (on DATI bus cycle).
The address of the mapped register holding the switch state is captured into register PANEL_ICHGREG then.
If the interrupt enable bit IIE is set, an interrupt is triggered in IEVNT. Vector and bus request level are given by parameters intr_vector and intr_level.
Setting Lamp patterns with the "Output" logic
Register PANEL_OCS (Output Command and Status) gives error and status for the output side.
Flag ERR indicates that we have no connection to the panel, so there are no lamps to handle.
Bitgroup OTSTMODE sets a self test mode: 0=normal,1=lamp test,2=full test,3=powerless
The panel lamps are updated periodically from the mapped registers. Update period is given by PANEL_OPERIOD, with reset value given by parameter update_period.
After each update cycle flag OEVNT is set, it's cleared on readout of PANEL_OCS.
Bit OIE controls enable of the periodic "output update" interrupt. Vector and bus request level are given by parameters intr_vector+4 and intr_level.
As the timer and interrupt work even without connected panel, you have a small programmable universal timer here.
Other as for the input side, it makes sense to control the lamp update period: you can program animated light patterns, and drive there display speed via interrupt and variable update frequency.
Or your light effects are changing with PDP-11 execution speed, so the update frequency should be as high as possible.
1kHz (period=1ms) is max, but may cause too much load on the network between BeagleBone and panel server.
As said, communication with a panel server involves network transactions.
This means: switches and lamps can not be updated with PDP-11 execution speed at ~1MHz, they are updated periodically with a slower synchronisation rate.
Keep in mind that routing to the server may involve unreliable WLAN segments, so be speed-conscious.
Accessing panel controls with mapped registers

Mapped Input Registers: Starting from fixed register PANEL_CONFIG+2, registers are mapped to input controls (=switches and knobs) of the selected panel.
These registers are "read-only" and reflect the state of switches. Number and function of these registers depend on the connected panel.
Every switch control is mapped at least to a full 16 bit PDP-11 register, whose name is that of the panel control as provided by the panel server.
If a control is more then 16 bit width, more PDP-11 registers are used. Then suffixes "_A_, "_B", "_C_", "_D" are used.
Example: on PDP-11/70, the ADDR switch bank is 22 bit width.
You read switches <15:00> from register "SR_A", and switches <21:16> from "SR_B", bits <5:0>.
Same for the Mapped Output Registers: they follow the panel input registers in address space.
Nothing new here: Writing into one of these puts lamps on and off, with some delay given by the periodic update logic..
Lamp controls wider then 16 bits also get multiple registers.
Controls of panel 0 "11/70" on server "bigfoot" mapped into PDP-11 address space:
Addr In/out Reg name Bits Panel control idx
---- ------ -------- ---- -----------------
760214 input SR_A <15:00> 2
760216 input SR_B <21:16> 2
760220 input LOAD_ADRS <0> 3
760222 input EXAM <0> 4
760224 input DEPOSIT <0> 5
760226 input CONT <0> 6
760230 input HALT <0> 7
760232 input S_BUS_CYCLE <0> 8
760234 input START <0> 9
760236 input ADDR_SELECT <2:0> 24
760240 input DATA_SELECT <1:0> 25
760242 input PANEL_LOCK <0> 1
760244 output ADDRESS_A <15:00> 10
760246 output ADDRESS_B <21:16> 10
760250 output DATA <15:00> 11
760252 output PARITY_HIGH <0> 12
760254 output PARITY_LOW <0> 13
760256 output PAR_ERR <0> 14
760260 output ADRS_ERR <0> 15
760262 output RUN <0> 16
760264 output PAUSE <0> 17
760266 output MASTER <0> 18
760270 output MMR0_MODE <1:0> 19
760272 output DATA_SPACE <0> 20
760274 output ADDRESSING_16 <0> 21
760276 output ADDRESSING_18 <0> 22
760300 output ADDRESSING_22 <0> 23
Hello Blinkenlight World
And here is the short-most program for the PDP11/70 panel. It just copies the state of 22 SR switches to the 22 ADDR LEDs.
1 .title BBHELLO - Short QUniBone BlinkenBone Device Demo
2 ; This program exercises a connected PDP11/70 panel.
3 ; It copies the state of 22 SR switches to the 22 ADDR LEDs.
4
5 .asect
6
7 160200 bbaddr = 160200 ; BlinkenBone device start addr in I/O page
8
9 000000 . = 0
10 000000 000137 001000 jmp @#start ; early emulation started code execution from 0
11
12 000024 . = 24 ; If not HALTed: start on power-up
13 000024 001000 .word start ; PC
14 000026 000340 .word 340 ; PSW with priority level 7
15
16 001000 . = 1000
17 start:
18 001000 012705 160200 mov #bbaddr,r5 ; r5 = panel base addr
19 loop:
20 ; for mapped registers offsets see 11/70 memory dump
21 001004 016500 000014 mov 14(r5),r0 ; get SR switches <15:00>
22 001010 016501 000016 mov 16(r5),r1 ; get SR switches <21:16>
23 001014 010065 000044 mov r0,44(r5), ; set ADDR LEDs <15:00>
24 001020 010165 000046 mov r1,46(r5) ; set ADDR LEDs <21:16>
25 001024 000767 br loop
26 ; shorter using PDP-11 address modes:
27 001026 016565 000014 000044 mov 14(r5),44(r5)
28 001034 016565 000016 000046 mov 16(r5),46(r5)
29
30 .end
UniBone - BlinkenBone Panels
About BlinkenBone
"BlinkenBone" is a project to revive historic front panels by giving them a network interface and drive them by software ... via the interface "Blinkenlight API".
Let's recapitulate some concepts:
Server: A BlinkenLight panel is remote from the driving application, and implemented as "Blinkenlight API server". It can be
- an instrumented historical panel (not necessarily built by DEC),
- a PiDP-8, PiDP-11 or perhaps other replicas,
- any of the pool of photorealistic Java emulations (11/20,40,70, PDP-8/I, PDP-15, PDP-10 KI10)
Download these from github, and use one of the "*_panel_only" starter scripts.

Controls: the BlinkenLight API exposes a panel as "set of panel control"s. A "panel control" is just a functional group of switch and/or lamp fields on a panel. Read more.

Client: A "Blinkenlight API client" connects to the panel, queries its switches and sets its lamp via network. A client can be
- a computer emulation like SimH with "REALCONS" extension.
- a separate test program like "blinkenlightapitest"
- or (and thats we you're here): a real PDP-11 equipped with QBone/UniBone running the "blinkenbone device".
Using a panel on QUniBone?
Bad news first: a DEC CPU panel is highly bound to a specific CPU hardware. That's clear: you can Start, Stop or Single step the CPU, see Micro code execution, see execution levels like "Super", "User", "Kernel", manipulate the Memory Mapping and so on.
As QUniBone is a pure UNIBUS peripheral device, it has no insight to the local CPU. The only thing a panel could do is simple EXAM and DEPOSIT, perhaps as well as displaying the flickering glow on some ADDR and DATA lines.
That has almost no entertainment value, and seems not worth the work of integrating BlinkenBone to QUniBone.
So a different approach was choosen: "Programmers Playground".
- The panels lamps and switches are under full software control by a PDP-11 or µVAX equipped with an UniBone/QBone.
- The panel itself has no build-in function, its just a passive peripheral.
- All switches and lamps of a given panel are simply mapped into the PDP-11 I/O page as a register set.
- So you can max out the show effect on your own.
- Reading / writing theses registers by PDP-11 programs directly influence the panel. Sync with the panel is done periodically in background. The PDP-11 itself is not involved with TCP/IP traffic, the BeagleBone handles that. Registers can be accessed at any time.
- Every Blinkenbone panel can be connected to your PDP-11: for example, a 11/05 can now play with the PDP10-KI10 monstrum.
- The layout of the switch/lamp mapping registers changes with panel type.
As a BlinkenBone panel is controlled via I/O page registers, you can play with a connect panel also via manual EXAM/DEPOSIT operation. You can issue these over the "demo" emulator command prompt, over ODT console on QBUS systems .... or over the system front panel on UNIBUS PDP-11s. And then things go weird: you operate one panel to checkout a 2nd one? Nerds!
Function overview
From PDP-11 side, die "BlinkenBone" device consists in fact of two logical separated devices, much like the serial DL11 has a receiver and a transmitter part.
The "Input" section reads the panels switches, the "Output" section controls the panels lamps.
First in the memory map comes a fixed register set, which is the same for all connected panels.
This includes the typical Command and Status registers (CSR) for input and output.
After the fixed register block follow registers which map the panels switches and lamps. Number and function of these depend on the connected panel.
For the input side, the polling frequency can be set to a period of 1..1000 millisconds.
If a change on one of the panel switches is detect, a flag is set in the input CSR, and optional an interrupt is triggered. You can read-out the address of the most recently changed panel input registers also.
On output, the panel lamps are updated periodically from the mapped registers. Update frequency can also be set, and after each update a status bit is set in CSR and an interrupt can be triggered. So you can drive dynamic light effects with the speed of the periodic output interupt.
The BlinkenBone device even can be operated without a connected panel ... the output side can be used as a programmable interrupting timer then.
Using the BlinkenBone device
Short version first:
- Start the panel on a remote PC or RPi.
- configure the "blinkenbone" device, set the panels host name, and "enable".
- read and write the I/O page registers, which map the panels switches and LED banks.
Longer:
Under QUniBone, a BlinkenBone panel is a device just like DL11 serial port or RL11 disk controller, or any other.
From UNIBUS/QBUS perspective the BlinkenBone device is a register block in the I/O page and an interrupt source.
All further examples are made with the PDP-11/70 front panel.
And the network name of the host running the panel is important ... let assume its called "bigfoot" for now.
Step 1: Get the panel online
Startup the physical, replicated or emulated panel on a remote PC, which is connected to the BeagleBone via LAN.

Make sure no other client is controlling the panel ... this can happen, if you run for example a simulator/panel bundle like PiDP-11. In that case, you have to kill the simulator, so the panel can be controlled by QUniBone alone.
Step 2: Verify panel access
Log on to BeagleBone and make a test access to the panel, so you know you can talk to it: Use "ping " and the Blinkenlight API test program "./91_3rd_party/blinkenlightapitst".
You see which panels the server provides (usually only one with index "0"), and what "control" elements are exposed.
login as: root
root@unibone's password:
Have fun with UniBone !
Last login: Tue Jul 29 12:27:27 2025 from bigfoot.fritz.box
root@unibone:~# ping bigfoot
PING bigfoot.fritz.box (192.168.1.163) 56(84) bytes of data.
64 bytes from bigfoot.fritz.box (192.168.1.163): icmp_seq=1 ttl=128 time=0.409 ms
64 bytes from bigfoot.fritz.box (192.168.1.163): icmp_seq=2 ttl=128 time=0.623 ms
root@unibone:~# ./91_3rd_party/blinkenlightapitst bigfoot
*** blinkenlightapitst v1.09 - client for BeagleBone Blinkenlight API panel interface ***
Build Feb 3 2019 11:31:25
Copyright (C) 2012-2016 Joerg Hoppe.
Contact:
Web: www.retrocmp.com/projects/blinkenbone
Connecting to bigfoot ...
*** Select a panel from server on host "bigfoot" ***
0) Panel "11/70"
27 controls: 13 inputs + 14 outputs
Total 88 value bits: 37 inputs + 51 outputs
Value data stream: all inputs = 15 bytes, all outputs 17 bytes.
State of BlinkenBoards Enable=ACTIVE. Panel mode = 0 = "normal"
e <n> 'enable': toggle tristate of BlinkenBoards assigned to panel <n>
t <n> 'test': circulate lamp & switch test modes for panel <n>
i) print info about server
q) quit
>>> 0
*** Controls of panel "11/70", screen #0 ***
* Inputs:
POWER ( 1 bit SWITCH )=1o ! || PANEL_LOCK ( 1 bit SWITCH )=0o || SR (22 bit SWITCH )=00000000o
LOAD_ADRS ( 1 bit SWITCH )=0o || EXAM ( 1 bit SWITCH )=0o || DEPOSIT ( 1 bit SWITCH )=0o
CONT ( 1 bit SWITCH )=0o || HALT ( 1 bit SWITCH )=0o || S_BUS_CYCLE ( 1 bit SWITCH )=0o
START ( 1 bit SWITCH )=0o || ADDR_SELECT ( 3 bit KNOB )=7o ! || DATA_SELECT ( 2 bit KNOB )=3o !
PANEL_LOCK ( 1 bit KNOB )=0o
* Outputs:
0) ADDRESS (22 bit LAMP )=00000000o || 1) DATA (16 bit LAMP )=000000o
2) PARITY_HIGH ( 1 bit LAMP )=0o || 3) PARITY_LOW ( 1 bit LAMP )=0o
4) PAR_ERR ( 1 bit LAMP )=0o || 5) ADRS_ERR ( 1 bit LAMP )=0o
6) RUN ( 1 bit LAMP )=0o || 7) PAUSE ( 1 bit LAMP )=0o
8) MASTER ( 1 bit LAMP )=0o || 9) MMR0_MODE ( 2 bit LAMP )=0o
10) DATA_SPACE ( 1 bit LAMP )=0o || 11) ADDRESSING_16 ( 1 bit LAMP )=0o
12) ADDRESSING_18 ( 1 bit LAMP )=0o || 13) ADDRESSING_22 ( 1 bit LAMP )=0o
Step 3: set QUniBone "BlinkenBone" device parameters
We're working under the "demo" application here, in the "device" menu.
Just like other devices, you setup operating parameters first, then "enable" it.
Default base address and interupt vector sit in the PDP-11 "floating" address space, check for collision with other devices.
DC>>> sd blinkenbone
DC>>> p base_addr 760200
DC>>> p intr_vector 310
DC>>> p intr_level 6
DC>>> p panel_host bigfoot
DC>>> p panel_addr 0
DC>>> p panel_id 1234
For a full list,see next "reference" page.
Step 4: activate the panel.
When you "enable" the BlinkenBone device, connection to server is established and the register set is construced in the I/O page.
DC>>> en
Connected to BlinkenBone server bigfoot:
QUnibusAdapter: Registering device BLINKENBONE
Server info................: Panelsim1170_app v1.01
Server program name........: blinkenbone.panelsim.panelsim1170.Panelsim1170_app
Server command line options: --width 1000
[... Dump of fixed registers and mapped panel control registers ...]
Again, for a full list,see next "reference" page.
Step 5: work with the panel
You can now
- manually EXAM and DEPOSIT in the BlinkenBone device registers.
- run standalone PDP-11 programs. The distribution contains a test program which show several techniques to access a panel.
- or modify some operating system like RT-11, RSX11 or UNIX to react on the panel. We're talking of idle patterns here.
Needless to say, all this works on UNIBUS and QBUS machines. Yes, QBUS machines have Blinkenlight panels now!
UniBone - Auto Start
Power-On Sequence
A PDP-11 or VAX with UniBone/QBone installed will not run, until QUniBone software closes the so-called "GRANT chain" for Interrupts and DMA transactions.
The Power-On sequence to get a PDP-11+QUniBone system up and running is:
- PDP-11 power is switched ON.
- PDP-11 shows interactive console monitor or tries to boot. This fails, because system not yet complete operational.
- Unibone/QBone onboard Linux boots and connects to network. This needs more than a minute ... (Linux gurus?)
- User logs in into QUniBone wie ssh as root/root. QUnibone software must be started and selected device emulation begins.
Now the PDP-11 system is complete, UNIBUS/QBUS DMA and Interrupt Grant signals are handled correctly. - PDP-11 gets a 2nd Power-On signal by the starting emulation script, and can boot correctly this time.
Item 4 in this sequence can be automated by the "auto-start" feature, so no manual intervention is required too boot.
Auto-Start - Running an Emulation after Power-On
In short:
- The special script "autostart.sh" runs always once after Linux reboot.
- Selected emulation scripts are automatically started after UniBone/QBone power-up.
- Up to 16 scripts can be selected via 4 onboard DIP switches.
- You can preset up to 16 run configurations by editing the script "autostart.sh", see below.
- Interactive ssh sessions may connect to the running application.
The autostart sequence is:
1. After BBB power-on, an automatic (but hidden) login session is started,
Shell scripts .profile and autostart.sh execute the script corresponding to the DIP switch settings, these encode a number 0 to 15.
When looking onto UniBone/QBone PCB front, switch arrangement is:

"Switch up"=0, "down" = 1. Example: switches = "down,down,up,down" => mirror => 1101 => 1+2+8 => run script #11.
2. the selected "demo" emulation confirms startup by echoing DIP switch settings to the LEDs.
So when LED == DIP switches, the emulation runs!
3. The emulation is now running in background.
On further logins via ssh, you may connect to the terminal window of the running "demo" emulation (Emulation console).
See "man screen" for what is happening.
Editing autostart.sh
Assign your run scripts to DIP switch values in autostart.sh, function get_config() :
Your autostart.sh will not be erased on software update.
A sample file is given with all switch cases commented out:
[...]
####################################################################
# get_config() {
# Set up "config_info" and "config_cmd" for config "n"
# Called applications ("demo") should echo "n" onto the LEDs on start.
function get_config() {
# param $1: selection number from switches
# Adapt to your own needs!
n=$1
case $n in
#1)
# config_info="Just memory"
# config_cmd="./memory.sh --leds $n"
# ;;
#2)
# config_info="XXDP on RL1"
# config_cmd="./xxdp2.5_dl1.sh --leds $n"
# ;;
#3)
# config_info="RT11 5.5 single on RL1"
# config_cmd="./rt11v5.5sj_dl1_34.sh --leds $n"
# ;;
#4)
# config_info="11/34: RT11 5.5 FB on DU0:"
# config_cmd="./rt11v5.5fb_du0_34.sh --leds $n"
# ;;
#5)
# config_info="11/34: RSX11M4.8 on DU0:"
# config_cmd="./rsx11m4.8_du0+rl_34.sh --leds $n"
# ;;
#6)
# config_info="11/34: Unix V6 on RK05"
# config_cmd="./unixv6_dk0_34.sh --leds $n"
# ;;
*)
config_info=
config_cmd=
;;
esac
}
[...]
UniBone - Host File Sharing
Why "host file sharing"?
Any vintage computer emulator uses one binary file per emulated disk to save the disk's data blocks. This file is called the "image" of a disk.
UniBone/QBone uses disk images which are compatible to the SimH emulator.
When running, the emulated DEC computer puts a file system onto this disk image, creating a collection of hierarchically directories and files.
Mostly we run pre-installed images though.
The vintage file system is only accessible through the emulated machine's user interface.
Exchanging files between PDP's and the modern world is a permanent challenge.
Current solutions include
- Tools to externally manipulate disk images. There are a lot, well-known projects are DBIT's "PUTR", Don North's "xxdpdir" or Will's Work "DIAGDIR" .
Then there's the recent FSX (2019), quite complete. And RSX11M always had "FLX", the file transfer program. - Creating and dumping files on the vintage machine, by "catching output" and "typing input" text via a terminal emulator.
- Light weight communication programs via serial line.
There are lots of special reader/dumpers, and of course KERMIT. Even PDP11GUI falls into this category. - Host programs and perhaps special hardware to read vintage floppy disks.
- Setup of Ethernet networking on both vintage machine and modern computer:
For instance DECnet is an option (http://retrocmp.com/decnet), TCP/IP works with 2.11BSD UNIX or the Billquist port on RSX-11M .
"Host File Sharing" now is another approach: QUniBone's emulated disk image is permanentely analyzed and kept in sync with an external "host" directory.
The host directory is a regular place in the BeagleBone's Linux filesystem, and can be used by any Linux tool ... also remote accessed via sftp.
So the files visible for the DEC operating system can be manipulated under Linux, changes under Linux are written back to the DEC disk.


From DEC view, the DEC disk content changes "auto-magically" to reflect the current content on the Linux side.
The DEC machine does not need to know anything about the "host file sharing" mechanism.
This is an attractive approach as:
- there's no need to learn a special workflow for file converting.
- it is very easy to operate: file share just with the "COPY/DIR/DELETE" commands of DEC and Linux operating systems.
- it needs no additional hardware or software resources on the DEC PDP-11 or Linux side.
So we can exchange data even with older and very limited PDP-11s. - the PDP-11 can directly boot from a set of host files.
- implementations need only to deal with the DEC filesystem layout. The type of emulated disk does not matter.
QUniBones "file sharing" is a successor of the tu58fs emulator, with many enhancements.
Currently RT11 and XXDP filesystem can be used, hierarchical filesystems like Unix V6 or Files-11 are also possible.
How to use
To publish a QUniBone disk as "shared directory", only two additional parameter lines are necessary:
- shared_filesystem specifies the type of DEC file system on the disk image.
Currently RT11 and XXDP (soon) are implemented. - shared_dir is the location of the synchronized Linux host directory, relative to "demos" work path.
In an actual "demo" application, these parameter where set for a RL02 drive #0 like this:
>>p image RT11v53.rl02
>>p shared_filesystem RT11
>>p shared_dir shared_rl1
>>p
Parameters of device rl0:
Name Short Value Info
------------------ ----- ------------ -----------------------------------------------------------------------------
name name rl0 Unique identifier of device
type type RL02 Type
...
image img RT11v53.rl02 Path to binary image file. Empty to detach. ".gz" archive also searched.
shared_dir shd shared_rl0 Path to directory with shared files. Created on demand, empty to disable sharing.
shared_filesystem shfs RT11 Encode shared dir in this file system (empty, RT11, XXDP).
...
Generally, think of the Linux host directory as "user interface" to the DEC disk image.
This means:
- on startup, the host directory is re-initialized from the DEC side.
- if the DEC image is formatted/initilialized, all files on the host side get lost.
- After RESET of the emulated DEC machine all disk images are reopened and the host directories are initialized again.
- the host directory should be considered "volatile" ... don't keep important files there.
Multiple drives can be "shared" in parallel. As a shared file system is hold in-memory, the BeagleBones may be a limit later.
Creating images from host files
Normally the shared host directory is initialized from the DEC image.
The reverse operation is: Building a new image from downloaded DEC files.
This is quite straigth forward:
- Mount an filled, empty or non-existant image with the "p image <filename>"
- Clear all files in the host directory - the image gets synced and is now empty.
- Copy new files to the shared dir - the image gets synced and contains now a DEC file system with the desired files.
The image can now be used without attaching a Linux host directory.
Synchronizing
Files changes can occur simultaneously on the DEC disk image and the host directory.
Also file operations (like writing file content or directory entries) may take some time on the DEC side, as we have a slow CPU here.
As one consequence, synchronisation takes only place if both sides haven't touched their respective files for a certain period (1 second at the moment).
After that it's assumed file system operations are completed and data content is stable, so synchronization can begin.
Bottom line: it make take some seconds until changes appear on the other side.
Keep in mind the synchronisation of host and DEC directory is memory, CPU and SDcard intensive.
That's because for each change on each side, the complete DEC image is re-analyzed and possibly rewritten.
As the content of the shared DEC disk image changes auto-magically, the DEC OS must not cache the shared disk in PDP memory !
This is no problem on non-caching operating systems like RT11 or XXDP.
Maybe this will cause trouble on other filesystem types later, defintive for newer UNIXes.
About RT-11
The RT-11 operating system was used over the whole PDP-11's lifespan. It's filesystem is also accessible by RSX-11, RSTS and VAX/VMS with the EXCHANGE utility. This makes it a primary candidate for "file sharing".
However the RT-11 filesystem is quite different from modern Linux standard, a lot of mapping is needed.
The defining document is AA-PD6PA-TC "RT-11 Volume and File Formats Manual" from August 1991.
Directories: RT-11 has no subdirectories. Linux subdirectories are ignored and erased.
The directory can not grow indefinitely, it consists of max. 31 disk blocks of 512 bytes, with each block holding max. 72 file entries of each 7 words in size.
So the absolute maximum of file count is 2232 files. (For "directory extensions" see below).
Filenames: Filenames are saved in "Radix-50" compression: 3 letters can be saved in one 16 bit word.
The naming pattern is "6.3", and possible filename letters are "space", "A"-"Z", "0"-"9", "$" and "." .
So a Linux filename is heavily truncated and modified to be RT-11 compatible. For example:
"aaa_readme.txt.ba~" goes to "AAA RE.BA$" ...
The modified RT-11 name will also re-appear on the Linux side, as it is different.
And there's a special problem: two Linux file may map to the same RT-11 filename, with confusing results.
Best use only RT-11 compatible names under Linux, staying all UPPERCASE.
File protection: RT-11 files have a "protection bit" (E.PROT) for read-only status. Linux mapping is:
PROT <-> chmod 444
UNPROT <-> chmod 644
There is an other protecion bit E.READ, which just inhibits file write, not file deletion. This is ignored under QUniBone.
File time stamps: Under RT-11 only the file creation date is saved, not the time. The first possible date is 1-Jan-72, the latest date is 31-Dec-99.
if Linux date year is > 1999, it is truncated to 1999.
There is an "AGE" bit in the RT-11 directory entry for larger years, but this is ignored by all RT-11 monitors and also by QUniBone.
File sizes: RT-11 files have sizes in multiple of 512 byte blocks.
This is preserved under Linux, files are patched with 00s where necessary.
Host files with a size of 0 are not ignored under RT-11.
File allocation: Files under RT-11 are always saved in consecutive disk blocks.
This creates unused areas on the disk if files are deleted and the empty space can not be reused by smaller files.
RT-11 compresses the file system manually with the SQUEEZE utility.
As QUniBone rewrites the RT-11 file system on any Linux host dir change, SQUEEZE is never necessary.
Special files: A RT-11 disk has two special areas, which make a disk bootable and which are visible as Linux files:
$BOOT.BLK: Block 0 is the primary bootloader.
$MONI.TOR: Blocks 2..5 are a secondary bootloader.
These entries are generated by RT-11 utility COPY/BOOT.
You can make a RT-11 disk bootable by copying "good" $BOOT.BLK and $MONI.TOR to the image under Linux.
$VOLUM.INF: this Linux file contains information about the current RT-11 disk layout. It not present on the RT-11 disk, and re-created on any image change.
User file system modifications:
The RT-11 file system can be modified in two ways:
- You can have bigger directory entries: "extra bytes" in the directory entry. This area is accessible to application programs only, not by the RT-11 monitor itself. As experimental feature, QUniBone generates an extra Linux file for this directory extension, with suffix ".dirext".
- Files can have "prefix blocks", with additional fixed space for each file. RT-11 itself provides no support for prefix blocks, you need to write special applications to use it. As experimental feature, QUniBone generates an extra Linux file for prefix blocks if present, with suffix ".prefix"
Interleaving
On some disks reading and writing disk data requires CPU support opposed to DMA operation. This is the case of RX01 and RX02 floppies. Time to transfer one sector would cause the next disk sector to pass the read/write head, requiring a full disk rotation to appear again under the head. This would be result in very slow overall transfer, so between two sectors with ascending number a gap filled with a sector with other number is placed. This is called "sector interleaving".
Normally the disk is low-level formatted with an interleaved sector pattern, so interleaving is transparent to the file system. In case of RX01 and RX02 however floppy sectors are preformatted in strict ascending order. To its up to the file system to use sectors in an itnerleaved way. Thus the interlaving pattern is visible in the binary disk image, giving a quite confusing data layout in the images hex dump.
Decoding/encoding of the disk image is done by an additional "partition layout module" in the hostfile sharing software.
About XXDP
"XXDP" is the device-independend diagnostic package. It consists of a small operating system monitor and 100s of test programs.
Classic distribution is on 10MB RL02 platters, which is large enough to hold the full set of programs.
The "XXDP" file system is a single-user version of the early DOS-BATCH-11 operating system.
Only a single user directory is implemented, so like RT-11 above there are no subdirectories.
From user view its much like RT-11, regarding timestamps, 6.3 file names, file size in whole blocks, boot block and monitor.
Internals
To sync a DEC disc image with a Linuxfile system, several conversion states are necessary.
The drawing show the overall signal and data flow:
Synchronization between disk image and host file system takes place very often, on any change on any side. So optimization is a must-have.
- No multiple memory copies of the binary image and the cached or filesystem tree.
In-memory trees contain only structural information, file data content is fetched from the disk.
The decoded DEC filesystem is hold in-memory however. - No full scan operations to determine file system state. An event based message system greatly reduces CPU load.
While synchronizing, the DEC disk controller may not write or read the image to avoid data inconsistencies. A global lock is used here.
Also synchronization is only performed when both DEC image and Linux host directory were idle for a certain period ... assuming all pending file activies were finished then.
Known bugs:
- a file can not be "renamed" via sftp. It can be copied and deleted via sftp, but "renaming" produces wrong "inotify" events.
UniBone - Debugging
From painful experience you will know: A hardware/software system is only as good as its debugging tools.
And investing time into debugging tools is always a good idea: you will spend so much time in debugging, you deserve some comfort.
Therefore UniBone contains several mechanisms to diagnose whats its doing.
In contrast to regular "desktop" software, embedded code needs to run in strict timing, which may not be changed by the debugging process. And timing relations (in sub-microsecond resolution) must be made visible. So direct debug-printouts ("printf()") or Breakpoints are of minor use.
Non-invasive debugging on embedded devices like UniBone uses GPIO pins, whose levels are changed by software under certain conditions. Signals are then captured with an logic analyzer and evaluated independet of the running device.
***
UniBone software is distributed over 2 processors: the ARM is executing Linux and does all the high level emulation, while PRU is low level real-time UNIBUS interface.
***
PRU debugging: GPIO only
The PRU runs independently from main Linux ARM (thats the idea!). It communicates with the normal Linux program over a mailbox and is not directly reachable for Debugging. Regular debugging tools like "printf()" diagnostic or Eclipse based symbolic debugging are not available. The PRU has its own set of debugging tools, but I found it easier to rely totally on "GPIO debugging".
Debugging via GPIO means: connect a logic analyzer to some of the General Purpose IO pins, and trigger these pins under software control. Its like a high speed binary "printf()" on hardware level. The big advantage here is that we see the timing relations between hardware activity and program flow, in sub-microsecond resolution.
Watching the PRU register bus
The PRU has not enough pins to read and write 56 UNIBUS signals, so an GPIO multiplier is used with 8 read registers, 8 write registersand a REG_WRITE signal. So UniBone has a local data bus with 8 DATAIN, 8 DATAOUT, 3 ADDRESS and 1 WRITE signal. For debugging purposes it is exposed on pin headers:

Under C the PRU register latches are accessed by function calls like
buslatches_setbits(...) ;
buslatches_setbyte(...) ;
buslatches_getbyte(...) ;
By monitoring the accesses to these gpio-registers it easy to follow PRUs program flow.
The relation between UNIBUS signals and buslatches and latch pins is given in the circuit schematic.
PRU debug GPIO pin(s)
Additionally one unused PRU GPIO "1_12" can generated debug signals. You do that by inserting the expression "PRU_DEBUG_PIN0(1)" in your code.
For performance reasons "1_12" is automatically when PRU accesses its register bus.
A second GPIO "1_13" needs a modified BeagleBone to be usable.
PRU Example debugging session
Here is a real-world PRU debugging session. Problem was: the RL11 device emulator had a buggy DMA protocol: it set NPR, received NPG, but then did not raised SACK.
Apparently this logic expression in was wrong, but which term of it?
grant_mask &= (sm_arb.device_request_mask & ~sm_arb.device_forwarded_grant_mask);
I peppered the code in pru1_statemachine_arbitration.c with PRU_DEBUG_PIN0() statements and watched "GPIO 1_12".
Code from pru1_statemachine_arbitration.c
... 144: uint8_t sm_arb_worker_device(uint8_t grant_mask) { ... 174: // Always update UNIBUS BR/NPR lines, are ORed with requests from other devices.
175: buslatches_setbits(1, PRIORITY_ARBITRATION_BIT_MASK, sm_arb.device_request_mask) ;
176: // now relevant for GRANt forwarding
177: sm_arb.device_request_signalled_mask = sm_arb.device_request_mask;
178:
179: // read GRANT IN lines from CPU (Arbitrator).
180: // Only one bit on cpu_grant_mask at a time may be active, else arbitrator malfunction.
181: // Arbitrator asserts SACK is inactive
182:
183: if (sm_arb.grant_bbsy_ssyn_wait_grant_mask == 0) {
184: PRU_DEBUG_PIN0(0); PRU_DEBUG_PIN0(0); PRU_DEBUG_PIN0(0);
185: // State 1: Wait For GRANT:
186: // process the requested grant for an open requests.
187: // "A device may not accept a grant (assert SACK) after it passes the grant"
188: if (grant_mask) PRU_DEBUG_PIN0_PULSE(50) ; else PRU_DEBUG_PIN0_PULSE(10) ;
189: PRU_DEBUG_PIN0(0); PRU_DEBUG_PIN0(0); PRU_DEBUG_PIN0(0);
190: if (grant_mask & sm_arb.device_request_mask) PRU_DEBUG_PIN0_PULSE(50) else PRU_DEBUG_PIN0_PULSE(10) ;
191: PRU_DEBUG_PIN0(0); PRU_DEBUG_PIN0(0); PRU_DEBUG_PIN0(0);
192: if (grant_mask & sm_arb.device_request_mask & ~sm_arb.device_forwarded_grant_mask)
193: PRU_DEBUG_PIN0_PULSE(50) ; else PRU_DEBUG_PIN0_PULSE(10) ;
194:
195: grant_mask &= (sm_arb.device_request_mask & ~sm_arb.device_forwarded_grant_mask);
196: if (grant_mask) {
197: PRU_DEBUG_PIN1(1);
198: // one of our requests was granted:
...
Commented Logic analyzer output:
Time resolution is 200MHz,
Channels are:
- "PRU_DBG0" is GPIO 1_12 / PRU_DEBUG_PIN0();
- REG_SEL, REG_WRITE. REG_DATOUT and REG_DATIN are the local PRU latch bus.
- NPR, SACK, BR4,5,6,7 etc are UNIBUS arbitration signals
- BBSYS, SMYN, etc are UNIBUS data signals (no activity here).
- ARM_DBG0,1,2,3 are GPIO pins used for ARM debugging., see below (not used here).
This subset of UNIBUS signals is of interest here:
- latch 1, bit #4 (mask 0x10): NPR
- latch 0: GRANT INs. BG4IN = bit #0, BR5 = bit #1, ... NPGIN = bit #4.
- latch 4, bit #4: MSYN
- latch 7: INTR, INIT, ACLO DCLO.
Comments in the LA screen shot are mostly line numbers in the code listing above.

You can follow the PRU signal processing loop by watching the REG_* signals. Here:
| Code (line# or remote function) |
Latch operation | Meaning |
| 175: | latch 1 is written with 0x10 | emulated device sets NPR |
| 10ns later | NPR reaches UNIBUS DS8641 driver output and is visible on the bus. | |
|
188: |
long and short pulses |
Debugging: evaluated 3 logic terms which are relevant for generating SACK. "Short 10ns pulse = "false", long 50ns pulse = "true". |
| do_event_initializationsignals() | latch #7 is read | check for ACLO, DCLO and INIT event. |
| sm_data_slave_start() | latch #4 is read | emulated devcies MSYN for start of bus cycle. |
| sm_intr_slave_start() | latch #7 is read, | emulated CPU checks for INTR |
| sm_arb_worker_cpu() | latch#1 is read | emulated CPU checks device SACK |
| main() | latch #0 is written | forward GRANTOUT signals |
| ... and so on ... |
PRU GPIOs enhance LA trigger logic
PRU debugging features are not jsut useful to debug PRU code itself. They also can be used to enhance the trigger capabilites of the logic anylzer.
"Triggering" means to generate an print-out or an logic-analyzer trace just for the error situation.
Bringing reliability from 99% to 99.9% is as much work as going from 50% to 90% and from 90% to 99%, hence the "90:90" rule (which better should be called "90:90:90:..."). When debugging for while, remaining errors gets more erratic and complicated than at first "power-on". Then setting up trigger conditions for a logic analyzer can be cumbersome or even impossible, as pre-history of many other conditions may be involved.
As UniBone is written in C/C++, its easy to write complex trigger conditions in C and make the final "trigger/no-trigger" condition visible via ARM/PRI_DEBUG_PIN GPIO pin.
"C code trigger conditions" can also be used to analyze behaviour of the PDP-11 itself: in PRU code access to UNIBUS addresses and DATA can be evaluated and converted to trigger conditions.
For example, triggering on UNIBUS cycles for a given address range (hre: DL11 console) may be as easy as
sm_data_slave_start() {
...
if (addr >= 0777560 && addr <= 0777566)
PRU_DEBUG_PIN0(1) ; // trigger to LA.
Luckily recompiling and restarting the PRU software is quite fast.
ARM debugging: LEDs and GPIO signals
ARM code (the regular Linux application compiled with "gcc") can also toggle GPIO pins for realtime debugging.
Everything described for the PRU GPIOs also applies here, especially the possibility to generate logic analyzer trigger signals.
All of the BeagleBone GPIOs can be used by ARM code, four of them are preconfigured. The code macros are ARM_DEBUG_PIN0 ... ARM_DEBUG_PIN3, which also drive the LEDs.

The BeagleBone exposes tons of GPIOs, some already routed out to additional pinheaders. To make these accessible, just generate ARM_DEBUG_PIN*, by copy&pasting existing code.
ARM debugging: device message printing
ARM code can be debugged like any other Linux-program with a symbolic debugger. UniBone has no graphical desktop, but development and debugging can be done on a remote PC, I use a cross-compile toolchain under Eclipse.
The emulator software is constructed from many intern "devices", which run on parallel threads.
Debugging multi-threaded applications is a challenge, as the most subtle errors occur from timing relations between different threads. Keywords here: race condition & Heisenbug. Not funny!
To allow debugging with least timing impact, two additional features were build into UniBone:
- Each device can print messages into a global trace buffer. The buffer can later be dumped to a file or console. Saving messages into a buffer has much less impact than direct printout.
To speed things up, the buffer entries contain just the raw data for delayed printf() formatting.
- Debug output verbosity can be fine tuned on a per-device. Message severity and device verbosity. In C++ code devices printed debug output with FATAL,WARNING,ERROR,INFO and DEBUG macros. Each device has a "verbosity level" which specifies which Macros to ignore. The "verbosity level" can be changed any time during runtime.
These severity levels exist:
| Macro | Severity |
Default output |
Info |
| FATAL() | 1 | Buffer+console | Program termination! |
| ERROR() | 2 | Buffer+console | Real problem. |
| WARNING() | 3 | Buffer+console | Possible problem. |
| INFO() | 4 | Buffer+console | Entertaining activity report. Enabled by default for all devices with cmdline option "--verbose" |
| DEBUG() | 5 | Buffer only | Enabled by default for all devices with cmdline option "--debug" |
Every C++ object derived from class "logsource_c" can use this message system.
ARM debugging: message printing example session
Lets say we debug the RL02 drive emulator (just the drive, not the RL11 controller).
Operation is shown for "demo" control program. Steps are:
- Instrumenting the code: add DEBUG() statements
- Test preparation: We set "verbosity" of the drive to "DEBUG" (level 5) and clear the debug log.
- Run a test: for debugging we watch drive activity while listing a small text file.
- Save results: printout the log buffer.
Instrumenting the code
Debug print statements are inserted on several code lines in source "rl0102.cpp":
DEBUG("Drive start seek from cyl.head %d.%d to %d.%d", cylinder, head,
destination_cylinder, destination_head);
...
DEBUG("Change drive %s state from %d to %d. Status word %06o -> %06o.",
name.value.c_str(), old_state, state.value, old_status_word, status_word);
...
DEBUG("Seek: head switch to %d", head);
...
DEBUG("drive seeking outward, cyl = %d", cylinder);
...
DEBUG("File Read 0x%x words from c/h/s=%d/%d/%d, file pos=0x%llx, words = %06o, %06o, ...",
sector_size_bytes / 2, cylinder, head, sectorno, offset, (unsigned )(buffer[0]),
(unsigned )(buffer[1]));
The DEBUG() statements remain in the code. They are only enabled if device "verbosity" is "DEBUG".
Test preparation
Setup the "rl0" device in "demo":
>>>sd rl0
Current device is "rl0"
Controller base address = 774400 *** Test of device parameter interface and states.
"UniBone devices are clients to PDP-11 CPU doing NPR/INTR Arbitrator
(CPU active, console processor inactive).
CPU is physical or emulated.
Memory access as Bus Master with NPR/NPG/SACK handshake.
Current device is "rl0"
UNIBUS unibuscontroller base address = 774400
UNIBUS memory emulated from 000000 to 757776. m i Install (emulate) max UNIBUS memory
m f [word] Fill UNIBUS memory (with 0 or other octal value)
m d Dump UNIBUS memory to disk
m ll <filename> Load memory content from MACRO-11 listing file (boot loader)
m ll Reload last memory content from file "dl.lst"
m lp <filename> Load memory content from absolute papertape image
m lp Reload last memory content from file "dl.lst"
ld List all defined devices
en <dev> Enable a device
dis <dev> Disable device
sd <dev> Select "current device"
p <param> <val> Set parameter value of current device
p <param> Get parameter value of current device
p panel Force parameter update from panel
p Show all parameter of current device
d <regname> <val> Deposit octal value into named device register
e <regname> Examine single device register (regno decimal)
e Examine all device registers
e <addr> Examine octal UNIBUS address.
d <addr> <val> Deposit octal val into UNIBUS address.
dbg c|s|f Debug log: Clear, Show on console, dump to File.
(file = unibone.log.csv)
init Pulse UNIBUS INIT
pwr Simulate UNIBUS power cycle (ACLO/DCLO)
q Quit >>>
>>>p v 5
Name Short Value Unit Access Info
--------- ----- ----- ---- -------- ------------------------------------------------------
verbosity v 5 writable 1 = fatal, 2 = error, 3 = warning, 4 = info, 5 = debug
>>> p
Parameters of device rl0:
Name Short Value Info
------------------ ----- ---------------------- --------------------------------------------------------
name name rl0 Unique identifier of device
type type RL02 Type
enabled en 1 device installed and ready to use?
emulation_speed es 10 1 = original speed, > 1: mechanics is this factor faster
verbosity v 5 1 = fatal, 2 = error, 3 = warning, 4 = info, 5 = debug
unit unit 0 Unit # of drive
capacity cap 10485760 Storage capacity
image img rt11v5.5_games_34.rl02 Path to image file
rotation rot 2400 Current speed of disk
state st 5 Internal state
powerswitch pwr 1 State of POWER switch
runstopbutton rb 1 State of RUN/STOP button
loadlamp ll 0 State of LOAD lamp
readylamp rl 1 State of READY lamp
faultlamp fl 0 State of FAULT lamp
writeprotectlamp wpl 0 State of WRITE PROTECT lamp
writeprotectbutton wpb 0 Writeprotect button pressed
coveropen co 0 1, if RL cover is open >>>dbg c
Debug log cleared. >>>
Run test
We boot the PDP-11 to RT11 from that drive. To exercise the drive under RT11, we list a small textfile then:
.type starts.com
ind datime
!.MODULE STARTS,03,<BL/SJ Startup command file)
!
! Select the editor of your choice. Take the default (KED) unless you are
! not using a VT100 compatible terminal. If you are using an incompatible
! terminal (or a hard copy terminal) select the following command.
!
!SET EDIT EDIT
!
! Get the revision levels of your MU's, if you have them
!
!R MSCPCK
set tt scope
set edit ked
The RL02 drive now has a lot to do: head positioning and transfering sector data via DMA.
Inspect results
Now we display the buffered message onto UniBone's "demo" console:
>>>dbg s
[07:26:27.568355 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0153] Drive start seek from cyl.head 15.1 to 16.1
[07:26:27.568380 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0178] Change drive rl0 state from 5 to 4. Status word 000235 -> 000234.
[07:26:27.574092 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0332] Seek: head switch to 1
[07:26:27.574105 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0340] drive seeking outward, cyl = 15
[07:26:27.574110 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0348] drive seek outwards complete, cyl = 16
[07:26:27.574141 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0178] Change drive rl0 state from 4 to 5. Status word 000334 -> 000335.
[07:26:27.574543 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0583] File Read 0x80 words from c/h/s=16/1/2, file pos=0x7700000000, words = 001124, 000020, ...
[07:26:27.575054 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0583] File Read 0x80 words from c/h/s=16/1/3, file pos=0x50000000000, words = 000104, 000020, ...
[07:26:27.575617 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0583] File Read 0x80 words from c/h/s=16/1/4, file pos=0x21200000000, words = 050513, 000020, ...
[07:26:27.576152 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0583] File Read 0x80 words from c/h/s=16/1/5, file pos=0xffe000000000, words = 122700, 000020, ...
[07:26:28.108880 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0583] File Read 0x80 words from c/h/s=16/1/34, file pos=0x35df00000000, words = 020000, 000020, ...
[07:26:28.109377 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0583] File Read 0x80 words from c/h/s=16/1/35, file pos=0x11c000000000, words = 062700, 000020, ...
[07:26:28.109831 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0583] File Read 0x80 words from c/h/s=16/1/36, file pos=0x2000000000, words = 012605, 000020, ...
[07:26:28.110295 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0583] File Read 0x80 words from c/h/s=16/1/37, file pos=0xe2c000000000, words = 103375, 000020, ...
[07:26:28.128894 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0153] Drive start seek from cyl.head 16.1 to 0.0
[07:26:28.128908 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0178] Change drive rl0 state from 5 to 4. Status word 000235 -> 000234.
[07:26:28.134829 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0332] Seek: head switch to 0
[07:26:28.134839 Dbg rl0This email address is being protected from spambots. You need JavaScript enabled to view it. :0361] drive seek inwards complete, cyl = 0
... and so on ...
>>>
