BMOW title
Floppy Emu banner

Archive for the 'Bit Bucket' Category

NASA Struggles to Fix Hubble Space Telescope’s 1980s Computer

Did anybody else see this week’s news about problems with Hubble’s 80s-vintage onboard computer and think: *gasp* My moment in life has arrived! Hold my beer.

“Hello, NASA? Did you guys try PR#6? Sometimes the boot disk gets dust on it; ask an astronaut to blow it off and then reboot. Hello? Hello? Why did they hang up?”

The Hubble’s computer is actually an NSSC-1, an 18-bit machine with 64KB memory. The version on Hubble is likely built from a few dozen discrete MSI chips. There are two fully redundant computers on board, and four independent 64KB memory modules, any of which can be enabled or disabled from the ground in the event of a problem. But 30 years of bombardment by cosmic rays will take a toll on the hardware.

Be the first to comment! 

When A Space Is Not A Space

I just spent more than two hours troubleshooting a seemingly simple HTML problem. When I copied and pasted a small section of HTML, the web browser displayed the newly-pasted section differently from the original. The horizontal spacing between some of the elements was slightly different, causing the whole page to look wrong. But how could this be? The two HTML sections were identical – the new one was literally a copy of the old one.

This simple-sounding problem defied all my attempts to explain it. I came up with lots of great theories: problems with my CSS classes, or with margins and padding. Mismatched HTML tags. Browser bugs. I tried three different browsers and got the same results in all of them.

Feeling very confused, I looked again at the two sections of HTML in the WordPress editor (text view), and confirmed they were exactly identical. Then I tried Firefox’s built-in web developer tools to view the page’s rendered elements, and compared all their CSS properties. Identical – yet somehow they rendered differently. I used the developer tools to examine the exact HTML received from my web server, checked the two sections again, and verified they were character-for-character identical. Firefox’s “page source” tool also confirmed the two sections were exactly identical.

By this point I was getting ready to blame cosmic rays or voodoo magic. I discovered that any time I copy-pasted any similar HTML section, the newly-pasted section would appear in the browser with the wrong element spacing. How could this possibly be? I then tried the W3C Validator, which found some other problems with my page, but nothing that could explain this behavior. And once again, it confirmed that despite rendering differently in the browser, the two sections of HTML were identical.

Clearly something wasn’t adding up. I used curl to download the web page from my web server, viewed the local copy, and saw the same behavior as before. But when I opened the stored .html document with a hex editor, I finally had my answer. The two sections of HTML were not identical: one section used a different type of space character from the other.

What the hell.

I discovered that the original HTML section contained non-breaking spaces. But instead of encoding them with the   entity, they were encoded directly as Unicode character C2A0. I’m not sure when or how this happened, but I blame WordPress. When viewing this section in the WordPress HTML editor, the C2A0 spaces appeared like normal spaces, and copy-pasting the section inside the editor silently converted non-breaking spaces to normal spaces with hex value 20. So the copied version rendered differently, even though the source HTML appeared to be the same.

This is like the 21st century version of confusing a zero with a capital letter O, yet worse. I wasn’t even aware that non-breaking spaces have a Unicode character value – I thought   was the only way to encode them. I changed the HTML back to use   and now it all works fine.

I’m surprised at how many different tools failed to reveal this subtle but important difference between types of spaces in the HTML source. The WordPress HTML editor failed to show or correctly handle the difference. The Firefox web developer tools and page source tools failed. The W3C Validator’s source view failed. Curl plus a hex editor was the only way to finally establish the ground truth about the precise contents of the HTML source.

Read 9 comments and join the conversation 

FPGA Block RAM Packing

In an earlier blog post, I was lamenting how one-ninth of an FPGA block RAM was wasted when storing 8-bit ROM data, because there’s no simple way to make use of the 9th parity bit in each word of a block RAM. Horrors! To fight this injustice, I’ve developed a solution that I call packed ROM. It stores nine 8-bit bytes in eight 9-bit words of block RAM, and provides an interface to read the data as if it were an 8-bit memory with a larger depth. Using this method, I’m able to store 1152 bytes of read-only data per block RAM instead of only 1024. The solution relies on the fact that the block RAMs are dual port – you can read from two different addresses simultaneously. Compared with using the same number of block RAMs as a standard 8-bit wide ROM, this solution consumes an extra 54 LUT4s in a MachXO2-1200 FPGA – about 4 percent of the total. It increases the MachXO2-1200’s effective capacity for this type of 8-bit ROM data from 7168 to 8064 bytes.

Here’s the Verilog code, as well as a Python program that reads a plain binary file and writes a “packed” file in .mem format. The code assumes 7 block RAMs, but should be easily adaptable to other numbers.

module packedROM #(parameter NUM_BLOCK_RAMS = 7) (
    input [12:0] addr,
	input clk,
	output reg [7:0] Q
    );
	
	// packs 1152*NUM_BLOCK_RAMS 8-bit data bytes into 1024*NUM_BLOCK_RAMS 9-bit words 
	// uses 54 LUT4s of the MachXO2
	// may need to change addr width depending on NUM_BLOCK_RAMS. Use $clog2()? 
	
	// nine bytes A-I are packed into eight 9-bit words as follows:
	// 0: I3 I2 I1 I0 A4 A3 A2 A1 A0
	// 1: I7 I6 I5 I4 B4 B3 B2 B1 B0
	// 2: F7 F6 E7 E6 C4 C3 C2 C1 C0
	// 3: H7 H6 G7 G6 D4 D3 D2 D1 D0
	// 4: A7 A6 A5 E5 E4 E3 E2 E1 E0
	// 5: B7 B6 B5 F5 F4 F3 F2 F1 F0
	// 6: C7 C6 C5 G5 G4 G3 G2 G1 G0
	// 7: D7 D6 D5 H5 H4 H3 H2 H1 H0
	// bytes A-H are sequental in the byte-oriented address space below addr 1024*NUM_BLOCK_RAMS
	// byte I is one of the "extra" bytes, in byte-oriented address space beyond addr 1024*NUM_BLOCK_RAMS
	
	reg [12:0] wordAddressA;
	reg [12:0] wordAddressB;
	
	wire [8:0] QA;
	wire [8:0] QB; 
	
	// dualPortROM is a wrapper for the MachXO2 block RAMs, created by the Lattice IP Express tool.
	// it is actually a dual port RAM with the write input unused
	dualPortROM myDualPortROM(
		.DataInA(9'b000000000),
		.DataInB(9'b000000000),
		.AddressA(wordAddressA),
		.AddressB(wordAddressB),
		.ClockA(clk),
		.ClockB(clk),
		.ClockEnA(1'b1),
		.ClockEnB(1'b1),
		.WrA(1'b0),
		.WrB(1'b0),
		.ResetA(1'b0),
		.ResetB(1'b0), 
		.QA(QA),
		.QB(QB)
	);
	
	wire [12:0] overflowAddr = addr - (NUM_BLOCK_RAMS * 1024);
			
	always @* begin
		if (addr < NUM_BLOCK_RAMS * 1024) begin
			// packed area, bytes A-H
			wordAddressA <= addr;
			// word address for the upper bits depends on low three bits of the byte address
			case (addr[2:0])
				0: begin // A
					wordAddressB <= { addr[12:3], 3'b100 };
					Q <= { QB[8:6], QA[4:0] };	
					end
				1: begin // B
					wordAddressB <= { addr[12:3], 3'b101 };
					Q <= { QB[8:6], QA[4:0] };	
					end
				2: begin // C
					wordAddressB <= { addr[12:3], 3'b110 };
					Q <= { QB[8:6], QA[4:0] };	
					end
				3: begin // D
					wordAddressB <= { addr[12:3], 3'b111 };
					Q <= { QB[8:6], QA[4:0] };	
					end	
				4: begin // E
					wordAddressB <= { addr[12:3], 3'b010 };
					Q <= { QB[6:5], QA[5:0] };	
					end		
				5: begin // F
					wordAddressB <= { addr[12:3], 3'b010 };
					Q <= { QB[8:7], QA[5:0] };	
					end											
				6: begin // G
					wordAddressB <= { addr[12:3], 3'b011 };
					Q <= { QB[6:5], QA[5:0] };	
					end						
				7: begin // H
					wordAddressB <= { addr[12:3], 3'b011 };
					Q <= { QB[8:7], QA[5:0] };	
					end
			endcase
		end
		else begin
			// overflow area, byte I
			// word address is byte overflow address times 8 for the lower bits, and times 8 plus 1 for the upper bits
			wordAddressA <= { overflowAddr[9:0], 3'b000 };
			wordAddressB <= { overflowAddr[9:0], 3'b001 };
			Q <= { QB[8:5], QA[8:5] };
		end
	end
endmodule




import os
from array import array

infile = "coderom.bin"
outfile = "coderom.mem"

inputData = array('B')

insize = os.path.getsize(infile)
with open(infile, 'rb') as f:
    inputData.fromfile(f, insize)
    out = open(outfile,"w") 
    
    num_block_rams = 7
    outsize = 1024 * num_block_rams
   
    for x in range(0,outsize):
        baseAddr = x & ~7
        if x & 7 == 0:
            out.write('{:02X}\n'.format( (((inputData[outsize+baseAddr//8])&0xF)<<5) | ((inputData[baseAddr])&0x1F)))
        elif x & 7 == 1:
            out.write('{:02X}\n'.format( (((inputData[outsize+baseAddr//8])&0xF0)<<1) | ((inputData[baseAddr+1])&0x1F)))
        elif x & 7 == 2:
            out.write('{:02X}\n'.format( (((inputData[baseAddr+5])&0xC0)<<1) | (((inputData[baseAddr+4])&0xC0)>>1) | ((inputData[baseAddr+2])&0x1F)))
        elif x & 7 == 3:
            out.write('{:02X}\n'.format( (((inputData[baseAddr+7])&0xC0)<<1) | (((inputData[baseAddr+6])&0xC0)>>1) | ((inputData[baseAddr+3])&0x1F)))
        elif x & 7 == 4:
            out.write('{:02X}\n'.format( (((inputData[baseAddr])&0xE0)<<1) | ((inputData[baseAddr+4])&0x3F)))
        elif x & 7 == 5:
            out.write('{:02X}\n'.format( (((inputData[baseAddr+1])&0xE0)<<1) | ((inputData[baseAddr+5])&0x3F)))
        elif x & 7 == 6:
            out.write('{:02X}\n'.format( (((inputData[baseAddr+2])&0xE0)<<1) | ((inputData[baseAddr+6])&0x3F)))
        elif x & 7 == 7:
            out.write('{:02X}\n'.format( (((inputData[baseAddr+3])&0xE0)<<1) | ((inputData[baseAddr+7])&0x3F)))

Read 4 comments and join the conversation 

UDC: The Next Generation

I’m still working on development of a disk controller card for the Apple II. As part of that effort, I’m still trying to understand the design of the UDC disk controller. My hope is to combine what I learn about the UDC, the Liron disk controller, and the standard Disk II controller in order to build something with the best qualities of all three. But for now I’m deep in the weeds with the UDC, and today I was pleased to finally confirm my long-held theory about UDC support for intelligent Smartport drives!

In the Apple II world, there are three primary types of disk drives: 5.25 inch drives, dumb 3.5 inch drives, and intelligent drives using the Smartport protocol. The best-known example of an intelligent Smartport drive is the Unidisk 3.5, but others include Floppy Emu’s emulated Smartport Hard Disk.

For quite a while now, I’ve been chasing after this question of whether the UDC supports intelligent Smartport drives. The two available UDC manuals are slightly vague, but could be interpreted as saying there’s no support. Several sources on the web say Smartport drives aren’t supported, and you’ll damage your drive or UDC card if you try. But a contemporary print ad for the UDC advertises Unidisk 3.5 compatibility. I looked at ROM dumps from several different UDC versions, and found what looked like Smartport-related code in some older ones but not in newer ones. It’s all very confusing.

 
Fun With ROM Hacking

After a month of theory and research, I finally got to try some hands-on tests. The original UDC card is called the “long” version, and the follow-up card with a custom ASIC is the “short” version. I’ve been unable to find anyone with a long UDC (call me!), but I was able to borrow a short UDC. I spent a while mapping all the connections on the card and constructing a schematic, until I was satisfied there were no lurking dangers from merely connecting a Smartport drive to it. I connected a Floppy Emu configured for Smartport emulation mode, booted it up, and… it did nothing. Then I connected a Unidisk 3.5 and tried again, and it also did nothing. Blah.

This UDC short card came with version 4.0 of the ROM, which I’d previously analyzed and concluded didn’t contain any code for Smartport drive support. So it’s not surprising that it didn’t work, but I’d hoped maybe I’d missed something in my ROM analysis. My earlier analysis of ROM version 2.3 from the long UDC found that it did contain code for Smartport drives. I strongly suspect the short UDC is functionally identical to the long, despite its different physical appearance. So what happens if you take ROM 2.3 from a long UDC, and stick it in a short UDC card? Let’s find out!

The ROM is stored in a 27C64 EPROM and is socketed, so no modifications were necessary on the UDC card. I used a 28C64 EEPROM as a drop-in replacement, and programmed it with ROM version 2.3 using my EasyPro universal programmer. (Coincidence: this EasyPro 90B programmer was first mentioned on this blog precisely 13 years ago today. I feel very, very old.) I unsocketed the original ROM chip, popped in the new one, booted up, and… it did nothing. Worse than nothing, it actually froze up the computer. I checked the disk I/O signals with a logic analyzer, and there was no activity at all. Blah again.

Feeling disappointed, I gave up. The next morning, I noticed that the logic analyzer wasn’t actually connected. Oops.

After I’d fixed that, I could clearly see evidence of a Smartport reset and initialization sequence on the disk I/Os, but it still wasn’t working. Eventually I concluded that the code in the ROM was crashing or getting stuck in an infinite loop somewhere. But where, and why? I would have tried single-stepping through the code, but quickly discovered that’s extremely difficult for code in ROM on an Apple IIe.

By carefully comparing the ROM 4.0 and ROM 2.3 code, eventually I was able to guess the problem. A key feature of the UDC is that it sometimes pulls the 6502’s RDY input low to temporarily halt the CPU. This is a sort of flow control mechanism, and happens whenever the code tries to read a byte from the disk, but a new byte isn’t available yet. It appears that the details surrounding use of RDY changed between the long and short UDC, and the short version will halt the CPU on reads to some memory locations that the long UDC ignores. By comparing the two ROMs and making some educated guesses, I was able to modify the version 2.3 ROM so that it no longer caused the short UDC to freeze the computer. But it still didn’t work. Blah for a third time.

After more tinkering and head-scratching, I disconnected the Floppy Emu and tried the Unidisk 3.5 again. I was amazed when it booted right up! But now I had a new puzzle to solve, to explain why the Unidisk 3.5 worked but Floppy Emu’s Smartport emulation didn’t, even when using the exact same disk.

The logic analyzer eventually revealed the answer. Something like 5-10% of the data packets from the Unidisk 3.5 were NAK’d by the UDC card, forcing them to be retransmitted. Some packets had to be retransmitted multiple times. In all my time tinkering with the Smartport protocol over the years, I’ve never before seen a NAK of valid data, and Floppy Emu doesn’t even implement retransmitting a packet in case of a NAK. I implemented the missing retransmit logic, and the Floppy Emu worked! Hooray!

The mystery still wasn’t completely solved, however. What was causing some packets to be NAK’d, from both the Floppy Emu and a real Unidisk 3.5? The NAK rate was also much higher with the Floppy Emu than with the Unidisk 3.5, approaching 30-50%. Acting on a hunch, I experimented with small changes to the Floppy Emu’s bit rate, and found that it caused some changes in the NAK rate, but the results weren’t conclusive.

The “correct” bit rate is either one bit every 4.0 microseconds, or every 3.96 microseconds, depending on your reference source and your assumptions. Floppy Emu with the latest firmware does one bit every 3.95 us. I found that at 4.2 us I got a 100% NAK rate, but at 4.1 us the NAK rate suddenly dropped to about 10%, and at 4.05 us and 4.0 us the NAK rate grew worse again. That doesn’t really make sense, and I didn’t repeat the tests enough times to be very confident in those results. My suspicion is that either the UDC short card is very sensitive to small changes in bit rate, or else that substituting the long UDC ROM is negatively affecting the behavior somehow. I’m not sure it’s worth chasing this mystery further, since it’s quite possibly caused by my weird hybrid card setup.

So the $64000 question is finally answered, sort of. The standard short UDC card does not support Smartport drives, but connecting one won’t damage anything. But it does support Smartport drives when using an appropriately hacked-up ROM, proving that it’s entirely a question of software rather than hardware. As for the long UDC, I still can’t say for certain without examining one directly, but all the evidence points to it supporting Smartport drives.

 
Where to Next?

This probably marks the end of my very long detour into “how does the UDC work?” and a return to actual development on my disk controller card. Next step: see if I can duplicate the UDC behavior (or something close to it) with my FPGA-based approach.

Read 7 comments and join the conversation 

Backyard Metal Sand Casting

Today’s project: metal casting! I’d been wanting to try this for years, because who doesn’t love playing with molten metal? This was a single-sided open air sand casting process, which is about as simple as it gets. You basically just press an object into some densely-packed sand, remove the object, and pour metal into the resulting sand cavity. Humans have been doing this for thousands of years. Easy peasy, sort of.

This project was a long time coming. I discussed some metal casting ideas last year, and acquired some equipment. Then everything sat on a shelf gathering dust for 13 months.

I used pure bismuth for the casting: atomic number 83 on your periodic table. Bismuth has a pretty low melting point of 521 F / 272 C, easily reached with an electric heater. The objects here are a quarter, a silver dollar, a wire nut, a DB-19 connector, and a teddy bear pencil eraser. A major limitation of this particular process is that the object must have parallel or tapered sides so you can remove it from the sand without disturbing the cavity you just made. That’s why the teddy bear looks like it’s embedded in a rock: I had to dig it out of the sand instead of lifting it out cleanly. This process is also limited to making one-sided reliefs instead of full 3D objects, because the back is just a molten pool of metal directly exposed to the air as it cools.

Sand casting is probably better suited for making tools and gears and big stuff, as opposed to small detailed models like those here, but you can’t beat its simplicity. I made a huge number of mistakes, and the quality is pretty bad, but hey… it worked! Molten metal! Try something, learn something. The images from the coins came through much better than I’d expected, and the teddy bear is pretty neat. I feel like I’ve discovered some secret alchemy with this ancient process.

 
Sand Casting 101

The traditional sand casting technique uses something called green sand, which is a mix of regular sand, clay, and maybe a bit of other stuff, plus a small amount of water to help it stick together. I used something called Petrobond which is an artificial oil-bonded sand designed specifically for metal casting. When you pack it down tight, it’s very firm – a consistency similar to silly putty or a super dense cheesecake. Either way the grains are small enough to get pretty fine details. Petrobond advertises an average grain size of 0.07 mm.

I began by building a small wooden frame – a four-walled box with no top or bottom. I placed the frame onto a metal lid from a cookie tin to create a temporary floor, then arranged my reference objects inside the frame face-up. I think maybe you’re supposed to dust the objects and the “floor” with a releasing agent, but I didn’t have any, so I skipped this step.

Next, I packed sand into the frame, slowly burying the reference objects. You’re supposed to sift the stuff to break up the chunks and remove any foreign material. My wife vetoed using the kitchen colander or sifter, and my DIY sifter built from window screen failed miserably, so my Petrobond didn’t get sifted properly. I packed it in as tightly as possible with my hands, then rammed it down hard with the base of a heavy flashlight. The top surface of the sand was scraped level with the frame, using a metal ruler. I then carefully flipped over the frame and removed the cookie tin lid. Because the sand was packed very tightly, it did not fall out. I placed the inverted frame back onto the cookie tin lid, with the reference objects now exposed.

The objects were perfectly flush with the surface of the dense-packed sand. How do you remove them without disturbing the sand? There must be some secret method that I’ve yet to learn. I damaged the mold substantially in the process of removing the objects, and also spilled loose sand into mold cavities. There was a lot of swearing involved.

The final step was to melt the bismuth and pour it into the mold cavities. This was somewhat anti-climactic. I used an electric ladle to melt a one pound chunk of pure bismuth purchased from rotometals.com. It took a few minutes to melt, and smelled like a tire fire. I’m not sure if the smell came from the bismuth or from the ladle, but it was terrible and I feared my neighbors would complain. Once melted, I poured the bismuth into the molds like soup into a bowl. I let everything cool down for about an hour before digging the castings out of the sand.

 
Room for Improvement

I was happy to get “read the lettering on a quarter” level of detail in some areas, but the final casting results were mixed. I would have gotten better results and finer detail if I’d been more careful with removing the original coins from the mold, and poured the metal faster. You can see where the silver dollar partially cracked due to uneven cooling. This happened when I poured some bismuth into that mold, but not enough to fill it fully, and then waited a few seconds before pouring in the rest. I was trying to avoid overflowing.

Bismuth probably wasn’t the best material for this project. It has a low melting point and is comparatively non-toxic, but it’s brittle. Next time I may try pewter or another low temperature alloy that’s specifically intended for metal casting.

When you heat the bismuth, or most any other metal except gold, the surface of the molten metal reacts with oxygen in the air to create an oxide layer. You’re supposed to skim this off before pouring the metal into your molds. I forgot to keep skimming tools at the ready, so my oxide layer dregs went into the mold along with everything else.

The biggest improvement would be graduating to two-sided casting, using two sand frames that lock together. You can buy ready-made frame sets inexpensively. But even after reading several tutorials about the process, I still don’t really understand how the two-sided mold making works. I’m sure it’s very simple, but something about the geometry of it still escapes me.

Read 4 comments and join the conversation 

Apple II Video Card Thoughts

Recently I’ve been toying with the idea of building an Apple II video card. Like many of you, I’ve grown tired of searching for the dwindling supply of monitors with a native composite video input, and frustrated with available solutions for composite-to-VGA and composite-to-HDMI conversion. This card would fit in an expansion slot of an Apple II, II+, IIe, or IIgs, and provide a high quality VGA output image suitable for use with a modern computer monitor. I welcome your thoughts on this idea.

 
Why Do This?

Another Apple II video solution? Aren’t there enough options already? It’s true there have been many different Apple II VGA solutions over the years, and a smaller number of HDMI solutions. Unfortunately almost all of them have been retired, or have very limited availability. I’m also interested in this project as a personal technology challenge. It could combine some of my past experience with Yellowstone (Apple II peripheral cards) and Plus Too and BMOW1 (VGA video generation).

 
How Would It Work?

I see three possible paths for a high-quality Apple II VGA video solution. One is to convert the signals from the computer’s external monitor port. But monitor ports are only present on the Apple IIc and IIgs, and so that wouldn’t meet my goals. The second approach is to tap into a few key video-related signals directly from the motherboard, before they’re combined to make the composite video output. This is a viable approach, and others have been successful using this method. The downside is that it requires a snarl of jumper wires and clips attached to various points on the motherboard, and the details vary from one motherboard revision to the next.

I’m considering a third approach: design a peripheral card that listens to bus traffic, watches for any writes to video memory, and shadows the video memory data internally. Then the card will generate a VGA video output using the shadowed video memory as its framebuffer. It’s simple in concept, but maybe not so simple in the details. The first two approaches require only a comparatively dumb device to do scan conversion and color conversion. In contrast, the bus snooping approach will require the video card to be a smart device that’s able to emulate all the Apple II’s video generation circuitry – its weird memory layouts and mixed video modes and character ROM and page flipping and everything else. But it promises a simple and easy solution when it comes time to use the card. Just plug it in and go.

To be specific, for basic support of text and LORES/HIRES graphics, the card will need to shadow writes to these memory addresses:

$0400-$07FF TEXT/LORES page 1
$0800-$0BFF TEXT/LORES page 2
$2000-$3FFF HIRES page 1
$4000-$5FFF HIRES page 2

and these soft switches:

$C050/$C051 graphics / text
$C052/$C053 mix / no mix
$C054/$C055 page 1 / page 2
$C056/$C057 lores / hires

 
Can It Work?

For basic 40 column text mode, I hesitate to say this will be easy, but I don’t anticipate it being difficult. LORES and HIRES graphics may be more challenging, because the memory layouts are increasingly strange, and because determining what color to draw will depend on careful study of NTSC color artifact behavior. But I’m relatively optimistic I could get this all working well enough for it to be useful.

After finishing the basics, there will remain several more difficult challenges. First among these is sync. If the vertical blank of the VGA image isn’t synchronized with the vertical blank of the Apple II’s composite video, then tearing or flickering may be visible in the image. Carefully-timed program loops that attempt to change video memory only during blank periods won’t work as intended. Fortunately motherboard revisions 1 and later provide the SYNC signal to peripheral cards in slot 7, which can be used to synchronize the VGA and composite video outputs. For installation in a different slot or on a revision 0 motherboard, a wire can be connected to the motherboard for the SYNC signal, or the card can run unsynchronized.

What about European PAL models of the Apple II? I think Eurapple machines should work OK, since the card’s function is based on bus snooping rather than the actual video data. The VGA output will be the same, since fortunately there are no NTSC/PAL distinctions to worry about for VGA video. I’m less sure about Eurocolor machines. I believe these already have slot 7 occupied with an Apple video card – can anyone confirm? If so, my video card will have to go in a different slot, where the SYNC signal won’t be available. That’s probably no great loss, since software designed to sync with a 50 Hz composite PAL display can’t be synchronized with 60 Hz VGA anyway.

Can double-hires graphics and 80 column text be handled too? I think so, but I haven’t looked at these modes in much detail yet. From what I’ve seen, they both involve bank switching RAM between the main and AUX banks. Supporting these modes would mean shadowing some additional areas of memory, and a few more soft switches that control bank switching, as well as more software complexity. But they should be doable.

Non-standard character ROMs are another question. The video card will need to duplicate the contents of the character ROM in order to generate text. The most obvious solution is to simply assume the character ROM is a standard one, and include a copy of the standard character ROM on the card. But in some models of Apple II, it’s possible to replace the normal character ROM with a ROM containing an international character set in place of the inverse characters. These seem to have been common for Apple II computers sold in several countries. My card won’t know about the custom character ROM, and won’t render those characters correctly. I don’t see any easy answer for this issue. Possibly the card could use DMA to read the character ROM, but that would be a major increase in complexity.

Finally there’s the question of the Apple IIgs. Will the card even work in a IIgs? Maybe not, if writes to video memory on the IIgs are handled specially, and the address and data never appear on the peripheral card bus. That will be easy enough to test. Another question to consider is super hires graphics on the IIgs. I don’t know anything about how this works. I can’t think of any fundamental reason it couldn’t be handled in the same way as the other graphics modes, but it might be very complex.

 
Hardware Choices

What kind of hardware would be appropriate for a video card like this? It needs at least 18K of RAM to shadow the memory regions for text, LORES, and HIRES graphics. If 80 column text and double-HIRES are also supported, then the RAM requirement grows to something like 54K. The hardware must also be fast enough to snoop 6502 bus traffic and to shadow memory writes that may appear as often as every 2 microseconds. And it must also be capable of generating a VGA output signal with consistent timing and a pixel clock about 25 MHz.

What about something like a Raspberry Pi? Yeah… it might work, but it’s not at all the kind of solution I would choose. Those who’ve read my blog for a while know that while I’ve used the Raspberry Pi in a few projects, it’s not my favorite tool. I strongly prefer solutions that keep as close to the hardware as possible, where I can control every bit and every microsecond. That’s where the fun is.

Could it be done with a fast 32 bit microcontroller, something like an STM32? 54K of RAM would be no problem, and a 100 MHz microcontroller could probably handle an interrupt every 2 microseconds just fine. That would be enough to snoop the 6502 bus and shadow the writes to RAM. I’m less confident that a microcontroller could directly generate the required VGA output signal. If the caches could be disabled, it might be possible to write carefully-timed software loops to output the VGA signal. But the interrupts for RAM shadowing would probably screw that up, and make a hash out of the VGA signal timing.

A small FPGA is probably the solution here. 54K is rather a lot of built-in RAM for an FPGA, so the FPGA would need to be paired with an external RAM of some type – most likely SRAM or a serial RAM. Or the FPGA could be paired with a microcontroller, providing both RAM and some extra processing horsepower.

Read 45 comments and join the conversation 

Older Posts »