Wednesday, 27 July 2016

Part 20: Debbuging the garbled game screen

Foreword

In the previous blog we added joystick emulation to our emulator and managed to start the game.

However, with the start of the game we were faced with a garbled screen.
In this blog we will investigate why we are presented with a garbled screen.

I will using an interesting debugging technique where we will compare the state of our emulator with the state of another known working emulator in order to isolate the issue in our emulator.

This is a bit of a lengthy blog, so maybe have a cup of coffee handy when reading :-)

Enjoy!

Initial round of debugging

Let us do an initial round of debugging to get some pointers into which direction we should go to find out why the game screen is garbled.

Let us have a look again at the problem screen of the previous blog:

We haven't got a change to properly test the multicolor text mode that we have implemented in a previous blog. Could it be that there is still some bugs in that functionality causing this rendering issues?

A quick sanity check for this is to inspect the screen character memory. There is something concrete we can look for, for the instance the out of place A in the top right corner. If we get that character in the same position in character memory, we know that our theory on a rendering bug in the multicolor text mode doesn't carry much weight.

From the looks of it we can see that the characters displayed is definitely custom defined characters and we are not guaranteed that it use standard PET screen codes. To get an idea what screen codes it is using, we can look at a sequence of screen characters, like DAN LANDED THE ANASTASIA.

The above mentioned sequence of screen codes is more less at the beginning of the second screen line.

So lets do some memory inspection:

 The second screen line starts at memory location 427h. We see they are using code A0 for a space and 81 for an A. From that point they use the ordinal position in the alphabet. So, the characters 84 81 8E A0 8C 81 8E 84 85 84 corresponds to DAN LANDED.

The out of place A is at the end of the second line which is at location 44E. Indeed, the code at that location is 81, which is an A!

So, something in the machine code program is populating the screen RAM incorrectly, but what?

At this point it would be really useful to do some comparisons against a second emulator that does render this screen correctly. The emulator I am going to use for this purpose will be VICE.

VICE by the way, gives you some nice debugging facilities. One of these facilities is that it allows you to break when the contents of a specific memory location change.

Fire up the loading of Dan Dare in Vice. As soon as the intro screen appears, hit ALT+H to activate the monitor.


Time to set a breakpoint. We would like to break as soon as an instruction writes to location 427. So enter the following in the monitor:

watch store 427

The monitor will give you a confirmation that the breakpoint is set. Enter goto to resume the emulator:



Now hit the firebutton to start the game. Your breakpoint will trigger in a flash of a second.

We know single step s couple of times to determine where the data comes from which is populated to the screen:

Looks like we hit a loop that is busy clearing the screen. Not exactly what we are looking for. So, type in goto to resume the emulator.

After a flash, our breakpoint gets triggered again. So lets single step a couple of times again.

This time it looks more promising:


Looks like it is literally copying data from the location 1C00. So lets us inspect this memory area. In the Vice Monitor you just need to issue m 1c00 to inspect this area:


And indeed, at memory location 1c27, we can make out the familiar DAN DARE LANDED THE ANASTASIA.

So, what populates the memory area at 1C00? To answer this question, we need to follow the above mentioned debug process, but with watch point at address 1C00.

Restarting the debug process with the new watch point will again break in loop filling the area with the value 20. So give it another goto.

When it breaks again, we give it a couple of single steps, yielding the following screen:


So, it retrieves the data via stored address at $96 and store the data via address stored at A8.

I think next we should determine what manipulate the contents of address $96. This yield the following:


I think this is a nice point to actually compare with our JavaScript emulator to see if our emulator comes up with the same register values when address 5358 is hit the first time.

Just to make sure we are not chasing out own tale, I have restarted the Vice emulator 3 times hitting breakpoint 5358 each time and verify that the register values is the same each time.

We start our JavaScript emulator with a breakpoint at 5358. When it hits this our breakpoint, our register values is a follows:

Acc:40 X:ae Y:02SP: f1 PC:5358 Z:0 N:0 C:0 V:0 D:0

Register X is clearly different!

It would be nice if we could go back to previous instructions executed to see where the world went pair shape.

In the next two sections I will show how to setup Vice and our JavaScript emulator respectively to log the state of each instructions executed. We can then do some comparisons against the two logs to see where we went wrong.

Enabling Instruction tracing in Vice

As mentioned in the previous section, it would be nice if we could log the state of the registers after each and every instruction execution.

How do you enable this in Vice? Reading through the documentation of Vice and consulting web resources is not very explicit about such a kind of functionality.

However, if you browse the source code of Vice, you do in fact see debug logging statements after executing a 6502 instruction.

So, it looks like the functionality that we want is within Vice. It is just a matter of knowing how to enable it.

After some further investigations I found that one is out of luck with pre-compiled Vice binaries. However, if you compile Vice yourself, your luck is in :-)

Ok, I am going to make one assumption at this point, and this is that you do have a workable environment for building Vice from source. Typically for a Debian/Ubuntu environment this would mean having installed the build-essential package, together with some GUI libraries like GTK2 development library.

Now, if you open up a terminal window, navigate to the Vice source and type:

./configure -help

You will see that one of the options is --enable-debug-code

This is the option we are looking for, so run configure with the following options:

./configure --enable-debug-code --enable-gnomeui

The --enable-gnomeui  option compiles your version of Vice with Gnome support, suitable for Debian/Ubuntu.

Kick off the building of Vice with:

make

We will not bother to install this version of Vice, since it is a debug version anyway. So, after the make process is finished, just navigate to the src folder and kick off the emulator with:

./x64


On startup, you might get some warning messages that the sound system could not be started. Not a problem in our scenario.

You will notice an extra menu category Debug in the Vice window:

In this category, the menu item of interest is Main CPU Trace. When you select this menu item, you will see the terminal window from which you invoked Vice will get cluttered with log items:

The most left hand column is the address, and the column on the right hand side is the status of the registers in the following order: Accumulator, X register, Y register and SP.

Typically your terminal window is limited by the number of lines it can store in the scroll back history, so it would be better to log the trace the a file to avoid possible loss of lines.

The following command will enable logging to a file for you:

./x64 -logfile log.txt

This will log console output to the file log.txt.

One thing that you will notice when you enable this logging, is that the performance of the emulator drop dramatically. On my desktop it performs at about 6% of the original speed. So it is advisable to only switch on logging at the point you need it. Luckily you can toggle this logging on and off while the emulator is running.

Enabling Instruction tracing for our Emulator

For our JavaScript Emulator we would like to enable the same kind of logging as we did for Vice in the previous section.

We already have two methods in emulator that will provide you a string representation of the state of the registers and the current instruction disassembled. So the only thing you need to do is to write these values with to write these values to the console with console.log.

In the previous section we learned an important lesson. This kind of logging slow down the emulator dramatically. We cannot bargain that our emulator would perform any better, so we also need to built in a functionality that you can enable/disable this logging at any time.

So, let us start by adding a checkbox to our index page for enabling/disabling logging.

<input type="file" id="file" name="myfile"/>
<button onclick="myTape.attachTape(document.getElementById('file').files[0])">Attach</button>
<br/>
<button onclick="mymem.togglePlayPresed()">Play</button>
<br/>
<input type="checkbox" id="allowDebug"/> Allow logging<br/>
<textarea id="registers" name="reg" rows="1" cols="60"></textarea>
<br/>
<textarea id="memory" name="mem" rows="15" cols="60"></textarea>
From Location:
<input type="text" id="frommem">
<button onclick="showMem()">Refresh Dump</button>
<br/>


Next, we add a property to our Cpu class for indicating whether logging is on or off:

function cpu(memory) {
...
  var allowLogging = false;
...
  this.setAllowLogging = function(flag) {
    allowLogging = flag;
  }
...
}

Next we need to add a check in the runBatch method that will check whether the checkbox is checked and set the applicable property in the cpu class.

      function runBatch() {
        if (!running)
          return;
        mycpu.setAllowLogging(document.getElementById("allowDebug").checked);
        while (true) { 
        ...
        }
        ...
      }

Next, we need add the method log_debug within the Cpu class, that will only log if logging is enabled:

  function log_debug(value) {
    if (allowLogging) {
      console.log(value);
    }
  }


And, we need to use this method within the step method:

  this.step = function () {
    log_debug(this.getDecodedStr() + "  " + this.getDebugReg());
    if ((myInterruptController.getCpuInterruptOcurred() | myvideo.vicIntOccured()) & (interruptflag == 0)) {
    ...
    }
    ...
  }

Now let us take this new functionality for a test drive. I was testing this functionality within the Google Chrome browser. To see the console it import that you have the Inspect an element view open. In Google Chrome you enable this view with Cntrl+Shift+I.


One thing I should mention is that Google console also have a limit on the number of lines it can display. We can also get around this limitation by enable file logging by starting chrome from the command line in the following way:

google-chrome --enable-logging

This create a file called chrome_debug.log in some folder in you user directory. The specific folder for this file differs from platform to platform. In Linux, you will find this file in the folder ~/.config/google-chrome.

So, in short, the file chrome_debug.log will contain  the un-truncated version for our instruction state logging for our JavaScript emulator.

Reconciling the two logs

With the logging capability added to both Vice and our emulator in the previous two sections, it is time we create a log for both systems doing the same action.

Do the following on both systems, and keep the logs:


  • Start off the loading of Dan Dare
  • Once you see the intro screen, enable logging
  • Press and hold the firebutton, until you see the game has started
  • Stop logging
In this section we will use our Vice log for reference and refer to our emulator log when necessary.

In the initial round of debugging we decided that the starting point for our debugging should be address

5358.

So within the Vice log search for .5358:



This log allows us to trace back to determine where register X got its value from. And we see the X register got its value from memory location AA.

So, what populated location AA? To answer this search for $AA:


And we spot the place where the initial value is assigned to memory location $AA.

Let us pause for a moment and compare the same snippet with the log of our own emulator:


If you compare the logs you will actually see that the register states are in sync between the two logs till it hits the instruction at 5343, which is LDA($aa),y

You might have a moment of despair, thinking: Know we need to do a similar exercise to find what is populating the effective address of ($aa),y

But, hang on, we are almost at the root of the problem. The effective address of ($aa), y is B000. What is special about the address B000? It falls within the address space of basic ROM! This screams: Bank switching!

And in fact, we haven't implemented bank switching of the Basic ROM within our emulator.

Well, I fully agree, we could have avoided this ugly detour by implementing all bank switching at once when we were busy implementing bank switching for the KERNEL ROM. But, I thought this is a nice debugging technique to use, should hit a brickwall.

Implementing Basic Bank Switching

Let us implement bank switching for the Basic ROM.

First we create a method within the memory class that tell if the Basic ROM is visible by looking at memory location 1:

  function basicEnabled() {
    temp = mainMem[1] & 3;
    return (temp == 3);
  }


Next, we should modify the readMem function:

  this.readMem = function (address) {
    if ((address >= 0xa000) & (address <=0xbfff) & basicEnabled())
      return basicRom[address & 0x1fff];
    else if ((address >= 0xe000) & (address <=0xffff) & kernelEnabled())
      return kernalRom[address & 0x1fff];
    else if ((address >= 0xd000) & (address <= 0xdfff) & IOEnabled()) {
      return IORead(address);
    } else if (address == 1) {
      var temp = mainMem[address] & 239;
      if (!playPressed)
        temp = temp | 16;
      return temp;
    }
    return mainMem[address];
  }


With all this changes let us give out emulator a test run again:



It already looks better, although Dan is still missing and something is wrong with the colors. We will cover these issues in the next blog.

In Summary

In this blog we fixed the garbled screen we were presented with when starting the game.

In the debugging process we used a technique where we compared the state of our emulator with the state of another emulator which renders the screen in question correctly.

In the next blog we will be fixing the remaining rendering issues like invisible Dan Dare, and the invisible Treen.

Till next Time!


Saturday, 23 July 2016

Part 19: Emulating the joystick

Foreword

In the previous blog we fixed the intro screen.

In this blog we will implement joystick emulation and see till which point the game will start up when we hit the fire button.

Implementing Joystick emulation

In the previous blog we covered the theory of a joystick interfaces to the C64.

We can therefore go straight ahead and start to implement joystick emulation to our emulator.


The joystick emulation in our emulator will also be interfacing with the keyboard, so we can utilise the keyboard class for joystick emulation.

So, which keys on the keyboard will we be using for the joystick? I was thinking to use the cursor keys on the numpad and Numpad zero for the fire button. To operate the joystick, Numlock should be off.

You may remember that the keyboard class maintains an array of which keys are hold down at any instance. We can also use this array to store joystick keys that are down. It is important though that the main keyboard emulation functionality should ignore these keys.

To ignore these keys we should add a default selector to the switch statement of the getScanCode method:

  function getScanCode(chr) {
    switch (chr) {
      case KEY_A:
        return 10;
      break;
      case KEY_B:
        return 28;
      break;
      case KEY_C:
        return 20;
      break;
      case KEY_D:
        return 18;

...
      case KEY_6:
        return 19;
      break;
      case KEY_7:
        return 24;
      break;
      case KEY_8:
        return 27;
      break;
      case KEY_9:
        return 32;
      break;
      default: return -1;

    }
    
  }


This default selector has the effect that for all joystick keys a -1 will be returned.

getColumnByte should then check for this -1 value and ignore if it is the case:

  this.getColumnByte = function(rowByte) {
    var rowArray = [0,0,0,0,0,0,0,0];
    rowByte = ~rowByte;
    rowByte = rowByte & 0xff;
    var i;
    for (i = 0; i < keyarray.length; i++) {
      var scanCode = getScanCode(keyarray[i]);
      var rowNum = (scanCode & 0x38) >> 3;
      if (scanCode != -1) {
        rowArray[rowNum] = rowArray[rowNum] | (1 << (scanCode & 7));
      }
    }

    ...

  }

Our joystick keys will now be ignored by our main keyboard emulation functionality.

We should now add a method that returns the joystick byte when CIA#1 register is read:

  this.getJoyStickByte = function () {
    var result = 0;
    for (i = 0; i < keyarray.length; i++) {
      var temp = keyarray[i];
      switch (temp) {
        //left
        case 37:
          result = result | 4;
        break;
        //right
        case 39:
          result = result | 8;
        break;
        //up
        case 38:
          result = result | 1;
        break;
        //down
        case 40:
          result = result | 2;
        break;
        //fire
        case 45:
          result = result | 16;
        break;
      }
    }
    return result;
  }


Our Memory class should invoke this method at the correct moment:

  function ciaRead(address) {
    if (address == 0xdc00) {
      return (~keyboardInstance.getJoyStickByte()) & 0xff;
    } else if (address == 0xdc01) {
      return keyboardInstance.getColumnByte(mainMem[0xdc00]);
    } else if (address == 0xdc04) {
      return mytimerA.getTimerLow();
    } else if (address == 0xdc05) {
      return mytimerA.getTimerHigh();
    } else if (address == 0xdc06) {
      return mytimerB.getTimerLow();
    } else if (address == 0xdc07) {
      return mytimerB.getTimerHigh();
    } else if (address == 0xdc0d) {
      return myinterruptController.getInterrupts();
    } else if (address == 0xdc0e) {
      return mytimerA.getControlRegister();
    } else if (address == 0xdc0f) {
      return mytimerB.getControlRegister();
    } else {
      return mainMem[address];
    }

  }

That was relatively painless!

Lets take our emulator for a test drive with these changes.

With the intro screen showing, lets hit the fire button, aka numpad 0. We can see that the game started, but we are presented again with the garbled screen that we briefly saw in the previous blog:


Some closer investigation of the VIC registers revealed that raster interrupts is enabled when this screen is shown.

We haven't enabled rasterline interrupts as yet within our emulator, but in the next section we will do that.

Implementing raster interrupts

Lets start coding the raster interrupts.

First, we implement the following methods:

...
  function rasterIntEnabled() {
    return (registers[0x1a] & 1) == 1;
  }

  function targetRasterReached() {
    var temp = registers[0x11] & 0x80;
    temp = temp << 1;
    temp = temp | (registers[0x12]);
    var tempCurrentLine = (cycleline == 312) ? 0 : cycleline;
    return (temp == tempCurrentLine);
  }
...

The first method determines if raster interrupts is enabled and the second method determines if the target raster line is reached yet. The target raster line is stored in register 12h and bit 7 of register 11h.

Something that might need some explanation is the check for 312 in the targetRasterReached method. Raster lines are within the range 0 to 311. At some point when this method gets called the raster line might be 312, e.g. roll over to 0 not yet applied. This line of code just caters for this condition.

Now, both these methods needs to be called in the processPixels method:

  this.processpixels = function() {
      ...
      if (cycleInLine > 63) {
        cycleInLine = 0;
        cycleline++;
        if (targetRasterReached() & rasterIntEnabled()) {
          registers[0x19] = registers[0x19] | 1 | 128;
        }
        updateCharPos();
      }
      ...
  }

So, if an interrupt occurred the applicable bit within register 19h is set.

With this code implemented, machine code polling the VIC interrupt register to see if a raster interrupt occurred would work correctly. However, at this point in time we would not be able to physically interrupt our emulated CPU with a raster interrupt.

We need to add some functionality to our CPU class to check during each call to step whether a raster interrupt occurred. This require us to also inject the Video class into our video class as a dependency. So lets add property and a setter for a video class into our cpu class:

function cpu(memory) {

...
 var myvideo;
...
  this.setVideo = function (video) {
    myvideo = video;
  }
...

}

We will let the index page handle the dependency injection for us:

      ...
      var mycpu = new cpu(mymem);
      myAlarmManager.setCpu(mycpu);
      myInterruptController.setCpu(mycpu);
      mycpu.setInterruptController(myInterruptController);
      var myvideo = new video(document.getElementById("screen"), mymem, mycpu);
      mymem.setVideo(myvideo);
      mycpu.setVideo(myvideo);
      ...

So far, so good. Next we need to add a method to out video class which will indicate for our Cpu whether an raster interrupt has occurred:

  this.vicIntOccured = function() {
    return registers[0x19] >= 128;
  }

Our Cpu needs to invoke this method:

function cpu(memory) {

...
  this.step = function () {
    if ((myInterruptController.getCpuInterruptOcurred() | myvideo.vicIntOccured()) & (interruptflag == 0)) {
        interruptOcurred = 0;
        Push(pc >> 8);
        Push(pc & 0xff);
        breakflag = 0;
        Push(getStatusFlagsAsByte());
        breakflag = 1;
        interruptflag = 1;
        tempVal = localMem.readMem(0xffff) * 256;
        tempVal = tempVal + localMem.readMem(0xfffe);
        pc = tempVal;
    }
...

}

We are almost done. We need to, however, give some attention attention to interrupt acknowledgement on the VIC side, very similar to what we have done in previous blogs with the CIA chip.

The interrupt acknowledge mechanism works a bit different in the VIC than in a CIA chip.

If you don't acknowledge interrupts on a CIA, your CPU will not receive new IRQ's.

The VIC works the the opposite. If you don't acknowledge an interrupt on the VIC, it will keep interrupting the CPU each time you clear the Interrupt disable flag.

Also, on the VIC, to acknowledge an intergroup you need  to write a 1 to the applicable bit in register 19.

With all this changes our writeReg method looks as follows:

  this.writeReg = function (number, value) {
    if (number == 0x19) {
      var temp = ~value & registers[number];
      temp = temp & 0xf;
      registers[number] = (temp > 0) ? (temp | 128) : 0;
    } else {
      registers[number] = value;
    }
  }

Before we take our emulator for a test drive there is just one more hack we should remove:

      function runBatch() {
        if (!running)
          return;
        //myvideo.updateCanvas();
        //var targetCycleCount =  mycpu.getCycleCount() + 20000;
        while (true) { 
          mycpu.step();
          myAlarmManager.processAlarms();
          //if (mycpu.getCycleCount() > 6000000)            
          //  mymem.setSimulateKeypress();
          //var blankingPeriodLow = targetCycleCount - 100;
          //if (myvideo.getCurrentLine() > 284) {
          //  mymem.writeMem(0xD012, 0);
          //} else  {
          //  mymem.writeMem(0xD012, 1);
          //}          if (mycpu.getPc() == breakpoint) {
            stopEm();
            return;
          }
          var framefinished = myvideo.processpixels();
          if (framefinished)            
            return;
        }
        //mycpu.setInterrupt();
      }

This hack stems from the early life of our emulator.

Lets us start our emulator again. This time, the screen looks better:


The majority of the screen is still garbled, but at least is the statusbar visible and the time updating every second.

Lets see what happens when we move around wit the the left/right arrows keys.

There is definitely some movement, and although we cannot see Dan Dare, we can see a battle with a Treen:


We will fix these remaining issues in the next blog or two.

In Summary

In this blog we implemented joystick emulation enabling us to start the game.

There is still a number of issues with rendering the graphic of the game.

We managed to fix the status bar and perfom some basic moves with the joystick.

In the next blog we investigate why the rest of the screen is garbled.

Monday, 18 July 2016

Part 18: Fixing Intro Screen

Foreword

In the previous blog we managed to successfully load the game Dan Dare, but ended up with a garbled intro screen.

In this blog we will investigate why the intro screen shows garbled and fix it.

Investigating the garbled intro screen

Lets start off by investigating why the intro screen looks garbled.

There is some random alpha numeric characters on the screen, suggesting that a custom character set is defined. We know for a fact that we haven't implemented custom characters yet in our emulator.

Let us do a quick refresher on how custom character sets work on the C64.

The VIC-II chip gives you the capability to specify different base locations in memory for the screen character memory and for the character set images. You configure the base addresses via location D018 and DD00. Location DD00 provides you with most significant two bits base address.

Something interesting about the character base addresses is that if it falls within the region 1000h-1fffh or 9000-9fffh in memory, then the VIC-II will read the character info from the character ROM. For all other regions the VIC-II will retrieve the info from RAM.

So, in a nutshell, if you set the character base address, avoiding above mentioned regions, you can define your own character set. Ok, it goes without saying that you actually need to write your character data at the specified base address.

Back to our problem. Lets do some memory inspection to determine the character base address. The inspection gives us the following:
  • Memory location D018: 14
  • Memory location DD00: 17
This translates to base address 1000h, which is in the character ROM space. So, we are actually back to square 1.

For another clue, lets stop the emulator and do some single stepping. Maybe we can see something interesting.

And, indeed there is something interesting:
5876 LDA $d012
5879 CMP #$d2
587b BNE $5876

The emulator is stuck in this loop. Keep checking till rasterline d2 arrives.
Currently  the VIC registers just map to plain RAM registers in our emulator. So reading D012 will keep returning the same result.

It is time we hook up the VIC-II registers to the Video class so we can get some sensitible values when a program reads them.


From our discussion so far, we have a number of things to do in this bog. Lets quickly create a short list:
  • Make Video class responsible for working with VIC-II register memory accesses
  • Change memory class to delegate read/write requests for VICII registers to the Video class
  • Assign a similar responsibility for the Video class regarding the color memory
  • Add a method to the memory class that the video class can call for memory access. This method should closely model the VIC-II memory model. This method should have the following properties:
    • Accept a 14 address
    • Create an effective memory address via location DD00
    • If the effective address is within the range 1000-1fff or 9000-9fff return data to the video class from the character ROM
    • If the effective address is not within above mentioned region return requested contents from the main memory.
The last heading in the short list is necessary because of luring custom character sets.

Tasking Video class with VICII register access

We have decided that VIC II register a should be the responsibility of the video class. There are 46 VIC registers, so lets start with defining a local array within the video class containing 46 elements, with accompanied getters and setters:

function video(mycanvas, mem, cpu) {
...
 var registers = new Uint8Array(0x2e);
...
  this.writeReg = function (number, value) {
    registers[number] = value;
  }

  this.readReg = function (number) {
    return registers[number];
  }
...
}

Lets do the same exercise for the colorRAM:

function video(mycanvas, mem, cpu) {
...
  var colorRAM = new Uint8Array(1000);
...
  this.writeColorRAM = function(number, value) {
    colorRAM[number] = value;
  }

  this.readColorRAM = function(number) {
    return colorRAM[number];
  }...
}

We need to add some functionality when reading D012 to retrieve current raster line number:

  this.readReg = function (number) {
    if (number == 0x11) {
      var bit8 = (cycleline & 0x100) >> 1;
      var temp = (registers[number] & 0x7f) | bit8;
      return temp;
    } else if (number == 0x12) {
      return (cycleline & 0xff);    } else {
      return registers[number];
    }
  }

You will notice that I have also included an entry for location D011. D011 contains the ninth bit of the raster counter.

We now need to change the Video class so that its internal operations also make use of the internal registers:

function video(mycanvas, mem, cpu) {
...
  function displayEnabled() {
    return ((registers[0x11] & 0x10) != 0)
  }
...
  function drawCharline() {
    var bitmapMode = ((registers[0x11] & 0x20) != 0) ? 1 : 0;
    var multicolorMode = ((registers[0x16] & 0x10) != 0) ? 1 : 0;    var screenMode = (multicolorMode << 1) | bitmapMode;
    switch (screenMode) {
      //text mode, normal
      case 0:
        drawTextModeNormal(charPosInMem + cycleInLine - 5);
      break;

      //bitmap mode, normal
      case 1:
      break;
      
      //text mode, multi color
      case 2:
      break;

      //bitmap mode, multi color
      case 3:
        drawBitmapModeMultiColor(charPosInMem + cycleInLine - 5);
      break;
    }
  }

  function drawTextModeNormal(charPos) {
    var screenCode = localMem.readMem(1024 + charPos);
    var currentLine = localMem.readCharRom((screenCode << 3) + ((cycleline - 42) & 7));
    var textColor = colorRAM[charPos] & 0xf;
    var backgroundColor = registers[0x21] & 0xf;    var currentCol = 0;
    for (currentCol = 0; currentCol < 8; currentCol++) {
      var pixelSet = (currentLine & 0x80) == 0x80;
      if (pixelSet) {
        imgData.data[posInCanvas + 0] = colors[textColor][0];
        imgData.data[posInCanvas + 1] = colors[textColor][1];
        imgData.data[posInCanvas + 2] = colors[textColor][2];
        imgData.data[posInCanvas + 3] = 255;
      } else {
        imgData.data[posInCanvas + 0] = colors[backgroundColor][0];
        imgData.data[posInCanvas + 1] = colors[backgroundColor][1];
        imgData.data[posInCanvas + 2] = colors[backgroundColor][2];
        imgData.data[posInCanvas + 3] = 255;
      }
      currentLine = currentLine << 1;
      posInCanvas = posInCanvas + 4;
   }

  }

  function drawBitmapModeMultiColor(charPos) {
    var currentLine = localMem.readMem(0xe000+(charPos << 3) + ((cycleline - 42) & 7));
    var textColor = colorRAM[charPos];
    var backgroundColor = registers[0x21];    var color1 = (localMem.readMem(49152 + charPos) & 0xf0) >> 4;
    var color2 = localMem.readMem(49152 + charPos) & 0xf;
    var color3 = colorRAM[charPos] & 0xf;
    var colorArray = [backgroundColor, color1, color2, color3];
    var pixPair = 0;
    for (pixPair = 0; pixPair < 4; pixPair++) {
      var colorValue = (currentLine >> 6) & 3;
      imgData.data[posInCanvas + 0] = colors[colorArray[colorValue]][0];
      imgData.data[posInCanvas + 1] = colors[colorArray[colorValue]][1];
      imgData.data[posInCanvas + 2] = colors[colorArray[colorValue]][2];
      imgData.data[posInCanvas + 3] = 255;
      imgData.data[posInCanvas + 4] = colors[colorArray[colorValue]][0];
      imgData.data[posInCanvas + 5] = colors[colorArray[colorValue]][1];
      imgData.data[posInCanvas + 6] = colors[colorArray[colorValue]][2];
      imgData.data[posInCanvas + 7] = 255;

      currentLine = currentLine << 2;
      posInCanvas = posInCanvas + 8;
   }

  }


  function fillBorderColor() {
    var borderColor = registers[0x20] & 0xf;
    var i;
    for (i = 0; i < 8; i++ ) {
      imgData.data[posInCanvas + 0] = colors[borderColor][0];
      imgData.data[posInCanvas + 1] = colors[borderColor][1];
      imgData.data[posInCanvas + 2] = colors[borderColor][2];
      imgData.data[posInCanvas + 3] = 255;
      posInCanvas = posInCanvas + 4;
    }

  }
...
}


We know need to modify the Memory class to use the Video class for VIC register accesses. It would probably make sense to add another statement to the nested if statement of readMem and writeMem. But, we already have IO region related entry for CIA access. I think it would look less clunky if we collate all IO region access into a mthod of its own.

With this in mind, our readMem and writeMem method will look as follows:

  this.readMem = function (address) {
    if ((address >= 0xa000) & (address <=0xbfff))
      return basicRom[address & 0x1fff];
    else if ((address >= 0xe000) & (address <=0xffff) & kernelEnabled())
      return kernalRom[address & 0x1fff];
    else if ((address >= 0xd000) & (address <= 0xdfff) & IOEnabled()) {
      return IORead(address);
    } else if (address == 1) {
      var temp = mainMem[address] & 239;
      if (!playPressed)
        temp = temp | 16;
      return temp;
    }
    return mainMem[address];
  }

  this.writeMem = function (address, byteval) {
    if ((address >= 0xd000) & (address <= 0xdfff) & IOEnabled()) {
      IOWrite(address, byteval);
      return;
    } else if (address == 1) {
      var temp = byteval & 32;
      temp = temp >> 5;
      tape.setMotorOn(temp);
    }   
    mainMem[address] = byteval;
  }

The IORead and IOWrite methods look as follows:

  function IORead(address) {
    if ((address >= 0xdc00) & (address <= 0xdcff)) {
      return ciaRead(address);
    } else if ((address >= 0xd000) & (address <= 0xd02e)) {
      return myVideo.readReg(address - 0xd000);
    } else if ((address >= 0xd800) & (address <= 0xdbe8)) {
      return myVideo.readColorRAM (address - 0xd800);
    } else {
      return IOUnclaimed[address - 0xd000];
    } 
  }

  function IOWrite(address, value) {
    if ((address >= 0xdc00) & (address <= 0xdcff)) {
      return ciaWrite(address, value);
    } else if ((address >= 0xd000) & (address <= 0xd02e)) {
      return myVideo.writeReg(address - 0xd000, value);
    } else if ((address >= 0xd800) & (address <= 0xdbe8)) {
      return myVideo.writeColorRAM (address - 0xd800, value);
    } else {
      IOUnclaimed[address - 0xd000] = value;
      return;
    } 
  }

You will see in this code the use of an array called IOUnclaimed. This is for IO devices that we haven't implemented yet like CIA2 and SID. This just to encapsulate the block switch in/out functionality for the IO region. For completeness, let me show the declaration of the IOUnclaimed array:

..
 var IOUnclaimed = new Uint8Array(4096);
...

The Memory class is now dependant on the Video class, so we need to add a setter in the memory class:

  this.setVideo = function(video) {
    myVideo = video;
  }

This setter needs to be called by the index page during initialisation just after the a video instance is created:

...
      var myvideo = new video(document.getElementById("screen"), mymem, mycpu);
      mymem.setVideo(myvideo);
...

VIC II memory accesses

We now need to add a method to the memory class that the video class can call for memory access. This method will comply to the VIC-II memory model.

Here is the code for that method:

  this.vicRead = function(address) {
    var topBits = IOUnclaimed[0xd00] & 3;
    topBits = 3 - topBits;
    var effectiveAddress = (topBits << 14) | address;
    if ((effectiveAddress >= 0x9000) & (effectiveAddress < 0xa000)) {
      effectiveAddress = effectiveAddress & 0xfff;
      return charRom[effectiveAddress];
    } else if ((effectiveAddress >= 0x1000) & (effectiveAddress < 0x2000)) {
      effectiveAddress = effectiveAddress & 0xfff;
      return charRom[effectiveAddress];
    } else {
      return mainMem[effectiveAddress];
    }
  }


This method gets bits 14 and 15 of the effective address from location DD00. It also reads the requested from character ROM if the effective address is within the range 1000-1fff or 9000-9fff.

We now need to modify the video class to use the method vicRead:

  function drawTextModeNormal(charPos) {
    var baseCharAdd = (registers[0x18] >> 1) & 7;    
    baseCharAdd = baseCharAdd << 11;    
    var baseScreenAdd = (registers[0x18] >> 4) & 0xf;    
    baseScreenAdd = baseScreenAdd << 10;    
    var screenCode = localMem.vicRead(baseScreenAdd + charPos);
    var currentLine = localMem.vicRead(baseCharAdd + (screenCode << 3) + ((cycleline - 42) & 7));    var textColor = colorRAM[charPos] & 0xf;
    var backgroundColor = registers[0x21] & 0xf;
    var currentCol = 0;
    for (currentCol = 0; currentCol < 8; currentCol++) {
      var pixelSet = (currentLine & 0x80) == 0x80;
      if (pixelSet) {
        imgData.data[posInCanvas + 0] = colors[textColor][0];
        imgData.data[posInCanvas + 1] = colors[textColor][1];
        imgData.data[posInCanvas + 2] = colors[textColor][2];
        imgData.data[posInCanvas + 3] = 255;
      } else {
        imgData.data[posInCanvas + 0] = colors[backgroundColor][0];
        imgData.data[posInCanvas + 1] = colors[backgroundColor][1];
        imgData.data[posInCanvas + 2] = colors[backgroundColor][2];
        imgData.data[posInCanvas + 3] = 255;
      }
      currentLine = currentLine << 1;
      posInCanvas = posInCanvas + 4;
   }

  }

  function drawBitmapModeMultiColor(charPos) {
    var baseCharAdd = (registers[0x18] >> 1) & 7;    
    baseCharAdd = baseCharAdd << 11;    
    var baseScreenAdd = (registers[0x18] >> 4) & 0xf;    
    baseScreenAdd = baseScreenAdd << 10;    
    var currentLine = localMem.vicRead(baseCharAdd+(charPos << 3) + ((cycleline - 42) & 7));    var textColor = colorRAM[charPos];
    var backgroundColor = registers[0x21];
    var color1 = (localMem.vicRead(baseScreenAdd + charPos) & 0xf0) >> 4;
    var color2 = localMem.vicRead(baseScreenAdd + charPos) & 0xf;    var color3 = colorRAM[charPos] & 0xf;
    var colorArray = [backgroundColor, color1, color2, color3];
    var pixPair = 0;
    for (pixPair = 0; pixPair < 4; pixPair++) {
      var colorValue = (currentLine >> 6) & 3;
      imgData.data[posInCanvas + 0] = colors[colorArray[colorValue]][0];
      imgData.data[posInCanvas + 1] = colors[colorArray[colorValue]][1];
      imgData.data[posInCanvas + 2] = colors[colorArray[colorValue]][2];
      imgData.data[posInCanvas + 3] = 255;
      imgData.data[posInCanvas + 4] = colors[colorArray[colorValue]][0];
      imgData.data[posInCanvas + 5] = colors[colorArray[colorValue]][1];
      imgData.data[posInCanvas + 6] = colors[colorArray[colorValue]][2];
      imgData.data[posInCanvas + 7] = 255;

      currentLine = currentLine << 2;
      posInCanvas = posInCanvas + 8;
   }

  }

A Test Run

With all these changes, lets do a test run.

The emulator didn't start so well:

Some closer investigation revealed that the problem is caused by the 6510 CPU register at memory location 1.

At startup I assign a value of 3. This is sufficient to ensure that your system boots, but it disables the IO Region. This has the effect that the IO register  at location DD00 misses the write from the CPU, so the Video class gets its data from the wrong bank.

We obviously got away with this previously because we haven't implemented complete bank switching of the IO region. All IO writes/reads went straight to the main memory.

A save value to write to memory location 1 at startup is ffh. So, lets adjust this value and restart the emulator.

This time the world is back to normal. So, lets start loading Dan Dare and see how far our emulator can get this time.

This time around the intro screen is just a blank screen.

Some investigation revealed that something switched to multicolor text mode. This is puzzling, but lets anyway implement multi color text mode in the video class.

First we create a method within the Video class for the functionality:

  function drawTextModeMultiColor(charPos) {
    var baseCharAdd = (registers[0x18] >> 1) & 7;
    baseCharAdd = baseCharAdd << 11;
    var baseScreenAdd = (registers[0x18] >> 4) & 0xf;
    baseScreenAdd = baseScreenAdd << 10;
    var screenCode = localMem.vicRead(baseScreenAdd + charPos);
    var currentLine = localMem.vicRead(baseCharAdd + (screenCode << 3) + ((cycleline - 42) & 7));
    var textColor = colorRAM[charPos] & 0xf;
    if ((textColor & 8) == 0)
      return drawTextModeNormal(charPos);
    var backgroundColor = registers[0x21];
    var color1 = registers[0x22];
    var color2 = registers[0x23];
    var color3 = textColor;
    var colorArray = [backgroundColor, color1, color2, color3];
    var pixPair = 0;
    for (pixPair = 0; pixPair < 4; pixPair++) {
      var colorValue = (currentLine >> 6) & 3;
      imgData.data[posInCanvas + 0] = colors[colorArray[colorValue]][0];
      imgData.data[posInCanvas + 1] = colors[colorArray[colorValue]][1];
      imgData.data[posInCanvas + 2] = colors[colorArray[colorValue]][2];
      imgData.data[posInCanvas + 3] = 255;
      imgData.data[posInCanvas + 4] = colors[colorArray[colorValue]][0];
      imgData.data[posInCanvas + 5] = colors[colorArray[colorValue]][1];
      imgData.data[posInCanvas + 6] = colors[colorArray[colorValue]][2];
      imgData.data[posInCanvas + 7] = 255;

      currentLine = currentLine << 2;
      posInCanvas = posInCanvas + 8;
   }
  }

Then we add an item to the color mode switch selector:

  function drawCharline() {
    var bitmapMode = ((registers[0x11] & 0x20) != 0) ? 1 : 0;
    var multicolorMode = ((registers[0x16] & 0x10) != 0) ? 1 : 0;
    var screenMode = (multicolorMode << 1) | bitmapMode;
    switch (screenMode) {
      //text mode, normal
      case 0:
        drawTextModeNormal(charPosInMem + cycleInLine - 5);
      break;

      //bitmap mode, normal
      case 1:
      break;
      
      //text mode, multi color
      case 2:
         drawTextModeMultiColor(charPosInMem + cycleInLine - 5);
      break;

      //bitmap mode, multi color
      case 3:
        drawBitmapModeMultiColor(charPosInMem + cycleInLine - 5);
      break;
    }


Lets restart our emulator once again with these changes and see how far our emulator can get.

This time something very interesting happened. The intro screen flash for a split of a second and then we are presented with the following screen:


We can some how make out that this is the game play screen, but it is also garbled. This is however an issue for the next blog.

We are more interested at this point in time, why the intro screen is skipped. It is as if something triggered the fire button.

To validate this theory on the fire button we need to know how joysticks interface on the Commodore 64. We will cover this in the next section.

Joystick Specifics

The snippet of a schematic shows how the two joystick ports is connected on the C64:


The two joystick ports is named as Control Port 1 and Control Port 2 on the schematic. These two ports are connected to port A and port B respectively of the CIA chip number 1.

The two joystick ports therefore share the same lines as the keyboard.

Now a bit of the anatomy of a C64 joystick. For each of the following directions there is an associated switch in the joystick: North, South, West, East. So, if for instance you move the joystick in a northerly direction, the north switch will close. Likewise, for instance if you move the joystick in an eastern direction, the East switch will close.

What happens if you move the joystick in an diagonal direction, like North-East? Then two switches will close at once. For the North-East direction both the North switch and the East switch will close.

One fact worth mentioning is that when a joystick switch closely, a logic zero will be registered on the associated line on the CIA. Every switch that is not closed will register as a logic one.

This is starting to give us a clue on to why a firebutton is initiated shortly after the Dan Dare intro screen is shown.

When we read CIA#1 port A in our emulator a zero will be returned, because we never have initialised this location. In the joystick world a zero means that its direction switches are closed!

So, we should actually add a check that when CIA#1 port A is read we should return ffh. Lets implement this right away in the ciaRead method:

  function ciaRead(address) {
    if (address == 0xdc00) {
      return 0xff;    } else if (address == 0xdc01) {
      return keyboardInstance.getColumnByte(mainMem[0xdc00]);
    } else if (address == 0xdc04) {
      return mytimerA.getTimerLow();
    } else if (address == 0xdc05) {
      return mytimerA.getTimerHigh();
    } else if (address == 0xdc06) {
      return mytimerB.getTimerLow();
    } else if (address == 0xdc07) {
      return mytimerB.getTimerHigh();
    } else if (address == 0xdc0d) {
      return myinterruptController.getInterrupts();
    } else if (address == 0xdc0e) {
      return mytimerA.getControlRegister();
    } else if (address == 0xdc0f) {
      return mytimerB.getControlRegister();
    } else {
      return mainMem[address];
    }

  }


In the next blog we will worry about properly emulating a joystick.

Lets see how the emulator behaves with this change.

At last! A proper intro screen:


In Summary

In this blog we managed to fix the garbled screen.

This investigation evolved to a refactoring exercise whereby we moved the responsibility of VICII register access to the video class. We also added
 a method to the Memory class that the Video class can use for memory class.

We also implemented another screen mode: multi color text model.

Finally, we had to fix CIA#1 emulation so that it correctly emulate a joystick with all its switches open.

In the next blog we will work on properly implementing the joystick.

Till next time!

Thursday, 14 July 2016

Part 17: Rendering the splash screen

Foreword

In the previous blog we managed to emulate the flashing borders of the loader. In this blog we will emulate the splash screen.

The screenshot of the splash screen might be copyrighted. I am not sure. If contacted by the publisher of the Dan Dare I will remove the screenshot by request.


Theory on the VIC-II graphic Modes

Before we figure out how to emulate rendering the splash screen, lets cover a bit of theory on the graphic modes of the VIC II.

Let us first zoom into what defines the graphic modes in the VIC II. There is two important bits:
  • Bit 5 of register D011: Switch bitmap mode on/off
  • Bit 4 of register D016: Switch multi-color mode on/off
Another important location for the graphic modes is location D018. This location tells the VIC-II where in RAM to retrieve its display information from. The bits on this register are as follows:
  • Bit#0: not used
  • Bit#1-#3: Address Bits 11-13 of the Character Set (*2048)
  • Bit#4-#7: Address Bits 10-13 of the Screen RAM (*1024) 
In bitmap mode, the character address is used to indicate where in memory the bitmap is stored.

You might notice that Most Significant bit (MSB) of these two base addresses is bit number 13 and not bit number 15. So, does this mean that the VIC can only acess the first 16KB of memory? Luckily not! Bits 14 and 15 is supplied by register DD00 (a register of CIA 2).

The following image from http://dustlayer.com/vic-ii/2013/4/22/when-visibility-matters explains very nicely how tese two bits work:

Notice the inverse bit order.

Emulating the Splash Screen

To emulate the splash screen we need to find out what is the graphics mode while the splash screen is displaying.

So, lets kick off the loading process and wait for the following screen to appear:


Add this point click the stop button. We now need to inspect the contents of location D011 and D016 to determine the graphics mode:

  • Location D011 has value 3B. Bit 5 is switched on, meaning bitmap mode is enabled
  • Location D016 has value D8. Bit 4 is switched on, meaning multi color mode is enabled.
So, the splash screen ses multi color bitmap mode. In multi color two bits used to represent a pixel. Each pixel is therefore twice as wide as an ordinary pixel, effectively cutting the horizontal resolution  into halve. However, each pixel you can represent four possible colors.

The four colors used in text-multicolor mode differs a bit from the four colours used in bitmap multi-colour mode. For now, we will only worry about bitmap multi-colour mode.

The following table, again taken from the dustlayer website, explain the four colors of multi-color bitmap mode:


Next, lets determine the bitmap/ screen RAM base addresses. First lets get hold of bit 14 & 15 via location DD00. The value we get via inspection is 4. The lowest two bits are zero. Via the lookup table from the previous section we determine that the VIC II operate in the bank C000-FFFF.

Lets try to put everything in code.The magic will happen with the method drawCharline:

  function drawCharline() {
    var bitmapMode = ((localMem.readMem(0xd011) & 0x20) != 0) ? 1 : 0;
    var multicolorMode = ((localMem.readMem(0xd016) & 0x10) != 0) ? 1 : 0;
    var screenMode = (multicolorMode << 1) | bitmapMode;
    switch (screenMode) {
      //text mode, normal
      case 0:
        drawTextModeNormal(charPosInMem + cycleInLine - 5);
      break;

      //bitmap mode, normal
      case 1:
      break;
      
      //text mode, multi color
      case 2:
      break;

      //bitmap mode, multi color
      case 3:
        drawBitmapModeMultiColor(charPosInMem + cycleInLine - 5);
      break;
    }
  } 

We start by throwing the bitmap-bit and the multi-color bit together to form a number. We then use a switch statement to serve the applicable graphics mode. For now, only Textmode-normal, and bitmap-multicolor is defined.

Text mode normal looks as follows:

  function drawTextModeNormal(charPos) {
    var screenCode = localMem.readMem(1024 + charPos);
    var currentLine = localMem.readCharRom((screenCode << 3) + ((cycleline - 42) & 7));
    var textColor = localMem.readMem(0xd800 + charPos) & 0xf;
    var backgroundColor = localMem.readMem(0xd021) & 0xf;
    var currentCol = 0;
    for (currentCol = 0; currentCol < 8; currentCol++) {
      var pixelSet = (currentLine & 0x80) == 0x80;
      if (pixelSet) {
        imgData.data[posInCanvas + 0] = colors[textColor][0];
        imgData.data[posInCanvas + 1] = colors[textColor][1];
        imgData.data[posInCanvas + 2] = colors[textColor][2];
        imgData.data[posInCanvas + 3] = 255;
      } else {
        imgData.data[posInCanvas + 0] = colors[backgroundColor][0];
        imgData.data[posInCanvas + 1] = colors[backgroundColor][1];
        imgData.data[posInCanvas + 2] = colors[backgroundColor][2];
        imgData.data[posInCanvas + 3] = 255;
      }
      currentLine = currentLine << 1;
      posInCanvas = posInCanvas + 4;
   }

  }


Pretty straightforward!

Next look at the bitmap-multicolor mode:

  function drawBitmapModeMultiColor(charPos) {
    var currentLine = localMem.readMem(0xe000+(charPos << 3) + ((cycleline - 42) & 7));
    var textColor = localMem.readMem(0xd800 + charPos);
    var backgroundColor = localMem.readMem(0xd021);
    var color1 = (localMem.readMem(49152 + charPos) & 0xf0) >> 4;
    var color2 = localMem.readMem(49152 + charPos) & 0xf;
    var color3 = localMem.readMem(0xd800 + charPos) & 0xf;
    var colorArray = [backgroundColor, color1, color2, color3];
    var pixPair = 0;
    for (pixPair = 0; pixPair < 4; pixPair++) {
      var colorValue = (currentLine >> 6) & 3;
      imgData.data[posInCanvas + 0] = colors[colorArray[colorValue]][0];
      imgData.data[posInCanvas + 1] = colors[colorArray[colorValue]][1];
      imgData.data[posInCanvas + 2] = colors[colorArray[colorValue]][2];
      imgData.data[posInCanvas + 3] = 255;
      imgData.data[posInCanvas + 4] = colors[colorArray[colorValue]][0];
      imgData.data[posInCanvas + 5] = colors[colorArray[colorValue]][1];
      imgData.data[posInCanvas + 6] = colors[colorArray[colorValue]][2];
      imgData.data[posInCanvas + 7] = 255;

      currentLine = currentLine << 2;
      posInCanvas = posInCanvas + 8;
   }

  }

A couple of things happening:
  • A 4 colour tablet is created for the pixels to be drawn
  • 2 pixels is shifted at a time. The resulting pixel color is drawn twice
  • You will also notice that we are adding 49152 to the address when we are reading from memory. This is a bit of hardcoding just to get the splash screen displayed
This time, when we load the game, the splash screen gets displayed:

So, lets wait and see how far our emulator can get with loading the game. After about five minutes the game loading gets stuck with the following screen:

What went wrong? In the next section we will do some investigations...

Investigating why game loading got stuck

Time for some debugging again. We will start by waiting for the moment when the splash screen has just disappeared then we will stop our emulator.

At this point we need to add a breakpoint at a suitable place to avoid endless stepping. A typical place would be where the bit read gets shifted into a temporary memory location. Here is the piece of assembly where it is happening:

0363   AD 0D DC   LDA $DC0D
0366   8E 0F DC   STX $DC0F
0369   4A         LSR A
036A   4A         LSR A
036B   26 C0      ROL $C0
036D   A5 C0      LDA $C0
036F   88         DEY
0370   F0 00      BEQ $0372

We will place a breakpoint at location 36B.

The Y register plays a crucial role in this piece of code. Since, at this point in time we are reading bytes, the Y register decrements from 8 each time till it reaches 0. So, we RUN a couple of times till Y has the value of one.When Y is one we slowly step through...

The interesting line is as follows:

03ba STA ($f9),Y

From this we see that F9/FA stores the current address where the next byte read should be written to.

If you read further down the assembly code, you will also discover that the end address is stored at FB/FC for the current block:

03BA   91 F9      STA ($F9),Y
03BC   A0 05      LDY #$05
03BE   84 01      STY $01
03C0   45 AF      EOR $AF
03C2   85 AF      STA $AF
03C4   E6 F9      INC $F9
03C6   D0 05      BNE $03CD
03C8   EE 20 D0   INC $D020
03CB   E6 FA      INC $FA
03CD   A5 F9      LDA $F9
03CF   C5 FB      CMP $FB
03D1   D0 0B      BNE $03DE
03D3   A5 FA      LDA $FA
03D5   C5 FC      CMP $FC
03D7   D0 05      BNE $03DE


So lets remove the breakpoint and let it run for intervals of about two seconds or so.

At one point you will notice that the game loader starts to load information in the peripheral region (D000-E000). This sounds dangerous. If you, however, look at the assembly instruction at location 3B8, we will realise that the peripheral region is disabled when the write happens:

03B8   84 01      STY $01

At this point a zero gets written to location 1. When you write a zero to this location, RAM will be visible at all three areas (e.g. BASIC ROM, KERNEL ROM and IO region).

Our emulator, however, doesn't check for this. To cater for this scenario, we need to make the following adjustment to our memory class:

  function IOEnabled() {
    var temp = mainMem[1] & 3;
    var temp2 = mainMem[1] & 4;
    return (temp2 != 0) & (temp != 0);
  }


  this.readMem = function (address) {
    if ((address >= 0xa000) & (address <=0xbfff))
      return basicRom[address & 0x1fff];
    else if ((address >= 0xe000) & (address <=0xffff) & kernelEnabled())
      return kernalRom[address & 0x1fff];
    else if ((address >= 0xdc00) & (address <= 0xdcff) & IOEnabled()) {
      return ciaRead(address);
    } else if (address == 1) {
      var temp = mainMem[address] & 239;
      if (!playPressed)
        temp = temp | 16;
      return temp;
    }
    return mainMem[address];
  }

  this.writeMem = function (address, byteval) {
    if ((address >= 0xdc00) & (address <= 0xdcff) & IOEnabled()) {
      ciaWrite(address, byteval);
      return;
    } else if (address == 1) {
      var temp = byteval & 32;
      temp = temp >> 5;
      tape.setMotorOn(temp);
    }      
    mainMem[address] = byteval;
  }

Lets restart our emulator with these changes. This time we past the stuck screen:

You can sort of identify the intro screen between the garbled characters. We will fix the intro screen in the next blog.

In Summary

In this blog we managed to emulate the spalsh screen. In the next blog I will fix up the Intro screen.

Till next time!

Friday, 8 July 2016

Part 16: Emulating flashing Borders

Foreword

In the previous blog we finished off with adding tape emulation to our emulator.

In this blog we will try and see if we could emulate the flashing borders of the loader for game Dan Dare. This will necessitate us to jack up our video class by adding colours to the video output. 

The flashing border effect

So, how does the tape loader achieve the flashing border effect?

The tape loader achieves the flashing border effect by constantly changing the border colour while the VIC II video chip draws the frame.

For our emulator to simulate this effect successfully, this would mean that we need to implement some form of scan line rendering.

Currently our emulator doesn't do scan line-based rendering, but frame based rendering. This means, we execute the amount of instructions that would fit into a frame display period, which is 20000 CPU cycles.

So, let us start by doing a bit of research on the relationship of CPU cycles to screen pixels.

There is a very nice picture on the following web site explaining very nicely the relationship:

http://dustlayer.com/vic-ii/2013/4/25/vic-ii-for-beginners-beyond-the-screen-rasters-cycle


First of all this picture tells us that the CPU completes 63 clock cycles per scan line. Also, in one CPU clock cycle, 8 pixels gets drawn to the screen.

It is also interesting to note that for each line of 63 cycles, there is 13 cycles for which there is no pixels drawn. These cycles are during the horizontal blanking period.

Although we don't need to draw anything for these 13 cycles per line, it is still important that we account for these cycles to get our timing right.

As you get a horizontal blanking period, you also get a vertical blanking period. The vertical blanking period is towards the end of the frame, wrapping to the beginning of the next frame. This period amounts to about 28 lines that amounts to nothing drawn unto the screen. These lines we also need to account for to get our timing write.

Implementing scan line rendering

So, lets implement scan line rendering into our emulator.

First, we need to get hold of RGB values for the 16 colours on the C64.

If you do a Internet search for these RGB values, you will get different values all over the show. This is probably as expected, since we all have different memories of these colours on these colours on our television sets at that time when the C64 was popular.

I am settling for the colour tablet from the following website:

https://www.c64-wiki.com/index.php/Color

With all this information, our video class looks as follows:

function video(mycanvas, mem, cpu) {
  var localMem = mem;
  var ctx = mycanvas.getContext("2d");
  var mycpu = cpu;
  var cpuCycles = 0;
  var cycleInLine = 0;
  var cycleline = 0;
  var charPosInMem = 0;  
  var posInCanvas = 0;
  var imgData = ctx.createImageData(400, 284);


  const colors = [[0, 0, 0],
                  [255, 255, 255],
                  [136, 0, 0],
                  [170, 255, 238],
                  [204, 68, 204],
                  [0, 204, 85],
                  [0, 0, 170],
                  [238, 238, 119],
                  [221, 136, 85],
                  [102, 68, 0],
                  [255, 119, 119],
                  [51, 51, 51],
                  [119, 119, 119],
                  [170, 255, 102],
                  [0, 136, 255],
                  [187, 187, 187]];

  this.getCurrentLine = function() {
    return cycleline;
  }

  this.processpixels = function() {
    var numBytes = mycpu.getCycleCount() - cpuCycles;
    cpuCycles = mycpu.getCycleCount();
    var i;
    for (i = 0; i < numBytes; i++) {
      if (isVisibleArea()) {
        if (isPixelArea() & displayEnabled()) {
          drawCharline();
        } else {
          fillBorderColor();
        }
      }

      cycleInLine++;

      if (cycleInLine > 63) {
        cycleInLine = 0;
        cycleline++;
        updateCharPos();
      }
      if (cycleline > 311) {
        cycleline = 0;
        ctx.putImageData(imgData,0,0);
        posInCanvas = 0;
        charPosInMem = 0;
        //imgData = ctx.createImageData(400, 284);
        return true;
      }


    }
      return false;
  }

  function displayEnabled() {
    return ((localMem.readMem(0xd011) & 0x10) != 0)
  }

  function updateCharPos() {
    if ( !((cycleline > 41) & (cycleline < (42 + 200))) )
      return;
    var lineInScreen = cycleline - 42;
    if (lineInScreen == 0) {
      charPosInMem = 0;
      return;
    }
    if ((lineInScreen & 7) == 0) {
      charPosInMem = charPosInMem + 40;
    }
  }


  function drawCharline() {
    var screenCode = localMem.readMem(1024 + charPosInMem + cycleInLine - 5);
    var currentLine = localMem.readCharRom((screenCode << 3) + ((cycleline - 42) & 7));
    var textColor = localMem.readMem(0xd800 + charPosInMem + cycleInLine - 5);
    var backgroundColor = localMem.readMem(0xd021);
    for (currentCol = 0; currentCol < 8; currentCol++) {
      var pixelSet = (currentLine & 0x80) == 0x80;
      if (pixelSet) {
        imgData.data[posInCanvas + 0] = colors[textColor][0];
        imgData.data[posInCanvas + 1] = colors[textColor][1];
        imgData.data[posInCanvas + 2] = colors[textColor][2];
        imgData.data[posInCanvas + 3] = 255;
      } else {
        imgData.data[posInCanvas + 0] = colors[backgroundColor][0];
        imgData.data[posInCanvas + 1] = colors[backgroundColor][1];
        imgData.data[posInCanvas + 2] = colors[backgroundColor][2];
        imgData.data[posInCanvas + 3] = 255;
      }
      currentLine = currentLine << 1;
      posInCanvas = posInCanvas + 4;
   }
  }

  function fillBorderColor() {
    var borderColor = localMem.readMem(0xd020) & 0xf;
    var i;
    for (i = 0; i < 8; i++ ) {
      imgData.data[posInCanvas + 0] = colors[borderColor][0];
      imgData.data[posInCanvas + 1] = colors[borderColor][1];
      imgData.data[posInCanvas + 2] = colors[borderColor][2];
      imgData.data[posInCanvas + 3] = 255;
      posInCanvas = posInCanvas + 4;
    }

  }

  function isVisibleArea() {
    return (cycleInLine < 50) & (cycleline < 284);
  }

  function isPixelArea() {
    var visibleColumn = (cycleInLine > 4) & (cycleInLine < (5+40));
    var visibleRow = (cycleline > 41) & (cycleline < (42 + 200));
    return (visibleColumn & visibleRow);
  }

}

Let us quickly pause and explain this code. The class start with the declaration of a two dimensional array storing the RGB values for our colour tablet.

Obviously, the updateCanvas method has became redundant. We now make use use of a new method called processpixels.

The processpixels method needs to be invoked each time a 6502 instruction was executed. processpixels starts off by determining  how many cycles has passed since executing the previous instruction. For each cycle an 8 pixel lines segment gets drawn. During the looping process we also keep track of the following:
  • Whether the pixel segment is a segment in the border
  • Whether the pixel segment is a segment in the character area.
  • Whether the pixel segment is a segment in the vertical/horizontal blanking area.
You will also noticed that I have added a method called displayEnabled. This method checks the enabled bit in memory location D011. I have added this to emulate the screen blanking during tape searching/loading.
For completeness, let us see how this method will be invoked in the runBatch method:

      function runBatch() {
        if (!running)
          return;
        //myvideo.updateCanvas();
        //var targetCycleCount =  mycpu.getCycleCount() + 20000;
        while (true) { 
          mycpu.step();
          myAlarmManager.processAlarms();
          //if (mycpu.getCycleCount() > 6000000)            
          //  mymem.setSimulateKeypress();
          //var blankingPeriodLow = targetCycleCount - 100;
          if (myvideo.getCurrentLine() > 284) { 
            mymem.writeMem(0xD012, 0);
          } else  {
            mymem.writeMem(0xD012, 1);
          }
          if (mycpu.getPc() == breakpoint) {
            stopEm();
            return;
          }
          var framefinished = myvideo.processpixels();
          if (framefinished)
            return;        }
        //mycpu.setInterrupt();
      }
      

You will notice that the main loop within the runBatch method is now an endless loop, depending on the processpixels method to tell it when to exit.

One fundamental change is also the dimensions of the canvas we are working with. The new dimensions is now 400x284, since we need to cater for the border. This dimension needs to be applied to the video class as well as our index page.

A Test Run

Let us give our changes a test run:


Cool! Everything starts to look very familiar now :-)

Let us now see how our emulator behaves when we load DAN DARE.

This time not so promising. We get up to the point where our emulator says loading/ready and then it just hangs.

It is time we get our fingers dirty and dig into the loader code for Dan Dare.

We need to inspect the header to determine at which memory location the loader code gets loaded into. We know that the header gets loaded into 33Ch, so the only thing we need to do is to hit the STOP button when we see FOUND... From that point we can just do some memory inspection to get the address.


The byte in 33c gives you the type of file. Type 3 corresponds to a non-relocatable file. The next two bytes is the start address and the two bytes following is the end address.

From the screenshot we can deduce that the start address is 2a7 and the end address is 304.

From this address range it is clear that some vector addresses will be overwritten.

The question is which vector address will be overwritten. To get this answer we need to load the rest of the loader and inspect the vectors. We can just resume execution of the emulator. But, before we resume, lets just add a breakpoint on address at fc93. This will halt the emulator just after the loader code is loaded into memory.

Inspecting the first couple of vectors yield the following:

300 = e38b
302 = 351
304 = a57c

We can clearly see that the vector at 302 is overwritten. This is the address of the basic idle loop. The address 351 is a memory address within the cassette buffer. So, it appear that the loader use part of the bytes in the header to store some machine code.

Lets do some disassembly of the code at 351:

                  * = 0351
0351   20 A7 02   JSR $02A7
0354   A5 FD      LDA $FD
0356   05 FE      ORA $FE
0358   F0 F7      BEQ $0351
035A   20 60 03   JSR $0360
035D   4C 51 03   JMP $0351
0360   6C FD 00   JMP ($00FD)
0363   AD 0D DC   LDA $DC0D
0366   8E 0F DC   STX $DC0F
0369   4A         LSR A
036A   4A         LSR A
036B   26 C0      ROL $C0
036D   A5 C0      LDA $C0
036F   88         DEY
0370   F0 00      BEQ $0372
0372   AD 20 D0   LDA $D020
0375   49 05      EOR #$05
0377   8D 20 D0   STA $D020
037A   40         RTI
037B   C9 AA      CMP #$AA
037D   D0 6E      BNE $03ED
037F   C6 C1      DEC $C1
0381   D0 05      BNE $0388
0383   A9 19      LDA #$19
0385   8D 71 03   STA $0371
0388   A0 02      LDY #$02
038A   40         RTI
038B   C8         INY
038C   4A         LSR A
038D   B0 5E      BCS $03ED
038F   C9 50      CMP #$50
0391   D0 F5      BNE $0388
0393   A9 25      LDA #$25
0395   D0 44      BNE $03DB
0397   85 FB      STA $FB
0399   EE 98 03   INC $0398
039C   AD 98 03   LDA $0398
039F   C9 FF      CMP #$FF
03A1   D0 3B      BNE $03DE
03A3   A5 F7      LDA $F7
03A5   CD FD 02   CMP $02FD
03A8   D0 43      BNE $03ED
03AA   AD 11 D0   LDA $D011
03AD   29 EF      AND #$EF
03AF   05 F8      ORA $F8
03B1   8D 11 D0   STA $D011
03B4   A9 46      LDA #$46
03B6   D0 23      BNE $03DB
03B8   84 01      STY $01
03BA   91 F9      STA ($F9),Y
03BC   A0 05      LDY #$05
03BE   84 01      STY $01
03C0   45 AF      EOR $AF
03C2   85 AF      STA $AF
03C4   E6 F9      INC $F9
03C6   D0 05      BNE $03CD
03C8   EE 20 D0   INC $D020
03CB   E6 FA      INC $FA
03CD   A5 F9      LDA $F9
03CF   C5 FB      CMP $FB
03D1   D0 0B      BNE $03DE
03D3   A5 FA      LDA $FA
03D5   C5 FC      CMP $FC
03D7   D0 05      BNE $03DE
03D9   A9 6F      LDA #$6F
03DB   8D 71 03   STA $0371
03DE   A0 08      LDY #$08
03E0   40         RTI
03E1   C6 AE      DEC $AE
03E3   C5 AF      CMP $AF
03E5   F0 F7      BEQ $03DE
03E7   EE 20 D0   INC $D020
03EA   4C E7 03   JMP $03E7
03ED   A9 40      LDA #$40
03EF   85 C1      STA $C1
03F1   A9 09      LDA #$09
03F3   8D 71 03   STA $0371
03F6   C8         INY
03F7   40         RTI
03F8   00         BRK
03F9   00         BRK
03FA   00         BRK
03FB   00         BRK
03FC   00         BRK
03FD   00         BRK
03FE   00         BRK
03FF   00         BRK
0400              .END



The first instruction calls a subroutine at 2a7, so let us also do a disassembly at that location:

                  * = 02A7
02A7   A9 7F      LDA #$7F
02A9   8D 0D DC   STA $DC0D
02AC   A9 5E      LDA #$5E
02AE   8D 06 DC   STA $DC06
02B1   A0 01      LDY #$01
02B3   8C 07 DC   STY $DC07
02B6   88         DEY
02B7   8C 1A D0   STY $D01A
02BA   84 AE      STY $AE
02BC   84 AF      STY $AF
02BE   A9 63      LDA #$63
02C0   8D FE FF   STA $FFFE
02C3   A9 7A      LDA #$7A
02C5   8D FA FF   STA $FFFA
02C8   A9 03      LDA #$03
02CA   8D FF FF   STA $FFFF
02CD   8D FB FF   STA $FFFB
02D0   A9 7B      LDA #$7B
02D2   8D 71 03   STA $0371
02D5   A9 F7      LDA #$F7
02D7   8D 98 03   STA $0398
02DA   A2 19      LDX #$19
02DC   A9 05      LDA #$05
02DE   85 01      STA $01
02E0   A9 90      LDA #$90
02E2   8D 0D DC   STA $DC0D
02E5   24 AE      BIT $AE
02E7   10 FC      BPL $02E5
02E9   A9 7F      LDA #$7F
02EB   8D 0D DC   STA $DC0D
02EE   A9 37      LDA #$37
02F0   85 01      STA $01
02F2   85 C0      STA $C0
02F4   A9 81      LDA #$81
02F6   8D 0D DC   STA $DC0D
02F9   EE FD 02   INC $02FD
02FC   60         RTS
02FD   00         BRK
02FE   00         BRK
02FF   00         BRK
0300   8B         ???
0301   E3         ???
0302   51 03      EOR ($03),Y
0304   7C         ???
0305   A5 1A      LDA $1A
0307   A7         ???
0308   E4 A7      CPX $A7
030A   86 AE      STX $AE
030C   00         BRK
030D   00         BRK
030E   00         BRK
030F   00         BRK
0310   4C 48 B2   JMP $B248
0313   00         BRK
0314   2C F9 66   BIT $66F9
0317   FE 47 FE   INC $FE47,X
031A   4A         LSR A
031B   F3         ???
031C   91 F2      STA ($F2),Y
031E   0E F2 50   ASL $50F2
0321   F2         ???
0322   33         ???
0323   F3         ???
0324   57         ???
0325   F1 CA      SBC ($CA),Y
0327   F1 ED      SBC ($ED),Y
0329   F6 3E      INC $3E,X
032B   F1 2F      SBC ($2F),Y
032D   F3         ???
032E   66 FE      ROR $FE
0330   A5 F4      LDA $F4
0332   ED F5 00   SBC $00F5
0335   00         BRK
0336   00         BRK
0337   00         BRK
0338   00         BRK
0339   00         BRK
033A   00         BRK
033B   00         BRK
033C   03         ???
033D   A7         ???
033E   02         ???
033F   04         ???
0340              .END


Aha! This piece of code change the IRQ address at memory location FFFE an FFFF and switches the kernel ROM out of view via memory location 1 so that the CPU can see the new IRQ address.

We haven't implemented the in/out switching of ROMS in our emulator. SO, lets implement functionality for switching in/out the KERNEL ROM.

We add the following method to our memory class:

  function kernelEnabled() {
    temp = mainMem[1] & 3;
    return (temp >= 2);
  }

We then use this method within readMem:

  this.readMem = function (address) {
    if ((address >= 0xa000) & (address <=0xbfff))
      return basicRom[address & 0x1fff];
    else if ((address >= 0xe000) & (address <=0xffff) & kernelEnabled())
      return kernalRom[address & 0x1fff];
    else if ((address >= 0xdc00) & (address <= 0xdcff)) {
      return ciaRead(address);
    } else if (address == 1) {
      var temp = mainMem[address] & 239;
      if (!playPressed)
        temp = temp | 16;
      return temp;
    }
    return mainMem[address];
  }


It is important to set memory location to 3  when you create the memory class, else the system will not boot:

function memory(allDownloadedCallback, keyboard, timerA, timerB, interruptController,tape)

{
  var mytimerA = timerA;
  var mytimerB = timerB;
  var myinterruptController = interruptController;
  var mytape = tape;
  var mainMem = new Uint8Array(65536);
  mainMem[1] = 3;
  var basicRom = new Uint8Array(8192);
  var kernalRom = new Uint8Array(8192);
 ...
} 

Let us try again. This time there was a faint piece of hope... a second of flashing borders, then it stopped.

Probably the first thing to do is to determine if the Tape class continues to fire interrupts at his point. Another thing to check is also whether the tape class is at the correct  position on tape at this point.

To summarise my findings on this issue. I found that the tape class indeed continued to fire interrupts when the flashing borders stopped. What I did picked up was that the Tape interrupt always fired before TIMER B expired. This meant that only a stream of zeros was was identified, no ones.

After some digging, I found that the culprit was the setTimerLow method in the timer class:

  this.setTimerLow = function(low) {
    timerHigh = low;
  }

The setter was setting the timerHigh value instead of timerLow!

This time we have better luck. Here is a set of screen shots:



In the last screenshot, the emulator was actually supposed to switch the screen to a high resolution multi-color mode for the splash screen. We will make the high resolution mode the task of our next blog.

In Summary

In this blog we jacked up our Video class so it can output colors. When then attempted to emulate the flashing flashing borders of the Dan Dare tape loader.

We encountered a couple of bugs in our emulators, which we have resolved.

In the next blog we will implement high resolution  mode and see if we can display the splash screen of Dan Dare tape loader.

Till next time!