Saturday, January 9, 2016

KIM Calculator Update: Learning 6502 and reducing code size

One of the functions of this KIM-Uno project involves functionality similar to the stock KIM monitor.  You press buttons 0-9, A-F to enter a number (a nibble, a half byte), and it scrolls in from the right side of the display.  Usually you can only enter the address bytes (first 4 digits) or the data byte (rightmost 2 digits).  I wanted to use all 6 digits for the values entered so I needed to write my own handler for this. I decided to kinda glance at the KIM Monitor code, but I wanted to make it all on my own.

Version 1 sketch...

Version 2 sketch.

Both of the above (which didn't quite work) were basically the same as the working version (v3) which I did implement and had in the code for a week or so.  It was 46 bytes long, plus a sub function which got called 3 times that was 20 bytes long.  Not very small.  The basic procedure was this:


  1. Get the key from the user (0x00 - 0x0F), store it aside
  2. for each of the three display bytes (eg 0xMN)
    1. Store it in X (input byte)
    2. shift it to the left by 4 nibbles (one display digit)
    3. store aside this value 0xN0
    4. restore the display byte (0xMN)
    5. shift it to the right by 4 nibbles (one display digit)
    6. store aside this value now (0x0M) this is the "carry out"
    7. Add the user input key with the first stored value  0xNK and put in "X" (output byte)
    8. restore the "carry out" to "A"

That's basically it. Nothing particularly wrong with it, it works fine, but once you understand more of the opcodes available in the system, we can drastically reduce this.

I was working on implenenting one of the calculator functions "Shift left one bit" when I learned/remembered about shifting with ROL (rotate left) and ROR (rotate right) which shift the bits around, storing the one going out and the one going in using the "carry" flags bit.  The implementation of this function was super easy using this:

clc          ; clear carry bit (shift in a '0')
rol DIGIT3   ; shift data in memory location DIGIT3 one bit
rol DIGIT2   ; these take the bit shifted out and store it
rol DIGIT1   ; in the carry bit, then shift that bit in
jsr DISPLAY  ; and display it

Then it hit me, I could leverage off of this carry bit for the above process, since it basically is:

  • Shift all three bytes to the left by one bit four times (one nibble) 
  • Shove the key in to the lower nibble of DIGIT3
This change of shifting everything by one bit four times, rather than the above where I was shifting four bits three times, worked out perfectly with the available opcodes.  Here's the final code for this routine:  (INH is the third digit pair, POINTL and POINTH are the second and first digit pairs of the display respectively)

We set up a loop using "x" as the counter register, notice we set it to '4' first.  We are also doing something that I just learned about which is non-named labels, which is why you see ":" starting a line, then a "bne :-"  (branch (goto) if not equal to the previous non-named label.)

keyShiftIntoDisplay:
        ldx     #$04            ; 4 bits to shift
:       clc                     ; rol pulls from carry, so clear it
        rol     KIM_INH         ; shift this byte by 1 
        rol     KIM_POINTL      ; shift this one, carry from INH
        rol     KIM_POINTH      ; shift this one, carry from POINTL
        dex                     ; x = x - 1
        txa                     ; a = x 
        cmp     #$00            ; a == 0?
        bne     :-              ; mot 0, repeat loop

        ; now shove the content in
        lda     KEYBAK          ; restore key 00 .. 0F to A
        ora     KIM_INH         ; A = A | INH
        sta     KIM_INH         ; INH = A
        jsr     SCANDS          ; and display it to the screen
        jmp     keyinput        ; next!

Which works out substantially smaller.  It has one chunk of 13 bytes that gets run four times, for the entire code block of 27 bytes.  Quite a lot less!  I'm really enjoying learning 6502 asm for this!

The code for this project can be found on github at The LlamaCalc directory of my Projects5502 repository.  This requires the cc65 toolset to build.

No comments:

Post a Comment