Saturday, 11 January 2025

A Commodore 64 Emulator in Flutter: Part 6

Foreword

In the previous post I have implemented arithmetic instructions and flag modification instructions.

In this post I will be implementing Bit logic instruction and bit shifting instructions.

The source code for this post can be found on Github, with the following tag:

https://github.com/ovalcode/c64_flutter/releases/tag/c64_flutter_part_6

Enjoy!

Logic Operators

Let us start with the logic operators:

/*
AND (bitwise AND with accumulator)
Affects Flags: N Z

MODE           SYNTAX       HEX LEN TIM
Immediate     AND #$44      $29  2   2
Zero Page     AND $44       $25  2   3
Zero Page,X   AND $44,X     $35  2   4
Absolute      AND $4400     $2D  3   4
Absolute,X    AND $4400,X   $3D  3   4+
Absolute,Y    AND $4400,Y   $39  3   4+
Indirect,X    AND ($44,X)   $21  2   6
Indirect,Y    AND ($44),Y   $31  2   5+

+ add 1 cycle if page boundary crossed

 */
      case 0x29:
        _a = _a & arg0;
        _n = ((_a & 0x80) != 0) ? 1 : 0;
        _z = (_a == 0) ? 1 : 0;
      case 0x25:
      case 0x35:
      case 0x2D:
      case 0x3D:
      case 0x39:
      case 0x21:
      case 0x31:
        _a = _a & memory.getMem(resolvedAddress);
        _n = ((_a & 0x80) != 0) ? 1 : 0;
        _z = (_a == 0) ? 1 : 0;

              /*
    EOR (bitwise Exclusive OR)
Affects Flags: N Z

MODE           SYNTAX       HEX LEN TIM
Immediate     EOR #$44      $49  2   2
Zero Page     EOR $44       $45  2   3
Zero Page,X   EOR $44,X     $55  2   4
Absolute      EOR $4400     $4D  3   4
Absolute,X    EOR $4400,X   $5D  3   4+
Absolute,Y    EOR $4400,Y   $59  3   4+
Indirect,X    EOR ($44,X)   $41  2   6
Indirect,Y    EOR ($44),Y   $51  2   5+

+ add 1 cycle if page boundary crossed

     */
      case 0x49:
        _a = _a ^ arg0;
        _n = ((_a & 0x80) != 0) ? 1 : 0;
        _z = (_a == 0) ? 1 : 0;
      case 0x45:
      case 0x55:
      case 0x4D:
      case 0x5D:
      case 0x59:
      case 0x41:
      case 0x51:
        _a = _a ^ memory.getMem(resolvedAddress);
        _n = ((_a & 0x80) != 0) ? 1 : 0;
        _z = (_a == 0) ? 1 : 0;

/*
ORA (bitwise OR with Accumulator)
Affects Flags: N Z

MODE           SYNTAX       HEX LEN TIM
Immediate     ORA #$44      $09  2   2
Zero Page     ORA $44       $05  2   3
Zero Page,X   ORA $44,X     $15  2   4
Absolute      ORA $4400     $0D  3   4
Absolute,X    ORA $4400,X   $1D  3   4+
Absolute,Y    ORA $4400,Y   $19  3   4+
Indirect,X    ORA ($44,X)   $01  2   6
Indirect,Y    ORA ($44),Y   $11  2   5+

+ add 1 cycle if page boundary crossed

 */
      case 0x09:
        _a = _a | arg0;
        _n = ((_a & 0x80) != 0) ? 1 : 0;
        _z = (_a == 0) ? 1 : 0;
      case 0x05:
      case 0x15:
      case 0x0D:
      case 0x1D:
      case 0x19:
      case 0x01:
      case 0x11:
        _a = _a | memory.getMem(resolvedAddress);
        _n = ((_a & 0x80) != 0) ? 1 : 0;
        _z = (_a == 0) ? 1 : 0;

Everything is straightforward here, and nothing to comment on.

Shifting operators

Next let us implement all the bit shifting operators:

      /*
ASL (Arithmetic Shift Left)
Affects Flags: N Z C

MODE           SYNTAX       HEX LEN TIM
Accumulator   ASL A         $0A  1   2
Zero Page     ASL $44       $06  2   5
Zero Page,X   ASL $44,X     $16  2   6
Absolute      ASL $4400     $0E  3   6
Absolute,X    ASL $4400,X   $1E  3   7

ASL shifts all bits left one position. 0 is shifted into bit 0 and the original bit 7 is shifted into the Carry.

 */
      case 0x0A:
        _a = _a << 1;
        _c = ((_a & 0x100) != 0) ? 1 : 0;
        _a = _a & 0xff;
        _n = ((_a & 0x80) != 0) ? 1 : 0;
        _z = (_a == 0) ? 1 : 0;
      case 0x06:
      case 0x16:
      case 0x0E:
      case 0x1E:
        int temp = memory.getMem(resolvedAddress) << 1;
        _c = ((temp & 0x100) != 0) ? 1 : 0;
        temp = temp & 0xff;
        _n = ((temp & 0x80) != 0) ? 1 : 0;
        _z = (temp == 0) ? 1 : 0;
        memory.setMem(temp, resolvedAddress);

      /*
LSR (Logical Shift Right)
Affects Flags: N Z C

MODE           SYNTAX       HEX LEN TIM
Accumulator   LSR A         $4A  1   2
Zero Page     LSR $44       $46  2   5
Zero Page,X   LSR $44,X     $56  2   6
Absolute      LSR $4400     $4E  3   6
Absolute,X    LSR $4400,X   $5E  3   7

LSR shifts all bits right one position. 0 is shifted into bit 7 and the original bit 0 is shifted into the Carry.

 */
      case 0x4A:
        _c = _a & 1;
        _a = _a >> 1;
        _n = ((_a & 0x80) != 0) ? 1 : 0;
        _z = (_a == 0) ? 1 : 0;
      case 0x46:
      case 0x56:
      case 0x4E:
      case 0x5E:
        int temp = memory.getMem(resolvedAddress);
        _c = temp & 1;
        temp = temp >> 1;
        _n = ((temp & 0x80) != 0) ? 1 : 0;
        _z = (temp == 0) ? 1 : 0;
        memory.setMem(temp, resolvedAddress);

/*
ROL (ROtate Left)
Affects Flags: N Z C

MODE           SYNTAX       HEX LEN TIM
Accumulator   ROL A         $2A  1   2
Zero Page     ROL $44       $26  2   5
Zero Page,X   ROL $44,X     $36  2   6
Absolute      ROL $4400     $2E  3   6
Absolute,X    ROL $4400,X   $3E  3   7

ROL shifts all bits left one position. The Carry is shifted into bit 0 and the original bit 7 is shifted into the Carry.


 */
      case 0x2A:
        _a = (_a << 1) | _c;
        _c = ((_a & 0x100) != 0) ? 1 : 0;
        _a = _a & 0xff;
        _n = ((_a & 0x80) != 0) ? 1 : 0;
        _z = (_a == 0) ? 1 : 0;
      case 0x26:
      case 0x36:
      case 0x2E:
      case 0x3E:
        int temp = (memory.getMem(resolvedAddress) << 1) | _c;
        _c = ((temp & 0x100) != 0) ? 1 : 0;
        temp = temp & 0xff;
        _n = ((temp & 0x80) != 0) ? 1 : 0;
        _z = (temp == 0) ? 1 : 0;
        memory.setMem(temp, resolvedAddress);
      /*
    ROR (ROtate Right)
Affects Flags: N Z C

MODE           SYNTAX       HEX LEN TIM
Accumulator   ROR A         $6A  1   2
Zero Page     ROR $44       $66  2   5
Zero Page,X   ROR $44,X     $76  2   6
Absolute      ROR $4400     $6E  3   6
Absolute,X    ROR $4400,X   $7E  3   7

ROR shifts all bits right one position. The Carry is shifted into bit 7 and the original bit 0 is shifted into the Carry.


     */
      case 0x6A:
        _a = _a | (_c << 8);
        _c = _a & 1;
        _a = _a >> 1;
        _n = ((_a & 0x80) != 0) ? 1 : 0;
        _z = (_a == 0) ? 1 : 0;
      case 0x66:
      case 0x76:
      case 0x6E:
      case 0x7E:
        int temp = memory.getMem(resolvedAddress) | (_c << 8);
        _c = temp & 1;
        temp = temp >> 1;
        _n = ((temp & 0x80) != 0) ? 1 : 0;
        _z = (temp == 0) ? 1 : 0;
        memory.setMem(temp, resolvedAddress);

As you can see, with the shifting instructions, either the contents of the accumulator is shifted, or the contents of a memory location is shifted. Obviously, when shifting the contents of a memory location, more steps are involved.

The Test Program

Here is a quick test program for testing the instructions we have implemented in this post:

/*asl C <- [76543210] <- 0
  lsr 0 -> [76543210] -> C
  rol C <- [76543210] <- C
  ror C -> [76543210] -> C*/

LDA #01   A9 01
ORA #02   09 02
EOR #06   49 06
LSR A     4A
ROL A     2A
SEC       38
AND #$fe  29 fe
ROR A     6a
LDA #3    a9 03
STA $60   85 60
CLC       18
LSR $60   46 60
ROL $60   26 60
ROR $60   66 60

I have added a comment on the top as a quick reference to how the different shifting instructions work, just making it easier to follow the program.

I have included this program in the Github tag for this post.

In Summary

In this post we implemented the Logic operator and Bit shifting instructions in our emulator.

In the next post we will be implementing the Compare and Branching instructions.

Until next time!

No comments:

Post a Comment