Oldskooler Ramblings

the unlikely child born of the home computer wars

Archive for the ‘Vintage Computing’ Category

Beyond Economic Recovery

Posted by Trixter on January 11, 2016

The phrase “Beyond Economic Recovery” is one of my favorite phrases because it succinctly describes how to determine if you can safely share an old program, manual, game, etc. online. ¬†Please note that safe != legal. ¬†It is always illegal to share things you don’t own and you are responsible for any¬†repercussions¬†if you break your country’s laws. ¬†This post isn’t about whether it is legal. ¬†This post is about whether or not you should be overly worried that you will be pursued by some IP holder’s legal department and sued into the ground.

This would be a good time to mention that I am not a lawyer, and this is not legal advice.

Unless you are a large-scale pirating operation already under government investigation, what usually happens when infringement is discovered is that the infringing party is notified through a cease and desist letter. ¬†Quick compliance with the terms of the letter is almost always¬†enough to avoid further action. ¬†But what if you are on a Quixote-like mission to share¬†this rare vintage¬†content with the world and really, REALLY want it to stay publicly available? ¬†That’s when you apply The Phrase.

“Beyond Economic Recovery” isn’t my phrase; it was uttered to me in more than one interview I’ve had with lawyers on this specific subject. ¬†Here’s how to use it: ¬†Let’s say you want to share a 30-year-old game on the web for others to grab. ¬†If you’re worried about legal repercussions,¬†perform some due diligence and research if the company is actively using the work (the code, its trademarks, its intellectual property, etc.) to earn money, or has immediate or announced plans to do so. If so, such as in the case of Super Mario Brothers, don’t share it. But if not, as in the case of something like Space Strike, you have almost nothing to worry about. ¬†When a company is made aware of¬†infringement (usually discovered via¬†automated google¬†searches and machine¬†learning), they perform a quick check of whether or not¬†they would lose money sending the infringing party a cease and desist letter. ¬†The average cost of a C&D letter, accounting for all time and services rendered, is roughly $4000. ¬†If the company has¬†an internal legal department or prepares communication in batches (or both), that number can be a little less, but it’s still thousands of dollars. So the mental check is essentially “Can we make more than $4000 on the asset or intellectual property this person is threatening¬†to dilute by giving it away for free?” If the answer is “no”, they don’t bother sending a C&D letter.

The Internet Archive enjoys both non-profit status and various DMCA exemptions, which allows them to make various historically-relevant software works available online — but a DMCA exemption doesn’t prevent companies from sending them C&D letters to protect their trademarks or intellectual property. ¬†(It also doesn’t succinctly define¬†what is covered under the exemption, as it uses words like “obsolete” without defining what time period “obsolete” refers to.) ¬†Some works that used to be public on the IA have since been hidden at the request of the IP holder. ¬†For everything else that is still public there,¬†The Phrase is the¬†principle that¬†“protects” those software portions of The Internet Archive; they are simply Beyond Economic Recovery.

Posted in Gaming, Software Piracy, Vintage Computing | 1 Comment »

Jack of all trades, master of one

Posted by Trixter on December 31, 2015

I’m sure there’s a famous quote that, paraphrased, reads “I’d rather be an expert in one thing than dabble in many things.”

2016 is the year I put that into practice with the start¬†of a year-long experiment. ¬†I’ll post more details in January, but the short answer¬†is that I’m going to focus on mostly one thing in 2016 and see what happens. ¬†It will involve audio production, light video production, and seeing if I’m still relevant in one of my preferred hobbies. ¬†I also plan on taking monthly metrics for what I’m doing and¬†will make those metrics available at the end of 2016.

Posted in Uncategorized, Vintage Computing | Leave a Comment »

Solving an IBM PS/2 Model 25-286 Chicken and Egg CMOS problem

Posted by Trixter on September 4, 2015

I solved something recently and thought others (read: vintage computer nerds) might be interested in the write-up. I recently acquired an IBM PS/2 Model 25-286 and wanted to read data off of the hard drive. ¬†The 25-286 relies on configuration data stored in CMOS, however the battery-backed CMOS is dead, leading to the error codes 161 and 163 on boot-up. The system miraculously boots from the hard drive just fine in this condition (documentation suggests the hard drive table is fixed to a single entry). But, you can’t transfer data off of a system in this condition because 1. The floppy drive table is wrong and thinks the 1.44MB drive is a DSDD drive and can’t read/write a 1.44MB diskette, and 2. There are no entries in the BIOS table for the built-in serial and parallel ports, so they don’t show up, can’t use MODE COM1, etc. Short of physically moving the hard drive into another system, there’s no way to get data in/out of it.

The obvious fix is to write the 8525-286 diag and setup diskette somewhere and boot it to set proper CMOS values, but the diag/setup diskette image is a 1.44MB image, and the system can’t read it because the scrambled-CMOS configuration only reads/writes DSDD disks. So this is where the chicken-and-egg problem lies: To fix the system, you need to boot a diskette — but the diskette isn’t bootable until the system is fixed. (There’s another issue: Since the battery is dead, the setup disk will set proper values, perform a warm reboot — and then the values are gone again since the battery is dead.)

Armed with the knowledge that that the system can read 720KB diskette media just fine if formatted in another computer, I was able to follow this procedure to temporarily force a functional system:

  1. Use tweener system to write out the 8525-286 diag/setup diskette from diskette image
  2. Copy resulting setup/diag files onto a 720KB DSDD diskette (NOT a 1.44MB diskette formatted as 720KB).
  3. Boot Model 25-286 from internal hard drive
  4. Run the “SC.EXE” program from the setup/diag disk
  5. Using SC.EXE, force correct values, then save them.
  6. Hit <ESC> to back out to DOS (do NOT hit enter to reboot the system)
  7. Perform an immediate, non-cold, non-warm reboot by issuing INT 19H (instructions below) — do not have bootable diskette in the drive, and for safety have an empty config.sys and autoexec.bat

Doing this will leave the system in a correct state until you perform a warm (ctrl-alt-del) or cold (power) reboot. DOS will reload and parse the new temporary CMOS values.  The floppy drive reads/writes 1.44MB in this state, and the serial and parallel ports are recognizable and function. While I wrote this, I was archiving the entire hard drive to another system using FastLynx and a parallel-port cable.

According to my friends at Fort Collins PC Repair company, the proper fix, of course, is a Dallas 12887+ replacement battery/clock chip. Three are already on their way to me from China (hope they aren’t pulls!).

To issue INT 19H, you can use DEBUG.COM. Start DEBUG, then type:

a &lt;enter&gt;
int 19 &lt;enter&gt;
g &lt;enter&gt;

Posted in Vintage Computing | Tagged: | 7 Comments »

Converting a newer CGA card to an older one

Posted by Trixter on August 28, 2015

I don’t normally just reblog stuff, but Great Hierophant’s blog entry on how to convert a “new-style” CGA card to an “old-style” card is worth taking a look at.

Why would you want to do this?  Slightly brighter output is a plus, and to run the most earliest games that support composite color output as their authors intended.

Posted in Vintage Computing | Leave a Comment »

Still no love for the IBM PC

Posted by Trixter on August 23, 2015

It’s been 2.5 years since I talked about how there’s no love for the IBM PC, and not much has changed. ¬†I’ve discovered one more¬†youtube channel that covers 808x-era games (dfortae’s game reviews), but that’s it. ¬†There are still no podcasts that cover the first decade of the PC; even the Retro Computing Roundtable hardly mentions it.

What has changed in 2.5 years is my understanding of why that is. ¬†I think the 808x-based era (8088 or 8086 computers that are IBM PC compatible, 1981-1989) is mostly overlooked because both the system and its users are stuck between worlds. ¬†Let’s start with the IBM PC itself:

  • Is it a home computer or a business computer? ¬†It was a business computer for the home, so try classifying that one. ¬†It definitely had business-class power and expandability (and price tag), but also had BASIC in ROM, cassette tape support, and could connect to home televisions.
  • Is it an 8-bit or 16-bit computer? ¬†It came out squarely in the middle of the 8-bit era, and had an 8-bit bus and an 8-bit path to memory. ¬†But, the 8088 is internally a 16-bit CPU, with 16-bit registers and a 16-bit ALU, ¬†Most people categorize it as a 16-bit computer and part of the 16-bit era, but considering the Atari ST and Amiga are the poster children for the 16-bit era, it doesn’t feel like it should be in that category as it is significantly less capable than those systems.
  • When was the system considering viable for gaming?¬† There were games available for the system in the same year it was launched, but many people consider games with ugly CGA graphics or text-only adventures a party trick and not actual games. ¬†I disagree, but ask most online people¬†what “DOS games” are and you’ll get a picture painted in¬†a VGA¬†palette.

The users/fans/retrohobbyists of the PC are also stuck between worlds:

  • Generation Y Millennials grew up with the web, blogging, social media, YouTube… but the IBM PC was not their system, so they don’t cover it. ¬†Most old PC gaming channels on YouTube, for example, consider “MS-DOS Gaming” as starting in the era of¬†386+VGA+soundcard systems. ¬†These are good channels, don’t get me wrong (Pushing up Roses, Lazy Game Reviews, Ancient DOS games, DOS Nostalgia come to mind), but they only rarely cover the first decade of PC gaming. ¬†That’s 10 years of games not getting coverage — quite a gap.
  • So that leaves us Generation X’ers to cover it, because we did grow up with the PC… but, there are two forces working against early PCs getting coverage. ¬†The first is that Gen X people also grew up with other systems. ¬†The second is that not every Gen X’er is comfortable doing a podcast or video channel. ¬†So out of the few that are, the attention is spread across all 1980s systems (including consoles), leaving a tragically small slice of people who are both capable and motivated to do so for the early PC.

Come on, I can’t be the only one. ¬†Won’t someone start a¬†1980s-era PC podcast or YouTube channel?

(I thought the original classic Mac would have this problem too, but a cursory search of the interwebz shows a plethora of websites dedicated to classic macs, and even a retro mac podcast coming up on its 376th episode.  Yikes!)

Posted in Vintage Computing | Tagged: | 19 Comments »

Comedy gold in the Handy specification

Posted by Trixter on August 12, 2015

The Atari Lynx is my favorite handheld console.  Planning and design started in 1986, long before the Nintendo Game Boy, but development took long enough that it was actually released half a year after the Game Boy.  Whether or not that makes it the first or second handheld console is up for discussion, but it was definitely one of the first two.

History shows that the Lynx had an unsuccessful run compared to other handheld consoles of the time, which includes¬†the Game Boy, Game Gear, and¬†TurboExpress. ¬†Lynx’s failure in the market was split fairly evenly¬†between three culprits:

  1. Price: $179 vs. Game Boy’s $100
  2. Battery life: It took 6 AAs to power the unit for only 4 hours
  3. A lack of compelling licensed or original titles

The first hardware revision was also somewhat large, but as a guy with large hands, that never bothered me.

There were some killer arcade ports to the Lynx that really showcased what the system was capable of, such as Road Blasters, Stun Runner, and Klax. ¬†But¬†these were ports of Atari games; Lynx never got the “big” licensees such as Mortal Kombat or Street Fighter (a double shame considering the Lynx supported true hardware multiplayer linkups).

I recently sought out the Lynx hardware specification so that I could reminisce, sadly, about all of the untapped power Lynx had that was never realized.  The Lynx was so powerful that it shamed other handhelds for nearly a decade after it was discontinued:

  • 4096-color palette
  • Unlimited sprites with scaling, clipping, and collision (multiple modes!) all performed in hardware
  • 4-channel stereo sound supporting both algorithmic and sampled waveforms (in other words,¬†it could play both pure-hardware chiptunes and Amiga mods)
  • Multiplayer cable connections up to 8 simultaneous players/units
  • Supported left-handed play via the ability to flip the screen

There were other cool bits for the time it came out too, like a limited 32-bit math-coprocessor (used for some true 3-D polygon games like Hard Drivin’ and Steel Talons). ¬†It wasn’t perfect; the system would¬†slow down if you pushed it too hard (too many sprites, too many sampled sounds playing simultaneously), but it was creative and ambitious.

The Lynx started life as a hardware project by Epyx, initially called “Handy” (because it was to be the first handheld console). ¬†Ex-Amigans RJ Mical and Dave Needle were involved in the design, which is why Handy feels like a tiny Amiga. ¬†The initial specification was written in June of 1987 by Dave Needle. ¬†Reading through it, two things are immediately evident:

  1. The designers of Handy had a passion for creating a fun device with as many progammer assists as possible
  2. Dave had a wry sense of humor

I will now¬†reproduce for you some of the comedy gold hiding in the Handy specification. ¬†I’ve truncated some sequences for brevity, but the words are all Dave’s:

The human input controls consist of a 4 switch (8 position) joy stick, two sets of 2 independent fire buttons, game pause button, 2 flablode buttons. power on, and¬†power off….Flablode is¬†a Jovian word meaning a device or function that we know is required or desired but for¬†which we don’t have an actual definition (noun: flabloden, verb: to flablode).

3. Software Related Hardware Perniciousness
(or why some software people hate some hardware people)

There are certain things that the software ought not to do to the hardware. While these things are not physically destructive on their own, they will cause the hardware to act unpredictably strange, which may cause the user to be physically destructive. While we certainly could have protected the system from many of the following problems, I doubt that we would have found them all. In addition, the act of software doing one of these things means that there is a problem in the software anyway and the intended function would probably not happen. Additionally, this is only a toy. If this unit were a bio-medical device I would have found a safer solution.

3.1 Don’t Do These Things.

If you do any of the things that I tell you not to do, the hardware will act¬†unpredictably and will not be expected to recover without a complete system¬†initialization. So don’t do them.

3.1.5 Palettes and Page Breaks

This one is an actual hardware bug of mine…(much technical info removed)…Pen index¬†numbers C,D,E, and F will be incorrectly unchanged. Sometimes I am such a jerk.

3.2 Please Don’t Do These Things.

There are also things that software people do that merely annoy the hardware¬†people rather than annoy the end user. This includes seemingly harmless activities¬†like sub-decoding bit values¬†from byte descriptions (sprite types, for instance), or¬†assuming address continuity between areas of hardware…Please don’t do them.¬†It would be difficult to list all of the possibilities, so I will list the general¬†considerations and ask you to be sensible about them. In addition, please feel free to¬†submit a request for an exemption-on a specific case. If it is granted, we will change¬†the specification so as to make the special case forever legal. The price will be small.

3.3 Do It This Way

Some of the hardware functions, as designed by us mindless brutes, require special handling. As we discover these requirements, I will list them here.

4.3 CPU Sleep

BIG NOTE: Sleep is broken in Mikey. The CPU will NOT remain asleep unless Suzy is using the bus. There is no point in putting the CPU to sleep unless you expect Suzy to take the bus. We will figure out how to save power some other way.

All hardware¬†collision detection is done with the data in the collision buffer, not the data in the video¬†buffer. This has obvious advantages and will be explained at seminars and in¬†newsletters forever….In addition, the software can either do its own collision detection, or use the¬†contents of the collision buffer for some weird collision detection algorithm. In either¬†event, I will be mortally offended.

In addition, this means that when some of the bytes are not reloaded, the length of the SCB will be smaller by the number of bytes not used. If I have said this in a confusing manner, then I have.

Well, I finally found the bug that required a pad byte of 0 at the end of each scan¬†line of data. But, It is actually 2 bugs. I have fixed one of them, but the other requires¬†an extensive change. Too bad, I am out of time. Therefore:¬†There is a bug in the hardware that requires that the last meaningful bit of the¬†data packet at the end of a scan line does not occur in the last bit of a byte (bit 0). This¬†means that the data packet creation process must check for this case, and if found,¬†must pad this data packet with a byte of all Os. Don’t forget to adjust the offset to¬†include this pad byte. Since this will only happen in 1/8 of the scan lines, it is not¬†enough overhead to force me to try to fix the bug. Sorry.

This bit can be used to pause the¬†sprite engine. The ‘unsafe’ condition of the internal registers is not directly¬†affected by this bit.

I need to think about how to use it.

Receive parity error can not be disabled. If you don’t want it, don’t read it….We have just discovered that the calculation for parity includes the parity bit¬†itself. Most of us don’t like that, but it is too late to change it.

11.7 Unusual Interrupt Condition

Well, we did screw something up after all. Both the transmit and receive¬†interrupts are ‘level’ sensitive, rather than ‘edge’ sensitive. This means that an¬†interrupt will be continuously generated as long as it is enabled and its UART buffer is¬†ready. As a result, the software must disable the interrupt prior to clearing it. Sorry.

11.8 left out

I left something out. I know what it is but by the next time I revise this spec, I may have forgotten.

I have forgotten.

12.1.4 Bugs in Mathland

BIG NOTE: There are several bugs in the multiply and divide hardware. Lets hope that we do not get a chance to fix them.
1. In signed multiply, the hardware thinks that 8000 is a positive number.
2. In divide, the remainder will have 2 possible errors, depending on its actual¬†value. No point in explaining the errors here, just don’t use it. Thank You VTI.

12.4 Qbert Root

As a compromise between the square root and qube root desires of the software people, and the schedule desires of the management, we have decided to incorporate the function of QbertRoot. The required steps are:
1. Start a 16 by 16 multiply.
2. Immediately write to ‘E’ which will try to start a divide.
3. Read the result from “D,C,B,A’.

(editor’s note: ¬†I can’t tell if QbertRoot is an actual function, or trolling.)

16. Known Variances From Anticipated Optimums
This is a list of the known bugs in the hardware.

…It will be OK to write to the twentyfour 16 bit registers of the Suzy SCB¬†PROVIDING that you only do it via the MACRO PROVIDED TO YOU BY RJ MICAL. In¬†addition, you must understand that the contents of this macro may change if future¬†revisions of the hardware so require. In addition, you must understand that future¬†hardware may make the process not work and therefore the macro will be changed to¬†be a warning that you can’t use it anymore.

Don’t cheat.

My personal favorite, saved for last:

7.3 Stereo

The 4 audio channels can be individually enabled into either of the two output drivers. This is not now in the hardware. It may never get into the hardware. After all, I hate music. However, since I acknowledge that I am wrong about some kinds of music (in the right circumstances, with me not present) I have graciously allocated one entire byte for the possible implementation of another useless and annoying sound feature.

(For the record, that feature did go into the hardware, in the second hardware revision of the Lynx.  If you have the later hardware, some audio is in true stereo.)

If all hardware/technical documentation was written this way, I’d be an embedded systems programmer by now.

I’ve scanned and OCR’d the full Handy specification for anyone wanting to learn more about the Lynx. ¬†There’s a compo-winning demo hidden in the hardware, just waiting to be found.

Posted in Gaming, Programming, Technology, Vintage Computing | Tagged: | 1 Comment »

8088 MPH: We Break All Your Emulators

Posted by Trixter on April 7, 2015

One of my bucket list items since I read my first party report back in 1991 was to attend a european demoparty and compete in a compo. ¬†I competed at NAID ’96 and placed there, which was awesome, but to compete with the best of the best, and win, has always been a dream of mine. ¬†I’m happy to announce that after six months of hard work with good friends and extremely talented people, we achieved just that. ¬†Our demo, 8088 MPH, won the Revision 2015 oldskool demo compo. ¬†(A personal victory for me was having it shown last in the compo, which is a sign of respect that the organizers think it’s the best high to end a compo in.) As of April 7th 2015, there are no IBM PC emulators in the world that can run the demo properly; they hang or crash before the demo is finished, and the colors are wrong. ¬†Same goes for anything that isn’t the target hardware (see below). ¬†To see what 8088 MPH looks like, I direct you to the video+audio capture of the demo running on real hardware: Because there are so many technological world-firsts in the demo, and because we’re bending the hardware in ways that people have never thought to do so, it’s only fair that we try to explain exactly how this was achieved. ¬†One of my roles was “organizer” for the demo, so I’ll break it down scene by scene, covering the basics of each trick. ¬†For parts that I wrote, I go into some¬†detail, but for a deep technical dive into certain areas, I’ll keep this blog entry updated to point to reenigne’s, VileR’s, and Scali’s blog posts about their parts. ¬†It is our hope that these discussions will foster revived “old-school” interest in software development for the platform. After you read this summary post, please visit the following links by us that break down,¬†in-depth, specific sections of the demo:

And for more general info:

Target Hardware Specifications

Before going into each part, let’s define what the target system was for the demo: ¬†A 1981 IBM 5150 (aka the very first “IBM PC”) with 640 KB RAM, a floppy drive, IBM CGA card, and internal speaker. ¬†That setup consists of:

  • 4.77 MHz 8088 CPU. ¬†5 MHz seems like a lot compared to other 8-bit micros, but it takes the CPU 4 cycles to read a single byte. ¬†So, compared to other 8-bit CPUs like the 6502 or 6809, which can read a byte in one clock cycle, the effective clock speed of the 8088 is more like (4.77 / 4) = 1.19 MHz.
  • Video adapter that has a 9-pin RGBI interface and an RCA NTSC composite video interface. ¬†Driven by a Motorola 6845 character generator. ¬†No facilities for changing text characters; font is fixed.
  • Internal speaker; no sound card like the Sound Blaster, or special sound hardware like the C64 SID. ¬†Speaker can be driven by a timer pin to produce a square wave, or can be bit-banged directly via a port or a one-shot timer.

The 640KB RAM requirement seems steep, but not only was it possible to add that to the first IBM PCs, by 1985 it was quite common. ¬†If you still want to cry foul, then please note the only effect that uses just about all of that RAM is the kefrens bars, so that the repeating pattern would take longer to repeat and be more pleasing to the eye. ¬†We could have reduced it, but then you might have noticed the pattern repeating quicker. ¬†With the kefrens bars effect, the demo uses 507 KB RAM; without it, the demo¬†uses 349 KB. ¬†Most effects use much less, and some are tiny,¬†like the plasma which uses only 6KB (which includes the¬†banner graphics) and the picture of the girl which uses 18K (2K more than the size of the raw picture data itself). ¬†We intentionally¬†traded size for speed, which was a deliberate decision to fit as many effects as we could into 8 minutes running time, the compo limit. ¬†If we had a few more minutes running time, we probably could have fit the entire demo into 256 KB or even less, but you would have waited longer between effects. I should also note here that there were two different versions of IBM CGA produced, which differ mainly¬†in how composite colors are generated. ¬†We had equal numbers of both “old” and “new” style IBM CGA cards, so we chose to compose graphics for the “old” style. ¬†If you have the “new” style CGA card, the demo will still run, but the colors will be off slightly.

Technical Breakdown

Development tools used

  • Turbo C
  • Turbo Pascal
  • Turbo Assembler
  • Turbo Debugger
  • Visual C++
  • OpenWatcom
  • NASM (and YASM)
  • DOSBox
  • A few real IBM 5160s (hardware equivalent to the 5150, but easier to find in the real world)

Any data files were directly included in the .exe/.com files themselves. ¬†This kept everything together in the same binary which means the data could benefit from compression (see below). Most development cycles involved designing in wetware, coding on a modern system (or DOSBox running on a modern system), testing/debugging in DOSBox,¬†and then transferring over to real hardware for a final test. ¬†Once¬†an effect grew so sophisticated it couldn’t run in an emulator any more, this cycle slowed down as testing could only be done on real hardware. ¬†Various transfer methods were used to get code to real hardware: ¬†Scali used a serial cable; I used an ethernet card running a packet driver and mTCP; at the party we used an 8-bit IDE ISA adapter (Silicon Valley ADP-50) connected to a CF-to-IDE adapter to make a CF card the hard drive, then used a USB CF card reader to move stuff back and forth. ¬†The most intriguing method of all was reenigne’s method, who used¬†a custom controller connected to the keyboard port that used the IBM BIOS factory test mode as a poor-man’s serial port. ¬†(I hope Andrew writes up some details on that!)

Loader, API, and general structure

We all had different preferred development languages and environments, so it was decided early on to create an overseeing “loader” that would execute .EXE and .COM files, and then people could develop effects in whatever environment they wanted to. ¬†This is not a new concept; the famous Second Reality demo did this for exactly the same reasons, and the same technique was used even earlier than that on numerous demos on other platforms. ¬†(Before you ask: No, the Second Reality code was not copied; in fact, it wasn’t even consulted, as we had to write extremely tight code to minimize memory usage, and also have it work on an 8088 (the Second Reality code uses 80186 opcodes). ¬†The loader API services assemble to about¬†450 bytes of code. The loader, as designed, would be responsible for:

  • Playing music in the background
  • Masking the load times and precalc times of various effects using “megademo”-style text
  • Providing synchronization services (such as providing a vertical-retrace interrupt in software, and a user-definable countdown timer)

Running effects with the loader consisted of this workflow:

  1. Print text on the screen and animate it using an interrupt and the 6845 start address register
  2. Execute the effect
  3. The effect would decompress, perform precalc, etc. and then signal the loader it is ready to start
  4. The loader cleans up the moving onscreen text, then signal the effect it can start
  5. Effect starts, magic occurs

Designing this correctly was extremely important, as any bugs would derail the entire thing. ¬†It was designed fully before even a single line of code was written. ¬†I’ve shared¬†the design doc online for the curious. ¬†(I wrote the loader.) The background music playback had to be as simple as possible so as to not interfere with any effects. ¬†A single PC beep, changing (or silencing) once every frame, was the only thing that was practical, so 60Hz beeping is what the background music consists of. ¬†The composition program used for generating the speaker timer values was MONOTONE. ¬†Even though the code for playback is only 18¬†lines of assembler, it takes up two scanlines onscreen, so you can see how anything even slightly¬†more complicated would have sucked much more CPU out of the system and some of the full-screen 60Hz effects simply would not have been possible.

Executable compression

Another decision early on was to see if executable compression was feasible, which means the following:

  • Does it actually compress things small enough to be worthwhile?
  • Is the decompression quick enough to avoid long pauses in the demo?
  • Does the decompression routine affect the system while it decompresses? (ie. does it disable interrupts or something else equally horrible while it decompresses, which would trash the demo?)

I gathered¬†most classic and modern executable compressors and ran tests against old programs that were representative of what we would be producing. ¬†The results were welcome surprises. ¬†The compression ratios were good enough that we could afford to include precalc’d data instead of calculating it on the fly, and the decompression was fast enough that the total end-to-end time loading a program from diskette was actually slightly faster than if it were to load uncompressed. ¬†In the end, pklite emerged as the winner. ¬†I’ve shared the comparison data online for comparison. ¬†(If I missed any packers that hold substantial advantages over the ones in the data, please let me know what they are. ¬†There were nearly 100 packers made for DOS, but unless they compress smaller than apack or upx, or decompress faster than pklite or lzexe — all while remaining compatible with 8088 — then I don’t want to hear about them.)

Scene-by-scene breakdown

What follows is a screen-by-screen explanation¬†of each effect. ¬†As previously stated, I’ll only describe scenes in detail if I wrote them; it will be up to the others if they want to write a technical breakdown for their parts. ¬†The explanation for each effect follows after the effect’s screenshot. mph_screenhots.avi.Still001 The¬†introduction was meant to serve two purposes: ¬†To educate the audience on the system and explain at just how much of a disadvantage we were trying to make a world-class demo on such hardware, and also simultaneously shatter their expectations :-) ¬†The text mode is obviously simulated; I essentially duplicated the basic BIOS functions for handling text mode but simulated in graphics mode. ¬†The cursor blinking and text blinking¬†are handled identically to how the 6845 does it,¬†adding to the illusion. It is (nearly) impossible to change the display¬†start address of graphics mode such that every single scanline comes from a different place, so the title screen unrolling was done brute force, by copying new scanlines into memory hidden by retrace. ¬†The title screen goes¬†away with a “fade” on the top edge by ANDing a mask on successive lines of the screen data. mph_screenhots.avi.Still002 A lot of people think the title screen is the same picture demonstrated by VileR a few years ago. ¬†It’s not! ¬†He recomposed it for 16-color composite specifically for this demo, and changed it subtlety as well. mph_screenhots.avi.Still003 The bobbing was achieved by creating a software vertical retrace interrupt that fired at the same place onscreen every time (just after the last displayed line) and then hooking it with a 6845 display start address change routine. ¬†Flags were used to communicate to the interrupt if it was time to erase the letters, which was done by simply using REP STOSW to fill screen memory with black lines. ¬†Because the 6845 displays two onscreen rows per “row”, the text could only move to even lines, which is why the movement isn’t as smooth as it could be. ¬†Well, to be fair, it could be made to move to any line we wanted, but doing so would be CPU intensive, and the whole point of the loader is to use as little CPU as possible, so this was the compromise. The simulated vertical retrace interrupt was provided through loader API services for the rest of the effects to use as well. ¬†Effects could disable it, re-initialize it, and hook/unhook their own routines to it. mph_screenhots.avi.Still004 The moire (interference pattern) effect was achieved using a base of 40×25 text mode, the half-char block extended ASCII characters, and lots of unrolled code. ¬†The circles were chosen to represent the classic effect, but in reality the effect can combine any two images. ¬†reenigne’s effect. mph_screenhots.avi.Still005 The rotozoomer is the same tired old routine I first rolled out in 1996 in the 8086 compo, but optimized to the hilt and sped up by only drawing every other line. ¬†A miscommunication between me and VileR resulted in probably not the best texture to demonstrate the effect, but it still runs well enough. ¬†There were plans to include a 60 Hz version of this effect, but we ran out of time. mph_screenhots.avi.Still006 The core concept of the 1024-color mode is a serious abuse of 80×25 text mode with the NTSC colorburst turned on. ¬†VileR made the first discovery with 512 colors, and reenigne was able to double this to 1024 with CRTC trickery. Some people thought the entire demo was in this mode. ¬†It was not, because 80-column text mode suffers from the famous CGA “snow” defect when you write directly to CGA RAM in this mode. ¬†This is unfortunately visible in the plasma effect (see below). BTW, when I saw this picture in 2013, that’s when I knew I had to¬†get all these people together to make a demo. ¬†I mean, geezus, look at it! ¬†My jaw dropped when I saw it. ¬†Had I never seen VileR’s collaboration with reenigne to make the above, 8088 MPH might never have existed. mph_screenhots.avi.Still007 These stars were actually the result of unrolled code and a precalc’d table that, together, take a byte from one location and moves it to another position in video RAM. ¬†While we had other patterns ready, such as a swirling display, we felt the starfield was most appropriate for a typical “oldskool” demo. ¬†reenigne’s effect. mph_screenhots.avi.Still008 The sprite part seems like black magic, but is the combination of using a sprite compiler written by Scali, and adjusting¬†the screen vertically using the 6845 start address register. ¬†CGA only has one screen’s worth of video memory, so moving the address down scrolls the screen up, with the data repeating across the boundary. ¬†The data doesn’t repeat evenly across the boundary, however, requiring handling. ¬†The timer was monitored to know when the screen line containing the last pixel of the sprite had been drawn, which prompted redrawing the sprite. ¬†(In other words, re-drawing the sprite was an exercise in racing the beam.) ¬†Timing was very tight to avoid screen/sprite tearing effects. mph_screenhots.avi.Still009 Also part of the compiled sprite effect, this displays 30 vectorballs at 30 Hz. ¬†We had an earlier display that used less balls to achieve 60 Hz, but Scali had the idea at the last minute to make them spell out something like “8088”, “IBM”, etc. and coded up the change at the party. ¬†The update is done using double-buffering; the sprites only take up a small rectangular area onscreen, so the screen mode’s CRTC settings were reprogrammed to provide a video mode with a small area in the middle of the physical screen, using only half of available video memory. ¬†This provided a true hidden page to draw/erase vectorballs to, which was then flipped to be visible using the 6845 display start address register. mph_screenhots.avi.Still010 Using a 1024-color variant screen mode that could be updated using only the attribute byte (thereby limiting the number of colors to 256), this plasma had to perform writes only when the CRT beam was retracing horizontally or vertically. ¬†Unfortunately, the timing required to get this right stopped working at the party for some reason (probably happened as we were rearranging effect order), and as a result you can see a line of noise along the left side of the screen, and a little bit of noise¬†at the top. ¬†This was my fault, as I wrote the effect using a somewhat lazy polling routine. ¬†It’s a shame CGA snow exists, because without all the retrace handling to avoid it, this effect runs at 60fps. ¬†In the demo with snow avoidance, it runs at only 20fps. ¬†VileR may write more about how this screen mode and color system is¬†constructed, and if so, I’ll update the links at the top of this article to point to the method. If we come out with a final version of the demo, fixing this is at the top of the priority list. ¬†In fact, I’m betting reenigne could change this from a polling effect to a cycle-counting effect, which would not only fix the snow, but speed it up. mph_screenhots.avi.Still011 The 1024-color mode reprograms the start address every two lines. ¬†I took advantage of this behavior to create a simple “drip” effect for VileR’s amazing artwork. ¬†Already you can posit¬†that much more complicated effects are possible (thinking of the Copper demo here) but I ran out of time to make it more awesome. mph_screenhots.avi.Still012 This classic Kefrens bars effect was done by reenigne in 320x200x4 mode. ¬†It’s a cycle-counting effect, as there is simply no time to monitor for horizontal retrace. ¬†To ensure the cycle counting was consistent, several things were done including changing the system default DRAM refresh from it’s default interval of 18 to 19, to get the DRAM refresh periods to line up with CRTC accesses. mph_screenhots.avi.Still013 This was Scali’s effect and inspired by his 1991 demo which also featured a large torus. ¬†There are several things going on here:

  • Only changed portions of the screen are calculated and drawn, to minimize the amount of bandwidth needed to update the screen (this is the same “delta drawing” idea used in XDC). ¬†This was done because CGA video memory has a wait state, so the less you need to write to it, the better.
  • 320x200x4 mode is used with a background and palette combination that gives this specific composite color palette, which included many shades of blue.
  • To help with the shading, dithering is applied during rasterization.

mph_screenhots.avi.Still014 At the party, reenigne posited that it should be possible to restart the CRTC start address every single scanline. ¬†This would result in a video mode that was only 100 lines high, and¬†would give a 80×100 resolution 1024-color mode. ¬†The above is the result of that coding, plus really extensive¬†work done on a CGA NTSC composite signal modeling program done by reenigne months earlier to perform the image conversion. ¬†(No, you can’t have it. ¬†And before you ask, the “girl” and “CGA 1k” pictures were not stock conversions, but were hand-pixeled by VileR in Photoshop, and the 4-colors/16-colors/”Until Now” screens in a customized version of Pablodraw he created.) We didn’t have time to put text into this picture, so the people you see above are the same as in credits order: ¬†Trixter, reenigne, Scali, VileR, Phoenix, and virt. ¬†Apologies to coda and puppeh, but as you can see, any more squishing and the faces would have been unrecognizable. ¬†Sorry! mph_screenhots.avi.Still015 Finally, the coup de gr√Ęce: ¬†A multichannel music engine for the PC speaker. ¬†We didn’t want to just copy a ZX Spectrum engine, nor other engines such as the one used in Music Construction Set, but rather set the bar impossibly high by playing a protracker mod through the speaker. ¬†Other modplayers for the speaker already exist, but they require a 10 MHz 80286, and can barely manage output at a 6KHz sampling rate. ¬†Ours faithfully reproduces all protracker effects, mixing and outputting to the speaker realtime at 16.5 KHz, all on a 4.77 MHz CPU. This was reenigne’s baby, and is a truly stunning technical achievement that required unconventional thinking and considerable 8088 knowledge to pull off. ¬†I’m sure he will write up a more detailed post on how it was done. ¬†Until then, I can mention the following details:

  • Preconversion of the module was necessary to align data structures and sample data to be favorable to how the 8088 indexes memory. ¬†Sample data is also converted.
  • Each sample must take exactly 288 cycles to calculate and output or else the sound goes completely pants. ¬†This was very difficult to achieve. ¬†4.77 MHz / 288 = 16572 Hz sample output.
  • Audio output was done using traditional Pulse-Width Modulation (PWM) techniques, such as the kind made popular by Access’s Realsound. ¬†PC speaker PWM is performed by tying the PC speaker input pin to the programmable interrupt timer’s (PIT) channel 2, then programming PIT 2 for byte value one-shot mode. ¬†Any value sent to PIT 2 with the system configured like this will set the speaker HIGH and start a count, and when the count expires (ie. the sent value is reached), the speaker goes LOW again. ¬†This results in an audible carrier wave “whine”, which was why the output needed to be fast (16.5 KHz) so that the carrier wave was above the range of human hearing.

Fun fact:  After preconversion of the song and being turned into a self-playing .exe, the final result is smaller after compression than the size of the original source module.

Party Sprint

At the party, we came with something that was 90% finished. ¬†Prior to arriving at the party, we created what we thought was a decent entry, and created two “failsafe” videos, one that was a capture for the bigscreen and another that showed the demo running on real hardware as verification for the judges. ¬†We were worried that the hardware we were bringing would get damaged in transit, so this was a precaution so that we could enter something if that happened. ¬†Thankfully, reenigne’s and Scali’s IBM¬†5160s arrived unharmed (which was especially remarkable since reenigne had to bring his from the UK to Germany on a series of trains!). ¬†We also brought two CGA cards, and two capture devices, and three different methods of exchanging new software bits from our laptops to the old hardware. ¬†You can never be too prepared! Most of the coding time at the party was spent adding the kefrens and ending portrait picture, eliminating bugs from each part where possible, adding nice transitions where possible, shaving seconds off of each part to stay within the compo limit, and rearranging¬†parts so that virt’s BTTF-inspired tune’s intro lined up with the sprite part. ¬†We spent pretty much all our time before the compo coding, eating, or visiting the bathroom, and only had time to socialize after that. While we came mostly prepared for something that was worthy of entering the compo, the time spent at the party was invaluable for turning a rough draft into something that could really compete for first place. ¬†Having all four of us at the same table meant we could collaborate instantly. ¬†So, lesson learned: ¬†There are rarely substitutes for working together in person! ¬†One of the biggest improvements of “party collaborating”¬†was the decision to change the credits from a variable-speed, text-only scrolling to a more evenly-paced, ANSI-style scrolling, which I think was the best implementation change¬†compared to¬†the bits we brought from home. To help save time (and to ensure the video was converted well —¬†sorry, but most people don’t know how to deal with interlaced video properly), I offered to provide Gasman with a 720@60p video. ¬†The NTSC output of CGA is slightly off; instead of 262.5 lines¬†per field, it generates¬†262. ¬†This means it generates 59.92 fields (29.96 frames) per second instead of the NTSC broadcast standard of 59.94 (29.97 fps). ¬†This throws off most modern capture devices; Scali¬†had access to a high-quality Blackmagic Intensity Shuttle, for example, but it couldn’t lock onto the signal. ¬†I knew¬†from experience that some cheap¬†video capture devices, such as the Terratec Grabby or the Dazzle DVC100, have extra tolerance built into them as they were designed to be used with VCR sources, so I bought a few and sent one to reenigne for testing. ¬†For the capture, we used a DVC100 with some slight proc amp adjustments so that the capture looked as close to the¬†CRT monitor output as possible. ¬†To further ensure better video capturing, we used VirtualDub for the capture software, which has an option to dynamically resample the input audio source to fit the capture framerate you are aiming¬†for in case it’s slightly off, and the software and hardware combination worked very well. ¬†For grabbing the audio, we initially tapped the¬†speaker with alligator clips, but Scali brought his Sound Blaster which had a real PC speaker tap you could hook up with an internal cable, so we used that for the final capture.

Looking to the future

After watching the demo and reading the above, you may be wondering if there is actually room for improvement. ¬†Believe it or not, there is: ¬†Alternative methods of sound generation and additional cycle-exact trickery are definitely possible. ¬†We had more effects to put into the demo, but ran out of time: ¬†We ran out of development time, and we also ran out of execution time, as the Revision compo limit was 8 minutes or less. I’ve known everyone who has worked on the demo collectively over 60 years. ¬†It was an honor and a privilege to work with them all to produce this demo. ¬†Will we work together again? ¬†I’d say it’s definitely possible; the day after the compo, we threw around some ideas, such as making a game next instead of a demo. ¬†Me personally, I’m burnt out and will be spending the next few weeks playing some games I’ve always wanted to finish, and working on my health. ¬†I also have some other large projects I want to get kickstarted this summer, such as something the PC software preservation movement desperately needs, and¬†an online sound card museum. ¬†But hey, who knows.

Blogosphere Coverage and Discussions

Posted in Demoscene, Programming, Vintage Computing | Tagged: , , , | 47 Comments »

Annoying adventures in disassembly: SNATCHIT

Posted by Trixter on February 5, 2015

SNATCHIT is a program written in the 1980s that loads COPYIIPC (a protected-disk copying program) and then interacts with it runtime to provide disk image saving and loading (features it did not ship with because such features expressly encouraged software piracy).  We used this in the 80s to transfer protected disk images around BBSes until someone could figure out how to crack the game and release it properly.

I recently had a need to run SNATCHIT on a 1GHz system. ¬†I recycled an old system to become a box for dumping all my media, from floppy disks to memory cards to hard drives, something that can do it in the background without tying up my main machine. Because it’s a 1GHz system, SNATCHIT won’t run,¬†even with hardware and software slowdowns. ¬†I wanted to run it there because I have some 3.5″ protected disks I want to¬†preserve, so I investigated patching it to work around whatever is tripping it up. ¬†It’s a .COM file, the very easiest type of executable program for old PCs to reverse-engineer, debug, and patch — should be easy, right?

When I try to debug SNATCHIT.COM,¬†I see it’s encrypted. Okay, not totally unexpected; simple encryption schemes were common at that time. ¬†Let’s try patching it to decrypt and continue… ¬†Okay, now it exploits a HLT bug to, I guess, fool debuggers (I didn’t see any PIC code disabling hardware interrupts, but whatever, maybe it was only partially implemented). ¬†Loading in the DOSBOX debugger bypassed that.

Protip: As soon as you¬†see anti-debugger tricks in something you’re¬†debugging, bypass all of them by loading the program into an emulator debugger (such as the DOSBOX debugger). ¬†This is an advantage that 25 years has given our hobby; in the 80s and 90s, the best we had was something like SoftICE, which wasn’t foolproof. ¬†Running in an emulator debugger view is fantastic because the target program has no idea it is being inspected.

Work continued. ¬†In a neat trick that I don’t fully understand yet, I found¬†that on real hardware it puts a certain value in a register, but in the DOSBOX debugger it puts the wrong value and locks up. I don’t quite understand why, but DOSBox debugger lets me force a value, so¬†I forced the correct value and kept going…

An hour later, I was tearing my hair out. ¬†SNATCHIT, in that fine software pirate impress-your-friends-and-bury-your-enemies¬†tradition, has multiple sections that decrypt, but not all at once, argh! ¬†(In a pirate voice: “aarrrrrgh!”) ¬†I had some of it decrypted in non-contiguous chunks, enough to see this:

db '(C) Copyright 1991, Software Pirates, INC.'
db ' Software Pirates will try to fool you'
db 'and hide code that you cannot find'
db 'and then decode it and then execute'
db 'this is to fake out the opposi$$$n,'

…and later:

; anything in (parenthesis) is not in the text, but implied

db '(they think) that they are smarter than us but no one can ever b'
db 'the infamous programmers that bring you the new and'
db 'fangled code thatknocks your socksoff and stinks up'
db 'the room and probbly your computeralso. Sometimes t(he)'
db 'best way to beat them is to look t(hem)'
db 'straight in the eye and ask them w!' (why) 

Great. Later, the somewhat ominous:

seg000:01D4 db 'PARITY ERROR, Continuing processing.',0Ah

Not only should that wording strike fear into your heart (parity errors are a BAD THING that should NOT CONTINUE PROCESSING), but this message proves they’re redirecting the NMI at some point. Gee, thanks. ¬†I can’t wait to get to THAT chunk of joy.

In the end, I decided it was faster to pull a vintage system out of storage and put a 3.5″ drive in it. ¬†I’m a Generation X guy going through multiple¬†mid-life crises — I haven’t got time for this shit.

I would love to see the actual source code someday, if the programmer would ever come forward (statute of limitations is way over, so I’m crossing my fingers). ¬†Hooking and patching copyiipc runtime was, and still is, incredibly impressive; the source would make a great read. ¬†The last version of this tool was 1991, and I remember seeing Software Pirates, INC. stuff as early as 1984. That’s a pretty good run.

Posted in Software Piracy, Vintage Computing | 6 Comments »

Fun for the feeble-minded

Posted by Trixter on October 26, 2014

As a teenager in the 1980s, I loved using computers, and also building things, and especially loved building things with computers. ¬†Naturally, I adored Music Construction Set and Pinball Construction Set. ¬†The version of Music Construction Set for the PC floating around in the wild was mine; it comes with 30+ tunes more than the original diskette had, mostly covers as I learned to use the program, or learned my music lessons, or transcribed tunes I heard on other computers (the “power bots” MCS tune should be familiar to Apple II owners). ¬†My Pinball Construction Set tables were not as enjoyable, so they stayed with me.

The more I built my own tables, the more I wanted to see how other tables were built. ¬†I¬†acquired a copy of Night Mission Pinball from a friend, and played it for hours, jealous of how much more it did than PCS: ¬†Sound was more “authentic”, more complicated scoring, better graphics. ¬†I ended up playing it exclusively and stopped using PCS.

Unfortunately, I played it so much that the disk wore out and wouldn’t boot any more. ¬†Worse, my friend no longer had a copy to make for me, and I was too broke to buy it proper. ¬†What to do?

Build my own in Pinball Construction Set, obviously:


I did this from memory just from playing the original game for so many hours. ¬†It’s not perfect, but when you compare it to the original, I think I did a pretty good job:


If you’d like to play the amateur¬†horror that is Jim Leonard’s Night Mission Pinball, you can now do so.

Posted in Gaming, Vintage Computing | 1 Comment »

The Outlandish Adventure of Miss Amanda Collins

Posted by Trixter on October 7, 2014

In the world of vintage computing history and preservation, I am both an archivist and a conservator.  There are very many archivists in various corners of our field; it is easy to gather up collections from multiple sources and dump them in a single place for the viewing public.  Conservators, however, are the people who roll up their sleeves and get their hands dirty.  They perform the task of extracting the archival works in the first place from the original distribution medium.  This can range from reading files off of a disk, to reconstructing long-past systems using 3-D printed replacement parts, to figuring out the structure of unfamiliar data.  Conservation is work.  Our hobby needs more conservators.

Some data rescues have high visibility, such as¬†the recovery of Andy Warhol’s early¬†digital art. ¬†But the majority of rescues¬†are not notable. ¬†My¬†specific area of focus is very early (think 1980s) PC games on floppy disks, and recently I’ve recovered a few items not yet found “in the wild”, but painfully boring: ¬†A horrible WWII flight combat¬†sim, a chess tutor, a children’s A-B-C program. ¬†The latter is PCjr-specific, which is¬†intriguing, but for the most part these items hold no significant place in history. ¬†Still, I do it for The Cause.

One enjoyable side benefit of this work is occasionally coming across something human¬†amongst the digital clutter. ¬†Like¬†a trunk discovered in an¬†attic that hasn’t been opened in decades, you can find all sorts of clues about a person’s or family’s past, pieces of their lives, hiding in the data: Letters to family, ledgers and receipts, creative writing, favorite recipes, comic book collections, a child’s digital fingerpainting. ¬†I’ve found these and more during my archaeology, and it always makes me smile. ¬†(Please don’t get the wrong idea; I don’t mean the above to sound voyeuristic, but rather more of admiration and respect for the original owners who actually used their systems to improve their lives.)

One favorite example in recent memory was when I rescued¬†a no-name taiwanese XT clone out of the trash (literally; it was in a dumpster). ¬†Based on the files left behind, the system¬†was owned by an asian female college student in the late 1980s who was an accounting major and used the computer exclusively for school… or so it looked at first glance. ¬†Tucked away among all of the spreadsheets, essays, and databases, in a tiny corner of the filesystem, was a single directory that was filled with poetry, in a style written with few carefully and powerfully chosen words. ¬†The entire system and its files gave the impression of a young woman who was following her father’s wishes, but who longed for something more fulfilling.

And it is here I will introduce you to miss Amanda Collins, who holds the record for the most endearing recovery I’ve had this decade. ¬†I don’t know anything about her other than her name, and I certainly don’t know which¬†out of the nearly 1500 “Amanda Collins” located in the USA White Pages she is. ¬†I don’t know how old she is, although I have a good guess how old she was All I can tell you is how I found her.

I was going through disks to archive and found a commercial business program from the early 1980s¬†that looked interesting; I had never run across it before, and it was copy-protected which is always a fun challenge. ¬†But more interesting is that it was sticky. ¬†As in, peanut butter and jelly sticky — on the media itself (ie. the part that shows in the “window” that the diskette sleeve’s pictographs illustrated¬†you were¬†never, ever supposed to touch). ¬†Rescuing this disk required actual warm water, a soft cloth, and careful rubbing. ¬†Unfortunately, the diskette had been permanently damaged by whatever had stuck¬†to¬†the media, but I figured I’d work a little harder on the first few tracks just so I could see what was on the disk and call it a day. ¬†At the end of the directory, after the commercial program files, was a file called ET with no date on it. ¬†I wasn’t able to rescue the program, but I was able to get the ET file. ¬†It is my pleasure to share it with you¬†exactly as I found it:

                   THE END.
                        AMANDA  COLLINS

This little girl had decided one day to write a story, and when the program prompted for a diskette to save it, she grabbed — with sticky jelly¬†fingers — whatever diskette was closest and jammed it in… in this case, Daddy’s expensive business program, ruining it in the process, and possibly the drive it was jammed into.

I lack the skill to convey how adorable I find this.

In your own digital archaeology adventures, may you someday feast on cheese bergers, and see dinasores at the park.

Posted in Vintage Computing | 1 Comment »