Monday 30 May 2016

Part 11: Fixing the missing cursor

Foreword

In the previous blog I implemented a very simple emulation of the C64 screen.

We came to the point where it showed the welcome message, but no flashing cursor.

In this blog we are going to investigate why the cursor is not shown and fix it.

I will be taking a bit of a detour in this blog showing how to get info for an emulator from schematics and chip Data Sheets.

Although not really necessary for this blog for investigating something as simple as a missing flashing cursor, this technique will be really useful for future blogs. 

Looking for a clue


So, we don't see a cursor. Where do we start to investigate this issue?

First price would be to have a commented disassembled version of the ROM code. There is indeed a resource on the internet that will provide us with this information:

http://www.ffd2.com/fridge/docs/c64-diss.html

We should now ask the browser to search for the keywords flash or cursor on the commented listing.

When I do the search I get very related hits like read/set XY cursor position, move cursor to previous line and so on.

Finally I found what I found what I was looking for:

; normal IRQ interrupt

EA31   20 EA FF   JSR $FFEA   ; do clock
EA34   A5 CC      LDA $CC   ; flash cursor
EA36   D0 29      BNE $EA61
EA38   C6 CD      DEC $CD
EA3A   D0 25      BNE $EA61
EA3C   A9 14      LDA #$14
EA3E   85 CD      STA $CD
EA40   A4 D3      LDY $D3
EA42   46 CF      LSR $CF
EA44   AE 87 02   LDX $0287
EA47   B1 D1      LDA ($D1),Y
EA49   B0 11      BCS $EA5C
EA4B   E6 CF      INC $CF
EA4D   85 CE      STA $CE
EA4F   20 24 EA   JSR $EA24
EA52   B1 F3      LDA ($F3),Y
EA54   8D 87 02   STA $0287
EA57   AE 86 02   LDX $0286
EA5A   A5 CE      LDA $CE
EA5C   49 80      EOR #$80
EA5E   20 1C EA   JSR $EA1C   ; display cursor

I have highlighted important parts in bold. It is clear that the flashing of the cursor is part of the job of an IRQ handler.

Aha! We haven't implemented interrupts yet in our emulator, though we already did something similar with the BRK instruction.

Together with discovery comes a secondary question: When should the interrupts fire?

This is perhaps a more difficult question to answer. To answer this question would require some knowledge of the hardware peripheral registers on the C64.

Surely, there are hundreds of C64 memory maps available on the Internet that will provide you with this information, but in this blog I am going to show you another interesting way: Using schematics and DataSheets.

Isn't this an overkill? Maybe, but in my experience with my Java emulator I actually found that there is some information that the C64 memory simply doesn't give you. Especially when you want to emulate hardware.

So, lets get snooping...

Making our own memory map

Let us start by finding out what is connected to the IRQ pin of the CPU. There is some schematics via the following link that will provide us with this information:

http://www.zimmers.net/anonftp/pub/cbm/schematics/computers/c64/

Here is a snippet from schematic:

n this snippet I have highlighted in red what is connected to the IRQ pin of the CPU.

In this case we see that the IRQ pin of a 6526 CIA chip is connected to the IRQ pin of the CPU. What is nice about this schematic is that they also wrote on the CIA block which space in memory it is occupying.

Next we need to dig into a DataSheet for the 6526 CIA chip. Many DataSheet web sites will give you the original Datasheet as a scanned copy in PDF.

This DataSheet is written in paragraph form, so it is not very handy as a quick reference. So I tried to give a summarised version as follows:

0 PRA Peripheral Data Register
1 PRB Peripheral Data Register
2 DDRA Data Direction Register
3 DDRB Data Direction Register
4 Timer A Low
5 Timer A High
6 Timer B Low
7 Timer B High
8 TOD Tenths
9 TOD Seconds
A TOD Minutes
B TOD Hour
C SDR (Serial Data) 
D Interrupt -> IR 00 FLG SP Alarm TB TA
E Control Bit 0 = 1 -> Start Timer A
          Bit 1 = Timer A on PB6
          Bit 2 = 1-> Toggle 0=Pulse
          Bit 3 = 1-> One Shot 0->Continious
          Bit 4 = Load Force
          Bit 5 = 1-> Timer A counts CNT signals
                  0-> Timer A counts 02
          Bit 6 = Serial
          Bit 7 = 1->50Hz 0->60Hz
F Same as E Except Bit 1 Timer B on
  CRB6 CRB5
   0    0   Timer B counts 02
   0    1   Timer B counts CNT
   1    0   Timer B counts Timer A underflows
   1    1   Timer B counts Timer A underflows while CNT high
          Bit7 writing 1 -> Writing to TOD registers set alarm
               writing 0 -> Writing to TOD registers set TOD 

As you can see the 6526 only has 16 registers. On the CIA block on the diagram, however, 256 memory locations is reserved  for CIA 1. This means that in the address range DC00 to DCFF only the least four bits will be used.

Another thing also to take note of is that for some registers a read operation and a write operation will each access a different register. More on this to follow.

With all this information at our disposal, what is the next step? A good place to start is to find out which interrupts our emulator attempted to enable. So lets do a memory dump at DC00:

The register to look for is DC0D and its value is 81h. This means an interrupt is only enabled for timer A.

So, lets try get all the settings for timer A. We will start at address DC0E. Bit 0 is set, so we know the ROM code attempted to start the timer.

The next important bit is bit three. We see it is set to 0. This means our timer is running in continuous mode. It will count down to zero, cause an interrupt and then start counting down again from a pre-defined value.

Next, we need to check bit 5. It is set to zero and this means it is counting 02 signals. 02 signals runs at the same clock speed as the CPU, which is more or less 1MHz.

Finally, lets look at the the Timer A Low and Timer A High value. If we puts these two values together, you get 4025h (This is 16421 in decimal). So what does this value mean? We know our CIA is counting at 1Mhz. So an interrupt is thrown every 16421 clock cycles. Lets convert this to seconds:

16421/1000000 = 0.016421

This corresponds to more or less 60 Interrupts per second.

At last! We know now we should trigger an interrupt 60 times per second. This is very close to our batch interval. So for now it would be sufficient to trigger an interrupt once every time we execute the runBatch method.

There goes another hack! In a later blog post we will eventually come back and replace the hack with some proper code.

Implementing IRQ's in our emulator

Firstly we need a way to inform our CPU to perform an interrupt. For this we will create a public method setInterrupt(). This method will merely set a private variable. So firstly, define the private variable:

  var localMem = memory;
  var acc = 0;
  var x = 0;
  var y = 0;
  var pc = 0x400;
  var sp = 0xff;
  var zeroflag = 0;
  var negativeflag = 0;
  var carryflag =0;
  var overflowflag =0; 
  var decimalflag = 0;
  var interruptflag = 1;
  var breakflag = 1;
  var interruptOcurred = 0;

And next, the public method:

    this.setInterrupt = function () {
      interruptOcurred = 1;
    }


Next, in our Cpu step method we should actually check this variable and trigger an interrupt if it is set. This trigger should only happen if the interruptFlag is 0:

  this.step = function () {
    if ((interruptOcurred == 1) & (interruptflag == 0)) {
    }    
    var opcode = localMem.readMem(pc);
    pc = pc + 1;
    var iLen = instructionLengths[opcode];
    var arg1 = 0;


What do we put inside the if statement? We already wrote similar code when we implemented the BRK instruction. Lets refresh our memory:

      case 0x00:
        var tempVal = pc + 1;
        Push(tempVal >> 8);
        Push(tempVal & 0xff);
        Push(getStatusFlagsAsByte());
        interruptflag = 1;
        tempVal = localMem.readMem(0xffff) * 256;
        tempVal = tempVal + localMem.readMem(0xfffe);
        pc = tempVal;
      break;


We can almost copy and paste this source just as is. There is just a couple of things we should be aware of:

  • Don't increment the program counter by one as done in the first line.
  • Remember to set interruptOccured to back to 0
  • Be careful of the breakflag!
The last point warrant a special discussion.

As you remember we have implemented the BRK instruction while we had been running the Klaus Test Suite. All the tests run through fine when we left the break flag as one.

When implementing an IRQ, however, we should be more cautious. When a IRQ occur, the break flag gets set briefly to 0 just to distinguish itself from a BRK interrupt.

The key here is to ensure that the status byte being pushed onto the stack have the break flag set as zero. So, in our case it would be sufficient if we set the break flag to zero before doing the status flag push. Directly after the push we need to change the break flag back to 1.

With all this in mind the code will look as follows:
    if ((interruptOcurred == 1) & (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;
    }

What remains to be done is to cause an interrupt each time the runBatch Method gets executed:

      function runBatch() {
        if (!running)
          return;
        myvideo.updateCanvas();
        var targetCycleCount =  mycpu.getCycleCount() + 20000;
        mycpu.setInterrupt();
        while (mycpu.getCycleCount() < targetCycleCount) { 
          mycpu.step();
          var blankingPeriodLow = targetCycleCount - 100;
          if ((mycpu.getCycleCount() >= blankingPeriodLow) & (mycpu.getCycleCount() <= targetCycleCount)) {
            mymem.writeMem(0xD012, 0);
          } else  {
            mymem.writeMem(0xD012, 1);
          }
          if (mycpu.getPc() == breakpoint) {
            stopEm();
            return;
          }
        }
      }


If we now rerun the emulator, we see a flashing cursor!

In Summary

In this blog we investigate why our emulator is not showing a flashing cursor. This investigation took us a bit of a detour by finding information from datasheets and schematics.

In the end the solution was to implement interrupts in our emulator and triggering an interrupt once every time the runBAtch method is called.

In the next Blog we will see if we could simulate a key press in our emulator. This will be the first step in adding keyboard support to our JavaScript emulator.

Till next time!

No comments:

Post a Comment