This video demonstrates how to recreate the Apple Lisa computer entirely in an FPGA using SystemVerilog, addressing challenges including clock synchronization between CPU and peripheral components, handling timing differences between asynchronous original hardware and synchronous FPGA block RAM, and managing clock skew issues that cause intermittent failures in complex designs. The project required implementing the Motorola 68000 CPU, RAM, IO boards with floppy controller and serial ports, and adding modernizations like HDMI output and USB support, ultimately achieving a functional Lisa that can boot operating systems at speeds 3.75 times faster than the original.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
How I Recreated the Apple Lisa Inside an FPGA - The (Open-Source) LisaFPGA ProjectAdded:
Well, hi everyone. I am Alex Anderson Mloud. I am a first year, I guess maybe now second year PhD student in computer engineering at the University of South Carolina. And today I'm going to be presenting on how I recreated the entirety of the Apple Lisa inside of an FPGA here. So, uh, let's get started with just a quick introduction. I'm assuming most people here probably know.
Oh, I deleted that slide actually. Never mind. Um, but let's start with uh why we want to recreate the Lisa in the first place. There are a few reasons for this.
First up, Lisas are not common machines.
They're very expensive, very difficult to get your hands on. Even if you do have one, they're quite difficult to maintain. Tons of circuit boards, very difficult to work on. Um, they're also quite slow. Anyone who's ever used one can attest to this. So, it would be nice to have a faster accelerated option out there. And finally, um, they use some weird non-standard peripherals. The keyboards are like $700 on eBay.
Absurdly expensive. So it' be nice to be able to modernize those in some way to make them more accessible to people. We can do all that inside an FPGA, which is really nice. Uh so let's talk about what an FPGA is as well. Um all the more technical details are listed out here, but just a quick summary because by the way, um this presentation is very long.
I may not have time to finish it, so I'm going to kind of have to gloss over a few things. Just the quick uh layman's explanation is you can kind of think of it as a chip that's basically a blank slate of a bunch of logic gates just arranged half-hazardly and then you can you can write code in what's known as a hardware description language. I use one known as system verarilog that basically describes how you want to wire all those logic gates and flip-flops together in order to form some sort of logic circuit to perform an arbitrary task and in this case emulating Alisa right um FPGAAS also have certain special purpose primitives inside them that allow you to accelerate or make certain tasks more efficient like uh DSPs for instance allow you to do multiplication more efficiently and there plenty of other primitives as well and we will use one or two of those later on. Um, our general overall strategy for this project is going to be to first start by doing the CPU board because you can't do much of anything inside of a computer without a processor. So, we're going to start with the board that has the processor on it. Um, then once we hit the limits of what we can develop and test with that, then we are going to start implementing the Lisa's RAM boards, which as the name implies, hold all the Lisa's system memory. And, uh, after we do that, we should be far enough that we can actually get a picture of some kind on the screen to confirm the viability of the project.
Then we will add the IO board which handles all the Lisa's input output functionality like floppy drive, hard drive, um, contrast, speaker, etc. And at that point, we should have a system that's pretty much as workable as an original Lisa. So then at that point, it's all about adding modernizations to make it more usable in modern times like HDMI, USB, keyboard, mouse, etc. And also somewhere in that process, we need to design a custom circuit board to kind of integrate all this together and incorporate all the custom Lisa hardware that we need in order to make this as ideal of a solution as possible. So let's start from the top with the CPU board. Um, oh well actually never mind.
First we need to talk about the hardware. The hardware that we need to use. Sorry, I've rearranged some of this presentation recently for the sake of time. So uh, a little bit out of practice here. But the hardware that I chose to use, at least at the very beginning, was this horrendous looking I actually have it right here. This horrendous looking Zylinks Pink Z2 dev board, which is based around a Zinc 7020 FPGA from XYlinks. I chose XYlinks because they just make way better FPGAAS than any other company in my opinion.
Better tool chain, better documentation, all that sort of stuff in my personal opinion, right? Um, and the reason I chose this particular board is just because it's what I had on hand. No other reason at all. Um, in fact, the FPGA on here, the Zinc uh 7020, it actually has a dedicated ARM core inside it, which I did not need for this project at all. So, it's not even really an ideal choice. It's just what I had on hand. Uh, but once we uh once we reach the limits of what this board can do, we will switch to a custom board, of course. But, this is a good start. And by the way, some of the things I talk about here are going to be completely out of order from how they actually happen in real life just for a for a for a better story and to make it more cohesive and less confusing. So, if you've followed along with any of my progress on this online, like on the Lisa List 2 forums, and you're like, "Wait, no, that's not how he did that at all." This is why I'm not trying to lie to you. It's just for a better story.
But yeah, let's start with the core Lisa functionality, starting with the CPU board, which is this board right here.
Um, what we need to do here is basically just study the schematics, the five pages of the CPU board schematics, and convert them into system verarilog into that code that we can use to program the FPGA. The big challenge here is that the CPU board has a Motorola 68,000 processor on it and implementing a cycle accurate 68,000 in system verog is a whole project on its own. I I do not want to have to deal with that, right?
So, um I wanted to see if there was a soft core out there that already existed for the 68,000. And sure enough, there was. It's called the FX68K and it's fully cycle accurate. So, I was able to just use that as a drop in core in my design. Everything else about the CPU board was completely manually implemented but that at least I was able to just drop in and everything else uh I wrote system varog modules which are basically like um drop in components of code that perform certain logic functions that you can create instances of throughout your design. I wrote those for some of the more complicated chips like a priority encoder or like a ROM chip or something. But for most of the logic like you know andgates orgates a lot of multipplexers things like that I just did those in line and just plain old system verarilog inside the top level CPU module.
Um there were some things I did wrong here though because I knew system verog at the time from a class I'd taken um in undergrad but I didn't have a ton of experience with a giant project like this. So I made some mistakes. One thing, um, FPGAAS, they're quite different from just old style TTL logic in a in a lot of ways. So, you can't just directly port TTL logic straight over to an FPGA. One of the biggest ways is that um, TTL logic tri-state signals are everywhere, right? LS245 bus transceivers, for instance, they can put a bus into a tri-state state, high impedance essentially, so that multiple things can drive the bus and send data back and forth in different directions, right? But inside an FPGA, you don't have tri-state signals, at least in modern FPGAs. So, you actually have to use multiplexers instead to determine what signals are feeding certain lines inside the FPGA. This is just about the only thing I knew at the time that was different between FPGAAS and like old style TTL logic. So, I did implement things inside multiplexers in the FPGA instead of attempting to use tri-state signals. But there were a lot of other things that I also need to change that I didn't know about at the time that will kind of come back to bite me a little bit later. But we will get to some of those a bit later on.
Um, but now that we have uh at least a preliminary implementation of the CPU board, which I was going to show, but I do not have time. The presentation is already like an hour and 40 minutes as is, so don't have time to show that. But um we need some way to simulate it before we actually run it on the FPGA just to work out as many of the kinks of the design as possible. Right? So um luckily a simulator exists inside of XYlink's Vado tool chain that we can use in order to view all of our signals in a nice logic analyzer style view. Um, it basically just runs a a a module known as a test bench that instantiates your design and then feeds it all the signals that it needs like clock and reset, etc. in order to run properly and then plots everything in a big logic analyzer trace for you to view. The downside though is that it's really slow. So, um, if you need to simulate more than a few seconds of your design running, it's going to take an impractical amount of time. So, it's going to be useful at the beginning. You have a question.
>> What scene were you running the simulator on? Uh it was a uh AMD Ryzen 9based laptop. Not not like a super I think 8 core 16 thread. So not super powerful or anything, but higher end than the average laptop for sure. Yeah.
But so we're going to use this a lot at first, but later on we're going to have to move away from it once we start getting into more complicated aspects of the design. This is what the logic analyzer traces look like, by the way.
It's just a logic analyzer trace as you would expect.
Um, so we we fire it up in the simulator and um let's start looking at what it's supposed to do and what it actually does. So what the Lisa boot ROM is supposed to do after Lisa first turns on is first it performs a a check sum of the entire ROM to make sure that it's good. Then after that it's going to look at the Lisa's MMU and test all of the MMU's registers to make sure they're functioning properly. And then if that passes, then it'll configure the MMU for just a standard flat memory map. And after that, it will go and perform RAM sizing. So it'll look to see if there's any memory installed in the system. And then if so, it'll attempt to determine its size and then move on with further testing. But given that we don't have any RAM installed, we expect it to get through these first two tests, but then kind of get stuck when it tries to look for RAM and just hang there, right? But unfortunately, it didn't even do either of those first two things. It did literally nothing. Like nothing at all.
The clock moved, but nothing else did anything. So what could be going on here? Well, undefined signal states is the answer. See all these red signals here? This is just an image I got off the internet. This isn't my actual design, but um all those red signals there are signals that have a state of X, meaning undefined. These signals aren't a one or a zero. They're just who knows because I didn't actually assign them a state when I created my design.
They're not being given a state at reset time because the original Lisa doesn't give them a state at reset time. It doesn't matter what state they come up in because the system doesn't care. But when you're simulating something, it absolutely matters because the simulator doesn't know how to treat it. It's just like, I don't know what this is. So if you feed it through a big logic circuit, the whole circuit will just propagate through this I don't know value and it'll just lock up your entire design.
So this is one of those things that we have to change between a real world TCL implementation and FPGA implementation.
We actually have to give all these signals defined states at reset. An example of that is this. This is the uh Lisa's video circuit right here or part of the video circuit. And you can see that this counter here that um hooks to the Lisa's video state machine ROM. The only thing that clears it is an output of the ROM itself. It doesn't get cleared back to a known state at reset.
So originally I'd implemented it just like that where it doesn't get cleared at reset. But that was making its outputs be all X's. So what I had to do is I had to add that if not reset block at the top that actually clears out the counter when we reset the system in order to put it into a defined state. So a bunch of stuff like that to get rid of all those undefined signals. And guess what happens now? Things actually start moving. It attempts to fetch instructions and execute code, but it can't fetch instructions properly. It at least tries, but it's not doing it properly. And it's because I'm not clocking the CPU quite right. Uh Motorola 68000 processor. There are eight different um timing states that it goes through during a bus cycle. They're called S states for some reason. I don't know why, but that's the Motorola 68,000 data sheet showing all the different timing states. And uh those states must be synchronized to some states that the CPU the Lisa CPU board has known as its T states. And um if those don't stay synchronized, then the CPU and all the Lisa circuitry won't stay in sync during a bus cycle. and the 68,000 will be like reading from the bus at a time that doesn't make any sense and invalid data will come back and you won't be able to read or write memory properly and thus not fetch instructions properly. Right?
So, um we are clearly having some kind of problem where they're not aligned right. And it turns out that it is because I'm just completely misunderstanding the clocking paradigm of the CPU. And I didn't understand this fully until much later on, which we will eventually get to. But a lot of FPGA cores just do clocking completely differently from how you would expect because like in in TTL logic or just in a normal logic circuit, you just feed a chip a clock or just a one clock signal, right? But in an FPGA, the way this works a lot of times is that you have multiple clock inputs. You have you have a fast pre-running clock that's just running at some higher speed than the actual speed that you want the the chip or the module to run at. And then you have a slower strobe known as a clock enable that feeds into the chip as well.
And you just pulse this clock enable strobe high on edges of the fast running clock that you want the chip to actually be clocked on. So it allows you to feed the chip a really fast clock but only clock it on certain edges of that clock.
And this may not make any sense at all right now. Like why would you do it this way instead of just feeding the chip a slower clock? But believe me, it will make a lot more sense later on. And I just didn't understand this at all at the time. So I was generating the clock enable strobes completely wrong. And um this CPU too, it doesn't just need one strobe, it needs two. It needs one on the rising edge of the clock and the falling edge. So it just made it even more complicated and I didn't understand it at all. So I just screwed it completely up. But after I fixed my strobes, things worked a whole lot better. And you can see what I'm talking about there. The strobes are the ENI1 and ENI2 lines. You see how they go high just on certain dot clock cycles. Those are the the cycles where it actually clocks the CPU whereas the dot clock is running at four times the speed of the actual CPU clock. But once I did that, we made it through the ROM check some tests which is pretty awesome. And uh we moved on to testing the MMU at that point. But that failed really badly.
Just some simple logic bugs. Nothing to really say. But once it passed its tests, it was time for memory sizing, which of course failed because we don't have any memory. So, we need to add a RAM board. Now, by the way, this is what the ROM checksum test looks like. You can see the addresses coming out of the CPU here. You can see it's like in a tight loop um looping over ROM addresses performing that check sum. And you can also see, let me get the pointer. You can also see that um 7F114E.
You can see that incrementing each time.
That is the actual address that it's performing the check sum on currently.
And then this is a trace of the MMU register test, which is pretty cool.
This is where it starts the test right here. And then you can see the SEG 1 and SEG 2 signals. These select which MMU contexts the MMU is operating in. So you can see it cycles through all four of the MMU's context testing all the registers. And then once all the tests pass, then it goes back through all four contexts again and initializes them with the proper memory map that we need. And then finally once the tests are done, it asserts the start signal to actually turn on the MMU. So it's just kind of cool to like actually see all that in the actual logic. But anyway, back to the RAM boards. We need to create those.
Now, the Lisa, it has two RAM slots on the actual computer. And you can put any combination of RAM boards into those slots that you want up to a maximum of 2 megabytes. But given that you can put just a single 2meg board into a single slot, we might as well just implement one slot with the max of two megs in it, right? No point in doing two slots. It's just simpler that way. But, um, we want to use SRAM. The original Lisa used DRAM because SRAM is just so much easier to work with. you don't have to worry about refreshing it like you do with DRAM. So, um, we are going to have to make a few changes to the design in order to account for that. But luckily, it's pretty easy. There's nothing weird we really have to do with the refresh circuitry. We can basically just ignore it because RAM is refreshed by the video circuitry, and we can basically just cut those signals off, pretend like they're not there, and it's perfectly fine. But there's also an issue where um, the DRAM chips are sent the address in two different pieces. I'm sure most people here are probably familiar with RAZ and CAS style addressing. Um that's how the DRAM chips are expected to work. We get the address in two different parts on the multiplex RAM bus. But the uh SRAMM chips expect the address just in one single big flat address. So we basically just have to take the two components of the address and combine them together into one big address. Simple as that.
But there is one more big question. What SRAMM do we use? Do we use like an external chip? Do what what do we do exactly? Well, for now, we want to use the FPGA's internal block RAM, which is one of those primitives that I was talking about earlier. This is just general purpose SRAMM that's integrated into the FPGA. You can use it for whatever you want. And um we actually need even though the 68K has a 16- bit bus, we need 18 bits of RAM because they're also two par bits, one for the low bite and one for the high bite of memory. So, we need 18 bit wide RAM bus.
But this uh kind of introduces a problem because when you account for both the 16 bits of actual memory plus the two parody bits, the FPGA only has room for 256K of RAM, not the full 2 megs that we want here. And 256K is less than the least it was ever designed to even run with to begin with. So that's going to present may maybe present a problem later. Spoiler alert. But for now at least, we can uh we can just ignore it and at least do some testing with this, right?
So, let's simulate this with with our new RAM board design. And at first, it didn't work because I was inhibiting the cast signal thanks to a bug on my CPU board because it's supposed to be inhibited when you're not accessing memory, like when you're accessing like IO and things like that. But I was accidentally inhibiting it when accessing memory, too. But once I fixed that bug, then it could access the memory and it could complete memory sizing. So, that's a very significant improvement, right? There were still some bugs after that and other checks that the ROM did, but after I fixed those, it was able to find a place in RAM to put the video memory, set the screen page address to that location in memory, and then attempt to talk to the IO board, which of course doesn't exist.
So, what it's supposed to do here is give an error 41 on the screen, and then boot loop. And it looked like that was what it was doing according to my logic analyzer traces, but I can't confirm that because we don't have a screen.
We're in we're in simulation right now.
So what do we need to do next? We need to hook it up to a video output. And what does that involve? That involves putting it on the actual FPGA. Now, so to do that, the very first thing we need to do is we need to actually assign all of the input output signals that come out of our design to physical pins on the FPGA. And we do that through what's known as a design constraints file. So we use these lines right here to basically just say, hey, this is the name of the signal in my system verarilog design. I want it to be routed out to this pin on the FPGA. These are just row and column coordinates going to a pin underneath the FPGA chip. And then here I'm saying I want it to use 3.3 volt CMOS logic levels. That's really all I'm saying there. And then this is just saying um there's a clock coming into the chip on the CIS clock pin that has an 8 ncond period. That's just the clock that's on the pink board that I'm using to clock the entire design.
Um, one other thing we need to do though is generate our clocks from that master clock that's coming in on that cis clock pin because in simulation we don't really care or at least at this point we didn't really care about clock speed, right? I was just clocking it at as fast as it could possibly go. Just see what happens, right? But in actual hardware clock speed does matter when doing timing analysis and when syncing it up with the rest of the Lisa design. So we need to make sure we're clocking this at the actual 20 megahertz dot clock that the original Lisa used. But how do we do that? Because the pink board, the crystal that comes into the FPGA on that CIS clock pin is 125 MHz. So we need to convert that down to 20 MHz in some way.
And the way we do that is using what's known as an MMCM, a mix mode clock manager, which is another primitive built into a XYlink FPGA that um it's basically a phase lock loop that can generate clocks from other clocks. So this is me configuring it over here. You basically just give it the input clock frequency and the output clock frequencies you want and it will generate a system vera log module for you that uh generates all those frequencies and then you just create an instance of that module like this with your input clock and all of your output clocks down here and bam now you got your clocks and uh once we've done all that we can finally actually synthesize the design to put it onto the FPGA and this is a three-step process you have synthesis implementation and bitstream generation so first up is synthes is this is just the process where you take your code, your system verogg code, and it gets converted into what's known as a net list. Basically, just a a list of all the gates and all the wire connections that formulate your design. Basically, a schematic.
And then the next step is implementation, which takes that net list and actually places all of the gates and all the flip-flops and all the primitives onto the chip, maps them to physical gates on the chip and then routes all the wires through all the routing nets that exist on the chip in between all the components. And it also does design rule checking to make sure you're not like crossing wires or violating anything like that. And then finally you have bitstream generation which literally just takes the implementation result and then converts it into a binary file you can stick into the FPGA. Once we've done all that we can actually program the FPGA. But by the way that entire process by the end of this project it took nearly an hour and a half. So you change a single character in your file you have to wait an hour and a half before you can test it. It's absolutely horrible. But eventually uh was able to program the FPGA and it didn't quite get as far as it did in simulation. It made it through the ROM check sum and MU tests, but it was not able to make it through memory sizing. So why could that be? Well, it's obviously because of some discrepancy between the real world the real world behavior of the block RAM and the simulated behavior of the block RAM. To actually see what's going on, we need to add what's known as an ILA, an integrated logic analyzer core, which is basically a logic analyzer that gets implemented on the FPGA alongside your design that then gets connected to all the signals in your design so that you can actually view a logic analyzer trace of what the physical design on the FPGA is doing in the same interface that you would use in simulation, right? The only catch here is that the ILA actually uses some of the block RAM on the FPGA as it sample memory. So you cannot hold very many samples before you have to report that data back to the host computer because there's not much block RAM on an FPGA. And that's not really a problem here, but later on in certain other parts of my design, that was super annoying. This is setting it up, by the way. You just select a bunch of signals in this little wizard that you want to debug and then it puts the core in your design for you.
So um yeah, what's going on here? Why why can't we complete memory sizing?
Well, it looked like writes just were not being committed to RAM for some weird reason. So why might that be?
Well, it was really stupid. It was because I was being dumb and I wasn't asserting the right enable signal going to the block RAM at the right time. I was asserting it at the exact same cycle that I was changing the address in the data, which for whatever reason, simulation was okay with this. It was like, all right, I see the new address and data. I'll I'll work with that. But in the real world, the address and data need to be stable before you attempt to write them to the memory. So I really needed to be asserting write enable one cycle later. But I wasn't doing that.
And so it was memory was getting completely corrupted and it was writing the previous address and data instead of the current one. So after I changed that, I got this.
It's an error 41, which is what I wanted to see. But clearly clearly something still isn't quite right. But we are getting a picture on the screen. So that's awesome, right? Big improvement, but it's really ugly. So something's still going on. And it turns out it's another timing issue. And it has to do with how the CPU board splits accesses between the processor and the video circuitry. So they they split access up 50/50 where the processor is able to access RAM for a little while and then it hands control over to the video circuit which is then able to read a 16- bit value from RAM and then send it out to the display. then processor uh video proc they just they they go back and forth like that, right? And um there was an issue here though where if the CPU was trying to do a write operation immediately after the video circuitry had just read something from memory, there was actually a chance that the right enable signal would get asserted before the CPU had put the new address on the bus. I'd actually introduced this bug by fixing the previous bug. I had introduced an edge case that broke everything. But right enable can some could sometimes be asserted before the CPU put the new address and data on the bus. So basically the old address and data that had to do with the video RAM could still be on the bus and then write enable would get pulsed while that was on the bus. So we would accidentally write data to video RAM instead of writing it to the CPU RAM where wherever it wanted to write to. So no wonder the video was getting completely corrupted, right? We were just writing random garbage into video RAM. After I did that, it looked great. So, um, at that point, it was time to move on to the video board to actually get this thing to do something other than boot looping and showing an error 41 on the screen.
So, video or sorry, not video board, IO board. What we need to do here is implement all the input output functionality of the Lisa. It's got quite a few different things it needs to do. The most complicated is the floppy disc controller. It's a whole separate computer inside the Lisa. It's run by a slave 6504 processor, which is basically a 6502, but with fewer address lines.
It's got its own RAM, ROM, everything.
Kind of overengineered, but whatever.
Then you've also got your profile interface. Profiles at least it's hard drive, by the way. That's ran through a 6522 VIA chip. You've got um speaker output, and it also has software volume control, which is pretty cool. That's also done through a VIA. Then you've got a contrast control for your display, also pretty cool. You've got software contrast control, very ahead of its time, also done through a VIA. Um, then you have serial ports, of course, that's done through an 8530 SEC chip. And then, um, finally, you have your keyboard and your mouse, soft power, and the real time clock, which are all handled through a microcontroller called the COP 421, which is this weird thing nobody's probably ever heard of before, but that's what it chose for some reason.
Um, so we got a lot of chips here that are very complicated, just not not standard off-the-shelf TTL parts. So, I was thinking maybe I need to implement some of these myself, but I I really hope not. It would be ideal if I could just find find off-the-shelf cores for these and use those. And fortunately, they existed for all of them except for the SEC here. And technically, one did exist for the SEC. It was just really, really terrible and like not not accurate to the original at all. But I got lucky that I had cores for pretty much everything else. So using those cores, I implemented the entire IO board and not really not really any issues or so I thought. It all went very smoothly, but they're going to be some huge pain points later on. It took me like 3 months to solve certain problems with this thing. Oh my god. But there were um there were several clocks I had to generate on the IO board with another MMCM. That was pretty easy, but that's going to be part of the problem. Spoiler alert, that um we're going to encounter later on. Um, I decided to use that off-the-shelf SEC, the really terrible one for now, just for the sake of testing, and then we'll move to something better later. And, uh, another mistake related to the clocks here, kind of going off that MMCM thing, is that the COP and the SEC actually their clocks are super slow, like just a couple mehz, and the MMCM can't generate a clock that low. So, I generated a higher frequency clock and then divide it down with a flip-flop. And you might think that's okay, but it is not for a reason that we'll get into a little bit later on.
So, um, actually firing up and testing the IO board. The boot ROM does a good bit of stuff here. Um, before it even gets to the actual IO board test over here, it actually tests a good bit of stuff on the IO board. So, after memory sizing, after all that stuff, first thing it does is it just puts some data in the SEC for like Apple net or something like that. It doesn't matter.
But then after that it tests the parallel port via which is one of those two 6522 vas by just writing all the values 0 to 255 to one of its registers and then reading it back. Then it turns off the display contrast just blacks out the display through that via then it will uh test all this the video memory in the main Lisa RAM. By the way the reason it turned off the contrast is so that you don't see that test happening.
Um then it tests the keyboard via the exact same way by writing 0 to 255 into its memory. Uh then it will uh turn the contrast back on again so that you can see the screen that you see right here.
It will display the CPU ROM revision up in the corner. Then it turns on that COP 421 that handles keyboard, mouse, and all that sort of stuff. And make sure that it's sent the proper I'm alive code essentially. Um then after that it will click the speaker, which sounds really simple, but it's actually insanely complicated. And um then it displays that screen you see right there with the CPU board highlighted and it goes to do some tests on the CPU board. So, how far are we getting through all of this? Take a guess. It's probably not very far. Um, we were not even getting through the first VIA test. Um, I was clocking the VAS wrong. It was, you know, the same clocking paradigm that was done with the FX68K where you have those clock enable strokes. The VIA works the same way. And once again, I didn't fully understand how this worked at the time. So, I was doing that wrong. Uh, I set the clock divider wrong for the cop. So, it was being clocked at the wrong speed. Uh, bus contention. and two things were driving one of the cops data lines. Uh, a whole lot more stuff than that, too.
But the big thing here was the floppy disc controller, which might surprise you cuz notice I didn't mention any tests being done on the floppy controller on that page.
But guess what? It's required anyway to click the speaker of all things. So, yeah, the the the reason that you need it for that is because there are two different versions of the Lisa. There's the 25 and the 210, and they use slightly different IO boards. These two IO boards clock the VAS at different speeds and uh the VA is what generates sound. So if you tell the VAS on the 25 and the 210 to generate the same frequency, one's going to be higher pitched than the other since they're clocked at different speeds. So the Lisa's boot ROM has to figure out which board is installed and adjust the audio frequency accordingly to make sure the audio sounds the same across the two boards. And guess how it knows which board is installed? By looking at the floppy controller. The floppy controller's ROM has a revision ID in it that corresponds to which IO board is installed. And the CPU can't just look at the ROM directly. It can only access the floppy controller through a set of shared RAM that is on the IO board. So this means in order to access that ROM ID, the floppy controller has to be alive enough to pass itself tests and copy that revision ID from its ROM into that RAM. So there's so much that has to work here just to click the speaker.
It's absurd. It's absolutely absurd. But um there were so many things not working in the floppy controller. It is so complicated. Look at the schematics.
You'll see what I mean. It's absurdly complicated. But um so many problems here. I can't really get into all of it for the sake of time. But um a lot a lot a lot of problems with the floppy controller. Mainly having to do with the original system using uh asynchronous memory but block RAM being synchronous and having to adapt a bunch of stuff to that. read the slide if you're curious and watch the presentation on YouTube afterwards if you want more detail. Um, but this is one of the things I had to do to fix it. If you read that slide, you would understand the fix. But once again, watch on YouTube for more info.
Um, but once I got through all that and figure out all those issues, the IO board was able to get a little further.
The floppy controller was able to put that ROM ID into the shared RAM and we were able to get a little further in testing. It went back to click the speaker and then it tested more of the CPU board stuff which there few more tests it does on that before it moves on to the RAM test. It um it tests the vertical blinking interrupt which uh all these things after a few bug fixes worked okay. Um it reads the serial number from the video state machine. Uh it does a right wrong par test which we'll talk about what that means later.
It worked fine for now but it will not work fine later on. Um and then it moves on to the RAM test. This worked fine too because we already know our RAM is pretty much functional. So it passes the full RAM test. Then after that it comes back and tests more stuff on the IO board. Three main things that it does here. It tests the SEC first by um just loading some config data and then it does a loop back test where it basically connects the RX and TX lines of the uh serial port together inside the chip and then transmits data and make sure it can receive it back again. Then after that it tests the floppy controller. It does a ton of stuff on the floppy controller.
It reads that ID again. It makes sure that floppy controller passed itself tests. Then it uh tests the RAM to make sure it can write to that shared floppy controller RAM properly. And then it sends the floppy controller command to make sure it can execute commands. Then after that, we talk to the cop to make sure that we can read key codes from the keyboard. And we also save the current time and date from the cop's clock at the RTC. And then finally, we do some expansion slot stuff, but there are no expansion slots, so who cares? And then we go to the boot menu essentially.
Well, we didn't do so hot here, unfortunately. Um, both the SEC and the floppy controller test failed. The cop did fine, but the other two did not. The SEC core, you know how I said it was garbage? It's so garbage that it didn't even implement internal loop back. So, it can't pass that test because it's not even in there. So, all I did there, I just I just patched the boot ROM so it skips that test. And we'll worry about that later. Um, the floppy controller though, much deeper seated issues here. It was intermittent. The worst kind of issue, especially when you're dealing with an FPGA. Sometimes it would make it all the way through the tests, but sometimes the IO board revision, that revision that it reads from the from the shared RAM, would come back as complete garbage and it would fail the test with an error 57.
You can see up there it says B5. That's supposed to be an A8, but it just came back as garbage instead. So, we're going to worry about that much later on.
Spoiler, it took me like two and a half months to figure it out.
But when when that test passes and when we ignore the SEC, we get to the startup from menu, which is the boot menu.
That's awesome, right? So, now let's try and connect some peripherals and boot, right? So, I connect a Lisa keyboard and a mouse and an ES profile hard disk emulator. And uh the keyboard and mouse worked after a little bit of messing around, but um and the profile worked just on the very first try, which was absolutely amazing. So, let's try and boot some operating systems at this point and see what happens.
Well, these aren't really operating systems, but they did boot. This is a Lisa utility. This lets you pick hard disk images. Um, this and this are well, this is a hard disk utility, and this is just a manual brought set plotter. They all work. So, it's looking good so far, right? No, literally all of these operating systems here fail in one way or another. Long story short, all of them boot for a little while and then hang somewhere. The only one really worth noting more than that is the Lisa office system/workshop.
It errors out with an error 10727 which means memory exhausted. So remember how I said we only had 256k of RAM earlier and that might be a problem. Well, there's a huge common thread here which is that everything booted for a while and then hung. That is a huge symptom of memory exhaustion. And LOS literally said that memory was exhausted. So I think one thing we might need to do here is just add more RAM, right?
The problem is we don't have any more inside the FPGA. So, we really need to design a custom board at this point.
Also, look how ugly this whole setup is.
Like so many wires everywhere. It's time for a custom board. So, let's design that custom board. We have a ton of requirements here for it. I was maybe a little too ambitious with the first revision, but it ended up coming out pretty good. So, I want USBC power, of course. Um, I chose the Arctic 7100T as my FPGA because it's cheaper than that zinc FPGA because it doesn't have the ARM core cuz I don't need that, but it actually has more logic elements inside it. So, more flip-flops, more LUTs, which are basically logic gates. It has more of all those things inside it, which is pretty nice. Um, then 2 megs of SRAM and a dedicated SRAM chip on the board to allow us to have the full amount of RAM that the Lisa has. Some of you might spot an issue here. I don't know if anybody does, but if you do, then props to you. Um, we we want onboard speaker, HDMI port for video output, um, onboard ES profile hard disk emulator. So, you don't need an external emulator for that. Also, an ESP32based floppy emulator that I do not have working yet, but I hope to get working in the future. USB keyboard, mouse, um, just a slap a real SEC on there to fix those problems for now because I already got enough other stuff to worry about.
Don't want to implement one of those internally for now, but I I I will later. Don't worry. Um, then, uh, I also wanted to route one of the serial ports over the USBC port, or at least have the option to do that, so that you can just do serial transfer straight over USB without having to have an external USB to serial adapter. Um, and then I also want you to be able to pick uh different ROMs by just flipping some switches on the board and also overclocking because that's pretty cool. So, with all those requirements, it's pretty ambitious, right? Ended up being a six layer board in the end. We need tons of voltage regulators. Signal integrity is very important for a lot of different things on the board, but especially HDMI. That was a big one. I'd never done anything quite this complicated before, so I was pretty scared doing all this crazy stuff. Um, tons of support circuitry needed for a lot of stuff. Just an absurd amount of I mean, look at the board. It's there's a lot of stuff on here. It was It was difficult. But eventually I got the whole thing designed and this is what it looked like. Zoom in on the FPGA. Look at all those wires. That was very difficult to get all this routed out properly. And I know it's ugly. This is my first time designing a board rail on FPGA. It cut me some slack. But um after after I designed it and placed the order, this is what I got in the mail. It's this right here. And uh just a quick tour.
This is all the voltage regulation stuff over here um for our seven different voltages we need. Uh this is our original Lisa keyboard mouse ports. This allows you to plug in USB keyboards and mice. Uh these are your front panel controls like the power button and stuff. That's the FPGA. This is your SRAMM chip. Uh ports over here for a real floppy and real profile. These are the profile and floppy emulators, ROM selection switches, overclocking switches, serial ports, HDMI, speaker amp, uh USBC, just and there's plenty of other smaller stuff on there, but I'm not going to cover all that because I don't have the time. So test the boards.
I was going to go into more detail here, but once again, don't have the time. So, basically, they they pretty much just worked with a few problems. The biggest one is there was a ROM on here, uh, this ROM right here that um I accidentally put a ROM on there that was half the size it was supposed to be, but luckily it just so happens to be like the only chip on the whole board that's a DIP chip. So, super easy to desolder and pop in a bigger one. Super easy. So, easy fix. And now we can try and adapt the code to these boards and get it working on there. Just adapting the code as is pretty straightforward. you just change some pin assignments to match the new board instead of the old board. Um, but then we also need to switch our block RAM over to that SRAM chip. And we also need to switch from the horrible SEC core to the real SEC. Um, but we're not going to do any of the modernizations yet cuz we just want to get the Core Lisa working first. So, starting off with the SEC, that's really easy. You just route those signals out of the FPGA and into the physical SEC. Super easy.
And it passed the loop back test now.
So, that's promising. That's another thing crossed off the list, right? The SRAMM was a little less straightforward because we have to go from synchronous block RAM to asynchronous SRAMM, but it's really not that hard. Just a few few always flip-flop blocks and system verilog and not not difficult at all.
Um, the only big issue though, this is the thing I was asking if anyone spotted a second ago. Um, our SRAM is only 16 bits wide. We need 18 bits because we need those two par bits as well. So, we can't do par anymore. So, what do we do?
Well, for now, just patch it out. Patch out the par test and make sure the par error signal is always just set low. Or actually, it's high because it's active low, but set that deasserted forever.
So, we just don't even worry about par.
And we'll come back to this later.
So, let's boot again. Now that we have two megs of RAM, let's boot and see what happens. Well, most everything works now. But here's a little video. I'm just going to skip ahead of the part that matters. This is a video of what happens when I attempt to boot.
Oh, well, the video started over for some reason when I uh here. Look at that. You can just barely look. That is Mac Works. You can see the blinking question mark through the static, but for some reason, the screen looks horrible and it's just glitching out like crazy. So, what's going on? Whoops. Uh, there we go. Um, it appears as if the RAM's getting corrupted, but that's actually not what was getting happen. the RAM is not getting corrupted at all. It's uh actually just how I'm accessing the RAM that's the problem. So the SRAMM I'm using it has two strobes. It has uh UDS and LDS which are um upper and lower data strobes to basically say do I want to access the upper bite, the lower bite or both of the 16- bit RAM bus, right?
And uh on this RAM chip, you're required to assert these for both reads and rights to determine which bytes you want to access. And the 68k generates these strobes just right off the bat. There are pins on the 68k that route straight to the RAM. So whenever the 68k accesses RAM, the RAM knows exactly which bytes it needs to return. But the video circuit does not generate UDS or LDS.
They are just always deasserted when the video circuit is directly reading video data from RAM. So you can see a problem here. When the video circuit is accessing RAM, it's just reading garbage because the strobes aren't set. But then this begs the question, why does it sometimes work? Because clearly we were getting something. Well, sometimes we get lucky and the strobes are just asserted from a previous CPU cycle. If the CPU had just accessed RAM and we got perfectly lucky with the timings of things, the strobes might have carried over into the video cycle just enough for us to grab the data before they deassert again. So, that's why it works sometimes. But this begs another question, which is why did this work with the original Lisa? Because the original Lisa also had the strobes deasserted when video was active and it worked fine. Well, the reason for that is because the original Lisa Ram board didn't care about these strobes for reads, only for rights, whereas mine cares about them for both. So, the solution here is to just fix things so that it only cares about them for rights and sends both fully asserted all the time to the RAM whenever we're doing a read. And when I do that, we can boot into the Lisa office system with perfect video. So, this is a big moment for the project. We actually have a mostly working Lisa at this point. And this is just a logic analyzer trace showing that you can see here that um we're trying to access RAM but the strobes aren't set.
So yeah.
So where are we right now? Most operating systems boot but X and UNIP plus which are both variants of Unix do not boot. Uh X kernel panics and Unix or sorry and Uniplus uh gets to system info screen and then hangs there. But we'll worry about those later. Uh, the floppy controller is still really intermittent and we still haven't even tested if it can read and write discs at all. Um, the SEC I did say it passes a loop back test now, but you cannot actually do any serial communications in any operating systems. So, there's still something wrong there. Um, of course, we have the parody RAM issue, but we'll worry about all that later because for now, I want to do some modernizations like HDMI and stuff.
So, those modernizations, here are all the ones I want to do. HDMI audio and video output, obviously USB keyboard and mouse so that you don't have to have an old expensive Lisa keyboard or a Mac mouse. Uh overclocking as fast as I can possibly make it go. Uh allowing you to switch between the square and rectangular pixels Lisa ROMs. Uh, by the way, square pixels allow you to make Mac Works, aka Mac OS, look more like real Mac OS, whereas rectangular pixels are the ROMs that you have to use if you want to run any other software on the Lisa. And I also want you to be able to select between Sony and Twiggy ROMs for the IO board, which allows you to either have a Sony floppy drive connected like what you would have in a Mac, or a Twiggy drive, which is a really rare floppy drive from the Lisa 1 connected.
And finally, I want you to be able to pick whether you want to have an the onboard profile and floppy emulators to be active or the external profile and floppy ports to be activated. Basically implementing all the switches and modernizations that are on this board.
So, starting with something really easy, just those switches that pick between the profile and floppy. It's literally just a multiplexer. That's all it is.
Um, this led to a bit of a revelation though. The level shifters that go to the external profile port were broken.
they all the signals just oscillated at 20 megahertz for some reason. And it turns out this is a common problem with the level shifters that I chose to use.
Um, so the solution on the V2 boards is just to switch to a different level shifter design, but at least I got the multiplexer implemented. You can see it's literally just a multiplexer. We're saying if that switch is in one position, feed the ES the onboard ES profile through. If the switch is in the other position, feed the the external profile through. It's just a multiplexer.
The uh IO board ROM selection is also really easy. It's basically just another multiplexer. Uh depending on the switch position, we just feed one ROM through or the other ROM through. And you can see that right here. It's just that select signal going into the chip enable of each of the two ROMs. One gets selected when it's high, the other gets selected when it's low.
The CPU board ROM selection is also quite easy, except the issue here is that we have multiple ROMs that we actually need to select together.
because not only do you swap out the boot ROM, but you also have to swap out the video state machine ROM cuz that one since we're changing the pixel aspect ratio, you have to modify the video state machine at the same time. Um, so but that's still pretty easy. It's just multiple multiplexers instead of just one. But there is another nuance here, which is that the HDMI video output logic needs to know which one is active so that it can scale the screen accordingly. Speaking of HDMI video logic, we need to implement that. Now, this is a little more complicated than any of those previous tasks because there are a lot of things we need to do.
First up, we need to sync to the Lisa's vertical and horizontal sync signals to figure out where we are in the video frame and then read in the serial video bit stream off the Lisa's vid signal into a frame buffer. And this frame buffer is just implemented in block RAM, right? And then once we have all the video data into that frame buffer, then we need to see which ROMs are enabled.
And depending on which ROMs are enabled, we need to apply a scale factor. So if the rectangular pixels ROMs are enabled, we need to make the pixels uh three high by two wide to make them rectangular in the same aspect ratio that the Lisa used. And if the square pixels ROMs are enabled, we keep the pixels square, but we just scale the screen up a little bit to make it fit our 1080p HDMI frame a little bit better. We also need to account for software controlled contrast of course and then transmit the frame out over 1080p HDMI and send audio out as well. There is one thing to note here though which is that um you'll notice here that when we scale the rectangular pixels up it's 1440 x 1092. 1092 is greater than 1080. So they're actually going to be 12 lines that are cut off.
But given that we're upscaling by 3x and each Lisa pixel is three HDMI pixels high, that only cuts off four Lisa rows, not 12. So, I just cut two off at the top and two off at the bottom and you you don't even notice. It's it's no big deal. Um, luckily there's an HDMI core on GitHub, so we don't need to actually implement the HDMI output logic ourselves. Just all the processing stuff I just mentioned that goes into it, right? So, it's mostly straightforward, but capturing stuff into a frame buffer was a little bit weird because the the the exact sync timings that the Lisa used are not really documented. So, I had to figure out the exact front and back porch timings for everything, which for VSync were very straightforward, but for HSYNC, I had to figure out exactly how many clock cycles you have to wait um before and after the sync pulse goes low and high at the starts and ends of the lines before you capture pixels.
That took quite a while to uh figure out because remember, you have to wait an hour and a half every time you change the design. But eventually, I figured it out after tons of trial and error. Audio is also kind of interesting because HDMI expects or at least in the configuration I'm using 16- bit PCM sampling at 48 hertz, but the Lisa is just putting out a square wave. So, how do we convert that to PCM sampling and account for volume control as well? Well, it's actually pretty simple. Um, if the Lisa's output square wave is high, we literally just take the LISA's volume value, which is a number from 0 to 7, and then scale it up to the range 0 to 65,535, which is the PCM sampling range, and we just send that out to the HDMI module. Or if the square wave is low, we just send out a zero. It's literally that simple. And we're doing that 48,000 times a second. That's all there is to it. After fixing a bunch of bugs, I got this working and it was pretty awesome.
HDMI output from Alisa. And here's what that looked like. Ignore the weird artifacts you may see. That is from my capture device, not from the actual board. But you can see here, this is the rectangular pixels. You might be able to see they're a little taller than they are wide. And you can see the image is perfectly centered. Um these gray bars on the side are generated by the um the FPGA. You may not be able to see them on here, but they are slightly gray.
They're not fully black. And then here is the square pixels. So now the pixels are actually square. And it's scaled is scaled differently to make it look right. I can't scale it to fulfill the full screen because well it would be too big at that point. I can only scale it by 2x but it still looks pretty good.
Right.
So we've done HDMI. Now time for USB keyboard and mouse. So USB is a very complicated protocol. So once again I don't really want to have to implement this myself if I don't have to because I've got enough work cut out for me already. So luckily I was able to find a Verarilog core on GitHub that implements USB human interface device for me and it just outputs uh keyboard and mouse packets in the form of like XY deltas for the mouse and uh just keyboard key codes for the keyboard and I just have to convert these into the Lisa protocol now but that's that's pretty easy for the mouse but not particularly easy for the keyboard. So for the mouse it's literally just uh quadature encoding.
That's how the Lisa mouse works. You just have these two pulses that go into the cop. And uh if the mouse is moving one way, the uh one signal will lead the other. And if the mouse is moving the other way, uh the other signal will lead that first one. And the frequency of the pulses uh determines how quickly the mouse is moving. So we literally just uh read how big the values in the delta x and delta y registers are coming from the USB mouse and we just send the appropriate number and direction of quadriure pulses to the cop um in order to simulate the mouse movement. But this didn't feel quite right. So I had to try a bunch of different things to get it going properly. Basically fast mouse movements felt normal but slow movements were not sensitive enough and I just could not get it to feel right. But eventually somebody private messaged me on the Lisa list 2 forums and just was like, "Hey, I have a solution for you."
And just gave me some code that just fixed everything for me, which is basically accumulate the mouse deltas over time and then uh decrease them slowly instead of decreasing them once for every single quadriure encoder count, which I wish I could explain and show in more detail, but once again, time is of the essence here.
Keyboard protocol though, it's a lot more complicated than that. Oh my god, I don't understand why they did it this way, but it's a it's a unidirectional or sorry, it's a single birectional uh data line going between the Lisa and the keyboard. And the timings here are super weird. The Lisa will send like a sync pulse down to the keyboard and the keyboard will respond with data uh if if there's any data available. But the timings are really weird. The bits are sent in this odd order for some reason.
And also notice here that the timings like all the bits are the same length except one which is just randomly longer for some reason. And you also have reset. I didn't list this here, but they're also like reset pulses. So, you have to actually time how long the sync pulse is because there can be longer sync pulses that mean something different. And it's just crazy crazy all around. And Lisa key codes are also transmitted. They're transmitted as a make and a break code. So, when you press the key down, you get a make code, which is um the key code but with the high bit set. And then when you let it go, you get a break code, which is the key code but with the high bit cleared.
as opposed to just a constant key code the whole time it's held down and that going away when you release it, which is interesting because the USB HID protocol does it the other way where you get a key code when you press it and get nothing when you release it. So converting between those was pretty annoying and um also just looking at how complicated all of this is, that's quite a state machine on its own, even ignoring the human interface device side of things. So implementing that took a very very very long time. Plus the modifier keys, I forgot about that because you have to handle what happens if you got a modifier key pressed at the same time as something else. It was horrible. But um eventually I got it all working and it's so cool to be able to use a USB keyboard with your Lisa. So cool. But now something a little cooler.
Overclocking. This is where it gets fun.
So I've got two switches on this board for overclocking. So that means that we have uh four possible speeds that we can clock this thing at. Of course, one of those is going to be stock. The other three are going to be faster. Um, there are multiple different clocks in the Lisa, but there's only one of those that we're actually going to be able to overclock, which is the Lisa's dot clock. This dot clock drives a variety of things in the Lisa, but the main ones are pretty much everything on the CPU board, your RAM, and the 6522 via chips that are on the IO board. We can't overclock really anything else on the IO board like the floppy controller because if we overclock that then timings going to the disc would be wrong and you can't write to a floppy disc at four times the speed the disc expects, right? Um we also we can't overclock the sec because bald rates would get messed up and we can't overclock the cop because it runs the real time clock and we don't want the clock running four times faster than it's supposed to. And uh we also it'll mess up the keyboard timings as well.
Those really really precise keyboard timings I just talked about. So just the dot clock. But the good news there is that it's fairly rare for the Lisa to access any of these other things. So they're not really a huge bottleneck.
The clock speeds I chose were of course our stock 20 MHz dot clock and then 40, 60, and 80 just increments of 20 from there, right? And the CPU clock is dot clock divided by four. So these are the corresponding CPU clocks for all of those dot clock values. And then we just we multiplex the clocks, right, based on the switch settings in order to uh determine which one gets fed through to the actual logic, right? It's just look at the switches and then feed one of the four clocks through to the system clock depending on what the switches say. So you think it would just be like this, but no, that is not how you do it. That is very, very, very bad. Do not do it that way. Why? Well, because in an FPGA, clocks run on what are known as clock nets, which are basically special wires inside the FPGA, which are distinct from data nets, which are the the wires that all your standard data signals run on.
And the reason why clock nets even exist is because clocks need to have very little skew. Skew is basically um where a signal arrives at one place inside the chip slightly before it arrives somewhere else. And you don't want that for a clock because clocks are basically synchronizing your entire design. So you want that to occur everywhere in your in your FPGA, everywhere in your design at the exact same time. If one thing is getting clocked slightly before something else, a signal may not arrive in time before a one of the clocks get gets pulsed and you can have crazy things happen where your design just breaks in really unpredictable ways. So that's why clock nets are absolutely crucial, especially if the clock is clocking tons of different things. And if you try and multiplex a clock like a a clock that's on a clock net, a multiplexer works on data. So the multiplex will turn that clock into a data net. So we we can't do that. We can't do that. What we need to do instead is we need to find some way to multiplex a clock and keep it on a clock net. But luckily that's not too hard.
This gives us this primitive known as a buff gm which is designed to multiplex clock nets. So all we do is we instantiate a few of these. We actually need three of them because they're 2:1 multiplexers and we need a 4:1 m multiplexer. So instantiate four of them or sorry instantiate three of them and um we can multiplex our clocks properly.
Now the keenide amongst you might realize what's wrong with the IO board and the floppy controller later on. Um so now uh can we overclock kind of 40 and 60 meghertz the Lisa can power up.
It can kind of boot but soft power doesn't work. So if I try and turn Lisa off with the soft power switch just nothing happens. Also, the office system, it just beeps randomly. Like, it'll be running. It'll just go beep beep beep beep beep just at completely random times. It's so weird. And we also we can't save the time and date. And the ROM says there's no keyboard connected even though there is. And it can talk to the keyboard just fine. So weird. And 80 MHz just is dead. Completely dead. 80 MHz though, it turns out the problem there is that my RAM chip on the board just physically isn't fast enough to keep up at that clock speed. Like the data, by the time the data is ready, the CPU has already moved on to the next bus cycle. So there's not anything we can do about that. That's just a physical limitation of the RAM chip. So I just toned 80 down to 75 MHz. And at that point, it had the same symptoms as 40 and 60, which is good. So that's going to be our max clock speed right there.
75, which is still 3.75 times the stock Lisa speed, which is pretty good if you ask me.
This is uh this is the timing, by the way, of getting messed up on the RAM when you're at 80 MHz. I don't have time to really show that, but look on YouTube later or ask me a question at the end if you want to see it. Um, but yeah, so overclocking, where are those issues coming from? Well, think about them.
Soft power, uh, not the beeping thing.
We can't really say anything about the beeping thing yet, but soft power, keyboard, and, uh, the other thing, which I forgot what it was, but, um, what was it? Something else, whatever it was. I forgot what the third issue was, but um, oh, clock. Yeah, those all have the cop in common, right? The cop handles all of those different functions. So I wonder if this has something to do with the cop. We can look though and see if that beeping might also be related to the cop in some way because that would further confirm that as a good suspicion. But we have the source code to the office system. So we can just look and see where that beat might be coming from.
Well, it's coming from right here. Look what this routine is called. It's called clock. It returns the date and time coming from the cop. And if it can't read the date and time properly, it beeps for some reason. I don't know why.
I don't know why, but they designed it that way for some reason. So that relates to the cops. Well, so we know that whatever this is, it's some kind of cop issue. So the problem, whatever it may be, likely has something to do with the fact that we're clocking the rest of the design four times faster than it's supposed to be. Almost four times faster than it's supposed to be, but we're clocking the cop at its stock speed. So something's probably not lining up quite right. maybe a timeout issue, I don't know, something. Uh, but the protocol the cop uses, just so you can understand how this works, is it's got an 8-bit data bus going to one of the VAS, plus it has three control lines for handshaking. Essentially, one of the control lines is called ready, and the cop asserts it every millisecond or so to say, hey, I'm ready for a command.
And when the Lisa sees the ready line go low, if uh if it wants to send a command to the cop, it has a very short time to put a command out on the data bus for the cop. And the cop can see that command, act upon it, and whenever the cop is ready to return data relating to that command, it puts the data on that bus, and then it asserts a signal known as data cued, or that's what I called it, at least. I don't think it has a formal name. And then when the Lisa sees that, it'll grab the data off the bus and then assert read app to acknowledge that it read the data. And then we're back idle again. And if we look at a logic analyzer trace of this, you can see ready going low here. And then the Lisa puts a command on the bus. And this is the output enable signal for the bus.
So basically the LISA is leaving the command on the bus for as long as this signal right here is high. So it's sticking the command on the bus, but then the cop just never responds. We never see a data cued ever. So no wonder we can't read from the clock because we're never getting clock data back. I wonder what the problem is here. Perhaps maybe it has something to do with the fact that we're running four times faster than we're supposed to. And so therefore, we're not keeping the command on the bus for as long as we're supposed to. We're keeping it on there four times shorter than we're supposed to. So what if we just artificially extend the length of this output enable pulse to make the command stay on the bus for longer? Well, we can do that with this little bit of code right here.
That pulse is a lot longer now. And look, now we get a data cube signal. Now we get data back from the cop and all the problems just magically go away. So problem solved. But another problem pops up cuz so far I'd only tested it with Lisa OS never anything else. So now we tested Mac works plus Mac OS and here well uh whenever it would get to the blinking question mark screen the system would just the mouse would like freak out and then the whole thing would just turn off. It would just turn itself off. It's like what what could be happening here?
Well, it it turns out it's another cop issue, right? Because soft power and mouse both have to do with the cop.
Probably a cop issue. So, we look into that. We disassemble Macs Plus, see what's actually going on in the code.
And uh turns out it's a timeout related issue. Basically, uh after Macs Plus sends a command to the cop, it expects a response really, really, really, really quickly. And if it doesn't get that response really quickly, for some reason, it's incredibly drastic response is to turn off the entire computer. I don't know why they did it that way.
It's so weird. It makes no sense, but that's what they did. So, clearly, we are timing out. And the reason we're timing out is because thanks to being overclocked, the timeout is four times shorter than it's supposed to be. So, the solution is just patch the disc image of Mac Works Plus with a longer timeout. And now it works. Problem solved. Overclocking works in every single OS that we have. So, now we're getting close to the end, but we still have a few things that we need to get working. So those things are the SEC which it works but it does not actually function inside of an operating system.
It passes itself test but can't do anything in an OS. Floppy controller tons of problems still intermittent and can't read discs. We got that parody RAM problem. Xen and Uniplus both crash. And uh the PCB, you know, the V1 board, it's time to retire this thing. It's got some problems. It's time to make a version two. So, let's start with the PCB because there's some things we can't do till we have a new board. So, all the things we fixed, I won't go over all of these, but there are a few of them.
Those level shifter oscillations, I replaced them with uh just instead of birectional shifters on everything, put unidirectional shifters on the signals where I only need one direction. And then on the birectional signals, use discrete MOSFET based shifters instead of those weird chips that were oscillating. Then replace that EPROM that was half the size it was supposed to be. Fix some labeling. make the LEDs less bright cuz oh my god they were blinding and a few other things too but just minor things, nothing huge. And uh I made some enhancements too, like I added an extra header. I can't show you cuz it's plugged in over here, but um I added an extra header that uh allows you to plug Twiggy drives, a really rare Twiggy drives into the board if you would like. Um also some power stuff because I was dumb and didn't generate 12 volts on board for some reason. Uh, and just some other cool minor stuff that doesn't really matter a whole lot, but you can read it if you want. And here's the V2 board. This is the V2 board. It's looks pretty similar on the hole except I got in white this time because it looks cooler.
So, testing them, everything basically just worked. Nothing really to say here.
Level shifters, everything else. It functioned. So, now let's try and fix the SEC.
The weird thing here is that when you try and access the SEC in the office system, it literally hangs the entire computer, which doesn't make any sense.
Why could it be doing that? Well, it turns out that it's because the priority encoder that encodes interrupts going to the 68,000 processor was just completely broken. It was encoding this SEC interrupt as a non-maskable interrupt for some reason instead of I think a level three interrupt is what it was supposed to be, but it's encoding as non-maskable instead. So the Lisa was just randomly getting non-maskable interrupts whenever the SEC was enabled.
It was just like where where is this coming from? I'm not expecting one of these and just crashing. So the solution here was just to rewrite the um priority encoder to fix that bug. And now it wouldn't hang anymore, but serial still wouldn't work. Like I would type stuff and it wouldn't be transmitted or received. So what's the problem? Well, it turns out I'm an idiot. And I had somehow not only gotten RX and TX crossed up on my board, but also the DTR and DSR handshaking lines. Yeah, >> I don't know how I did that. And I did it on both ports as well as the USB to serial interface that goes to the USB port. I did it everywhere. I don't know what was wrong with me, but some BGE wires and uh it worked. So, um, easy fix on the version three boards, right?
Well, we still need to work on the floppy controller, right? This is where things start to take a an unpleasant turn. I eventually got it working, but very unpleasant. So, remember right now, sometimes it boots, sometimes we get that error 57 with the bad ROM revision.
Um, but uh it certainly can't read discs or well, who knows? But really, it it certainly can't. There's no way it could read a disc right now. Um, an interesting thing about the intermittence of this design is that whenever it succeeds or whenever it fails, it will always succeed or always fail within a single synthesis run of my design. It's only when I change something about the design that it will break or start magically working again.
And the thing I change doesn't even have to have anything to do with the floppy controller. I could change something about the video circuit and the floppy controller magically starts working or magically breaks. Or I could add a signal to my debugger, my ILA, and it could magically start working or magically break. I spent so long on this. Like, what what what could possibly be causing that? I was just so confused. Oh my god. I tried so many different things. I rewrote the entire flying controller four times just from completely from scratch trying to figure out what could be going wrong here. So much stuff. Spent two and a half months just on this trying to figure out what's going on. No progress at all. Just bang my head against the wall until I eventually had an idea. What if it has something to do with clock skew? That whole issue we talked about earlier of clocks being on data nets. I started thinking about it and the floppy controller has a lot of different clocks. And many of them are in fact on data nets. There's one clock that's not 16 mehz that comes straight out of an MMC, but then I'm using flip-flops and a bunch of clock divider stuff and a bunch of weird stuff like that to generate a bunch of other clocks from that. And I'm using other signals too, like DTAC for instance, as clocks in some of my flip-flops. So, I wonder if that's the problem. Wonder if we have so much clock skew that it's just breaking everything.
So, what if we what if we try and go to the strategy of just using the 16 mehz clock as the clock for everything and then using clock enables to um actually clock everything off of those other different speed clocks. Well, that's what I did here. You can see how that actually works in logic here. I wanted to clock this always. This basically creates a flip-flop that does this on every single clock. And I was saying that I wanted to clock it off the address strobe here. But this is bad because this isn't a clock. This is just a data net. So instead of this, if I want to make it actually occur on a clock net, I can do this. I clock it off the 16 mehz clock, but then I only have it actually do anything if there is a rising edge on the address strobe. So I'm basically replicating this functionality here, but clocking it off of a different clock instead.
So I did that with everything and it was pretty annoying because the 6504 core that I used wasn't even written with clock enables in mind. So had to rewrite a big chunk of the processor in order to get it to work like that. But um after I did all that, the intermittence vanished. No more intermittence after all these months of work. It just went away completely. And um I realized there were a lot of other places in my design where I made the same mistake. So I went back and fixed them too. and very occasional intermittence that had occurred in other parts of my design completely vanished as well. Amazing.
Amazing. I was so excited when this happened. It was so crazy. But we still can't read discs. So, progress, but still not quite there yet. So, what do we need to do to actually attempt to communicate with a real floppy drive?
Well, first we need to create something known as the light adapter, which is this board that exists in the Lisa that um adapts the Twiggy drive connector to the drive the connector that's used on a Sony 3 and 1/2 in drive. And it also generates a PWM signal that the Sony drive uses to spin its motor. So this is a super easy system verog module, though like 20 lines long. Easy. Super easy.
Worked on the first try. But then I plugged in a floppy drive. Nothing happened. So this means that even though I have the processor side of the floppy controller working, clearly the actual disc side of things still has some problems. And there were quite a few problems. I hooked up the ILA and don't want to bore you with all the details because there were quite a few bugs here. But um just some examples, there's a shift register. It basically uses the same floppy controller design as the Apple 2, if you know how that works. Um there's a shift register. I basically had all the bits hooked up backwards on it. And I also had a few of the bits reversed on uh the state machine that the uh floppy controller uses. But I fixed a bunch of things like that and it could read a floppy finally and boot from it. And then eventually after a few more fixes, it could write to floppies as well. And three months later, the floppy controller works. Amazing. I'm so excited. So excited. After that par still have those par issues where we just don't have par memory. So what do we do? Well, um probably need to explain what the right wrong par test actually does here. So basically what right wrong par is is it's a test that the boot ROM does that's a lot more complicated than literally just hey let me test the RAM and if I get any parody errors then I know that something's wrong. the the CPU board physically has the ability to intentionally set bad par so that it can test to make sure that the par error checking circuitry is working properly.
That's why it's called a right wrong par test. So, um, what we can do here is we can actually kind of fudge this a little bit by, um, relying on the lucky fact that the CPU board ROM only performs this test on a single address at a time.
It's not like it goes through and writes invalid par to like 30 addresses in a row and then reads them all back and make sure it detects them on all of them. It does it literally on just one address. So what this means is that we can just write some simple logic that literally just remembers the last address that it attempted to perform this test on and then next time that address is accessed generate bad par. So it's a way to kind of get around this test even though we don't actually have par memory. This won't work in any programs that write bad par to a ton of addresses and then go back and read all of them. But it'll work for the boot ROM. So LISA test which is a big test suite for the Lisa. It does write a bunch of addresses. So the test will fail on it, but I don't really think that's a big deal cuz it's whatever parody memory. It's not it's not the end of the world. This worked though. This is this is the logic that implements it.
Once again, pause here or ask me a question afterwards if you want to know more about how it works. But this is the logic that implements that uh right wrong par check.
Only a couple things left. We need to fix and we need to fix Uniplice because those two Unix distributions haven't worked this entire time. So, Xenex, remember the issue here is that after it prints your system information, it kernel panics for some reason. And I did a little more testing and I discovered that actually sometimes that isn't strictly true. Sometimes it can actually get all the way to the login prompt, but it's very intermittent and a lot of times it prints out disk errors all the time. So, what's going on there? Why is it so intermittent? Well, I probed it with a logic analyzer and it turns out that a lot of times X was just ignoring the profile's busy signal entirely. The busy signal basically just says, "Hey, I'm busy." is the profile telling the Lisa, I'm busy. I'm busy reading a disc.
I'm busy doing something, so wait for me to finish before we proceed with our communications. And Xenex was just blowing through that and continuing anyway, even though the profile was still busy, but it was only doing it sometimes, which was the weirdest part.
So why? Let's disassemble X and find out. So, we disassemble it and it turns out that not only will X continue a transaction when the drive says it's ready, but there's also another case where it will just blow through and continue a transaction if the watchdog timer in one of the VAS times out. So, this was clearly designed so that um if a profile transaction hung for a long time, the watchdog timer would just go off and cause it to complete the transaction with an error and return control back to the operating system.
The interesting thing here though is that X never actually initializes the timer. So it's kind of just dead code that doesn't do anything, but it's still implemented in the profile driver anyway. So if you were to use the timer for anything, the profile driver would still time out properly.
But it doesn't really make sense that the timer would be timing anything out because the boot ROM sets the timer up in oneot mode where the timer runs one single time, expires, and then never runs again. Right? So, why would why would X be noticing anything a miss about this timer? Doesn't make any sense. Well, it turns out that the VIA core I was using that I got got off the internet, there's a bug in it where when you set the timer up in oneshot mode, it doesn't actually set up in oneshot mode.
It sets up in free running mode instead where it counts down over and over and over again. So, you keep getting timer interrupts over and over. So, no wonder it was occasionally skipping the busy pulse because we were just randomly getting interrupts. So, I I fixed the uh the via core by just moving one line around and all of a sudden now X worked.
Simple as that. Here's the fix by the way. It was literally just this line was outside of the if statement and I had to move it inside of the if statement.
That's all that was wrong.
Uniplus, our final thing here, Uniplus would kernel panic or I think actually it hung. I think that's incorrect. um it would uh it would hang at the system info screen a lot like X except with Uniplus it would never get further than that. It was always deterministic. Um so I probed tons of different things. It turns out this was also profile related though. The profile has a signal called CRES controller reset which u the LISA can send to the profile to tell it to reset itself. And um obviously given that the Lisa sends it out, it's an output, right? It's an output from one of the VAS. But for whatever reason, once Unipus has started itself up and gotten to that system info screen, Uniplus will set it to an input instead.
Literally no idea why they would do that because it's never designed to be an input. But Unipus would do that for some reason. So on the real Lisa though, that signal floats high and CRES is an active low signal. So on the real Lisa, it floats high. Meaning that even if Uniplus sets it to an input, the profile will still work fine. But on my FPGA implementation, it floats low. So when Uniplus set it to an input, it would hold my profile and reset forever. So you can never talk to it again and things would just hang. So the simple fix there was just to change my code so that it would keep it high instead of low when an input. And now Uniplus would boot as well. And you can see the code for that here, too. But once again, I'm not going to really go into it. So, what now? Everything seems to be fixed. Only thing really left is we need a version 3 PCB cuz remember I made those stupid mistakes with the uh serial port. So, I fixed the serial port, fixed LED brightness again, put a bigger OLED on there for the floppy emulator, which I still haven't gotten working, by the way, but I plan on getting working in the future. Um, then some other less important stuff, but very minor fixes on the V3 boards. And uh this this the V3 is the final like release version of the board, assuming it works properly. And I figured I would just buy like two of them or something and stick the design on GitHub because yeah, why not? But then this happened. I uploaded a YouTube video about it and it kind of blew up. I got an absurd number of views off of it and there were tons and tons of comments of people saying they wanted to buy one.
Like everybody wanted one. I kind of I have to brag about this top one though.
This is Chris Espinosa from Apple. He actually commented on my video. Like that's just I can't believe that.
I was I was in shock when I saw that.
But like so many people so many people said they wanted one. So I was just like, "All right, I I think I might need to like actually I need to like actually manufacture one." And there were also some less positive people like this guy completely roasting my PCB. That was kind of funny. and uh some some other people who uh were complaining about my video quality, which is understandable.
But um anyway, uh I figured maybe I should buy a little more than two. So, thanks to a loan from my dad in the front row here, I ended up buying uh 50 of them instead. And hopefully they work cuz if they don't work, then that might have been a little bit of a mistake. But um assuming they work, uh those will be for sale to anybody who wants one. They were supposed to be here in time for this for VCF, but they arrived Thursday night after I had already left. So, unfortunately, they won't be, but they will be for sale through both Mac effects and Joe's Computer Museum in just a couple weeks for anybody who wants one for $300. A little more expensive than I wanted, but um it's just the way things are with the chip shortage. You can see FPGAs went up by $30 over the course of this project. So, sorry, that's just the way it is. And it will still be open source as well. Um, next steps, of course, I need to get the floppy emulator working still. And then I want to design a second version of the board that slots into an actual Lisa.
So, if your Lisa is battery bombed or damaged in some way, you can just replace all of its guts with one of these boards uh in a completely accurate way. And this will be a more stripped down version that's cheaper and gets rid of all the things like HDMI and USB keyboard and mouse to be more faithful to the original Lisa. Um, and let's do a quick little demo. We don't have a lot of time left, but I do have time, a little bit of time for a demo. So, let me unplug this, plug it into my board here.
I don't know why it's buzzing. Oh, I see why it's buzzing. It's because I need to reset it.
Okay. Wait for this to come on.
It's on on here.
>> It's on on there. Okay, cool. So, if I hit the power button, there it is. That's the Lisa coming to life. Um, THANK YOU.
IS IT in slow mode at the moment?
>> No, it is in fast mode right now.
>> Put it to stop first.
>> Okay, I will. Uh, well, since we don't have much time, let me boot into LOS and fast mode and then I'll slow it down once we are in LOS. Um, so I'm going to boot into LOS here and um, see, I'm using a USB keyboard here, by the way.
Um, and a USB mouse. So, we are booting into LOS and all these are things that people were messing with out on the floor earlier. Um, but yeah, this is fast mode. So, just a little demo of that. If I drag the window around, see how fast it redraws and then I compare that to stock Lisa speed, which you can change on the fly. Watch how slow it is now.
>> Significantly slower. So, the overclocking really makes a big difference. But I don't really know that I have time to show a whole lot more than that. Um, so might just Oh, by the way, if you want to play with this or see it in action or ask me any more questions, I'm at the table with the four leases. This will be on display working. Feel free to come over and ask me anything. And with that, if anybody has any questions, I'm happy to answer them in the little bit of time we have left.
Uh will >> just as a benchmark what was the difference in time between compiling >> Oh yes compiling on >> over good question. Yes. So um if you were if you were at my presentation last year will Will Will was actually part of it too. Um we figured out how to compile the Lisa's operating system from source and it took 9 hours or so on stock Lisa hardware. On here it takes about three hours. So significantly faster.
What um what storage is available to boot all these applications?
>> Oh, yeah. Yeah, good good question. Um there's an SD card slot right here and the ES profile uh profile emulator here.
Just reads a bunch of disc images.
They're just stored as standard files in a FAT32 file system. It reads the whole catalog of images off the SD card here.
And then you can select one from that menu that you probably saw me using at boot time to pick whatever OS you want to fire up. Yeah.
>> Y >> uh you mentioned several times that you made changes to the release of ROM code.
>> So I'm assuming that's the patch version what's on your board.
>> Uh I ended up >> Yeah, I ended up undoing um most of those patches, right? cuz like the patches I had to do for the sec those were because loot back didn't work but now loot back does work so I was able to undo that patch and then same for the right wrong parody test that was patched because I didn't have a solution yet but then I created that solution at the end with the uh just remembering the last address so then I undid the patch after that so yes the version that's in the board it still does have one patch that I didn't have time to talk about but it's only it's only the square pixel version that has a patch the rectangular version is just the uh stock ROM and it's something very very minor.
>> Yeah.
>> Is there an FPGA compatible path to putting the RAM off the board >> to putting the RAM off the board entirely?
>> Yeah.
>> Um >> is that just >> I mean >> sure you could probably I don't know why you would want to put the ram off the board but you what what was your reason for wanting to do that?
What's your limit right now on RAM? And again, it's it would have to do with whole things.
>> Oh, yeah. Yeah. Right now, um it's I got two megs of RAM on here, which is the max that the Lisa can support, at least without modifications that make it incompatible with a bunch of stuff. So, there wouldn't really be a reason to upgrade the RAM if that's kind of what you're getting at.
>> Okay. So, the OS itself is basically hard capt. There's no point beyond >> the physical hardware even is hardcap to that like the MMU can't address more than two megs of RAM without modifications that then make it incompatible with everything other than Mac OS which is stupid. So >> yeah, >> you mentioned several problems that were months long to solve.
>> Yes.
>> How long from beginning to end?
>> Good question. Good question. um about nine months or so in total. I started uh last early late August somewhere somewhere in August and I've been working on it basically until now. So I finished for real like two weeks ago, three weeks ago maybe.
>> What inspired you to do this?
>> Great question. Great question. I don't know. I've always just had a fascination or I guess you could say obsession with the Lisa and uh just think it's hardware or its architecture is really cool and I've also always well not always but lately I've been really interested in FPGA so I figured why not try and fuse the two and see what I can come up with and I was able to come up with something that works pretty well. So, um, no like real hard motivation from the start. It was just kind of this would be pretty cool and I like both of these things, so let's put them together.
>> After this is said and done, what's your next project? Do you have any ideas?
>> Um, I'm not completely sure. I mean, I'm doing I don't want to say much about it because I'm not sure if they would like me to, but um, I'm doing something for Mac effects right now involving FPGAAS that will be released at some point in the future. But in terms of personal projects, I'm not really sure what will come next to be honest. I'm sure there will be something, but I'm not sure what it will be. Like, of course, there's going to be the Lisa form factor version of this board, but other than that, I'm not sure what the next thing will be.
They always kind of just like pop up spur of the moment. I'm like, "Oh, this will be cool." And then I do it and it takes nine months. So, >> what's your PhD dissertation going to be on?
>> Not sure yet. I just finished my first year in the program, so I'm still very early into it. Haven't decided yet, but ideally something to do with FPGAAS.
>> Nobody else.
>> No. Okay. Well, I guess that's that's it then.
Related Videos
Agentforce NOW AMA: Build with React and Salesforce Multi-Framework
SalesforceDevs
490 views•2026-05-28
How agent o11y differs from traditional o11y — Phil Hetzel, Braintrust
aiDotEngineer
450 views•2026-05-28
WEB TECHNOLOGIES UNIT-2 | Degree 4th sem BCOM Computers web technologies unit-2 full explanation💯✅
LearnwithSahera
1K views•2026-05-29
More tests are always better? How to use AI to identify tests that bring little value
Alliance4Qualification
335 views•2026-05-29
Search Algorithms Explained in 60 Seconds! 🤖💨
samarthtuliofficial
218 views•2026-06-01
People of Game of Thrones using JavaScript DOM
AltCampus
296 views•2026-05-30
Introduction to Problem Solving Part - 1 | Lecture 1 | Intermediate DSA
ascensionix
107 views•2026-05-29
So What's Odin Lang Even Good For
TechOverTea
131 views•2026-06-01











