Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.
Category: Software
General-purpose computers doing something specific
While trying to persuade a Windows program to run under Wine, I stumbled across this useful script: winetricks.
Basically, it downloads & installs the myriad runtime libraries / DLLs / programs that Windows programs generally assume are installed. Those are the things you don’t know are missing and generally can’t figure out how to install on your own.
It didn’t actually help get the program in question running. As nearly as I can tell, if at first you can’t get a program installed & running under Wine, just give up…
WordPress doesn’t allow ZIP files, but very often I want to upload a collection of files that all relate to a common topic. For example, the three Tek 492 EPROM HEX files ought to come with a bit of documentation about how to use them.
Fortunately, OpenDocument documents (sounds like something put out by the Department of Redundancy Department) are actually ordinary ZIP files with a different extension. There’s no good reason you can’t tuck some additional files into that container. Nay, verily, if you use a word processor file, then you can have documentation accompany your files!
Note that the additional files don’t have any effect on the word processor document: OpenOffice simply uses the files it knows about and ignores the additional ones. You won’t see them in the word processor document; you won’t even know they’re present.
However, because OpenOffice doesn’t know about them, it won’t transfer them to the new document when you save the file. You must add the files as the last step, after editing and saving the word processor document for the last time.
I suppose there are pathological cases where this will cause trouble and I certainly hope that OpenDocument validators will complain vehemently about the presence of additional files. Use this knowledge wisely, OK?
So, for example…
Create a document and save it as the default ODT format using OpenOffice; let’s call it Tek 492 ROM Images.odt, just so you can see one in action.
Now, to add those HEX files to it, you’d use the ordinary ZIP utility:
zip "Tek 492 ROM Images.odt" *hex
The quotes protect the blanks in the file name and you must type the entire file name out with the extension, because ZIP doesn’t expect to work with ODT files.
You can list the file’s contents, which will show you all the other files that go into making an OpenDocument document work:
For obvious reasons, if you’re stuffing a bunch of files into an ODT file, you should probably ZIP them into a single ZIP file of their own, then add that single file to the ODT file. That means your victims users must also apply UNZIP twice, which may be expecting too much.
When you want to use the HEX files, extract them:
unzip "Tek 492 ROM Images.odt" *hex
And there they are again:
ls -l
-rw-r--r-- 1 ed ed 15447 2009-08-11 20:51 Tek 492 ROM Images.odt
-rw-r--r-- 1 ed ed 4876 2009-07-30 11:56 U1012 - 160-0886-04.hex
-rw-r--r-- 1 ed ed 19468 2009-07-30 11:56 U2023 - 160-0838-00.hex
-rw-r--r-- 1 ed ed 19468 2009-07-30 11:56 U2028 - 160-0839-00.hex
That’s all there is to it…
For what it’s worth, Microsoft DOCX files (and their ilk) are also ZIP files in disguise, so this same hack should work there, too. However, many folks (myself included) treat MS DOC files with the same casual nonchalonce as they do any other hunk of high-level radioactive waste, so stashing an additional payload in those files might not have a happy ending.
This trick will certainly come in handy again, so I better write it down…
General idea: replacing a failing Mostek MK36000-series masked ROM in a Tektronix 492 Spectrum Analyzer memory board with an equally obsolete 27HC641 EPROM, using the bits found there.
The various datasheets give contradictory advice concerning device programming:
Assuming you have appropriate power supplies, then the waveform on VCE looks like this. The upper trace is the -OE signal for the data latch supplying the byte-to-be-burned, the lower trace is VCE on EPROM pin 21. Set VCC = 6 V on pin 24 while all this is going on. The currents range into the hundreds of mA; this is not a low-power device!
The squirts on the -OE latch signal before each programming cycle are eight quick writes to those antique DL-1414 displays that share the data bus with the EPROM. They show the current address and data byte during programming, plus other status & error messages.
The Microchip and GI datasheets both claim that the EPROM cells erase to all 1 bits, like every other EPROM I’ve ever used. The Philips datasheet says:
… after each erasure, all bits of the 27HC641 are in an undefined state. […] which is neither a logical “1” or a logical “0”
27HC641 EPROM in programming socket
The chip markings suggest they were made by Signetics, which got Borged by Philips some years ago. Lo and behold, the chip erases to a bizarre pattern that may be “undefined” but is perfectly consistent from erasure to erasure.
Therefore, you cannot blank-check a 27HC641 EPROM before programming it!
The Philips / Signetics datasheet doesn’t have a programming algorithm and the GI datasheet says you’re supposed to hold VCE at +12.5 V except when you’re asserting the programming data. So I used the Microchip algorithm on a Signetics chip and it seems to program properly.
The only quirk is that the Arduino Diecimila doesn’t have enough storage to hold the entire EPROM at once, so I verified each data byte as I wrote it, rather than doing the whole chip after the entire programming loop. The top picture shows a single 1 ms programming pulse followed by the 3-ms overburn pulse; the byte read back from the EPROM must agree with the source byte after each pulse. When it’s all done, I manually dump the entire EPROM as an Intel HEX file and verify that against the original HEX file: if it matches, the burn is good.
The byte-burning function goes a little something like this:
int BurnByte(word Address, byte Data) {
unsigned Iteration;
byte Success;
SetVcc(VH); // bump VCC to programming level
SetVce(VIH); // disable EPROM outputs
Outbound.Address = Address; // set up address and data values
Outbound.DataOut = Data;
Success = 0;
for (Iteration = 1; Iteration <= MAX_PROG_PULSES; ++Iteration) {
RunShiftRegister();
digitalWrite(PIN_DISABLE_DO,LOW); // present data to EPROM
SetVce(VH); // bump VCE to programming level
delayMicroseconds(1000); // burn data for a millisecond
SetVce(VIH); // return VCE to normal logic level
digitalWrite(PIN_DISABLE_DO,HIGH); // turn off data latch buffer
SetVce(VIL); // activate EPROM outputs
CaptureDataIn(); // grab EPROM output
SetVce(VIH); // disable EPROM outputs
RunShiftRegister(); // fetch data
if (Data == Inbound.DataIn) { // did it stick?
Success = 1;
break;
}
}
MaxBurns = max(MaxBurns,Iteration);
if (Success) { // if it worked, overburn the data
digitalWrite(PIN_DISABLE_DO,LOW); // present data to EPROM (again!)
SetVce(VH); // bump VCE to programming level
delay(3 * Iteration); // overburn data
SetVce(VIH); // return VCE to normal logic level
digitalWrite(PIN_DISABLE_DO,HIGH); // turn off latch buffers
SetVce(VIL); // activate EPROM outputs
CaptureDataIn(); // grab EPROM output
SetVce(VIH); // disable EPROM outputs
RunShiftRegister(); // fetch data
Success = (Data == Inbound.DataIn); // did overburn stick?
}
return !Success; // return zero for success
}
NOTE: the MK36000 and 27HC641 have slightly different address bit assignments.
The MK36000 address bits look like this:
pin 18 = A11
pin 19 = A10
pin 21 = A12
The 27HC641 address bits look like this:
pin 18 = A12
pin 19 = A11
pin 21 = A10
Now, if you’re building a programmer, just wire up the 27HC641 socket as if it were a MK36000 and everything will be fine. The byte locations within the chip won’t match those in the original MK36000, but it doesn’t matter because you store bytes at and the Tek CPU fetches bytes from the same addresses.
However, if you’re using a commercial EPROM programmer, it will write the bytes at the locations defined by the 27HC641 address bit assignments (because that’s all it knows), which will not work when plugged into the Tek board. Choose one of the following options:
build an interposer board to permute the address bits
cut-and-rewire the Tek board (ugh!)
write a program to permute the bytes in the HEX file
Think about it very carefully before you build or program anything, OK? The checksum will most likely come out right even with permuted bits, but the CPU will crash hard as it fetches the wrong instructions.
Memo to Self: always RTFM, but don’t believe everything you read.
While I was putting together the Tek 492 memory board reader, I though it’d be a nice idea to add a small display. While the Arduino has USB serial I/O, the update rate is fairly pokey and an on-board display can provide more-or-less real time status.
Given that the reader was built for an early-80s memory board, I just had to use a pair of Litronix DL-1414 4-character LED displays from my parts heap. The DL-1414 datasheet[Update: link rot? try that] proudly proclaims:
The 0.112″ high characters of the DL1414T gives readability up to eight feet. The user can build a display that enhances readability over this distance by proper filter selection.
I think that distance is exceedingly optimistic.
DL-1414 LED display schematic
However, I needed to see only a few feet to the benchtop. Even better, adding the displays required no additional hardware: the SPI-driven shift registers on the board already had address and data lines, plus a pair of unused bits for the write strobes. What’s not to like?
This schematic connects to the one you just clicked on; the two big blue bus lines are the same bus as in that schematic.
If you don’t have anything else riding the data bus, adding a pullup on the D7 bit that isn’t used by these displays will make all the bits float high; the DL-1414s seem to pull their inputs upward. That came in handy when I was debugging the EPROM-burning code, because reading data without an EPROM in the socket produced 0xff, just like an erased EPROM byte.
The two displays are the dark-red rectangle in the lower-right of the first picture, covered with a snippet of the Primary Red filter described there.
These closeups, without and with the filter, demonstrate why you really, really need a filter of some sort.
DL1414 UnfilteredDL1414 Filtered
Using the displays is straightforward, given the hardware-assisted SPI code from there. You could actually do it with just the I/O pins on an Arduino board, but you wouldn’t be able to do anything else. If you don’t have any other SPI registers, you could get away with a pair of HC595 outputs: 7 data + 2 address + 2 strobes + 5 outputs left over for something else.
A few constants set the display size and a global buffer holds the characters:
#define LED_SIZE 4 // chars per LED
#define LED_DISPLAYS 2 // number of displays
#define LED_CHARS (LED_DISPLAYS * LED_SIZE)
char LEDCharBuffer[LED_CHARS + 1]; // raw char buffer, can be used as a string
A routine to exercise the LEDs by scrolling all 64 characters they can display goes a little something like this:
Serial.println("Exercising LED display ...");
Outbound.Controls |= CB_N_WRLED1_MASK | CB_N_WRLED0_MASK; // set write strobes high
digitalWrite(PIN_DISABLE_DO,LOW); // enable data outputs
while (digitalRead(PIN_PB)) {
digitalWrite(PIN_HEARTBEAT,HIGH);
byte Character, Index;
for (Character = 0x20; Character < 0x60; ++Character) {
for (Index = 0; Index < LED_CHARS; ++Index) {
LEDCharBuffer[Index] = Character + Index;
}
UpdateLEDs(LEDCharBuffer);
delay(500);
if (!digitalRead(PIN_PB)) {
break;
}
}
digitalWrite(PIN_HEARTBEAT,LOW);
}
WaitButtonUp();
A routine to plop a string (up to 8 characters!) on the LEDs looks like this:
void UpdateLEDs(char *pString) {
byte Index = 0;
while (*pString && (Index < LED_CHARS)) {
Outbound.DataOut = *pString; // low 6 bits used by displays
Outbound.Address = ~Index; // low 2 bits used by displays, invert direction
Outbound.Controls &= ~(Index < LED_SIZE ? CB_N_WRLED1_MASK : CB_N_WRLED0_MASK);
RunShiftRegister();
digitalWrite(PIN_DISABLE_DO,LOW); // show the data!
Outbound.Controls |= CB_N_WRLED1_MASK | CB_N_WRLED0_MASK;
RunShiftRegister();
digitalWrite(PIN_DISABLE_DO,HIGH); // release the buffers
++pString;
++Index;
}
You can use sprintf() to put whatever you like in that string:
Not, of course, that anybody would actually use DL-1414 displays in this day & age, but the general idea might come in handy for something more, mmmm, elegant…
Having gotten my buddy Eks back on the air with new EPROMs for his Tek 492 spectrum analyzer, here are the Tek 492 ROM Images (← that’s the link to the file!) you’ll need to fix yours.
[Update: the comments for that post have pointers to other images and a clever hack to use a standard EPROM. If you’re not a stickler for perfection, that’s the way to go.]
They’re taken from a “known good” Tek 492, so they should work fine: the firmware verifies the checksum in each chip as part of the startup tests; if it’s happy, we’re happy.
Because WordPress doesn’t allow ZIP files, I tucked the HEX files into an OpenDocument file that also contains the pinouts and some interposer wiring hints & tips.
If you’re using the OpenOffice.org word processor, you’re good to go. Open the document and get all the instructions you need to extract the files and put them to good use.
If you’re not using OOo, then choose one of:
Install OpenOffice.org (it’s free software, so kwitcher bitchin’)
Futz with whatever Microsoft claims will import ODT files (if it doesn’t work, don’t blame me)
Just extract the HEX files and do whatever you want (if you know what you want)
The trick, explained in the document itself, is that ODT files are just ZIP files with a different file extension, so any unzip program will unpack them. You won’t see the HEX files in the document, you must apply unzip to the ODT file itself.
After unzipping, you’ll find three HEX files in the directory that originally held the ODT file, along with the collection of files that make up the OpenDocument document.
Oh, if you haven’t already figured it out, the DIP switch on your board is also bad. Saw the damn thing apart with a Dremel tool, pry off the debris, unsolder the pins, and install a new one. Just Do It.
Watching the tree frogs crawl up the tent from inside let us see how they move: hand-over-hand up the fabric. A dozen of them crawling along was spooky…
I took a movie with my pocket camera that turned into an 8 MB AVI, which I can’t upload here. Most of it isn’t all that interesting, anyway, an observation which hasn’t stopped YouTube dead in its tracks yet, but we can do better than that.
A pair of Free Software programs extracts the interesting part and produces a (somewhat) smaller animated GIF that works with WordPress.
A bit of browsing showed that I wanted frames 227 through 265 and that the frog was pretty much in the upper-middle of the image. So, crop a 320×240 image around the frog from those 640×480 frames:
cd frames
mkdir stills
for f in `seq 227 265` ; do convert frame-$f.jpg -crop 320x240+160+60 stills/still-$f.jpg ; done
Then convert them into an animated GIF with a 500-ms frame rate (the -delay ticker is 10 ms):
cd stills
convert -delay 50 still-2* frogwalk.gif
It’s a 1.6 MB wad, but gets the message across: frogs keep three paws stuck to the floor.
Remember, that little guy is moving at glacial speed in the GIF: those 40 frames of video last just over a second in real time.
Memo to self: MPEG-1 and MPEG-2 only support video-style frame rates around 30 fps.
Update: Regular reader Przemek Klosowski showed me how to generate those numeric sequences on the fly, without using a for loop:
There's this neat Bash construct {1..10} that you can use directly after ffmpeg:
convert -delay 50 still-{227..430}* result.gif
You can even skip every nth frame:
convert -delay 50 still-{227..430..5}* result.gif
I’m doing some work with a one-off ROM reader & EPROM programmer, so it’s once again time to mess around with Intel HEX files, raw binary images, and the like.
The key routine (which runs on an Arduino Decimila) to dump a ROM in HEX format goes like this, with all the constants & variables & functions doing the obvious things:
void DumpHC641(void) {
word Address,Offset;
byte DataRd,Checksum;
for (Address = 0; Address < ROM_SIZE; Address += IHEX_BYTES) {
sprintf(PrintBuffer,":%02X%04X00",IHEX_BYTES,(word)Address); // emit line header
Serial.print(PrintBuffer);
Checksum = IHEX_BYTES + lowByte(Address) + highByte(Address) + 0x00; // record type 0x00
for (Offset = 0; Offset < IHEX_BYTES; ++Offset) {
digitalWrite(PIN_HEARTBEAT,HIGH);
DataRd = ReadHC641(Address + Offset);
digitalWrite(PIN_HEARTBEAT,LOW);
Checksum += DataRd;
sprintf(PrintBuffer,"%02X",DataRd); // data byte
Serial.print(PrintBuffer);
}
Checksum = -Checksum; // two's complement
sprintf(PrintBuffer,"%02X",Checksum);
Serial.println(PrintBuffer);
}
Serial.println(":00000001FF"); // emit end-of-file line
}
So getting an Intel HEX file is just a matter of capturing the serial output, whacking off any debris on either side of the main event, and saving it.
The srec_cat program handles conversions among a myriad formats, most of which I can’t even pronounce. The few I use go a little something like this:
It’s sometimes handy to apply srec_cat to a group of similarly suffixed files, in which case some Bash string chopping comes in handy. For example, to convert some hex files into binary:
for f in 27HC641*hex ; do echo ${f%%hex} ; srec_cat "$f" -intel -o "${f%%hex}"bin -binary ; done
Good old diff works fine on text files, but in this case it’s better to see which bytes have changed, rather than which lines (which don’t apply in the context of a binary file). The vbindiff program looks great on a portrait-mode display.
I don’t do much binary editing, but tweak serves my simple needs. Confusingly, members of this class of program are called “hex editors”, but they really work on binary files.
There’s also diff3, for those rare cases where you must mutually compare three text files. Makes my head spin every time…
All those programs are likely packages in your favorite Linux distro.