From 9490b470f3c0ac2357b60a56a893ab86556af634 Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Sat, 2 Jan 2021 20:23:52 +0100 Subject: added VIA added some more opcodes (SEI, CLI) --- README | 8 + doc/6502.org_ Tutorials and Aids.html | 635 ++++++++++++++++++++++++++++++++++ emu/6502.c | 67 +++- emu/6502.h | 43 ++- emu/6522.c | 68 +++- emu/6522.h | 19 +- emu/emu.c | 3 +- emu/emul.c | 13 +- emu/tests/test_cpu_6502.c | 412 ++++++++++++++++++++++ roms/7seg_counter_irq_timer.asm | 4 +- 10 files changed, 1219 insertions(+), 53 deletions(-) create mode 100644 doc/6502.org_ Tutorials and Aids.html diff --git a/README b/README index d01bc3f..18343b1 100644 --- a/README +++ b/README @@ -127,6 +127,14 @@ work on emulator (disassembly and single-stepping) 10.12.2020 unit testing framework and first unit tests +till 31.12.2020 +baby steps in testing +via 6522 +handle timing/cycles properly + +2.1.2021 +work on via and interrupts + commands -------- diff --git a/doc/6502.org_ Tutorials and Aids.html b/doc/6502.org_ Tutorials and Aids.html new file mode 100644 index 0000000..f9a597f --- /dev/null +++ b/doc/6502.org_ Tutorials and Aids.html @@ -0,0 +1,635 @@ + + +6502.org: Tutorials and Aids + + + + +[Return to Main Page] +NMOS 6502 Opcodes by John Pickens, +Updated by Bruce Clark and by Ed Spittles +
[Up to Tutorials and Aids] +
+
+

INDEX

+ + + + + + + + + + +
BranchesDecimal ModeInterrupt FlagOverflow FlagProgram + CounterStackTimesWrap-around
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ADCANDASLBCCBCSBEQBITBMIBNEBPLBRKBVCBVSCLC
CLDCLICLVCMPCPXCPYDECDEXDEYEORINCINXINYJMP
JSRLDALDXLDYLSRNOPORAPHAPHPPLAPLPROLRORRTI
RTSSBCSECSEDSEISTASTXSTYTAXTAYTSXTXATXSTYA
+

  +

ADC (ADd with Carry)

+

Affects Flags: N V Z C

MODE           SYNTAX       HEX LEN TIM
+Immediate     ADC #$44      $69  2   2
+Zero Page     ADC $44       $65  2   3
+Zero Page,X   ADC $44,X     $75  2   4
+Absolute      ADC $4400     $6D  3   4
+Absolute,X    ADC $4400,X   $7D  3   4+
+Absolute,Y    ADC $4400,Y   $79  3   4+
+Indirect,X    ADC ($44,X)   $61  2   6
+Indirect,Y    ADC ($44),Y   $71  2   5+
+
++ add 1 cycle if page boundary crossed
+
+
ADC results are dependant on the setting of the decimal flag. In decimal +mode, addition is carried out on the assumption that the values involved are +packed BCD (Binary Coded Decimal). +

There is no way to add without carry. + +

  +

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
+
+ +

  +

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. +

+

  +

BIT (test BITs)

+

Affects Flags: N V Z

MODE           SYNTAX       HEX LEN TIM
+Zero Page     BIT $44       $24  2   3
+Absolute      BIT $4400     $2C  3   4
+
+
BIT sets the Z flag as though the value in the address tested were ANDed +with the accumulator. The N and V flags are set to match bits 7 and 6 +respectively in the value stored at the tested address. +

BIT is often used to skip one or two following bytes as in: +

CLOSE1 LDX #$10   If entered here, we
+       .BYTE $2C  effectively perform
+CLOSE2 LDX #$20   a BIT test on $20A2,
+       .BYTE $2C  another one on $30A2,
+CLOSE3 LDX #$30   and end up with the X
+CLOSEX LDA #12    register still at $10
+       STA ICCOM,X upon arrival here.
+
+
+Beware: a BIT instruction used in this way as a NOP does have effects: the flags +may be modified, and the read of the absolute address, if it happens to access an +I/O device, may cause an unwanted action. +

                  +

Branch Instructions

+

Affect Flags: none +

All branches are relative mode and have a length of two bytes. Syntax is "Bxx +Displacement" or (better) "Bxx Label". See the notes on the Program Counter for more on +displacements. +

Branches are dependant on the status of the flag bits when the op code is +encountered. A branch not taken requires two machine cycles. Add one if the +branch is taken and add one more if the branch crosses a page boundary.

MNEMONIC                       HEX
+BPL (Branch on PLus)           $10
+BMI (Branch on MInus)          $30
+BVC (Branch on oVerflow Clear) $50
+BVS (Branch on oVerflow Set)   $70
+BCC (Branch on Carry Clear)    $90
+BCS (Branch on Carry Set)      $B0
+BNE (Branch on Not Equal)      $D0
+BEQ (Branch on EQual)          $F0
+
+
There is no BRA (BRanch Always) instruction but it can be easily emulated +by branching on the basis of a known condition. One of the best flags to use for +this purpose is the oVerflow which is unchanged +by all but addition and subtraction operations. +

+A page boundary crossing occurs when the branch destination is on a different +page than the instruction AFTER the branch instruction. For example: +

  SEC
+  BCS LABEL
+  NOP
+
+A page boundary crossing occurs (i.e. the BCS takes 4 cycles) when (the +address of) LABEL and the NOP are on different pages. This means that +
        CLV
+        BVC LABEL
+  LABEL NOP
+
+the BVC instruction will take 3 cycles no matter what address it is located +at. +

+

  +

BRK (BReaK)

+

Affects Flags: B

MODE           SYNTAX       HEX LEN TIM
+Implied       BRK           $00  1   7
+
+
BRK causes a non-maskable interrupt and increments the program counter by +one. Therefore an RTI will +go to the address of the BRK +2 so that BRK may be used to replace a +two-byte instruction for debugging and the subsequent RTI will be correct. +

+

  +

CMP (CoMPare accumulator)

+

Affects Flags: N Z C

MODE           SYNTAX       HEX LEN TIM
+Immediate     CMP #$44      $C9  2   2
+Zero Page     CMP $44       $C5  2   3
+Zero Page,X   CMP $44,X     $D5  2   4
+Absolute      CMP $4400     $CD  3   4
+Absolute,X    CMP $4400,X   $DD  3   4+
+Absolute,Y    CMP $4400,Y   $D9  3   4+
+Indirect,X    CMP ($44,X)   $C1  2   6
+Indirect,Y    CMP ($44),Y   $D1  2   5+
+
++ add 1 cycle if page boundary crossed
+
+
Compare sets flags as if a subtraction had been carried out. If the value +in the accumulator is equal or greater than the compared value, the Carry will +be set. The equal (Z) and negative (N) flags will be set based on equality or lack +thereof and the sign (i.e. A>=$80) of the accumulator. +

+

  +

CPX (ComPare X register)

+

Affects Flags: N Z C

MODE           SYNTAX       HEX LEN TIM
+Immediate     CPX #$44      $E0  2   2
+Zero Page     CPX $44       $E4  2   3
+Absolute      CPX $4400     $EC  3   4
+
+
Operation and flag results are identical to equivalent mode accumulator CMP ops. +

+

  +

CPY (ComPare Y register)

+

Affects Flags: N Z C

MODE           SYNTAX       HEX LEN TIM
+Immediate     CPY #$44      $C0  2   2
+Zero Page     CPY $44       $C4  2   3
+Absolute      CPY $4400     $CC  3   4
+
+
Operation and flag results are identical to equivalent mode accumulator CMP ops. +

+

  +

DEC (DECrement memory)

+

Affects Flags: N Z

MODE           SYNTAX       HEX LEN TIM
+Zero Page     DEC $44       $C6  2   5
+Zero Page,X   DEC $44,X     $D6  2   6
+Absolute      DEC $4400     $CE  3   6
+Absolute,X    DEC $4400,X   $DE  3   7
+
+
+

  +

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
+
+

              +

Flag (Processor Status) Instructions

+

Affect Flags: as noted +

These instructions are implied mode, have a length of one byte and require +two machine cycles.

MNEMONIC                       HEX
+CLC (CLear Carry)              $18
+SEC (SEt Carry)                $38
+CLI (CLear Interrupt)          $58
+SEI (SEt Interrupt)            $78
+CLV (CLear oVerflow)           $B8
+CLD (CLear Decimal)            $D8
+SED (SEt Decimal)              $F8
+
+
Notes: +

  The Interrupt flag is used to prevent (SEI) or +enable (CLI) maskable interrupts (aka IRQ's). It does not signal the presence or +absence of an interrupt condition. The 6502 will set this flag automatically in +response to an interrupt and restore it to its prior status on completion of the +interrupt service routine. If you want your interrupt service routine to permit +other maskable interrupts, you must clear the I flag in your code. +

  The Decimal flag controls how the 6502 adds and +subtracts. If set, arithmetic is carried out in packed binary coded decimal. +This flag is unchanged by interrupts and is unknown on power-up. The implication +is that a CLD should be included in boot or interrupt coding. +

  The Overflow flag is generally misunderstood and +therefore under-utilised. After an ADC or SBC instruction, the overflow flag +will be set if the twos complement result is less than -128 or greater than ++127, and it will cleared otherwise. In twos complement, $80 through $FF +represents -128 through -1, and $00 through $7F represents 0 through +127. +Thus, after: +

  CLC
+  LDA #$7F ;   +127
+  ADC #$01 ; +   +1
+
+the overflow flag is 1 (+127 + +1 = +128), and after: +
  CLC
+  LDA #$81 ;   -127
+  ADC #$FF ; +   -1
+
+the overflow flag is 0 (-127 + -1 = -128). The overflow flag is not +affected by increments, decrements, shifts and logical operations i.e. only +ADC, BIT, CLV, PLP, RTI and SBC affect it. There is no op code to set the +overflow but a BIT test on an RTS instruction will do the trick. +

+

+

  +

INC (INCrement memory)

+

Affects Flags: N Z

MODE           SYNTAX       HEX LEN TIM
+Zero Page     INC $44       $E6  2   5
+Zero Page,X   INC $44,X     $F6  2   6
+Absolute      INC $4400     $EE  3   6
+Absolute,X    INC $4400,X   $FE  3   7
+
+

+

+

  +

JMP (JuMP)

+

Affects Flags: none

MODE           SYNTAX       HEX LEN TIM
+Absolute      JMP $5597     $4C  3   3
+Indirect      JMP ($5597)   $6C  3   5
+
+
JMP transfers program execution to the following address (absolute) or to +the location contained in the following address (indirect). Note that there is +no carry associated with the indirect jump so:
AN INDIRECT JUMP MUST NEVER USE A
+VECTOR BEGINNING ON THE LAST BYTE
+OF A PAGE
+
For example if address $3000 contains $40, $30FF contains $80, and $3100 +contains $50, the result of JMP ($30FF) will be a transfer of control to $4080 +rather than $5080 as you intended i.e. the 6502 took the low byte of the address +from $30FF and the high byte from $3000. +

+

+

  +

JSR (Jump to SubRoutine)

+

Affects Flags: none

MODE           SYNTAX       HEX LEN TIM
+Absolute      JSR $5597     $20  3   6
+
+
JSR pushes the address-1 of the next operation on to the stack before +transferring program control to the following address. Subroutines are normally +terminated by a RTS op +code. +

+

+

  +

LDA (LoaD Accumulator)

+

Affects Flags: N Z

MODE           SYNTAX       HEX LEN TIM
+Immediate     LDA #$44      $A9  2   2
+Zero Page     LDA $44       $A5  2   3
+Zero Page,X   LDA $44,X     $B5  2   4
+Absolute      LDA $4400     $AD  3   4
+Absolute,X    LDA $4400,X   $BD  3   4+
+Absolute,Y    LDA $4400,Y   $B9  3   4+
+Indirect,X    LDA ($44,X)   $A1  2   6
+Indirect,Y    LDA ($44),Y   $B1  2   5+
+
++ add 1 cycle if page boundary crossed
+
+

+

+

  +

LDX (LoaD X register)

+

Affects Flags: N Z

MODE           SYNTAX       HEX LEN TIM
+Immediate     LDX #$44      $A2  2   2
+Zero Page     LDX $44       $A6  2   3
+Zero Page,Y   LDX $44,Y     $B6  2   4
+Absolute      LDX $4400     $AE  3   4
+Absolute,Y    LDX $4400,Y   $BE  3   4+
+
++ add 1 cycle if page boundary crossed
+
+

+

+

  +

LDY (LoaD Y register)

+

Affects Flags: N Z

MODE           SYNTAX       HEX LEN TIM
+Immediate     LDY #$44      $A0  2   2
+Zero Page     LDY $44       $A4  2   3
+Zero Page,X   LDY $44,X     $B4  2   4
+Absolute      LDY $4400     $AC  3   4
+Absolute,X    LDY $4400,X   $BC  3   4+
+
++ add 1 cycle if page boundary crossed
+
+

+

+

  +

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. +

+

+

  +

Wrap-Around

+

Use caution with indexed zero page operations as they are subject to +wrap-around. For example, if the X register holds $FF and you execute LDA $80,X +you will not access $017F as you might expect; instead you access $7F i.e. +$80-1. This characteristic can be used to advantage but make sure your code is +well commented. +

+It is possible, however, to access $017F when X = $FF by using the Absolute,X +addressing mode of LDA $80,X. That is, instead of: +

  LDA $80,X    ; ZeroPage,X - the resulting object code is: B5 80
+
+which accesses $007F when X=$FF, use: +
  LDA $0080,X  ; Absolute,X - the resulting object code is: BD 80 00
+
+which accesses $017F when X = $FF (a at cost of one additional byte and one +additional cycle). All of the ZeroPage,X and ZeroPage,Y instructions except +STX ZeroPage,Y and STY ZeroPage,X have a corresponding Absolute,X and +Absolute,Y instruction. Unfortunately, a lot of 6502 assemblers don't have an +easy way to force Absolute addressing, i.e. most will assemble a LDA $0080,X +as B5 80. One way to overcome this is to insert the bytes using the .BYTE +pseudo-op (on some 6502 assemblers this pseudo-op is called DB or DFB, +consult the assembler documentation) as follows: +
  .BYTE $BD,$80,$00  ; LDA $0080,X (absolute,X addressing mode)
+
+The comment is optional, but highly recommended for clarity. +

In cases where you are writing code that will be relocated you must consider +wrap-around when assigning dummy values for addresses that will be adjusted. +Both zero and the semi-standard $FFFF should be avoided for dummy labels. The +use of zero or zero page values will result in assembled code with zero page +opcodes when you wanted absolute codes. With $FFFF, the problem is in +addresses+1 as you wrap around to page 0. +

  +

Program Counter

+

When the 6502 is ready for the next instruction it increments the program +counter before fetching the instruction. Once it has the op code, it increments +the program counter by the length of the operand, if any. This must be accounted +for when calculating branches or when pushing bytes to create a false return +address (i.e. jump table addresses are made up of addresses-1 when it is +intended to use an RTS rather than a JMP). +

The program counter is loaded least signifigant byte first. Therefore the +most signifigant byte must be pushed first when creating a false return address. + +

When calculating branches a forward branch of 6 skips the following 6 bytes +so, effectively the program counter points to the address that is 8 bytes beyond +the address of the branch opcode; and a backward branch of $FA (256-6) goes to +an address 4 bytes before the branch instruction. +

  +

Execution Times

+

Op code execution times are measured in machine cycles; one machine cycle +equals one clock cycle. Many instructions require one extra cycle for +execution if a page boundary is crossed; these are indicated by a + following +the time values shown. +

+

+

  +

NOP (No OPeration)

+

Affects Flags: none

MODE           SYNTAX       HEX LEN TIM
+Implied       NOP           $EA  1   2
+
+
NOP is used to reserve space for future modifications or effectively REM +out existing code. +

+

+

  +

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
+
+

+

+

                +

Register Instructions

+

Affect Flags: N Z +

These instructions are implied mode, have a length of one byte and require +two machine cycles.

MNEMONIC                 HEX
+TAX (Transfer A to X)    $AA
+TXA (Transfer X to A)    $8A
+DEX (DEcrement X)        $CA
+INX (INcrement X)        $E8
+TAY (Transfer A to Y)    $A8
+TYA (Transfer Y to A)    $98
+DEY (DEcrement Y)        $88
+INY (INcrement Y)        $C8
+
+

+

+

  +

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. +

+

+

  +

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. +

+

+

  +

RTI (ReTurn from Interrupt)

+

Affects Flags: all

MODE           SYNTAX       HEX LEN TIM
+Implied       RTI           $40  1   6
+
+
RTI retrieves the Processor Status Word (flags) and the Program Counter +from the stack in that order (interrupts push the PC first and then the PSW). +

Note that unlike RTS, the return address on the stack is the actual address +rather than the address-1. +

+

+

  +

RTS (ReTurn from Subroutine)

+

Affects Flags: none

MODE           SYNTAX       HEX LEN TIM
+Implied       RTS           $60  1   6
+
+
RTS pulls the top two bytes off the stack (low byte first) and transfers +program control to that address+1. It is used, as expected, to exit a subroutine +invoked via JSR which +pushed the address-1. +

RTS is frequently used to implement a jump table where addresses-1 are pushed +onto the stack and accessed via RTS eg. to access the second of four routines:

 LDX #1
+ JSR EXEC
+ JMP SOMEWHERE
+
+LOBYTE
+ .BYTE <ROUTINE0-1,<ROUTINE1-1
+ .BYTE <ROUTINE2-1,<ROUTINE3-1
+
+HIBYTE
+ .BYTE >ROUTINE0-1,>ROUTINE1-1
+ .BYTE >ROUTINE2-1,>ROUTINE3-1
+
+EXEC
+ LDA HIBYTE,X
+ PHA
+ LDA LOBYTE,X
+ PHA
+ RTS
+
+

+

  +

SBC (SuBtract with Carry)

+

Affects Flags: N V Z C

MODE           SYNTAX       HEX LEN TIM
+Immediate     SBC #$44      $E9  2   2
+Zero Page     SBC $44       $E5  2   3
+Zero Page,X   SBC $44,X     $F5  2   4
+Absolute      SBC $4400     $ED  3   4
+Absolute,X    SBC $4400,X   $FD  3   4+
+Absolute,Y    SBC $4400,Y   $F9  3   4+
+Indirect,X    SBC ($44,X)   $E1  2   6
+Indirect,Y    SBC ($44),Y   $F1  2   5+
+
++ add 1 cycle if page boundary crossed
+
+
SBC results are dependant on the setting of the decimal flag. In decimal +mode, subtraction is carried out on the assumption that the values involved are +packed BCD (Binary Coded Decimal). +

There is no way to subtract without the carry which works as an inverse +borrow. i.e, to subtract you set the carry before the operation. If the carry is +cleared by the operation, it indicates a borrow occurred. +

+

+

  +

STA (STore Accumulator)

+

Affects Flags: none

MODE           SYNTAX       HEX LEN TIM
+Zero Page     STA $44       $85  2   3
+Zero Page,X   STA $44,X     $95  2   4
+Absolute      STA $4400     $8D  3   4
+Absolute,X    STA $4400,X   $9D  3   5
+Absolute,Y    STA $4400,Y   $99  3   5
+Indirect,X    STA ($44,X)   $81  2   6
+Indirect,Y    STA ($44),Y   $91  2   6
+
+

+

              +

Stack Instructions

+

These instructions are implied mode, have a length of one byte and require +machine cycles as indicated. The "PuLl" operations are known as "POP" on most +other microprocessors. With the 6502, the stack is always on page one +($100-$1FF) and works top down.

MNEMONIC                        HEX TIM
+TXS (Transfer X to Stack ptr)   $9A  2
+TSX (Transfer Stack ptr to X)   $BA  2
+PHA (PusH Accumulator)          $48  3
+PLA (PuLl Accumulator)          $68  4
+PHP (PusH Processor status)     $08  3
+PLP (PuLl Processor status)     $28  4
+
+

+

+

  +

STX (STore X register)

+

Affects Flags: none

MODE           SYNTAX       HEX LEN TIM
+Zero Page     STX $44       $86  2   3
+Zero Page,Y   STX $44,Y     $96  2   4
+Absolute      STX $4400     $8E  3   4
+
+

+

+

  +

STY (STore Y register)

+

Affects Flags: none

MODE           SYNTAX       HEX LEN TIM
+Zero Page     STY $44       $84  2   3
+Zero Page,X   STY $44,X     $94  2   4
+Absolute      STY $4400     $8C  3   4
+
+

+

Last Updated Oct 17, 2020. + +

\ No newline at end of file diff --git a/emu/6502.c b/emu/6502.c index 98af83a..77ea952 100644 --- a/emu/6502.c +++ b/emu/6502.c @@ -168,17 +168,17 @@ bool cpu_6502_is_carry( cpu_6502_t *cpu ) static const int cycles[NOF_OPCODES] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ - /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, /* 1 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 2 */ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + /* 2 */ 6, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, /* 3 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, - /* 5 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 5 */ 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, /* 6 */ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 7 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 8 */ 0, 0, 0, 0, 0, 0, 3, 0, 2, 0, 0, 0, 0, 0, 4, 0, + /* 7 */ 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + /* 8 */ 0, 0, 0, 0, 0, 3, 3, 0, 2, 0, 0, 0, 0, 4, 4, 0, /* 9 */ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, - /* A */ 2, 0, 2, 0, 3, 3, 3, 0, 0, 2, 0, 0, 0, 0, 0, 0, + /* A */ 2, 0, 2, 0, 3, 3, 3, 0, 0, 2, 0, 0, 0, 4, 0, 0, /* B */ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C */ 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, /* D */ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -188,17 +188,17 @@ static const int cycles[NOF_OPCODES] = { static const char mnemonic[NOF_OPCODES][MAX_MENMONIC_LENGTH] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ - /* 0 */ "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", + /* 0 */ "???", "???", "???", "???", "???", "???", "???", "???", "???", "ORA", "???", "???", "???", "???", "???", "???", /* 1 */ "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", - /* 2 */ "JSR", "???", "???", "???", "???", "???", "???", "???", "???", "???", "ROL", "???", "???", "???", "???", "???", + /* 2 */ "JSR", "???", "???", "???", "???", "???", "???", "???", "???", "AND", "ROL", "???", "???", "???", "???", "???", /* 3 */ "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", /* 4 */ "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "JMP", "???", "???", "???", - /* 5 */ "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", + /* 5 */ "???", "???", "???", "???", "???", "???", "???", "???", "CLI", "???", "???", "???", "???", "???", "???", "???", /* 6 */ "RTS", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", - /* 7 */ "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", - /* 8 */ "???", "???", "???", "???", "???", "???", "STX", "???", "DEY", "???", "???", "???", "???", "???", "STX", "???", + /* 7 */ "???", "???", "???", "???", "???", "???", "???", "???", "SEI", "???", "???", "???", "???", "???", "???", "???", + /* 8 */ "???", "???", "???", "???", "???", "STA", "STX", "???", "DEY", "???", "???", "???", "???", "STA", "STX", "???", /* 9 */ "BCC", "???", "???", "???", "???", "???", "???", "???", "???", "???", "TXS", "???", "???", "???", "???", "???", - /* A */ "LDY", "???", "LDX", "???", "LDY", "LDA", "LDX", "???", "???", "LDA", "???", "???", "???", "???", "???", "???", + /* A */ "LDY", "???", "LDX", "???", "LDY", "LDA", "LDX", "???", "???", "LDA", "???", "???", "???", "STA", "???", "???", /* B */ "BCS", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", /* C */ "???", "???", "???", "???", "???", "???", "???", "???", "INY", "???", "DEX", "???", "???", "???", "???", "???", /* D */ "BNE", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", @@ -345,6 +345,13 @@ void cpu_6502_step( cpu_6502_t *cpu ) update_negative_and_sign( cpu, cpu->A ); break; + case LDA_ABS: + operand16 = cpu_6502_read_word( cpu, cpu->PC ); + cpu->PC += 2; + cpu->A = cpu_6502_read_byte( cpu, operand16 ); + update_negative_and_sign( cpu, cpu->A ); + break; + case STX_ZERO: operand8 = cpu_6502_read_byte( cpu, cpu->PC ); cpu->PC++; @@ -356,7 +363,19 @@ void cpu_6502_step( cpu_6502_t *cpu ) cpu->PC += 2; cpu_6502_write_byte( cpu, operand16, cpu->X ); break; - + + case STA_ZERO: + operand8 = cpu_6502_read_byte( cpu, cpu->PC ); + cpu->PC++; + cpu_6502_write_byte( cpu, operand8, cpu->A ); + break; + + case STA_ABS: + operand16 = cpu_6502_read_word( cpu, cpu->PC ); + cpu->PC += 2; + cpu_6502_write_byte( cpu, operand16, cpu->A ); + break; + case DEX_IMPL: cpu->X--; update_negative_and_sign( cpu, cpu->X ); @@ -391,6 +410,20 @@ void cpu_6502_step( cpu_6502_t *cpu ) cpu->A = tmp & 0xFF; update_negative_and_sign( cpu, cpu->A ); break; + + case AND_IMM: + operand8 = cpu_6502_read_byte( cpu, cpu->PC ); + cpu->PC++; + cpu->A &= operand8; + update_negative_and_sign( cpu, cpu->A ); + break; + + case OR_IMM: + operand8 = cpu_6502_read_byte( cpu, cpu->PC ); + cpu->PC++; + cpu->A |= operand8; + update_negative_and_sign( cpu, cpu->A ); + break; case CPX_IMM: operand8 = cpu_6502_read_byte( cpu, cpu->PC ); @@ -463,6 +496,14 @@ void cpu_6502_step( cpu_6502_t *cpu ) cpu->PC++; break; + case CLI_IMPL: + cpu->PS &= ~PS_I; + break; + + case SEI_IMPL: + cpu->PS |= PS_I; + break; + case TXS_IMPL: cpu->SP = cpu->X; break; diff --git a/emu/6502.h b/emu/6502.h index c462fcc..a34443f 100644 --- a/emu/6502.h +++ b/emu/6502.h @@ -51,29 +51,36 @@ enum { // opcodes enum { - LDX_IMM = 0xA2, - LDX_ZERO = 0xA6, - LDY_IMM = 0xA0, - LDY_ZERO = 0xA4, - LDA_IMM = 0xA9, - LDA_ZERO = 0xA5, + LDX_IMM = 0xa2, + LDX_ZERO = 0xa6, + LDY_IMM = 0xa0, + LDY_ZERO = 0xa4, + LDA_IMM = 0xa9, + LDA_ZERO = 0xa5, + LDA_ABS = 0xad, STX_ZERO = 0x86, - STX_ABS = 0x8E, - DEX_IMPL = 0xCA, + STX_ABS = 0x8e, + STA_ZERO = 0x85, + STA_ABS = 0x8d, + DEX_IMPL = 0xca, DEY_IMPL = 0x88, - INY_IMPL = 0xC8, - INC_ZERO = 0xE6, - ROL_ACC = 0x2A, - BNE_REL = 0xD0, + INY_IMPL = 0xc8, + INC_ZERO = 0xe6, + ROL_ACC = 0x2a, + AND_IMM = 0x29, + OR_IMM = 0x09, + BNE_REL = 0xd0, BCC_REL = 0x90, - BCS_REL = 0xB0, - JMP_ABS = 0x4C, + BCS_REL = 0xb0, + JMP_ABS = 0x4c, JSR_ABS = 0x20, RTS_IMPL = 0x60, - TXS_IMPL = 0x9A, - CPX_IMM = 0xE0, - SBC_IMM = 0xE9, - NOP_IMPL = 0xEA + CLI_IMPL = 0x58, + SEI_IMPL = 0x78, + TXS_IMPL = 0x9a, + CPX_IMM = 0xe0, + SBC_IMM = 0xe9, + NOP_IMPL = 0xea }; enum { diff --git a/emu/6522.c b/emu/6522.c index df9144d..7cde206 100644 --- a/emu/6522.c +++ b/emu/6522.c @@ -22,14 +22,37 @@ void via_6522_init( via_6522_t *via, uint16_t addr, bool initialize ) if( initialize ) { via->ddra = 0x00; + via->ddrb = 0x00; + via->pcr = 0x00; + via->ier = 0x00; } - bus_init( &via->bus ); + bus_init( &via->busa ); + bus_init( &via->busb ); } -void via_6522_register( via_6522_t *via, device_t *device ) +void via_6522_reset( via_6522_t *via ) { - bus_register( &via->bus, device, 0, 0 ); + via->ddra = 0x00; + via->ddrb = 0x00; + via->pcr = 0x00; + via->ier = 0x00; +} + +void via_6522_register( via_6522_t *via, int bus, device_t *device ) +{ + switch( bus ) { + case PORTA: + bus_register( &via->busa, device, 0, 0 ); + break; + + case PORTB: + bus_register( &via->busb, device, 0, 0 ); + break; + + default: + fprintf( stderr, "ERROR: unable to register to VIA 6522 bus, use PORTA or PORTB\n" ); + } } uint8_t via_6522_read( void *obj, uint16_t addr ) @@ -42,18 +65,38 @@ void via_6522_write( void *obj, uint16_t addr, uint8_t data ) via_6522_t *via = (via_6522_t *)obj; switch( addr - via->addr ) { - case DDRA: - via->ddra = data; - break; - case PORTA: data &= via->ddra; - for( int i = 0; i < via->bus.nof_devices; i++ ) { - device_t *device = via->bus.devices[i].device; + for( int i = 0; i < via->busa.nof_devices; i++ ) { + device_t *device = via->busa.devices[i].device; device->vtable->write( device, 0, data ); } break; + + case PORTB: + data &= via->ddrb; + for( int i = 0; i < via->busb.nof_devices; i++ ) { + device_t *device = via->busb.devices[i].device; + device->vtable->write( device, 0, data ); + } + break; + + case DDRB: + via->ddrb = data; + break; + + case DDRA: + via->ddra = data; + break; + case PCR: + via->pcr = data; + break; + + case IER: + via->ier = data; + break; + default: fprintf( stderr, "ERROR: VIA 6522 not implemented address '%04X', data: '%02X'\n", addr, data ); break; @@ -65,7 +108,8 @@ void via_6522_draw( void *obj, SDL_Renderer *renderer ) { via_6522_t *via = (via_6522_t *)obj; - via->bus.base.vtable->draw( &via->bus, renderer ); + via->busa.base.vtable->draw( &via->busa, renderer ); + via->busb.base.vtable->draw( &via->busb, renderer ); } #endif @@ -73,6 +117,8 @@ void via_6522_deinit( void *obj ) { via_6522_t *via = (via_6522_t *)obj; - bus_deinit( &via->bus ); + bus_deinit( &via->busa ); + bus_deinit( &via->busb ); + device_deinit( &via->base ); } diff --git a/emu/6522.h b/emu/6522.h index 853bd3e..d3bade7 100644 --- a/emu/6522.h +++ b/emu/6522.h @@ -16,8 +16,16 @@ // the VIA is initialized with a base address, the addresses for // the registers are relative to this base. enum { + PORTB = 0x00, PORTA = 0x01, - DDRA = 0x03 + DDRB = 0x02, + DDRA = 0x03, + T1LCL = 0x04, + T1LCH = 0x05, + ACR = 0x0b, + PCR = 0x0c, + IFR = 0x0d, + IER = 0x0e }; typedef struct via_6522_t @@ -27,14 +35,19 @@ typedef struct via_6522_t uint16_t addr; uint8_t ddra; + uint8_t ddrb; + uint8_t pcr; + uint8_t ier; bool debug; - bus_t bus; + bus_t busa; + bus_t busb; } via_6522_t; void via_6522_init( via_6522_t *via, uint16_t addr, bool initialize ); -void via_6522_register( via_6522_t *via, device_t *device ); +void via_6522_reset( via_6522_t *via ); +void via_6522_register( via_6522_t *via, int bus, device_t *device ); uint8_t via_6522_read( void *obj, uint16_t addr ); void via_6522_write( void *obj, uint16_t addr, uint8_t data ); diff --git a/emu/emu.c b/emu/emu.c index 590f93e..b2293f9 100644 --- a/emu/emu.c +++ b/emu/emu.c @@ -53,7 +53,7 @@ int main( int argc, char *argv[] ) bus_register( &bus, &via.base, VIA_START, VIA_END ); seg7_init( &seg7, args_info.initialize_given ); - via_6522_register( &via, &seg7.base ); + via_6522_register( &via, PORTA, &seg7.base ); cpu_6502_init( &cpu, &bus, args_info.initialize_given ); if( args_info.debug_given ) { @@ -85,6 +85,7 @@ int main( int argc, char *argv[] ) emul_start( &emul ); cpu_6502_reset( &cpu ); + via_6522_reset( &via ); emul_run( &emul, args_info.steps_arg ); diff --git a/emu/emul.c b/emu/emul.c index ac4b64c..21ab58b 100644 --- a/emu/emul.c +++ b/emu/emul.c @@ -76,13 +76,14 @@ void emul_start( emul_t *emul ) static void print_help( void ) { - fprintf( stderr, "CPU is paused, press\n" + fprintf( stderr, "Keyboard commands, press\n" "(s) for single step\n" "(f) fini (continue to next rts)\n" "(c) for continue running\n" "(b) break to single stepping\n" "(+) speed up\n" "(-) speed down\n" + "(p) push the button\n" "(q) or (ESC) for shutting down\n" ); } @@ -105,6 +106,9 @@ void emul_run( emul_t *emul, int nof_steps ) switch( event.type ) { case SDL_KEYDOWN: switch( event.key.keysym.sym ) { + case SDLK_h: + print_help( ); + break; case SDLK_ESCAPE: case SDLK_q: done = true; @@ -138,6 +142,9 @@ void emul_run( emul_t *emul, int nof_steps ) } fprintf( stderr, "CPU speed is %1.6f MHz now\n", ( (double)emul->speed / 1000000 ) ); break; + case SDLK_p: + // TODO:push the push button connected to CA1 + break; } break; @@ -153,10 +160,6 @@ void emul_run( emul_t *emul, int nof_steps ) SDL_RenderCopy( emul->renderer, emul->background_texture, NULL, NULL ); emul->bus->base.vtable->draw( emul->bus, emul->renderer ); - //~ for( int i = 0; i < emul->bus->nof_devices; i++ ) { - //~ device_t *device = emul->bus->devices[i].device; - //~ device->vtable->draw( device, emul->renderer ); - //~ } SDL_RenderPresent( emul->renderer ); diff --git a/emu/tests/test_cpu_6502.c b/emu/tests/test_cpu_6502.c index 94b68b4..2425be8 100644 --- a/emu/tests/test_cpu_6502.c +++ b/emu/tests/test_cpu_6502.c @@ -148,6 +148,68 @@ START_TEST( test_cpu_6502_txs ) } END_TEST +// CLI + +START_TEST( test_cpu_6502_cli ) +{ + INIT_CPU_TEST + + CODE_SET1( 0x58 ); // CLI + + cpu.PS |= PS_I; + + COPY_CPU_TEST + + cpu_6502_run_steps( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+1 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~PS_I, before_cpu.PS & ~PS_I ); + ck_assert( !cpu_6502_is_interrupt( &cpu ) ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + ck_assert_int_eq( cpu.cycles, 2 ); + + DEINIT_CPU_TEST +} +END_TEST + +// SEI + +START_TEST( test_cpu_6502_sei ) +{ + INIT_CPU_TEST + + CODE_SET1( 0x78 ); // SEI + + cpu.PS &= ~PS_I; + + COPY_CPU_TEST + + cpu_6502_run_steps( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+1 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~PS_I, before_cpu.PS & ~PS_I ); + ck_assert( cpu_6502_is_interrupt( &cpu ) ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + ck_assert_int_eq( cpu.cycles, 2 ); + + DEINIT_CPU_TEST +} +END_TEST + // LDA START_TEST( test_cpu_6502_lda_flags_positive ) @@ -238,6 +300,37 @@ START_TEST( test_cpu_6502_lda_flags_zero ) } END_TEST +START_TEST( test_cpu_6502_lda_addr_abs ) +{ + INIT_CPU_TEST + + CODE_SET3( 0xad, 0x03, 0x01 ); // LDA #$0103 + + cpu.A = 0xff; + ram.cell[0x0103] = 0xea; + + COPY_CPU_TEST + + cpu_6502_run_steps( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+3 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, 0xea ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + ck_assert_int_eq( cpu.cycles, 4 ); + + DEINIT_CPU_TEST +} +END_TEST + // LDY START_TEST( test_cpu_6502_ldy_flags_positive ) @@ -480,6 +573,68 @@ START_TEST( test_cpu_6502_stx_addr_abs ) } END_TEST +// STA + +START_TEST( test_cpu_6502_sta_addr_zero ) +{ + INIT_CPU_TEST + + CODE_SET2( 0x85, 0x03 ); // STA $03 + + cpu.A = 0xea; + + COPY_CPU_TEST + + cpu_6502_run_steps( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+2 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS, before_cpu.PS ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( &ram.cell[0x0], &before_ram.cell[0x0], 3 ); + ck_assert_int_eq( ram.cell[0x03], 0xea ); + ck_assert_mem_eq( &ram.cell[0x04], &before_ram.cell[0x04], 0x1FF - 0x03 ); + ck_assert_int_eq( cpu.cycles, 3 ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_sta_addr_abs ) +{ + INIT_CPU_TEST + + CODE_SET3( 0x8d, 0x03, 0x01 ); // STA #$0103 + + cpu.A = 0xea; + + COPY_CPU_TEST + + cpu_6502_run_steps( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+3 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS, before_cpu.PS ); + ck_assert_int_eq( cpu.A, before_cpu.A ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( &ram.cell[0x0], &before_ram.cell[0x0], 0x102 ); + ck_assert_int_eq( ram.cell[0x0103], 0xea ); // modified value in memory + ck_assert_mem_eq( &ram.cell[0x104], &before_ram.cell[0x104], 0x1FF - 0x103 ); + ck_assert_int_eq( cpu.cycles, 4 ); + + DEINIT_CPU_TEST +} +END_TEST + // INC START_TEST( test_cpu_6502_inc_flags_positive ) @@ -888,6 +1043,250 @@ START_TEST( test_cpu_6502_rol_flags_accumulator_carry ) } END_TEST +// AND + +START_TEST( test_cpu_6502_and_flags_positive ) +{ + INIT_CPU_TEST + + CODE_SET2( 0x29, 0x0f ); // AND #$0f + + cpu.A = 0x36; + + COPY_CPU_TEST + + cpu_6502_run_steps( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+2 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( !cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, 0x06 ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + ck_assert_int_eq( cpu.cycles, 2 ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_and_flags_negative ) +{ + INIT_CPU_TEST + + CODE_SET2( 0x29, 0x8f ); // AND #$8f + + cpu.A = 0xf6; + + COPY_CPU_TEST + + cpu_6502_run_steps( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+2 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, 0x86 ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + ck_assert_int_eq( cpu.cycles, 2 ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_and_flags_zero ) +{ + INIT_CPU_TEST + + CODE_SET2( 0x29, 0x00 ); // AND #$00 + + cpu.A = 0xf6; + + COPY_CPU_TEST + + cpu_6502_run_steps( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+2 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( cpu_6502_is_zero( &cpu ) ); + ck_assert( !cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, 0x00 ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + ck_assert_int_eq( cpu.cycles, 2 ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_and_addr_imm ) +{ + INIT_CPU_TEST + + CODE_SET2( 0x29, 0xf0 ); // AND #$f0 + + cpu.A = 0xff; + + COPY_CPU_TEST + + cpu_6502_run_steps( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+2 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, 0xf0 ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + ck_assert_int_eq( cpu.cycles, 2 ); + + DEINIT_CPU_TEST +} +END_TEST + +// ORA + +START_TEST( test_cpu_6502_ora_flags_positive ) +{ + INIT_CPU_TEST + + CODE_SET2( 0x09, 0x0f ); // ORA #$08 + + cpu.A = 0x37; + + COPY_CPU_TEST + + cpu_6502_run_steps( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+2 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( !cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, 0x3f ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + ck_assert_int_eq( cpu.cycles, 2 ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_ora_flags_negative ) +{ + INIT_CPU_TEST + + CODE_SET2( 0x09, 0x22 ); // ORA #$22 + + cpu.A = 0x86; + + COPY_CPU_TEST + + cpu_6502_run_steps( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+2 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, 0xa6 ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + ck_assert_int_eq( cpu.cycles, 2 ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_ora_flags_zero ) +{ + INIT_CPU_TEST + + CODE_SET2( 0x09, 0x00 ); // ORA #$00 + + cpu.A = 0x00; + + COPY_CPU_TEST + + cpu_6502_run_steps( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+2 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( cpu_6502_is_zero( &cpu ) ); + ck_assert( !cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, 0x00 ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + ck_assert_int_eq( cpu.cycles, 2 ); + + DEINIT_CPU_TEST +} +END_TEST + +START_TEST( test_cpu_6502_ora_addr_imm ) +{ + INIT_CPU_TEST + + CODE_SET2( 0x09, 0x55 ); // ORA #$55 + + cpu.A = 0x1a; + + COPY_CPU_TEST + + cpu_6502_run_steps( &cpu, 1 ); + + PRINT_CPU_TEST + + ck_assert_int_eq( cpu.error_state, ERROR_STATE_OK ); + ck_assert_int_eq( cpu.PC, before_cpu.PC+2 ); + ck_assert_int_eq( cpu.SP, before_cpu.SP ); + ck_assert_int_eq( cpu.PS & ~( PS_N | PS_Z ), before_cpu.PS & ~( PS_N | PS_Z ) ); + ck_assert( !cpu_6502_is_zero( &cpu ) ); + ck_assert( !cpu_6502_is_negative( &cpu ) ); + ck_assert_int_eq( cpu.A, 0x5f ); + ck_assert_int_eq( cpu.X, before_cpu.X ); + ck_assert_int_eq( cpu.Y, before_cpu.Y ); + ck_assert_mem_eq( ram.cell, before_ram.cell, ram.size ); + ck_assert_int_eq( cpu.cycles, 2 ); + + DEINIT_CPU_TEST +} +END_TEST + // CPX START_TEST( test_cpu_6502_cpx_flags_equals ) @@ -1267,9 +1666,12 @@ static Suite *make_cpu_6502_testsuite( void ) create_cpu_6502_testcase( suite, "NOP", test_cpu_6502_nop ); create_cpu_6502_testcase( suite, "TXS", test_cpu_6502_txs ); + create_cpu_6502_testcase( suite, "CLI", test_cpu_6502_cli ); + create_cpu_6502_testcase( suite, "SEI", test_cpu_6502_sei ); create_cpu_6502_testcase( suite, "LDA (flags, positive)", test_cpu_6502_lda_flags_positive ); create_cpu_6502_testcase( suite, "LDA (flags, negative)", test_cpu_6502_lda_flags_negative ); create_cpu_6502_testcase( suite, "LDA (flags, zero)", test_cpu_6502_lda_flags_zero ); + create_cpu_6502_testcase( suite, "LDA (addr, absolute)", test_cpu_6502_lda_addr_abs ); create_cpu_6502_testcase( suite, "LDX (flags, positive)", test_cpu_6502_ldx_flags_positive ); create_cpu_6502_testcase( suite, "LDX (flags, negative)", test_cpu_6502_ldx_flags_negative ); create_cpu_6502_testcase( suite, "LDX (flags, zero)", test_cpu_6502_ldx_flags_zero ); @@ -1278,6 +1680,8 @@ static Suite *make_cpu_6502_testsuite( void ) create_cpu_6502_testcase( suite, "LDY (flags, zero)", test_cpu_6502_ldy_flags_zero ); create_cpu_6502_testcase( suite, "STX (addr, zero)", test_cpu_6502_stx_addr_zero ); create_cpu_6502_testcase( suite, "STX (addr, absolute)", test_cpu_6502_stx_addr_abs ); + create_cpu_6502_testcase( suite, "STA (addr, zero)", test_cpu_6502_sta_addr_zero ); + create_cpu_6502_testcase( suite, "STA (addr, absolute)", test_cpu_6502_sta_addr_abs ); create_cpu_6502_testcase( suite, "INC (flags, positive)", test_cpu_6502_inc_flags_positive ); create_cpu_6502_testcase( suite, "INC (flags, negative)", test_cpu_6502_inc_flags_negative ); create_cpu_6502_testcase( suite, "INC (flags, zero)", test_cpu_6502_inc_flags_zero ); @@ -1291,6 +1695,14 @@ static Suite *make_cpu_6502_testsuite( void ) create_cpu_6502_testcase( suite, "DEY (flags, negative)", test_cpu_6502_dey_flags_negative ); create_cpu_6502_testcase( suite, "DEY (flags, zero)", test_cpu_6502_dey_flags_zero ); create_cpu_6502_testcase( suite, "ROL (flags, carry)", test_cpu_6502_rol_flags_accumulator_carry ); + create_cpu_6502_testcase( suite, "AND (flags, positive)", test_cpu_6502_and_flags_positive ); + create_cpu_6502_testcase( suite, "AND (flags, negative)", test_cpu_6502_and_flags_negative ); + create_cpu_6502_testcase( suite, "AND (flags, zero)", test_cpu_6502_and_flags_zero ); + create_cpu_6502_testcase( suite, "AND (addr, immediate)", test_cpu_6502_and_addr_imm ); + create_cpu_6502_testcase( suite, "ORA (flags, positive)", test_cpu_6502_ora_flags_positive ); + create_cpu_6502_testcase( suite, "ORA (flags, negative)", test_cpu_6502_ora_flags_negative ); + create_cpu_6502_testcase( suite, "ORA (flags, zero)", test_cpu_6502_ora_flags_zero ); + create_cpu_6502_testcase( suite, "ORA (addr, immediate)", test_cpu_6502_ora_addr_imm ); create_cpu_6502_testcase( suite, "CPX (flags, equals)", test_cpu_6502_cpx_flags_equals ); create_cpu_6502_testcase( suite, "CPX (flags, less)", test_cpu_6502_cpx_flags_less ); create_cpu_6502_testcase( suite, "CPX (flags, greater)", test_cpu_6502_cpx_flags_greater ); diff --git a/roms/7seg_counter_irq_timer.asm b/roms/7seg_counter_irq_timer.asm index b031886..3da0f3c 100644 --- a/roms/7seg_counter_irq_timer.asm +++ b/roms/7seg_counter_irq_timer.asm @@ -30,8 +30,8 @@ reset: sta PCR lda ACR - AND #$7F - ORA #%01000000 ; setup timer for free running timer1, no PB7 + and #$7F + ora #%01000000 ; setup timer for free running timer1, no PB7 sta ACR lda #%00000111 ; set output on 7seg control pins of PORTA -- cgit v1.2.3-54-g00ecf