Tuesday, October 14, 2014

Found a sneaky CPU bug

While trying to run some graphic test programmes supplied by Rayne, I found that the CPU was mis-behaving in a way that reminded me of the bug I was seeing with BoulderMark, and probably Lemmings as well.  Basically all was fine until a raster interrupt occurred, and then things would go odd or outright crash.  What was extra odd was that BoulderMark would still run on the FPGA at work, but not on the one here at home, which shouldn't happen -- FPGAs shouldn't be picky like that.

Anyway, Rayne's programmes are much simpler, and offered the prospect of easily debugging what was going on.

So after a bit of poking around I discovered that the C65GS would go to lala-land after INC $D019.

This got me thinking, because $D019 is special in my CPU, because it adds a dummy write for RMW instructions that touch $D019, but not any other address.  This is to avoid wasting a CPU cycle on the dummy write of the original value back to memory, except when required for C64 compatibility.

The lack of this dummy write on the C65, that acts to clear the VIC-II interrupt on a C64, is one of the major sources of incompatibility between the C65 and C64, and stops the majority of software from running on it.  Thus I had gone to special effort to make sure it wouldn't be a problem on the C65GS, but without the CPU speed penalty of doing it on every address.

However, I had messed up the dummy write state in the CPU: it was not setting the target address on the bus, and so instead was writing to the last accessed memory location, which was the first byte of the following instruction.  The net result is that the old contents of $D019, usually $F0 or $F1, would get written to the next byte in the instruction stream.  I confirmed this in simulation, where the dummy write and final write can be seen marked in bold.  Note that the dummy write is going to $F60F, not $D019!

gs4510.vhdl:1685:11:@700ns:(report note): MEMORY reading $FFFF60C = $EE
gs4510.vhdl:1004:7:@700ns:(report note): MEMORY long_address = $FFFF60D
gs4510.vhdl:1685:11:@780ns:(report note): MEMORY reading $FFFF60D = $19
gs4510.vhdl:1004:7:@780ns:(report note): MEMORY long_address = $FFFF60E
gs4510.vhdl:1685:11:@860ns:(report note): MEMORY reading $FFFF60E = $D0
gs4510.vhdl:1004:7:@860ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@940ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:1004:7:@940ns:(report note): MEMORY long_address = $FFD3019
gs4510.vhdl:1685:11:@1020ns:(report note): MEMORY reading $FFD3019 = $70
gs4510.vhdl:1004:7:@1020ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1100ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:1304:9:@1100ns:(report note): writing to shadow RAM via chipram shadowing. addr=$000F60F
gs4510.vhdl:1689:11:@1140ns:(report note): MEMORY writing $000F60F <= $70
gs4510.vhdl:1689:11:@1180ns:(report note): MEMORY writing $FFD3019 <= $71
gs4510.vhdl:1004:7:@1180ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1260ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:738:9:@1260ns:(report note): $F60C EE 19 D0  inc  $D019         A:11 X:22 Y:33 Z:00 SP:01FF P:24 $01=3F MAPLO:0000 MAPHI:8F00   ..E-.I..   

So a quick fix and re-run simulation and suddenly we can see that it is all fixed:

gs4510.vhdl:1685:11:@700ns:(report note): MEMORY reading $FFFF60C = $EE
gs4510.vhdl:1004:7:@700ns:(report note): MEMORY long_address = $FFFF60D
gs4510.vhdl:1685:11:@780ns:(report note): MEMORY reading $FFFF60D = $19
gs4510.vhdl:1004:7:@780ns:(report note): MEMORY long_address = $FFFF60E
gs4510.vhdl:1685:11:@860ns:(report note): MEMORY reading $FFFF60E = $D0
gs4510.vhdl:1004:7:@860ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@940ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:1004:7:@940ns:(report note): MEMORY long_address = $FFD3019
gs4510.vhdl:1685:11:@1020ns:(report note): MEMORY reading $FFD3019 = $70
gs4510.vhdl:1004:7:@1020ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1100ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:1304:9:@1100ns:(report note): writing to shadow RAM via chipram shadowing. addr=$000D019
gs4510.vhdl:1689:11:@1140ns:(report note): MEMORY writing $000D019 <= $70
gs4510.vhdl:1689:11:@1180ns:(report note): MEMORY writing $FFD3019 <= $71
gs4510.vhdl:1004:7:@1180ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1260ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:738:9:@1260ns:(report note): $F60C EE 19 D0  inc  $D019         A:11 X:22 Y:33 Z:00 SP:01FF P:24 $01=3F MAPLO:0000 MAPHI:8F00   ..E-.I..   

Oops.. not actually all fixed.  It is now writing to $D019 in RAM, not IO.  Lucky I decided to write this blog post, or I wouldn't have spotted that I still had the memory write flags slightly messed up.  Specifically memory_access_resolve_address wasn't asserted, so the 16-bit address was not being translated to the physical 28-bit address.  Fix that and try again:

gs4510.vhdl:1004:7:@1020ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1100ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:1689:11:@1140ns:(report note): MEMORY writing $FFD3019 <= $70
gs4510.vhdl:1689:11:@1180ns:(report note): MEMORY writing $FFD3019 <= $71
gs4510.vhdl:1004:7:@1180ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1260ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:738:9:@1260ns:(report note): $F60C EE 19 D0  inc  $D019         A:11 X:22 Y:33 Z:00 SP:01FF P:24 $01=3F MAPLO:0000 MAPHI:8F00   ..E-.I..

Ah, that's better!

Now to resynthesise, and see if BoulderMark, Lemmings and Rayne's MUIFLI all work properly.

5 comments:

  1. This is a very cool project.

    I've always thought about how the successors of the c64 could have been....

    ReplyDelete
  2. I second that - I follow every single blog post, and I can't wait for the next progress update! I'd easily shell up some money for a finished C65 clone with Ethernet-connectivity, a C64/128-compatible keyboard and a plastic wedge case in classic Commodore pale-beige! Count me in if printed manuals for the finished product are on your agenda and you need a Swedish translation!

    ReplyDelete
    Replies
    1. Hello,

      Thanks -- Also if you do wish to write any manuals, in English or Swedish, you are most welcome to do so :)
      As for the eventual cost of a conversion kit (including PCB with many or all C64 connectors that accepts the FPGA board) for an old C64 keyboard, my best estimate at this point in time would be ~AU$200-AU$400 + the cost of the FPGA board, which is US$160 with academic pricing or US$320 without. So expect total cost to be between US$350 and US$800. But these are only wild estimates at this point in time.

      Paul.

      Delete
    2. The pleasure is all mine - and I may very well take you up on that offer, if this project will (eventually) set commercial sails, so to speak. With print-on-demand services and the dawn of 3D printing technology in mind, I'm hoping that low-cost solutions for shipping complete DIY (or pre-assembled) C65 kits will actually be viable - and that's where those comb bound manuals should prove valuable! :)

      Delete