I can now boot my PCW from ROM, instead of using a disk. Unfortunately, it doesn't boot into CP/M or LocoScript — it just boots into my test programme which turns the screen and buzzer on and wait for you to press space bar, after which it inverts the screen and turns the buzzer off. Not at all useful, but an easy programme to write to prove it's really booting from my ROM.
How does it do it?
When the PCW is switched on, first it loads a small programme from the ROM in the printer controller into RAM. Once this is done, it sends 0 to port F8, which tells the PCW to stop reading from the printer ROM, and to use normal memory.
My CPLD watches for this command, and when it is seen it goes into its own special boot mode, where the first page of ROM is in all banks, and execution then continues at address 0002. At this point my code sets up the banks of memory it really wants and then writes to port B0 to turn ROM override mode off.
You can also disable ROM override mode by setting one of the CPLD pins high, so it's still possible to boot the computer normally.
And here's a short and very boring video of my PCW booting from the ROM:
After getting the extra RAM to work, I added an EEPROM as well. After which my PCW started randomly crashing. It's taken several days to get to a state where it works reliably, and I think I'm there now. There were several mistakes I'd made. For a long time I thought it was the breadboard's fault, as when I built my homebuilt Z80 computer then I had a bunch of dodgy breadboards which didn't make good electrical connections. I'm not totally sure this one is fine, but I don't think it's the main problem. I wondered whether I was drawing too much power, although this seemed unlikely. I hoped I hadn't blown up my PCW, but disconnecting my external circuit proved that I hadn't. After fixing a few silly mistakes in the VHDL code and still not curing the crashes, I tried leaving MDIS floating when I wasn't disabling the internal memory, instead of driving it high, and that seems to have worked.
Anyway, I've managed to copy some data to the ROM and have it still be there when I switched the computer off and on again:
Now, unless it starts randomly crashing again, I can get onto the fun stuff, which is making the PCW boot from the ROM, and then adding an SD card.
I bought an LCD screen on eBay about three weeks ago. It said it was UK stock, being shipped from Leicestershire — just down the road from Coventry, so it should have arrived really quickly. Unfortunately, it was actually shipped from Hong Kong, so it only just turned up today. So I had something fun to play with this evening:
However, that's for my homebuilt Z80 project (it's going to connect to a Raspberry PI Zero to make a serial terminal), so I've had to put it to the side for now. Instead, I fixed my PCW's memory expansion.
It took a while. I improved my Z80 test programme, to copy things around a bit more and give me a good idea of what works and what doesn't. As I'd been informed, when I write to a high RAM page it does actually write to a low one instead — it has absolutely no idea that my memory is there. To start with, there was some corruption in the text I was copying though. After connecting up my logic analyser I realised that I'd managed to get the OE and WE pins the wrong way round. I swapped them, and hey presto — still no extra memory. :(
As far as I could tell, the PCW had no interest at all in my memory, connected to the external connector. And then I suddenly remembered that the PCW's memory is connected to a separate set of connections on the gate array, and that there's a mysterious signal on the edge connector called MDIS. Perhaps this disables the internal memory and lets the external memory be used? I connected the RAM_CE signal to MDIS and hey presto — the memory was finally recognised, and my test programme worked.
Now I'm feeling quite pleased with myself! Although my breadboard is looking a bit of a mess:
I tried to add an extra 512Kb of RAM to my PCW at the weekend. I used an as6c4008 static RAM chip to make things easy — no need to mess around with refresh signals or RAS and CAS and address decoding — and borrowed some VHDL code from my homebuilt Z80 project so that it would be switched in when a bank select command was sent requesting banks 32-63. I was hoping that this would be enough to make it work, but it doesn't seem to be.
The documentation I'd seen said that the PCW will use the existing RAM if external RAM doesn't exist, but there's no mention of how to detect the external RAM. I wrote a small machine code programme which switches banks and copies data around, and the data I copy gets corrupted, which seems to indicate that the computer is writing and reading from my external RAM and the internal RAM at the same time.
I started disassembling CP/M (the J14CPM3.EMS file) to see if it could throw any light on the matter, and I think I've found the RAM detection code. Memory location 007F contains the number of 16Kb pages of memory in the system, so I just had to find where this location was written. I found one location, and it is preceded by a loop of bank switches and memory reads and writes, so it looks like it's the right bit of code. I haven't properly worked out the algorithm yet. However, I'd be surprised if there's any code which can tell the gate array not to access internal memory.
(There's also still quite a large chance that my VHDL code isn't quite right.)
As an interesting aside, I was intrigued by how J14CPM3.EMS copies itself into memory. The file is loaded into memory at 0000 and execution starts there too. It then switches banks and copies parts of itself into other locations in memory using LDIR. It also zeroes out some parts of memory using LDIR too. All pretty straightforward so far.
But then it comes to the weird part. There's a routine which copies 128 byte blocks of memory in reverse. It copies 128 bytes from SOURCE to DESTINATION, then it copies 128 bytes from SOURCE-128 to DESTINATION+128, then from SOURCE-256 to DESTINATION+256. At the moment I'm at a loss as to why it would do this. Actually, I'm still not quite sure I haven't misunderstood the code, because it seems such an odd thing to do.
I got an IDC connector today, which meant I was able to connect the PCW data bus to my AY-3-8912A sound chip without having to go through the CPLD. While I was about it I also moved the clock signal onto one of the CPLD's clock pins and took the video signal directly to the video output circuit too.
I had to fiddle with my logic analyser a little — I was expecting to be able to map ports 0xA8 to 0xAB to the sound chip, and have address line A1 connected to BDIR and A0 connected to BC1, but it wasn't quite that simple — I had to invert A0 when A1 is 1.
Then all of a sudden, Head over Heels started detecting the joystick and sound card, and I started to get the most musical sounds I've ever heard from a PCW.
I haven't sorted out the sound output circuit properly yet, so for this video I just connected all the three channel outputs together and fed them through my guitar amp — hence the distorted sound! It was also quite hard holding the wires together at the same time as holding my camera and operating the joystick. So, not the best video you'll have ever seen:
Having said that all I did this evening was cleaning, I then remembered I had some files I wanted to transfer to the PCW. So I plugged in my homemade serial port and got copying.
When I got my first PCW, the LocoScript disk was corrupt, which meant I didn't have MAIL232.COM available for copying programmes from my PC to the PCW. So initially, the only way of copying files across was by using PIP. I can't remember the exact details of how I did it, but I believe it involved sending MAIL232 in hex format over a slow connection, which I then had to compare visually and correct and transmission errors. Then I think I had to write a programme which converted the hex file to an executable — I don't remember if this was in assembler, or Mallard BASIC. Once MAIL232 was working, I was then able to transfer kermit, which is the software I now use.
After doing a fair amount of experimenting I managed to get a good set of settings for making reliable transfers, although I can't go very fast before things start to break down. These are my settings (I'm using ckermit on Ubuntu on the PC):
Type this on the PCW:
A> setsio 9600 int on hand on xon off A> kermit kermit> set baud 9600 kermit> set flow-control off kermit> set file-mode binary
Type this on the PC (I know, sudo is bad. But I keep forgetting to set up permissions for /dev/ttyUSB0 properly):
$ sudo kermit kermit> set line /dev/ttyUSB0 kermit> set baud 9600 kermit> set carrier-watch off kermit> set flow-control rts/cts kermit> set file type binary
To send a file, first I type 'get filename' on the PCW, and then 'send filename' on the PC. To receive a file I have to type 'receive filename' (not get) on the PC and 'send filename' on the PCW.
This evening I was cleaning. I cleaned my spare 3" disk drive. Using isopropyl alcohol and cotton buds I scrubbed away all the old drive belt rubber from the pulleys, and then I fitted the replacement drive belt. I haven't tested it out yet. I should have taken photos, but as I've done this before it didn't seem like something which needed documenting.
After that, I took my PC apart and took all the RAM sticks out and re-seated them, and then disconnected and reconnected all the drives and power, and then squirted compressed air all over the place. Now I'm coughing from all the dust, but hopefully my PC will now stop segfaulting all the time, and I'll stop wanting to throw it out of the window.
Everything else I wanted to do was dependent on getting some more bits and pieces first.
The Amstrad PCW comes with either 256 or 512 Kb of memory installed. And it's easy to upgrade — you just have to add extra RAM chips and flip some DIP switches (unless you have a really early machine, where you have to do some soldering).
But you were also able to buy an external RAM pack (or 'RamPac' as it was branded), which you could just plug in and go. How did the PCW know that there was extra RAM available? I haven't been able to work this out. Does it perhaps try writing to different RAM banks and seeing if it can read the same values back, at startup? I will have to test this theory.
I looked at the motherboard schematic for clues, and found it difficult to even find the DIP switches. Eventually I found them, and they're in quite a surprising place — they are connected to the CS and A0 lines between the printer controller chip and the gate array. The DIP switches change whether the lines are pulled high or low. I guess that when the PCW is operating normally then it drives these lines, and the pull-up/down resistors are ignored, and when it wants to know the switch values it floats the lines and reads their values. It's an interesting way of using a pin for two different things.
Today I was looking up how the PCW's memory management works, when I came across information about PCW joystick ports in John Elliott's very useful PCW Hardware document. And I remembered that I have a joystick which someone gave me, which I've never used. It seemed like it would be the perfect thing to add to my PCW to test out some really basic CPLD code.
A PCW joystick is very simple — it's pretty much just a bunch of microswitches which connect one of the output pins to ground when the joystick moves in the appropriate direction, or the fire button is pressed. So you just need to add pull-up resistors to each of the outputs and feed them into the CPLD. In the CPLD this code is sufficient to implement a Kempston joystick interface:
if clk4'event and clk4 = '1' then if iorq_n = '0' and rd_n = '0' and a(7 downto 0) = "10011111" then d(7 downto 0) <= "000" & (not joy_fire) & (not joy_down) & (not joy_up) & (not joy_left) & (not joy_right); else d(7 downto 0) <= "ZZZZZZZZ"; end if; end if;
And it works. I just played Head over Heels with a joystick for the first time. To be honest, I think I prefer using the keyboard though!
As you can see, the setup I have so far isn't pretty. The CPLD board is sitting on a plastic bag to make sure I don't short it out on anything. The video circuit is at the top right, with the red and black wires jammed into the S-Video socket on the Video Converter. I also had to improvise a socket for the joystick connector. But hey, it all works for now, and I can make a PCB later (or put it on stripboard).
Today I went back to trying to get my CPLD to work. I suspected that there's probably a problem with my soldering, so I wrote a configuration which set alternate pins to 1 and 0, so that I'd be able to test for shorts. I loaded it and tested all the pins (by connecting them to an LED), and they were okay. So I switched the 1s and 0s and tested again, and this time I found a pin which should have been 1, but was set to zero. It was next to one of the ground pins, and although the pins looked ok through a magnifying glass I plugged in my soldering iron anyway, and ran it between the offending pins. Then I tested it again, and the pin was working properly. I plugged the PCW in, and it also worked properly. Hooray!
I haven't done anything useful with the CPLD yet — so far I just have an LED connected up to A7 which flickers, and I've routed the VIDEO and NSYNC signals through so I can still use them for the external screen.
Speaking of the screen, here's a random picture of Head over Heels on it, after going through the CPLD.