 So I would like to first apologize for all this hardware, but I think a retro talk should have retro stuff working, so we'll show it after it. So we'll start presenting this. So the agenda is basically to define the motivation for doing this thing, mainly what are the X cartridges, what were the objectives, what were the challenges, and the solution, the hardware I designed, the firmware, and lately the software that I implemented. Okay, the motivation was basically to, I started my knowledge of computer programming by starting to program ZX Spectrum in 82. I started by learning basic, and from that time I was not aware how to do stuff in assembly. So the idea was I wanted to do something in assembly, but the time went by and then I went to college, university, got a computer course, and a few years later, like in 2015, I decided to make this game in assembly. So I needed something to be able to do this game and to be able to do larger games because the Spectrum is limited to 48K RAM, right? So this spawned the idea, and then someone created this competition and I went to do a game based in assembly. But my real goal was to make a part of this game, it's called POW, Prisoners of War, that was in the arcade. It was called Datsugoku in Japan, and it was called Prisoner of War in the UI, EU, sorry, and in the USA. So what's the big difference on these goals? This is the ZX Spectrum 48K as you, I suppose you all know, and this one is a picture of a board that I own of POW. As you can see, it has a lot of different specs when compared to the mighty ZX Spectrum, right? So there is a big difference either in CPU, ROM, RAM, and video capabilities. So if you can see there is, this game uses something like 800KB of ROM, and the Spectrum we have like 16, and we don't own it, right? So we need to fix that. And the rest I think we can just fake it and try to get to it. So that's what we usually do, right? So first tests were to find if it was possible to convert this game. So this is the arcade marquee, the thing that is on the top with a backlight over it, and this is my experimental images for the ZX loading screen. And this is the arcade first level, it's a prison area, and this is my idea of what it will look like on the ZX Spectrum. As you can see it's not finished, and the game is large, I haven't done the rest yet, but these were the goals. So having such a large requirement for ROM means that we can't get away with the simple 48K that we have on the ZX Spectrum. Obviously we could go to a 128, but it's not enough again. So maybe we could just use a cartridge that actually replaces the internal ROM. But the problem is that the ROM has a maximum size of 16KB, right? It is not enough again. And also it requires an interface adapter, but most of the people from back in the day know that there are at least these three ones that were very well known that provide the required interface to be able to use a cartridge. Officially there were only 10 cartridges that were released, there are something like 10 more that were unofficial or never released that you can get online if you want to try it on an emulator. So what were my objectives? So I wanted to create a cartridge with electronics on it. I wanted to support at least 512KB of ROM. I need to map that ROM to the Z80 address space which is only 64KB, right? It only has 16 bits, and so it clearly doesn't fit, right? So obviously we have to do something like paging or similar. And I also wanted to have PCB and chips must fit inside the original case. The idea is to either recover old cases and use a new hardware inside, or also support the cloning of the old hardware and also the case. There are many of these small cartridges out there, but they are expensive. So if you can buy one or replace one that is damaged, that's also a goal of this project, okay? So what were the challenges? If you haven't noticed already, there are a lot of physical limitations. The first one is 16KB, right? Of the address space, so it goes from 0 to 3FFF. And the connector on the spectrum, maybe I could show one, sorry. So this is one of the cartridges, so the connector is something like this. It has a big drawback because this was designed only for ROM. So Sir Clive Sinclair, as his usual self, decided we are going to save some bucks and not put the required lines. So read, write and IO signals are not present, which is a problem hard to solve. And I also wanted to have the original case or make the hardware that fits in the original case, as I said earlier. And the problem is that after measuring it, there is 2.2 mm of space between the case and the board on the backside and 1 mm on the front side, which is a problem. So issues to solve, how to trigger bar-slash-small-staging. Can you hear me better? This thing is falling off. Sorry. This is this actual clip work. Maybe I'm moving too much. Okay, so this is the issue to solve. And another one is I need to find components that actually fit inside the case. And the only ones that are available with that small clearance are SMD chips and not all SMD chips fit. So we have to find ones for it. Okay, this is probably the most important slide so that you understand what's going on. If you can look at it, this is a usual instruction opcode fetch on a Z80 processor. So it always has that 4 phases, T1 to T4. And you can see that the spectrum always does the instruction fetch with the PC on the address space. And then to support dynamic memory, also there's a refresh address that is automatic. So every instruction implies a refresh address that increments every instruction. Only this first seven bits, but it does increment. And the refresh address is calculated based on two registers, the I register and the R register. The problem, as I said, is that this is the interface of the cartridge. And we don't have all the other lines except these ones that are marked. So we only have address, date, and the memory request. Because that's what Circle Life Sinclair found to be enough, which is not for us. So what other things could we think about? Maybe we could use the IO request and solve our problem, right? But Circle Life Sinclair again decided no, we cannot have that. We have to earn more money by putting less stuff in the machine. So what kind of solutions can we find so that we can create paging triggers when we can actually not communicate with the thing, right? So one idea. Maybe we could use a fixed address and just hack it, right? Or maybe we could do like repeatable read. So if I read a specific address a few times, maybe I can get away with it. Or maybe use a pattern sequence like an eProm. So an eProm or a flash chip has a sequence to protect against write. So if we do a sequence of reads in a specific pattern, it unlocks. But that is really complex. And we would need to know that is a read. We don't know, right? We don't have the read or the write. So maybe we can exploit CPU features. Like what? Ports out of the question, right? Maybe we could use the eye register. It's messing up with the refresh, right? And maybe we can use the R register, which also messes with the refresh. Or maybe we do something like I'm going to use an improbable address. Something that will probably never happen when you're running an application. Is that possible? I think it is. So I went for it. And the solution I found is that if you remember correctly, every instruction does up code fetch, which is an address, and then does a refresh address that indirectly we can control, right? So if I can somehow define that I can read the same address three times in a row, which seems impossible, right? Because the other guy is controlling the registers and is incrementing. But it's possible. And it's possible because of this. Because there are two instructions called LDI and LDD that transfer a byte from one location to another location that does exactly this. So they copy it from one to the other. So there are two addresses that are being used, and I can force them to be identical. And if I do that, I still need the third access, right? So the next instruction is my next access. So if the two addresses that I use on the first instruction are exactly the instruction of the next address, bingo, right? I have three identical addresses. So what do I need more to make this work? I need a way to inform the cartridge that I want page X and not some random page or the next page or something like that so I can have better control. So what I need is something that will give me a parameter, an input. So what I did is, well, the next instruction has an opcode, but what comes next? The refresh address. Can I change it? I can with the I under registers. So now I started thinking, how am I going to implement this, right? So let's start adding up what we need. So we need two pins for VCC and ground. We need one bit for bus memory control line. This is available in the cartridge, right? And I need one bit to control bus ROM so that I can disable the internal ZX, internal ROM, so that it uses mine, like the cartridge does. I have eight bit data lines, 16 bus address lines, three bits that I need extra because I have 512 kilobytes which needs at least 19 pins, right? So three more than 16. And I need three extra bits just to control the flash. So chip enable, output enable, and write enable. Well, if we add all this up, it's like 38 pins. That's hard to get on a CPLD and it occupies a lot of lines. And as you may probably know or I can show you, here is one, there isn't a lot of space in this, right? Even by today's standards. So this is a chip that is glued with epoxy and that's it. That's why it's so thin, okay? So I can't route too many lines over there because it won't fit. Okay, so I have to do something, right? What? I can start cutting off stuff I don't need. Do I need a data line? No, I can do magic, right? It's gonna appear. Why do I need that? Well, but I would like to cut those 16 bits also because they are too many, right? I can cut any of the rest, right? I need them. So I can cut the 16 bits, well, kind of. The problem is that I need those bits to determine my special dress that I can repeat three times. So if you look at the dress space of ZX48K, it's actually 64K because 16 kilobytes is wrong and we know that because the last bits of the address is 00 and all the others is wrong. So we can only mess up with the first part, right? So what can we do? Maybe we could force that if A15 and A14 is 00, we are in business. Actually, we can't because every excess would give us that to wrong. So maybe you can have a special address and include like A7 to A0 with A15 and A14. So like the ULA does in a weird way. So what this means is that I would have something like this which is a lot less than 16 bits. It's 9 bits, right? Well, not really. Maybe we can simplify this, okay? Do we really need this? Actually, we don't because we only need to know that it's a wrong excess but we don't need that to control the flash because the flash can read directly those two bits, right? Because as you can see, so I didn't need this. Let me go a little bit further. So if you can see this is the address space of the flash. So the last two bits of the 16-bit bus are in fact part of my flash page address, right? So I don't need those to enter directly to my device that controls the logic so they can go directly to the flash. I just need a way to trigger from them. So I cut the buzz data lines but now I'm using the 8-bit buzz address lines to do this. And I said I don't need the data lines, right? Why? I don't need them because I can pass all the information I can using the refresh address, right? So I'm going to use the refresh address as input so I don't need those lines. So I'm saving a lot of bits. Currently, I will map the A4 to A0 when I'm giving the special instruction to change page to the flash page area, okay? So I'm going to use this part, the A0 to A4 to give that page address. Now, this is a simulation of the logic I created and on top you see green, it's the input lines and then you see blue and kind of... I don't know that color because it's different here but the other ones were blue and this was like magenta or something but it's different here on the screen, okay? So if we look on top, we have A15 and A14 which are the ROM addresses, right? What it identifies. So in dark gray, we see that it's a ROM address. They are both zero. Next, we see that they are swapping so it's RAM address. So I don't want to... If you see here, there is a chip select, okay? That's the one with the magenta color that is only enabled when I'm accessing ROM, okay? When it is accessing RAM, I don't want to mess with it, right? So that it works. And now here is the special case. If you see the white line, it's the trigger address. So if you look closely, I had three clocks on top where the address didn't change. So it's my trigger, right? And the yellow line represents the input, my parameter. That's the flash page I want to select, right? Then I have in green bars another access to ROM but it's not special in any way. And then I have another access to ROM again and this was just to try out if mixed accesses would work. And then here we have another example. This was... I have a flash, right? I can read it already with this idea but could I write for it? Could I find a way to make it work? So the idea is I'm going to use the A7 address bit to signal me a flag if it's a read or a write. And that has a problem at least on this version because the flash chip doesn't like the chip select or the ROM something. I can't read it right now from that. It doesn't like it to be swapping. It must be stable. So what I did is I still had space so I added two more inputs with the advantage that this will allow me to put the control command in the I register, which is the Iger part of the address. Okay? So anyone scary of assembly? This is really easy. It's commented, right? So the major, the most important thing is down here. So we have an LDI, it's the trigger sequence address and then we have the load A with something. So these two instructions is what are going to do the job. The idea is that every instruction that I do the R register will increment. So I'm going to fool him and before I calculate the change until I get to the LDI, right? To LDI and LDA and I will put minus 2 because as soon as I write LDR comma A I'm going to do two extra instructions, right? With a special twist here because I need the R to be correct on the second instruction and there is a twist. LDI has a special instruction that increments twice. So that's why it's minus 2. So this will implement this. Now the smart guys in the room would tell me there's something wrong with this. There are smart guys here, right? Well, I haven't put it because it's a lot of work to explain in detail in such a small area on screen but first I need to set the I register which is not there, right? It's usually fixed at a value but we can change it and there is another important aspect which is we need to disable interrupts, right? Or else they will mess up my timing, right? Okay, so this is the version for write. So it's very similar. We have to do an increase here and an instruction that just swaps the A and F, the flags register so that you can save the data while we change the R register but actually it's just the same until we find here which is a write and not a read. But it will work the same. I've tried it and it was able to read the flash type which gives me the size. So what do we need to do to make this work? We need to define a circuit diagram and I decided to use wire wrap prototype boards something from the past so it's suitable. Data analyzer for debugging define a PCB board and lately and also design a case. So let's go and do this. So this is a very simple circuit. We have here on top it's the connector that represents the cartridge connector. Here we have the CPLD which is an ATF 22 V10C and we have the flash chip from etnal that is hard to find because parallel chips, parallel flash chips are currently very hard to find. So this is my prototype that you can see here and that you will see working and it was done in three phases like three slots because it's easier. This mass you see here in the middle is the wire wrapping or a way to build a circuit without having to pay for the cost of the PCB. We use something that is already made and then route the rest to our shelves but sometimes this gets really messy when you have to take one of those wires that is behind the others and another concern is that if the speed is very high every loop that you do to fix those wires will make it work like a coil so that's the problem. This is hardware debugging and you can see on top, I hope, there are three identical addresses because I haven't all the addresses here but the most important ones. So I have three addresses and after in that green line you can see that the green line is triggering one position too far so it should be one before. Why does this happen? Because the guys from the manufacturer from the CPLD forgot to mention that there were flip-flops that were master slaves so it delays one clock cycle. So this was the first version to see if the board would fit with the chips. This was the version 1.0 with the planes already defined and this is the board that was manufactured. I have some here if you then want to see. So it has small heads on the front so that you can see what's going on and the chips is on the backside where we have the 2.2 mm space that is enough for the chips. So this is the original board that I've shown you. This is the 3D representation of it so that I could model the case and next step and then I also want to do the part of the rubber because there is this red thing that is rubber to protect the connectors. So this was my first version of the firmware. The idea is to run code and see if the paging is actually working. This is a simple gif. It's not running anything right here but it's to show that we can use the 512 kilobytes not just to have a bigger game but a list of games that we can just dump to RAM so that the Z80 can start running the old games. This was a page I designed to be able to debug also the memory that is on the ROM that is not implemented yet. So what do we need for software? Well, we need to make a proof-of-concept adapt existing multi-level game R-type was my choice to thank Bob Pape which was the original guy that transformed this from the arcade to the spectrum. He participated and helped to do this. So I have to thank him. Thank you very much, Bob. So what we do is copy what is on ROM to RAM and run and when we have the need to load because this game has eight levels and every time you play after level one you have to go back to level two and that means you have to press the tape wait five minutes or something like that and if you die, you have to go back to level one so back the tape and do it again. That's what this is going to avoid, right? And we can do some extra stuff when we do this so we can implement invulnerability and load level with this so we can try, go and try the game. So let me try and show you sorry for being hidden it's top secret. We have to change the video, sorry. Oh, by the way, this is the link to the github with the code for NoxROM for the firmware and it will also have the code for flashing the CPLB Do I need to change something? Maybe power up. You know, this is my special tool. It's a wand. So if I can fit it here I told you guys it was magic, right? And now we have to clean down a bit but now we can play this game, right? And it's a special NoxROM version the game is running. Okay, now if you press enter you can select what you're going to do fire and detach and you can start playing, right? And I can't see, I can't say the other word but I can see what I'm doing, right? Right now, so I'm gonna die. Great, so how do I fix this? Easy. I can boot again in vulnerability mode. Okay, let's select some keys again. Sorry. Up, down, fire, and this one, right? Let's hope I left the key pressed enough. Let's try and die. Maybe down. Hmm, doesn't die. Great. So this is great. If you can't play, you can go to the top level. Okay, and now what if I want to load the next level or the last level? I never reached the last level, right? It was so hard. So let's try that. So resetting again and for example let's load level three. Okay? By the way, Bob was nice enough to tell me the codes to do this. I didn't have to search the code for it. So let's go to level three directly from the cartridge and I can load any level and this is great if you want to get a good eye score because you can have passed the same level several times for example if you reached the last one. But it's working. So the concept works. It's possible to communicate with the ROM and expand its size. I'm also planning to... test boards like these ones. Don't mind, don't mind. It didn't break. And this is an original box, a case, sorry. And I'm going to just see if it fits. And it fits, right? It's still missing this, right? But I'll get there. And I have to do the board like 3D printed or mass produced or something. I don't want to make... wait 50 minutes for a case, right? And here is a warning for you all. So I made the design and trusted my libraries. So the layout of the chip should be correct, right? At least we hope so. But what happens? I don't know, you probably can't see. Maybe I could find something white. The chip is here, right? And the contacts are there. Can you see it? Probably not, but it can come up and see because I'm done. So be careful when you do draw this so that it doesn't work like you expect. Thank you. Any questions? What's your background? I'm a software developer by profession. So how did you get into hardware? Well, I like hardware a lot and I started studying it on my spare time for a few years back. So you started with a book about CPUs and then... No, I have a background in programming so I know what CPUs I like and how they work. So I had digital classes on the university, right? So that was when I learned the more detailed stuff. But what you need to know is already on the web. So if you find it, you take a few iterations and you get there. Yeah, before it wasn't possible yet to go in the RB sometimes you'd find a book. Yeah. I've got a proper stupid question. The image that you took, although it was taken off the RK machine, why did you not need to patch it to access other regions of memory? Or did this game not use all the 500? The idea... No, this game uses like 120 kilobytes, okay? I'm planning to do POW that will use the 512 kilobytes, okay? But this one doesn't use that. Okay, so you didn't need to rewrite the binary in any way? No, I did. I did. But with the help from the guy that did it in the beginning... Oh, so he actually helped you... He helped me to split the code in parts and then I implemented the part of the paging and loading, et cetera. And everything was untouched except the small item that he wrote like Noxrom special version, okay? So it was a collaboration effort. Pretty cool. There is one more here. They don't need to be all over each other. This is the original. This is a pristine one. You can also print a rubber. Yes, you can. It doesn't feel the same. It doesn't. It doesn't feel the same. But it's not hard to study yourself and do an old... It will implement blow. You know how to... You have to blow. Yeah, this is not Atari. It doesn't have the same problem. It doesn't have the same problem, yes. Very impressive. So it's just hacking, right? It's just hacking. So we have to... But this is... It's the same. It's the same. Yes. I have this one. But this driver cuts the image like two characters on the left. So I use that one. But it works on both. Okay. Okay? I can show you. Well done, by the way. That was an amazing work. One small space room. I'm no more than was designed for. Yes. Actually I was trying to see if someone would ask me why haven't I used something like DivMMC or there are other interfaces that allow you to load from SSD card, right? So why can't we use that? We can. But the problem is that you load games that only fit in 48K. I can do a game that is 512 kilobytes plus 48K of RAM, right? And I also can use this on Z80, ZX80, which is the ship that only has one kilobytes. How are you talking? Okay? So the firmware has to be adapted because the screen rendering is not the same, right? But it will work. Eight kilobytes of RAM. Let me see if it works without the one. No, no. Maybe, maybe. Yeah, it's working. The problem is that there is a small connector on this board. So sometimes it does a bad connection. We just put the magic one and it works.