Wednesday, 3 January 2018

MEGA65 Boots GEOS ... From a serially attached 1571

Following on from our recent success in getting the MEGA65 to work with real disk drives (my half-dead 1581 not withstanding), Falk, one of our developers, decided to give it a bit of a stress test: Could the MEGA65 handle the timing sensitive fast-loader that GEOS uses?

Well, the answer is in the video below:


There really isn't much more to say, except that I know I have a couple of sprite rendering bugs to sort out (on today's list, all going well).

Tuesday, 2 January 2018

The detective work in getting International Soccer running on the MEGA65, direct from cartridge

This blog post was written as I went through the process of getting International Soccer working on cartridge on the MEGA65. It is written in the present tense, as I went about solving each problem along the way, so that you can get a sense of some of what is involved in making and debugging the MEGA65.  The whole process took about 4 days, mostly due to the time it takes the synthesise (compile) the FPGA program.  But anyway, here it is.


My current challenge is to see if the expansion port works electrically, and what changes we need to make to it, if any.

(Note that the MEGA65 has a C64-compatible 44-pin cartridge port, not a C65-compatible 50-pin cartridge port. We believe that improved compatibility with C64 accessories is more important that compatibility with non-existant C65 cartridges. There are a few other ways that we have worked to improve C64 compatibility in the MEGA65, such as ensuring that RMW instructions retain their old side-effects that are often relied upon for clearing VIC-II interrupts -- but that's a topic for other blog posts.) 

We already know that we accidentally made /EXROM and /GAME bidirectional instead of inputs, but that is only a minor inconvenience, as we can work around that by scanning them as inputs periodically (their direction is switched together with a bunch of other bidirectional signals on the bus, otherwise we could leave them set as inputs the whole time).

A while back I implemented an entire cartridge port framework for the MEGA65, and tested it with a dummy cartridge implemented in VHDL, with both ROMs and I/O area expansions.  So I know that, in principle at least, it should work.  But let's just go over the theory of how a cartridge works on the C64 first.  We will stick with simple cartridges, and only C64 mode cartridges, not C128 ones or C65 ones, partly because that covers most cartridges, and partly because the MEGA65 will support only C64 and MEGA65 cartridges.

The cartridge signals what resources it has to the C64 via the /EXROM and /GAME input lines. These are internally pulled up in the C64, and cartridges connect them to ground if they want to activate them.  You can read more about their effects here, but a quick summary is:

/EXROM = 5V, /GAME = 5V gives the normal C64 memory map, as when no cartridge is connected.

/EXROM = 5V, /GAME = 0V gives "Ultimax" mode, where most of internal RAM is banked out, and $8000-$9FFF and $E000-$FFFF are provided by the cartridge. Mostly of interest only if you want to make a cartridge that replaces the KERNAL in a C64.

/EXROM = GND, /GAME = 5V gives a single 8KB cartridge rom at $8000-$9FFF

If both /EXROM and /GAME = 0V, then $8000-$BFFF is a 16KB ROM provided by the cartridge.

In most cases, the contents of the CPU IO port at $01 controls which of these ROMs are actually visible, versus the RAM underneath.  The signals from $01 and from /EXROM and /GAME, together with the upper bits off the address that the C64 is trying to access at any point in time go to the PLA chip in the C64, which works out which chip-select lines get activated, and hence, what memory the C64 sees at that loccation at that time.

Now, for cartridges, the external ROMs also have chip-select lines, /ROML and /ROMH, which are driven to 0V by the PLA in the C64 when trying to access the ROM.

For cartridges that provide I/O expansions at $DExx or $DFxx, the process is similar, except that the C64 doesn't need to be told, it simply pulls the /IO1 or /IO2 line to ground, if either of those areas are being accessed.

All up, this makes for surprisingly simple electronics in a cartridge.

The MEGA65 supports all those signals, and also allows the Hypervisor to disable a connected cartridge, by refusing to take the /GAME and /EXROM signals into account, and thus never activating the chip-select lines for them.

For convenience, and as part of the mechanism for supporting much larger capacity cartridges on the MEGA65 (a topic that will be covered in more detail another day), the full 64KB of address space exposed by the cartridge is able to be accessed at $7FFxxxx.  Accesses to $8000-$BFFF in that space will activate /ROML, while those to $E000-$FFFF will activate /ROMH, and of course $DExx and $DFxx accesses will activate /IO1 and /IO2.

This makes for convenient cartridge testing, independent of the memory map, because we can simply issue an access request to the cartridge port.  Trying this, I found that there appeared to be activity when accessing $DExx and $DFxx. So far so good.  However, the address decoding for $DExx and $DFxx is somewhat special, as it appears in every memory bank when IO is visible. However, a quick check revealed that it also attempts to access the cartridge port when I use addresses $7FFDExx and $7FFDFxx.  This is expected.

So at this point, I know that the CPU can delegate memory accesses to the cartridge port, but not whether anything sensible happens there. Also, critically for cartridges, I don't know whether the /ROML and /ROMH accesses are also being sent to the cartridge port.  Attempting to access $7FF8xxx or $7FFExxx should reveal that -- and indeed, this works also.

So now we know that all of the cartridge chip select lines are being driven.  Quick checks revealed that R/_W is floating high to read, and that the 1MHz phi2 clock.  The DMA line is floating high, indicating that the bus is not unavailable due to DMA.  That leaves just the BA signal outstanding -- which is also floating high, which tells the bus that the VIC-II doesn't need the bus for anything (this is in fact always high on the MEGA65, because the VIC-IV doesn't use the cartridge port for anything).

Thus, all the conditions are ripe for a cartridge to be visible on the expansion port, and for us to be able to read the data from a ROM or IO bank in it.  Time to insert a cartridge. In thtis case, an exemplar of the classic "International Soccer"



Quick initial attempts to read memory locations $7FF8000, $7FFA000, $7FFDE00, $7FFDF00 and $7FFE00, however, showed no signs of data.  At this point, I don't know if the cartridge is not presenting data, or whether the MEGA65 is not taking note of the read data.  Sticking the oscilloscope onto one of the data lines, and accessing $7FFE000 and above, I can see signs of data:


That we are seeing varying data indicates that we are able to drive the address lines correctly, something which I was also able to confirm using the oscilloscope.

There is something a bit odd, however, in that the data lines are resting low in many cases, rather than floating high.  I need to investigate the cause of this.  It might be that we need pull-ups on the data lines, or it might be that something in the cartridge is continuing to drive the lines.  Removing the cartridge has the lines continuing to rest low, which means it must be the lack of pull-up resistors.

This in fact points to two problems that we need to solve: First, the lack of pull-ups, and second, if the lines are sitting low, and we see data being read from the cartridge, why on earth we are not seeing the data being read by the CPU? Is it a timing/logic error, or are the data lines not able to be read for some reason? Again, in the absence of pull-ups, the data being read should be all zeroes, but what I am seeing is all ones.

One challenge to finding and fixing these bugs is that it takes several hours to resynthesise the FPGA program, even if making just a trivial change.  Thus, it can be helpful to have a minimal FPGA program that allows us to probe some of these pins, and see what we can see there.  This allows iteration taking only a few minutes a time, instead of several hours.

Thus my next step is to see whether I can read from the data lines on the bus, so that I know whether the problem is there, or whether it is in the logic of my expansion port controller in VHDL.  To make my life as easy as possible, this program generates a simple VGA frame, and displays incoming signals as white or black squares for 1 or 0. It also allows setting upto 256 different signals, some of which I bound to the cartridge port control and data lines. You then just use a joystick to move around and toggle the lines.

The display of me using it to read a byte from an International Soccer cartridge can be seen below:


The top white and green row is the cartridge port control signals.  White = 1, green = 0.  They are, from left to right:

cart_ctrl_dir  - Sets the direction of R/W, /ROMH, /IO1, /GAME, /EXROM, /IO2, /ROML and /BA to be output if set.
cart_data_dir - Sets the direction of the data lines to output if set.
cart_haddr_dir - Sets the direction of the upper address lines to output if set.
cart_laddr_dir - Sets the direction of the lower address lines to output if set.
cart_phi2 - Directly sets the phi2 clock line.
cart_dotclock - Directly sets the dotclock line.
cart_reset - Directly sets the /RESET line.
cart_nmi - Reads the /NMI line.
cart_irq - Reads the /IRQ line.
cart_dma - Reads the /DMA line.
cart_exrom - Reads/writes the /EXROM line.
cart_game - Read/writes the /GAME line.
cart_ba - Reads/writes the /BA line.
cart_rw - Reads/writes the R/_W line.
cart_roml - Reads/writes the /ROML line.
cart_romh - Reads/writes the /ROMH line.
cart_io1 - Reads/writes the /IO1 line.
cart_io2 - Reads/writes the /IO2 line.
cart_data_en - If 0, connects the data lines between FPGA and the cartridge slot.
cart_addr_en - If 0, connects the address lines between FPGA and the cartridge slot.

Below this line are a couple of white blocks. They just mark where /ROML and /ROMH are in the line above, for my convenience.  Then there is a blank and unused line. The far left blocks on this line if set (which they are not) would tri-state the address and data lines, respectively.

The fourth line has the address bits (bit 15 is on the far left) in white=1 and green=0, a gap and then the data bits (bit 7 on the left).  These are the bits that the FPGA is outputting (or wishes to output, if the _en and _dir lines are not set correctly).  The fifth line, in red=1 and black=0, are the values read from the cartridge port, if it is connected. More precisely, they read back whatever the FPGA has on those pins, whether connected or not.



With this VHDL test setup working, I was able to quite quickly establish that the problem was real - I couldn't drive or read the real pins at all.  This turned out to be because I had not connected the _en lines. After fixing that, I was able to obtain the picture above, where it can be seen that memory location $FFFA has the value of $9F on this cartridge, when /ROML is active low.  This corresponds to address $9FFA

Better, once I had corrected the _en bug in the MEGA65 VHDL, I could now read the contents of the ROM at $8000:

.M7ff8000                                                      
 :7FF8000 27 80 A8 80 C3 C2 CD 38 30 00 40 C0 40 C0 40 C0
 :7FF8010 40 C0 40 C0 40 C0 40 C0 00 80 80 10 10 10 10 20
 :7FF8020 20 20 20 40 40 40 40 20 84 FF 20 87 FF A9 2F 85
 :7FF8030 00 A9 00 8D 0E DC 8D 1A D0 A9 7F 8D 0D DC AD 0D
 :7FF8040 DC AD 19 D0 8D 19 D0 A9 25 8D FE FF A9 91 8D FF
 :7FF8050 FF A9 9C 8D FA FF A9 80 8D FB FF A9 00 85 6F A9
 :7FF8060 80 85 70 A0 00 B1 6F 91 6F C8 D0 F9 E6 70 A5 70

Further hunting around in the $7FFxxxx address space, I can see that this cartridge has 16KB of ROM at $8000, and 8KB at $E000, and indeed, my test program was reading the correct value:

.m7ff9ff0                                                      
 :7FF9FF0 85 75 68 85 73 68 85 72 4C FE 9F 68 68 68 A9 00


That is, $9F is indeed at location $9FFA in the ROM.

So, this is a really nice significant step forward, in that I can correctly access the memory from the cartridge.

The next trick is to get it enabled in the computer's memory map.  The Hypervisor on the MEGA65 has the ability to disable cartridges in software, so that they don't affect the C64 mode memory map (although they continue to appear at $7FFxxxx).  This is the default mode in the current version of Kickstart.  To enable the cartridge, one just needs to set bit 0 of $D67D from within the Hypervisor (if you try to touch this register from user land, it instead causes a trap to the Hypervisor).

The only trouble is, when I set this bit, the cartridge didn't suddenly get mapped into memory.  I also realised that I hadn't added any means to examine what the CPU thinks the /EXROM and /GAME lines are set to.  So, I have added these in, and resynthesised the design. As sometimes happens with VHDL for me, although it is deeply unsatisfying, the act of putting instrumentation on a signal seems to suddenly make it work. 

Anyway, these signals are now available to the CPU, and when the Hypervisor enables cartridges, the ROM suddenly maps at $8000-$BFFF as it should, and I can trace it single-step through its startup process.  However, it wouldn't start the game.  Tracing further, I found that it copies the ROM to the RAM underneath, which is not uncommon for cartridges to do. Seeing this, I checked to make sure that I had this implemented for cartridge ROMs, but apparently not. It works for the standard ROMs, but I had the cartridge memory space mapping for both reads and writes.  Again, a quick fix once I knew what the problem was, but again, I have to resynthesise to test again, which probably means waiting until morning.  But now I am getting excited -- everything looks like this cartridge should work when I try it with this fix.  We will see whether my excitement is misplaced or not.

Hmmm... Well, that managed to get the cartridge copied into RAM. Now, however the cartridge is getting stuck in a funny little loop:

$8211 98      TYA
$8214 91 1D    STA ($1D),Y
$8216 11 1D    ORA ($1D),Y
$8218 C9 CE    CMP #$CE
$821A D0 F7    BNE $8211

The vector at $1D points to $0000, the data-direction register for the 6510, and Y=0. Thus, this has the effect of ... well, to be honest, I am not quite sure what it is supposed to be achieving.

So I started tracing again from reset.

The cartridge entry point is at $8027, which calls some KERNAL routines and begins to setup memory, and copying the cartridge to RAM as described above, it then switches out the ROM, and continues running the cartridge from the RAM beneath at $8074:

$8074 A9 05    LDA #$05
$8076 85 01    STA $01       ; Bank out cartridge  ROM
$8078 58       CLI
$8079 A0 18    LDY #$18
$807B A9 00    LDA #$00
$807D 99 00 D4 STA $D400,Y   ; Clear SID registers
$8080 88       DEY
$8081 10 FA    BPL $807D
$8083 A9 8F    LDA #$8F
$8085 8D 18 D4 STA $D418     ; Set SID volume
$8088 20 43 AC JSR $AC43
$808D 20 40 AC JSR $AC40

All is fine until it gets to these two instructions.  The trouble being that $AC43 appears to be in the middle of an instruction. At first I thought that maybe the copy loop had failed for some reason, but comparing the ROM and RAM sections of memory, and also double checking that this matches the memory actually mapped by the CPU there, it all looks the same:

.mac40
 :000AC40 69 40 85 87 A9 00 85 3C 85 3F B9 A1 C1 85 3A B9
.m701ac40
 :701AC40 69 40 85 87 A9 00 85 3C 85 3F B9 A1 C1 85 3A B9
.dac40
 :777AC40 69 40 85 87 A9 00 85 3C 85 3F B9 A1 C1 85 3A B9
.


The sensible disassembly of this would seem to be:

$AC40 69 40     ORA #$40
$AC42 85 87     STA $87

In which case there is no reason to ever JSR into $AC43, which would in fact be an illegal opcode (SAX $A9, i.e., store the and of X and A into location $A9 if you are curious). This doesn't seem to make any sense to me.

So then I wondered if setting $01 to $05 might just bank out the cartridge ROM, and leave C64 BASIC visible.  $AC43 is at least the beginning of an instruction in the C64 BASIC, but not the start of a routine. Indeed, it is in the middle of the GET command, which seems an unlikely choice here. Also, $AC40 is not the start of an instruction in the C64 BASIC ROM. But more to the point, after consulting the C64 memory banking table, it is not possible to bank the BASIC ROM in, while /EXROM and /GAME are both low.

So at this point, I am beginning to wonder if the cartridge isn't faulty, and that it is supposed to have a different byte sequence at $8088. So I downloaded a copy of International Soccer, which I know is based off of at least some cartridge version, and took a look. Not surprisingly, the loadable version has the ROM to RAM copy routine replaced by NOP instructions. Then in the process of saving it, I spotted something important: $AC40 is completely different in that version to what I was seeing. Then, finally the penny dropped: The MEGA65 was seeing the first 8KB of the cartridge ROM twice, instead of all 16KB, because I should be asserting /ROMH for the second 8KB, instead of /ROML again.  Time for another synthesis run ...  Now I am less excited, perhaps because it is almost 4AM (I got up early, not went to bed late), when part of me says I should be more excited, since I found and fixed what seems like it should be the last barrier to the cartridge mapping properly, and indeed automatically on power-up.

So, that fix did indeed fix the mapping of the 2nd 8KB of the cartridge ROM.  However, it still doesn't work.  It now makes the JSR to $AC43, without trouble.  However, then jumps to $AF6C which is an RTI instruction.  On the disk version of the game, there is something completely different there.  Comparing the two, I am seeing a lot of repetative data from the cartridge, but not from the disk version.

At this point, I am left wondering if this cartridge is working correctly, or if it has a hardware fault, or indeed, if my cartridge port bus has poor timing, that is causing it to read incorrect data, or perhaps that my signalling is so bad in some way, that it is harming the cartridge, e.g., through cross-driving data lines.

As I don't have an easy way to hook up an actual C64 here, I don't have too many options except to try a different cartridge.  For (the as it turned out unfounded) fear that the problem might be the MEGA65 toasting cartridges, the only one I had a double of was Radar Rat Race, so I tried that, but that turns out to be an Multimax-mode cartridges, and trying that revealed some fun bugs with my Multimax Mode setup for cartridges.  Bugs hopefully fixed, and once again, off synthesising.

So now I was back to the International Soccer cartridge. I was really beginning to think that it was a MEGA65 logic problem, but the repeated blocks I was seeing had raised a suspicion in me that it could be a faulty ROM in the cartridge. So I opened the cartridge up, and was very happy to find this:


First up, we can see that this is a cartridge made on Commodore's multi-purpose PCB, with the three traces on the left that allow reconfiguration between different cartridge types, and has spots for two 8KB ROMs, plus one small RAM, so that it can be used for Multimax cartridges. What isn't quite so obvious above, is that one of the ROMs was looking a bit short on solder on one side, and it looks like a couple of pins were bridged by a solder thread:


 On the reverse side, it was obvious that someone at some point had tried (in vain) to desolder one of the ROMs:


That someone is quite possibly a much younger me, who wanted to make a custom cartridge, although I don't specifically remember.  The empty socket for the RAM is also just sitting there, not soldered down at all.  All most curious.

Anyway, for the task at hand, I needed to re-solder those dry pins, so that all the address lines would work again, and thus, hopefully, fix the repeated blocks problem.

That I did, and then boldly put the cartridge back together, and inserted it into the MEGA65, and started it up.  Finally, after tracking down such a diverse variety of problems, I was greeted with scenes like this:



Finally! So, we can now say that the MEGA65 can use (at least some) real C64 cartridges.

Here is a short video I made, showing the cartridge being inserted, and the MEGA65 powering up from cold to run the cartridge.  These r1 PCBs have a larger FPGA that takes longer to configure on power up, so the power-on process will likely be a shade quicker on the final machines. Nonetheless, it is already very fast, and feels like a real 8-bit computer, which of course is the point.




Monday, 1 January 2018

Fixing a few rendering glitches and re-testing enhanced sprite modes.

While I continue to work on some of the missing hardware items, such as cartridge support, I find I have quite a bit of time waiting for synthesis runs to complete for the FPGA. For the MEGA65 PCB these can take anywhere between 1 and 10 hours.  So, I am using some of that latent time to work on fixing some rendering glitches that have snuck in with the whole move to 800x600 video modes.

Exhibit A: Bitmap mode glitch.  Initially I suspected that the problem here was that each 8x8 block was rotated by one physical pixel -- and that may well have been the case, but after attempting to fix that, a glitch remained.  This is really annoying, because it is super ugly. First step to fixing a problem is to fully understand it.  So I switched the MEGA65 to bitmap mode, and drew some dots, and set different background colours on the 8x8 blocks, so that I could see what was going on:

Well, it is nice to see that there is no rotation, as the top pixl is two physical pixels high. Instead, the problem is the bottom physical pixel row of the last VIC-II logical pixel row in each card.  A bit of poking about, as above, made it clear to me that the problem is that the last physical pixel row is being drawn from the next 8x8 block down, instead of the correct one.  This is easy to fix, by using a version of the row counter that has been delayed by one physical raster line.  Ok. That's that one with a fix (but we will have to wait for synthesis, before knowing for sure).

The other is that expanded sprites seem to be a little too narrow. This results in some annoying artifacts, e.g., in Boulder Dash, where the black bar beneath the core is actually made with a bar of expanded sprites.  Here is a quick shot of the problem taken as  my son was coincidentally playing Boulder Dash as I wrote this post.

 The little vertical bars beneath the score line are the problem we are talking about here.  Looking closer on the screen, I can see that these are one 320x200 pixel wide, i.e., half an expanded sprite pixel. This makes me suspect that the odd/even toggle for working out which half of an expanded sprite pixel is being drawn might be the problem.  Let's test that theory by drawing a simple sprite, expanding it, and seeing if any of the pixels are only 1/2 width.

First, unexpanded:



This revealed something I hadn't previously noticed, but am not surprised to see:  The bottom physical pixel has been shaved off.  Since sprites are effectively expanded vertically the whole time on the MEGA65, they also have an odd/even toggle.  This reinforces my suspicion that those toggles are possibly to blame.

Now expanded in both X and Y directions:



We can see that the left half a pixel has been trimmed when the sprite is expanded, and the bottom half of the last pixel is always missing, whether expanded or not.

Looking through the code, I can see that the odd/even toggle in the X direction doesn't get reset at the end of a sprite. This is quite easy to fix, by resetting it each time a sprite starts drawing on a raster line.  I've written a fix for this now, as well, which will also have to wait for synthesis to see if it has fixed the problem.

The missing row (or rows of pixels) on the bottom is not quite so straight forward.  Here unexpanded, we are seeing one physical row of pixels is missing.  When expanded, it is difficult to tell if we are seeing only two physical rows of the four we should be getting, or whether we are getting three. The difference is important, as each result would suggest different root causes. If two, then the sprite expander is probably to blame.  If three, then it is the double-scanning of the screen that is probably to blame.

To answer this question, I switched back to text mode, and drew some characters on the screen that would let me determine exactly how high that bottom pixel block is:


Here we can see a couple of very interesting things:

First, the sprite is mis-aligned with the text area by one physical 800x600 pixel vertically, rather than on a VIC-II pixel boundary.  That will have to get fixed, as well. But more importantly for now, we can see three whole pixels in the grey box, plus the 1 phsyical pixel = 1/4 of a vertically expanded sprite pixel, an the lower sprite pixel block, just above the A, ends exactly on the bottom of the grey box. That is, we have 1/4 + 1 + 1 + 1 + x = 4 pixels. Thus x must be 3/4 of a pixel high, and must therefore be missing only the bottom physical pixel.

This means that it is our scan-doubling that is the problem. Looking at the code, the end of sprite is determined based on reaching the last pixel in the sprite. However, since multiple physical rasters may have the same VIC-II raster number, we should instead check for the first raster after the end of the sprite, and stop drawing there, instead. Again, once identified, the (hopeful) fix, is quite simple, and will be thrown into the next synthesis run.

While I am here working on sprites, it also gives me the opportunity to re-test some of the MEGA65 enhanced sprite modes, to make sure that those still work.

We have:

Super-hires sprites (enabled for all by bit for in $D054), which also doubles the sprite X resolution. We will be using these in the special MEGA65 version of GEOS, so that the mouse-pointer is also hi-res, and can point at any pixel. If you use it with multi-colour mode, it effectively gives you 320x200 4-colour sprites. Yup, super-hires sprites still seems to work.

Extended height mode (enabled with one bit per sprite in $D055, and the height /of the sprites set by $D056, up to 255 pixels high). Yup, that seems to work.

 

Extended width mode (64 pixels wide, instead of the usual 24, enabled per sprite by bits of $D057). Yup, that too. So far so good. This mode will also be useful with the super-hires mode and multi-colour mode, to allow sprites to be 32x21 (or 32 x extended sprite height) hi-res pixels in size, but with three colours.


Bitplane mode sprites (sprite modifies one of the bits in output colour, instead of setting colour, enabled by upper bits in $D049 and $D04B). Yup, that, too.


And finally, horizontal tiling mode, where the same 24 or 64 bits of sprite data are repeated until the edge of the screen is reached. Yup, that works, and also confirms that bitplane mode sprites (for better or for worse) currently ignore the presence of the border.


I thought I had also implemented 16-colour sprite mode, where 4 bits are used together for each pixel (so they will be either 6 or 16 pixels wide, depending on whether extended width mode is used), but apparently not. I might have to revisit that, as it won't be hard to add, but will certainly make the 8 sprites we have go further, and will also make it possible to have interesting colourful backgrounds when used with tile mode.  

I'd be interested to hear how people think they might use these enhanced sprite modes, or whether there is something obvious that I have missed.

But for now, it is the impatient wait while these changes synthesise, and hopefully, fix those annoying display glitches.
 

Sunday, 31 December 2017

Connecting a C64 disk drive to the MEGA65 - part 2

The other day I wrote about getting the first communications between the MEGA65 prototype PCB and a real C64 disk drive (my old sick 1581).

At that point, I had it working to be able to read the DOS status exactly once, but then it would hang.  Similarly, trying to LOAD anything (even a disk directory) would cause strange lock ups.  Tracing through where it got stuck using the MEGA65 serial monitor (which I am always so glad I went to the effort of implementing), I found that it was waiting for timer B on the CIA to time out, to determine when it was an EOI condition.  However, timer B never triggered.  This set me on a journey of implementing a pile of missing functionality in our CIA implemenation.

First, I tracked down and fixed the bugs in timer B, and resynthesised the bitstream, to confirm that this indeed fixed the problem I was seeing in C64 mode.




I couldn't tell if it would now work for loading files, as I can't even load a directory on my sick old 1581. Of course, I only got to this point after having borrowed a 1541-II from a local to confirm that I could read the status message only once from that as well -- so even now, I don't know if I would be able to load something or not from C64 mode. However, C65 mode is a different question...

C65 mode, like C128 mode on a C128, uses fast serial when it can.  Long, long ago, I had bodged up the status register on the CIA shift register, that is used for fast serial. This was to work around the start-up code on the C65 that looks for a disk drive, and if present, tries to boot from it.  My bodge was just to always set the shift-register complete flag.  This worked back then, and stopped the boot process hanging like this:




But now that we are trying to talk to real disk drives, it makes the C65 think that every connected disk-drive can do the fast serial mode, which results in hanging.  (Note that while a 1581 can do fast serial, we don't currently have the SRQ line connected on the MEGA65's IEC serial port. This will change in the production versions, so that fast serial mode will be available.)

So, as well as fixing timer B, I spent part of the day implementing the shift-register, at least for sending, so that I could get rid of the bodge, and have, hopefully, both the C65 booting correctly, and working with external drives in C65 mode. This is still a work in progress, but I hope to have it all working in the next couple of days.

There are also some residual bugs I need to track down, which might just be poor timing closure on the FPGA, that cause device not present errors and the occasional corruption of bytes received, such as in the following image. It could also be that my 1581 is sick (it sometimes flashes three blinks on start up, to indicate that it has a problem with its RAM, so this is not out of the realms of possibilities).


The next stages on this will probably have to wait now, until I collect the 1541 that is sitting in the lab at work sometime this coming week.  But I am happy that significant progress has now been made.

Saturday, 30 December 2017

Connecting a C64 disk drive to the MEGA65 - first success

I have spent the last couple of days working on getting the IEC serial bus working on the MEGA65.  This has been possible, because we now have the CPU able to run at instruction-level accurate timing, which is generally sufficient for serial disk access. There are two purposes behind this: (1) we just want it working, and (2) we need to make sure that the circuitry on the MEGA65 mother board is correct for the IEC serial port.

I started out with a simple VHDL program that would let me control the CLK and DATA lines via joystick, and show their status on the two LEDs on the mother board.  That is, without a CPU, and without a video controller. The purpose here is to keep the VHDL super-simple, and thus super-fast to synthesise, so that I could iterate quickly, and avoid the 2 - 10 hour synthesis runs that have become normal.

We already knew that we had forgotten to specify the inclusion of pull-up resisters on the IEC serial lines, and this was indeed confirmed.  So I built an an adapter cable that takes 5V from a joystick port, and uses that to pull-up the serial lines via 1K resisters.  Ideally, it should also have diodes to prevent back-flow of current from the bus powering parts of the computer when it is off, as well. This I have noted in the errata for the next revision of the board, but didn't need to implement on my adapter cable for testing.  I'll probably adapt the 5V take-off on the joystick port into a joystick pass-through cable, so that I don't lose a joystick in order to have the IEC bus working. (It isn't unfortunately just a case of not using the cable when I don't want to use an external drive, because without it, it looks to the C65 ROM like there is a drive on the bus from which to try to boot, so the computer just hangs on start without it).  Here is the little cable I made up:



A side-benefit of this little cable with exposed pull-up resistor, is that it gave me somewhere convenient to attach the oscilloscope probles, so that I could watch what was going on:


After that, it was a bit of trial and error with my poor old 1581, that doesn't seem to be able to read disks any more.


 Then finally, once I had everything properly connected, and the, and the device ID set correctly on the 1581, it was success!


And here is the video of it:



Because I am using the MEGA65 in headless mode like this, I also extended the monitor_load program that can load a bitstream, and load a program into memory and do other useful stuff, to also allow typing input into the computer, as though it were being typed on the real physical keyboard. This uses the keyboard virtualisation layer that already exists in the MEGA65 to allow use off C65/C64/USB and other keyboard types, without having to mess with things.  So all I had to do was tell monitor_load to put the correct keyboard matrix events into the queue, and then I could type like magic.  One of the very nice aspects of this, is that one can run otherwise interactive tests completely automatically.

Here is the command line that I used to type this little disk status check in and run it. Note that I call monitor_load twice, once to boot the machine with a specific bitstream, and then again to do all the typing:

monitor_load -b bin/mega65r1.bit -4 ; monitor_load -T '10open15,11,15~M20get#15,a$:?a$;".";:ifa$>chr$(13)goto20~Mrun'
Now the next step is to figure out why it gets stuck after reading the disk status message once, instead of reading it repeatedly.  After displaying the disk status message once, the CPU gets stuck looping around in the input byte from serial bus routine at $EE13.  Clock and data are high, suggesting that the 1581 is not trying to send anything. What I can't work out is whether this is just because my 1581 is on its death bed (LOAD"$" yields a 74,DRIVE NOT READY,00,00 message), or whether there is still some problem with the MEGA65 side of things.  Unfortunately I don't have another drive here to compare with, and won't be able to get another one for a few days. Hopefully some of the MEGA guys in Germany will be able to test this there, where they do have some drives on hand. If there are remaining problems, I suspect that they will be CPU or CIA implementation related, since the electrical side of things clearly works, if we can instruct a device to come to attention, and then send a number of bytes.

In any case, I have plenty more to get on with in the meantime, for example, testing the ethernet and cartridge ports, and trying to work out why I can't get any audio out of the left channel.

Thursday, 28 December 2017

Building Joysticks

Now that the bitstream is working fairly well at 800x600 (at least well enough to cause our children to squabble about whose turn it is to play Boulder Dash next), I am beginning to turn my attention back to finishing testing the physical ports on the first revision of the MEGA65 PCB, and then get as many of those ports working as possible.

The keyboard is already working, thanks to the overhaul of the keyboard input code.  The joystick ports also used to work. The past tense being employed there, because the overhaul of the keyboard input broke it.  I have since fixed that (it was just that I hadn't fed the real joystick pins into the combined joystick input code), and am synthesising the resulting bitstream for testing later today. 

For those with an interest in VHDL, here is the logic that is used to update the joystick state:

      for n in 0 to 4 loop
        joya(n) <= '1' and (joya_physkey(n) or joykey_disable)
                   and (joya_widget(n) or widget_disable)
                   and (joya_real(n) or joyreal_disable)
                   and (joya_ps2(n) or ps2_disable);
        joyb(n) <= '1' and (joyb_physkey(n) or joykey_disable)
                   and (joyb_widget(n) or widget_disable)
                   and (joyb_real(n) or joyreal_disable)
                   and (joyb_ps2(n) or ps2_disable);
      end loop;


Basically for each line on both joysticks, we consider all the possible input sources, and merge them together, these are USB/PS2 keyboard using dedicated keys mapped to the joystick, the MEGA65 Widget Board that can be connected to a Nexys4 FPGA development board to allow use of a real keyboard and joysticks on that, and apparently the USB/PS2 keyboard a second time (I just noticed this, and will have to investigate why on earth I have it twice).

However, getting to the point of being able to test joysticks has led me to a long term problem I have had: I don't have any properly working C64 joysticks left. I have a Quick Fire 2, with its lead in very poor condition, with loops of wires on the outside holding it in strange kinks that cause it to sometimes work.  I could have fixed that, but the quality of the switches and everything would mean that it would soon die again.  Also, it wouldn't solve the above-mentioned problem of both of our children wanting to play games at the same time.

Thus I finally got around to doing what I had intended to do for the last 20 years, and build up a couple of proper arcade joysticks.  There is something about the feel of a fully free and well constructed arcade joystick controller, as well as the fact that they are much, much more durable than commodity joysticks, and also much more serviceable. The TAC2 is about the closest C64 joystick I have found, and even those are not as durable, and to me at least, don't quite have the same feel.

Anyway, this adventure in making arcade joysticks was made easier by the fact that Jaycar now stock the joystick assemblies and buttons, as well as cheap plastic boxes in which to put everything.  I had previously bodged one up about 20 years ago with parts from a pinball rental company, but Jaycar is much, much cheaper, and the quality of the mechanisms seem to be pretty good.

Wiring up a C64 joystick is really simple: There are dedicated lines for up, down, left, right and fire, and these get connected through the switches to ground.  If you want any electronics in there, e.g., an auto-fire function, there is also 5V available.  There are also two lines for the analog inputs, but we are not using those.

The arcade joysticks have spade connectors, so I got some nice black 7-core cable and put spade connectors on one end, and the correctly wired DB9 on the other end.  I did try to get all clever-pants, and bought the cable as DB9 serial null-modem cables, with the female DB9 on the end, however, they didn't have all nine pins connected, so I had to go for the hand-soldered and screw-box cover. Here is the joystick wired up with spade-connectors and a soldered ground-loop, so that if I have messed up the joystick directions in the wiring, I can easily fix it.



If I were building an arcade game for public use, I would have used spades for the ground-loop as well, so that the micro-switches could be more easily replaced as they wear out. However, for home use, that is unlikely to ever happen, and a bit of remedial soldering doesn't scare me. The spade connectors also mean I can switch one of them to being "left-handed" (button on right), if my daughter's fears about not being able to use a "right-handed" joystick prove to be genuine.  I should say that I am also left-handed, but don't have trouble using a right-handed joystick, so we will see how we go.

Anyway, here is one of the finished units. Total cost per unit, about AU$45 (ca. 30€).



They are quite big, which is partly necessitated by the rather large joystick mechanisms, and also because I like a big solid joystick, so that you can whack the stick around as you need, and thump the button with impunity.

The arcade buttons from Jaycar have an LED in them, so you can make the button illuminate, which I decided to do on one of them. The other was already sealed up, so I didn't make the modification to that one, but I might another day. For now, it means that I have an easy way to tell them apart.



I'm pretty happy with the result. They are unlikely to ever appear in an Ikea catalog or anywhere else where styling is important, however, they are solid, should last the next 20 years, and have that nice arcade game feel to them.  Now I just need to wait for the FPGA synthesis to finally finish, so I can give it a try...

Tuesday, 26 December 2017

A day of squashing bugs

Part way through today I was feeling that I hadn't really achieved very much, but then as I started thinking about it, and making a list, I realised that I have done more than I first thought.

The list of bugs squashed since the 24th now includes:

24DEC17 - 800x600 video modes work
24DEC17 - Joystick input not working
24DEC17 - CPU bug fixed (Boulder Mark etc runs fine)
24DEC17 - b0 command in UART monitor stops CPU on BRK instruction
25DEC17 - Fix $DC00 always reading as zeros
26DEC17 - Fix sprite fine horizontal placement problem
26DEC17 - PDM/Sigma-Delta audio output working (audio was broken)
26DEC17 - Kickstart looks for file "NTSC", if not present, switches to PAL
26DEC17 - CIA clock speed is always 1MHz, except in C128 2MHz mode.
26DEC17 - Fix CIA clock halving bug
26DEC17 - $D016 smooth scroll in 320H mode fixed
26DEC17 - CIA is 1MHz even in 2MHz mode (turns out to be the correct behaviour)
26DEC17 - NumLock on PS/2 / USB keyboard is now "joystick lock" (WASD+shift, cursors+space)

This has largely been a process of getting all the stuff working again that has broken during the change of video mode, and the incorporation of bitplanes and other missing features.

While there are still a lot of fixes to be made, it is now back to the point where a number of games load and run just fine, and with the improved joystick emulation, these are both easier and more fun to test.  This of course necessitated some play-testing.  There are still quite obvious display glitches, but many of these are due to a relatively few bugs, including some raster position bugs that are on the list to work through.

For now, I will leave you with a set of images following the process of startup, to playing a game: Kickstart loads C65 mode, from there to C64 mode, mount a disk image from SD card, load the menu from the disk, play the game.  Of course, this will all be much more streamlined for final release, but it is already quite usable.  My 7 year old son is just about on top of how to load and play Ghosts and Goblins, which is his current favourite.