CPU bug fixes and Simulator enhancements.

Bug Fixes:

- Fixed several bugs in the CPU that caused processor status flags to
  be set incorrectly.  Instructions affected were: STA, STX, STY, CMP,
  CPX, CPY, BIT.

- Made some internal-use-only methods on the CPU class private.

- Fixed incorrect disassembly of (Indirect,X) and (Indirect),Y
  instructions. Although this didn't affect behavior, it certainly
  caused me some confusion in debugging.

- Added missing "BCS" instruction to instruction table.

Enhancements:

- Now includes a full version of Lee Davison's Enhanced 6502 BASIC
  bundled as source code and a ROM image. Get that REAL COMPUTER
  EXPERIENCE!(tm)

- If a file named "rom.bin" exists in the same directory where the
  simulator is executed, it will be loaded at addresses $d000-$ffff.

- Gave the CPU an idle loop to make simulated timing a little more
  realistic (but this is still an area needing major improvement)

- Changed the CPU's toString() method to give better debugging output.

- Added a small typeahead buffer to the Console.

- Better exception messaging.

Misc:

- Bumped version to 0.5, updated README.
This commit is contained in:
Seth Morabito committed 2012-10-21 20:05:05 -07:00
1 parent c1caf8c6b4
commit 795ccfde5d
25 files changed
+9369 -255

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License The MIT License
Copyright (c) 2008-2010 Seth J. Morabito <sethm@loomcom.com> Copyright (c) 2008-2012 Seth J. Morabito <sethm@loomcom.com>
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
+23 -11
View File
@@ -1,12 +1,11 @@
SYMON - A 6502 System Simulator SYMON - A 6502 System Simulator
=============================== ===============================
**NOTE: THIS IS ALPHA QUALITY SOFTWARE UNDER ACTIVE DEVELOPMENT. IT IS **NOTE: THIS IS BETA QUALITY SOFTWARE UNDER ACTIVE DEVELOPMENT. Feedback is
NOT YET FULLY FUNCTIONAL. IT MAY BE USEFUL, BUT IT IS NOT YET INTENDED welcome!**
TO BE USED BY ANYONE BUT DEVELOPERS. Feedback is welcome!**
**Version:** 0.3 **Version:** 0.5
**Last Updated:** 14 October, 2012 **Last Updated:** 21 October, 2012
Copyright (c) 2008-2012 Seth J. Morabito &lt;web@loomcom.com&gt; Copyright (c) 2008-2012 Seth J. Morabito &lt;web@loomcom.com&gt;
@@ -70,23 +69,36 @@ for testing.
- 'hello.prg' will continuously print "Hello, 6502 World!" to the console. - 'hello.prg' will continuously print "Hello, 6502 World!" to the console.
### 3.3 Running The sample directory also contains a ROM image of Lee Davison's
Ehanced 6502 BASIC. For instructions on loading the rom, please see
the README file in that directory.
After loading a program, clicking "Run" will start the simulator ### 3.3 ROM files
Any 12KB file named 'rom.bin' that exists in the same directory where
Symon is launched will be loaded at address $d000. If the file is
larger than 12KB, loading will fail. This functionality will be
improved in a future release!
### 3.4 Running
After loading a program or ROM image, clicking "Run" will start the simulator
running at address $0300. running at address $0300.
## 4.0 To Do ## 4.0 To Do
- Accurate timing (all simulated instructions currently take - Better debugging tools from the UI, including memory inspection,
only one step to execute) disassembly, breakpoints, and execution tracing.
- Better ROM loading (and re-loading)
- More accurate timing.
- Interrupt handling! - Interrupt handling!
- UI needs a ton more polish. - UI needs a ton more polish.
- Add a simple menu interface for common tasks.
- More extensive testing. - More extensive testing.
- Clean up JavaDoc. - Clean up JavaDoc.
+32 -2
View File
@@ -1,9 +1,39 @@
A Sample Program Sample Programs
---------------- ----------------
1. hello.prg
When loaded at address $0300, this program will print "Hello, 6502 World!" in When loaded at address $0300, this program will print "Hello, 6502 World!" in
infinite loop. infinite loop.
Assembled with the Ophis assembler: 2. echo.prg
When loaded at address $0300, this program will echo back to the console
anything typed.
Both hello.prg and echo.prg were assembled with the Ophis assembler:
https://hkn.eecs.berkeley.edu/~mcmartin/ophis/ https://hkn.eecs.berkeley.edu/~mcmartin/ophis/
3. ehbasic.rom
This is Lee Davison's Enhanced 6502 BASIC.
To use this ROM image, just copy the file 'ehbasic.rom' into the directory
where you run Symon. Rename the file to 'rom.bin'. When you start Symon,
the ROM file will be automatically loaded at address $d000.
Click the "Run" button and EhBASIC should automatically start running.
Type 'C' to do a cold start.
Then, type $C000 when prompted for the memory size.
NOTE: EhBASIC only wants upper-case input. This confused me at first!
More information can be found in the 'ehbasic' directory, and by visiting
the EhBASIC web page:
http://mycorner.no-ip.org/6502/ehbasic/index.html
+2 -2
View File
@@ -27,9 +27,9 @@ getkey: lda iostatus ; Read the ACIA status
;; Write the current char in the accumulator to the console ;; Write the current char in the accumulator to the console
write: pha ; Save accumulator write: pha ; Save accumulator
lda iostatus ; Read the ACIA status writel: lda iostatus ; Read the ACIA status
and #$10 ; Is the tx register empty? and #$10 ; Is the tx register empty?
beq write ; No, wait for it to empty beq writel ; No, wait for it to empty
pla ; Otherwise, load saved accumulator pla ; Otherwise, load saved accumulator
sta iobase ; and write to output. sta iobase ; and write to output.
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
*.rom
*.lst
*.map
*.o
+13
View File
@@ -0,0 +1,13 @@
CA=ca65
LD=ld65
all: ehbasic
ehbasic: ehbasic.o
$(LD) -C symon.config -vm -m ehbasic.map -o ehbasic.rom ehbasic.o
ehbasic.o:
$(CA) --listing -o ehbasic.o min_mon.asm
clean:
rm -f *.o *.rom *.map *.lst
+85
View File
@@ -0,0 +1,85 @@
This directoy contains a very slightly modified version of EhBASIC 2.22
to support the Symon simulator.
Usage
-----
The pre-assemled ROM image 'ehbasic.rom' in the "samples" directory is all you
need unless you want to rebuild this source code.
Just copy the image 'ehbasic.rom' to the directory where you run Symon, and
rename the file to 'rom.bin'. It will be loaded at memory location $D000 when
the simulator starts up. Click "Run" and you'll be presented with BASIC.
At the first prompt, type 'C' for a Cold Start
When prompted for free memory, type: $C000
Note that EhBASIC only accepts upper case input, so you'll need to use caps
lock (the cruise control for cool) to really make the most of it.
Building
--------
To build it, you'll need the CC65 tool chain from http://www.cc65.org/
and a suitable version of 'make'. Just typing:
% make
in this directory should re-build everything. You'll get a listing,
some object files, and the ROM image itself.
Changes from the EhBASIC 2.22
-----------------------------
- Minor syntax changes to allow assembly with the cc65 tool chain.
- Memory map modified for Symon.
- At reset, configure the 6551 ACIA for 8-N-1, 2400 baud.
- Monitor routines 'ACIAin' and 'ACIAout' modified for the 6551 ACIA.
Specifically, reading and writing will check the 6551 status register for
the status of rx and tx registers before receive or transmit.
EhBASIC is copyright Lee Davison <leeedavison@googlemail.com>
My changes are so slight that I hesitate to even say this, but for "CYA"
reasons:
Changes are copyright Seth Morabito <web@loomcom.com> and are distributed under
the same license as EhBASIC. I claim no commercial interest whatsoever. Any
commercial use must be negotiated directly with Lee Davison.
Original EhBASIC 2.22 README
----------------------------
Enhanced BASIC is a BASIC interpreter for the 6502 family microprocessors. It
is constructed to be quick and powerful and easily ported between 6502 systems.
It requires few resources to run and includes instructions to facilitate easy
low level handling of hardware devices. It also retains most of the powerful
high level instructions from similar BASICs.
EhBASIC is free but not copyright free. For non commercial use there is only one
restriction, any derivative work should include, in any binary image distributed,
the string "Derived from EhBASIC" and in any distribution that includes human
readable files a file that includes the above string in a human readable form
e.g. not as a comment in an HTML file.
For commercial use please contact me, Lee Davison, at leeedavison@googlemail.com
for conditions.
For more information on EhBASIC, other versions of EhBASIC and other projects
please visit my site at ..
http://mycorner.no-ip.org/index.html
P.S. c't magazin, henceforth refered to as "those thieving german bastards", are
prohibited from using this or any version of EhBASIC for any of their projects
or products. The excuse "we don't charge people for it" doesn't wash, it adds
value to your product so you owe me.
File diff suppressed because it is too large. Load diff
+152
View File
@@ -0,0 +1,152 @@
; minimal monitor for EhBASIC and 6502 simulator V1.05
; To run EhBASIC on the simulator load and assemble [F7] this file, start the simulator
; running [F6] then start the code with the RESET [CTRL][SHIFT]R. Just selecting RUN
; will do nothing, you'll still have to do a reset to run the code.
.feature labels_without_colons
.include "basic.asm"
; put the IRQ and MNI code in RAM so that it can be changed
IRQ_vec = VEC_SV+2 ; IRQ code vector
NMI_vec = IRQ_vec+$0A ; NMI code vector
; setup for the 6502 simulator environment
IO_AREA = $C000
ACIAdata = IO_AREA ; simulated ACIA r/w port
ACIAstatus = IO_AREA+1
ACIAcommand = IO_AREA+2
ACIAcontrol = IO_AREA+3
; now the code. all this does is set up the vectors and interrupt code
; and wait for the user to select [C]old or [W]arm start. nothing else
; fits in less than 128 bytes
.segment "MONITOR"
.org $FF00 ; pretend this is in a 1/8K ROM
; reset vector points here
RES_vec
CLD ; clear decimal mode
LDX #$FF ; empty stack
TXS ; set the stack
; Initialize the ACIA
ACIA_init
LDA #$09
STA ACIAcommand
LDA #$1A ; Set output for 8-N-1 2400
STA ACIAcontrol
; set up vectors and interrupt code, copy them to page 2
LDY #END_CODE-LAB_vec ; set index/count
LAB_stlp
LDA LAB_vec-1,Y ; get byte from interrupt code
STA VEC_IN-1,Y ; save to RAM
DEY ; decrement index/count
BNE LAB_stlp ; loop if more to do
; now do the signon message, Y = $00 here
LAB_signon
LDA LAB_mess,Y ; get byte from sign on message
BEQ LAB_nokey ; exit loop if done
JSR V_OUTP ; output character
INY ; increment index
BNE LAB_signon ; loop, branch always
LAB_nokey
JSR V_INPT ; call scan input device
BCC LAB_nokey ; loop if no key
AND #$DF ; mask xx0x xxxx, ensure upper case
CMP #'W' ; compare with [W]arm start
BEQ LAB_dowarm ; branch if [W]arm start
CMP #'C' ; compare with [C]old start
BNE RES_vec ; loop if not [C]old start
JMP LAB_COLD ; do EhBASIC cold start
LAB_dowarm
JMP LAB_WARM ; do EhBASIC warm start
; byte out to simulated ACIA
ACIAout
PHA ; save accumulator
ACIAout_w
LDA ACIAstatus ; Read 6551 status
AND #$10 ; Is tx buffer full?
BEQ ACIAout_w ; if not, loop back
PLA ; Otherwise, restore accumulator
STA ACIAdata ; write byte to 6551
RTS
; byte in from simulated ACIA
ACIAin
LDA ACIAstatus ; Read 6551 status
AND #$08 ;
BEQ LAB_nobyw ; If rx buffer empty, no byte
LDA ACIAdata ; Read byte from 6551
SEC ; Flag byte received
RTS
LAB_nobyw
CLC ; flag no byte received
no_load ; empty load vector for EhBASIC
no_save ; empty save vector for EhBASIC
RTS
; vector tables
LAB_vec
.word ACIAin ; byte in from simulated ACIA
.word ACIAout ; byte out to simulated ACIA
.word no_load ; null load vector for EhBASIC
.word no_save ; null save vector for EhBASIC
; EhBASIC IRQ support
IRQ_CODE
PHA ; save A
LDA IrqBase ; get the IRQ flag byte
LSR ; shift the set b7 to b6, and on down ...
ORA IrqBase ; OR the original back in
STA IrqBase ; save the new IRQ flag byte
PLA ; restore A
RTI
; EhBASIC NMI support
NMI_CODE
PHA ; save A
LDA NmiBase ; get the NMI flag byte
LSR ; shift the set b7 to b6, and on down ...
ORA NmiBase ; OR the original back in
STA NmiBase ; save the new NMI flag byte
PLA ; restore A
RTI
END_CODE
LAB_mess
.byte $0D,$0A,"6502 EhBASIC [C]old/[W]arm ?",$00
; sign on string
; system vectors
.segment "VECTORS"
.org $FFFA
.word 0 ; NMI vector
.word RES_vec ; RESET vector
.word 0 ; IRQ vector
+18
View File
@@ -0,0 +1,18 @@
MEMORY {
RAM1: start = $0000, size = $C000;
ROM1: start = $D000, size = $2F00, fill = yes;
MONITOR: start = $FF00, size = $FA, fill = yes;
ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
}
SEGMENTS {
CODE: load = ROM1, type = ro;
DATA: load = ROM1, type = ro;
MONITOR: load = MONITOR, type = ro;
VECTORS: load = ROMV, type = ro;
}
SYMBOLS {
__STACKSIZE__ = $0300;
}
+5 -4
View File
@@ -42,9 +42,10 @@ public class Bus {
MemoryRange memRange = device.getMemoryRange(); MemoryRange memRange = device.getMemoryRange();
for (Device d : devices) { for (Device d : devices) {
if (d.getMemoryRange().overlaps(memRange)) { if (d.getMemoryRange().overlaps(memRange)) {
throw new MemoryRangeException("The device being added " + throw new MemoryRangeException("The device being added at " +
String.format("$%04X", memRange.startAddress()) +
" overlaps with an existing " + " overlaps with an existing " +
"device."); "device, '" + d + "'");
} }
} }
@@ -114,7 +115,7 @@ public class Bus {
return d.read(devAddr); return d.read(devAddr);
} }
} }
throw new MemoryAccessException("Read failed! No device at address."); throw new MemoryAccessException("Bus read failed. No device at address " + String.format("$%04X", address));
} }
public void write(int address, int value) throws MemoryAccessException { public void write(int address, int value) throws MemoryAccessException {
@@ -127,7 +128,7 @@ public class Bus {
return; return;
} }
} }
throw new MemoryAccessException("Write failed! No device at address."); throw new MemoryAccessException("Bus write failed. No device at address " + String.format("$%04X", address));
} }
public SortedSet<Device> getDevices() { public SortedSet<Device> getDevices() {
+61 -36
View File
@@ -29,6 +29,10 @@ public class Cpu implements InstructionTable {
public static final int NMI_VECTOR_L = 0xfffe; public static final int NMI_VECTOR_L = 0xfffe;
public static final int NMI_VECTOR_H = 0xffff; public static final int NMI_VECTOR_H = 0xffff;
// The delay in microseconds between steps.
// TODO: Make configurable
private static final int CLOCK_IN_NS = 1000;
/* The Bus */ /* The Bus */
private Bus bus; private Bus bus;
@@ -41,6 +45,8 @@ public class Cpu implements InstructionTable {
private int pc; // Program Counter register private int pc; // Program Counter register
private int sp; // Stack Pointer register, offset into page 1 private int sp; // Stack Pointer register, offset into page 1
private int ir; // Instruction register private int ir; // Instruction register
private int lastPc; // The program counter before the most recent step.
private int[] args = new int[3]; // Decoded instruction args private int[] args = new int[3]; // Decoded instruction args
private int instSize; // # of operands for the instruction private int instSize; // # of operands for the instruction
@@ -435,8 +441,8 @@ public class Cpu implements InstructionTable {
/** BIT - Bit Test ******************************************************/ /** BIT - Bit Test ******************************************************/
case 0x24: // Zero Page case 0x24: // Zero Page
case 0x2c: // Absolute case 0x2c: // Absolute
tmp = a & bus.read(effectiveAddress); tmp = bus.read(effectiveAddress);
setZeroFlag(tmp == 0); setZeroFlag((a & tmp) == 0);
setNegativeFlag((tmp & 0x80) != 0); setNegativeFlag((tmp & 0x80) != 0);
setOverflowFlag((tmp & 0x40) != 0); setOverflowFlag((tmp & 0x40) != 0);
break; break;
@@ -553,7 +559,6 @@ public class Cpu implements InstructionTable {
case 0x99: // Absolute,Y case 0x99: // Absolute,Y
case 0x9d: // Absolute,X case 0x9d: // Absolute,X
bus.write(effectiveAddress, a); bus.write(effectiveAddress, a);
setArithmeticFlags(a);
break; break;
@@ -562,7 +567,6 @@ public class Cpu implements InstructionTable {
case 0x8c: // Absolute case 0x8c: // Absolute
case 0x94: // Zero Page,X case 0x94: // Zero Page,X
bus.write(effectiveAddress, y); bus.write(effectiveAddress, y);
setArithmeticFlags(y);
break; break;
@@ -571,7 +575,6 @@ public class Cpu implements InstructionTable {
case 0x8e: // Absolute case 0x8e: // Absolute
case 0x96: // Zero Page,Y case 0x96: // Zero Page,Y
bus.write(effectiveAddress, x); bus.write(effectiveAddress, x);
setArithmeticFlags(x);
break; break;
@@ -704,6 +707,8 @@ public class Cpu implements InstructionTable {
setOpTrap(); setOpTrap();
break; break;
} }
delayLoop(ir);
} }
/** /**
@@ -715,7 +720,7 @@ public class Cpu implements InstructionTable {
* @param operand The operand * @param operand The operand
* @return * @return
*/ */
public int adc(int acc, int operand) { private int adc(int acc, int operand) {
int result = (operand & 0xff) + (acc & 0xff) + getCarryBit(); int result = (operand & 0xff) + (acc & 0xff) + getCarryBit();
int carry6 = (operand & 0x7f) + (acc & 0x7f) + getCarryBit(); int carry6 = (operand & 0x7f) + (acc & 0x7f) + getCarryBit();
setCarryFlag((result & 0x100) != 0); setCarryFlag((result & 0x100) != 0);
@@ -729,7 +734,7 @@ public class Cpu implements InstructionTable {
* Add with Carry (BCD). * Add with Carry (BCD).
*/ */
public int adcDecimal(int acc, int operand) { private int adcDecimal(int acc, int operand) {
int l, h, result; int l, h, result;
l = (acc & 0x0f) + (operand & 0x0f) + getCarryBit(); l = (acc & 0x0f) + (operand & 0x0f) + getCarryBit();
if ((l & 0xff) > 9) l += 6; if ((l & 0xff) > 9) l += 6;
@@ -748,12 +753,8 @@ public class Cpu implements InstructionTable {
* Common code for Subtract with Carry. Just calls ADC of the * Common code for Subtract with Carry. Just calls ADC of the
* one's complement of the operand. This lets the N, V, C, and Z * one's complement of the operand. This lets the N, V, C, and Z
* flags work out nicely without any additional logic. * flags work out nicely without any additional logic.
*
* @param acc
* @param operand
* @return
*/ */
public int sbc(int acc, int operand) { private int sbc(int acc, int operand) {
int result; int result;
result = adc(acc, ~operand); result = adc(acc, ~operand);
setArithmeticFlags(result); setArithmeticFlags(result);
@@ -762,12 +763,8 @@ public class Cpu implements InstructionTable {
/** /**
* Subtract with Carry, BCD mode. * Subtract with Carry, BCD mode.
*
* @param acc
* @param operand
* @return
*/ */
public int sbcDecimal(int acc, int operand) { private int sbcDecimal(int acc, int operand) {
int l, h, result; int l, h, result;
l = (acc & 0x0f) - (operand & 0x0f) - (carryFlag ? 0 : 1); l = (acc & 0x0f) - (operand & 0x0f) - (carryFlag ? 0 : 1);
if ((l & 0x10) != 0) l -= 6; if ((l & 0x10) != 0) l -= 6;
@@ -784,23 +781,19 @@ public class Cpu implements InstructionTable {
/** /**
* Compare two values, and set carry, zero, and negative flags * Compare two values, and set carry, zero, and negative flags
* appropriately. * appropriately.
*
* @param reg
* @param operand
*/ */
public void cmp(int reg, int operand) { private void cmp(int reg, int operand) {
int tmp = (reg - operand) & 0xff;
setCarryFlag(reg >= operand); setCarryFlag(reg >= operand);
setZeroFlag(reg == operand); setZeroFlag(tmp == 0);
setNegativeFlag((reg - operand) > 0); setNegativeFlag((tmp & 0x80) != 0); // Negative bit set
} }
/** /**
* Set the Negative and Zero flags based on the current value of the * Set the Negative and Zero flags based on the current value of the
* register operand. * register operand.
*
* @param reg The register.
*/ */
public void setArithmeticFlags(int reg) { private void setArithmeticFlags(int reg) {
zeroFlag = (reg == 0); zeroFlag = (reg == 0);
negativeFlag = (reg & 0x80) != 0; negativeFlag = (reg & 0x80) != 0;
} }
@@ -1134,6 +1127,10 @@ public class Cpu implements InstructionTable {
this.y = val; this.y = val;
} }
public int getLastProgramCounter() {
return lastPc;
}
public int getProgramCounter() { public int getProgramCounter() {
return pc; return pc;
} }
@@ -1285,15 +1282,15 @@ public class Cpu implements InstructionTable {
*/ */
public String toString() { public String toString() {
String opcode = opcode(ir, args[0], args[1]); String opcode = opcode(ir, args[0], args[1]);
StringBuffer sb = new StringBuffer(String.format("$%04X", addr) + StringBuffer sb = new StringBuffer(getInstructionByteStatus());
" "); sb.append(" ");
sb.append(String.format("%-14s", opcode)); sb.append(String.format("%-14s", opcode));
sb.append("A=" + getAccumulatorStatus() + " "); sb.append("A:" + String.format("%02x", a) + " ");
sb.append("X=" + getXRegisterStatus() + " "); sb.append("X:" + String.format("%02x", x) + " ");
sb.append("Y=" + getYRegisterStatus() + " "); sb.append("Y:" + String.format("%02x", y) + " ");
sb.append("PC=" + getProgramCounterStatus() + " "); sb.append("F:" + String.format("%02x", getProcessorStatus()) + " ");
sb.append("SP=" + getStackPointerStatus() + " "); sb.append("S:" + String.format("1%02x", sp) + " ");
sb.append("P=" + getProcessorStatusString()); sb.append(getProcessorStatusString());
return sb.toString(); return sb.toString();
} }
@@ -1397,6 +1394,19 @@ public class Cpu implements InstructionTable {
bus.write(RST_VECTOR_L, address & 0x00ff); bus.write(RST_VECTOR_L, address & 0x00ff);
} }
/*
* Perform a busy-loop for CLOCK_IN_NS nanoseconds
*/
private void delayLoop(int opcode) {
int clockSteps = Cpu.instructionClocks[0xff & opcode];
// Just a precaution. This could be better.
if (clockSteps == 0) { clockSteps = 1; }
long startTime = System.nanoTime();
long stopTime = startTime + (CLOCK_IN_NS * clockSteps);
// Busy loop
while (System.nanoTime() < stopTime) { ; }
}
/** /**
* Given an opcode and its operands, return a formatted name. * Given an opcode and its operands, return a formatted name.
* *
@@ -1430,10 +1440,10 @@ public class Cpu implements InstructionTable {
sb.append(String.format(" ($%04X)", address(op1, op2))); sb.append(String.format(" ($%04X)", address(op1, op2)));
break; break;
case XIN: case XIN:
sb.append(String.format(" ($%02X),X", op1)); sb.append(String.format(" ($%02X,X)", op1));
break; break;
case INY: case INY:
sb.append(String.format(" ($%02X,Y)", op1)); sb.append(String.format(" ($%02X),Y", op1));
break; break;
case REL: case REL:
case ZPG: case ZPG:
@@ -1449,4 +1459,19 @@ public class Cpu implements InstructionTable {
return sb.toString(); return sb.toString();
} }
public String getInstructionByteStatus() {
switch (Cpu.instructionSizes[ir]) {
case 0:
case 1:
return String.format("%04X %02X ", addr, ir);
case 2:
return String.format("%04X %02X %02X ", addr, ir, args[0]);
case 3:
return String.format("%04X %02X %02X %02X", addr, ir, args[0], args[1]);
default:
return null;
}
}
} }
@@ -119,7 +119,7 @@ public interface InstructionTable {
"TYA", "STA", "TXS", null, null, "STA", null, null, "TYA", "STA", "TXS", null, null, "STA", null, null,
"LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null,
"TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null, "TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null,
null, "LDA", null, null, "LDY", "LDA", "LDX", null, "BCS", "LDA", null, null, "LDY", "LDA", "LDX", null,
"CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null, "CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null,
"CPY", "CMP", null, null, "CPY", "CMP", "DEC", null, "CPY", "CMP", null, null, "CPY", "CMP", "DEC", null,
"INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null, "INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null,
@@ -173,13 +173,13 @@ public interface InstructionTable {
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x8c-0x8f Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x8c-0x8f
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x90-0x93 Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x90-0x93
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97 Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97
Mode.IMP, Mode.ABX, Mode.IMP, Mode.NUL, // 0x98-0x9b Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x98-0x9b
Mode.NUL, Mode.ABY, Mode.NUL, Mode.NUL, // 0x9c-0x9f Mode.NUL, Mode.ABX, Mode.NUL, Mode.NUL, // 0x9c-0x9f
Mode.IMM, Mode.XIN, Mode.IMM, Mode.NUL, // 0xa0-0xa3 Mode.IMM, Mode.XIN, Mode.IMM, Mode.NUL, // 0xa0-0xa3
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xa4-0xa7 Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xa4-0xa7
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xa8-0xab Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xa8-0xab
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xac-0xaf Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xac-0xaf
Mode.NUL, Mode.INY, Mode.NUL, Mode.NUL, // 0xb0-0xb3 Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xb0-0xb3
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7 Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7
Mode.IMP, Mode.ABX, Mode.IMP, Mode.NUL, // 0xb8-0xbb Mode.IMP, Mode.ABX, Mode.IMP, Mode.NUL, // 0xb8-0xbb
Mode.ABX, Mode.ABY, Mode.ABY, Mode.NUL, // 0xbc-0xbf Mode.ABX, Mode.ABY, Mode.ABY, Mode.NUL, // 0xbc-0xbf
@@ -239,7 +239,7 @@ public interface InstructionTable {
2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, 2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0,
2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, 2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0,
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, 2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0,
0, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, 2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0,
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
+33 -30
View File
@@ -2,6 +2,7 @@ package com.loomcom.symon;
import com.loomcom.symon.devices.Acia; import com.loomcom.symon.devices.Acia;
import com.loomcom.symon.devices.Memory; import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.exceptions.FifoUnderrunException;
import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException; import com.loomcom.symon.exceptions.MemoryRangeException;
import com.loomcom.symon.exceptions.SymonException; import com.loomcom.symon.exceptions.SymonException;
@@ -14,7 +15,6 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.io.*; import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.Observable; import java.util.Observable;
import java.util.Observer; import java.util.Observer;
import java.util.logging.Level; import java.util.logging.Level;
@@ -27,16 +27,16 @@ public class Simulator implements ActionListener, Observer {
private static final int BUS_BOTTOM = 0x0000; private static final int BUS_BOTTOM = 0x0000;
private static final int BUS_TOP = 0xffff; private static final int BUS_TOP = 0xffff;
// 32K of RAM
private static final int MEMORY_BASE = 0x0000; private static final int MEMORY_BASE = 0x0000;
private static final int MEMORY_SIZE = 0xc000; // 48 KB private static final int MEMORY_SIZE = 0xC000;
private static final int ROM_BASE = 0xe000; // IO area at $D000
private static final int ROM_SIZE = 0x2000; // 8 KB private static final int ACIA_BASE = 0xC000;
private static final int ACIA_BASE = 0xc000; // 8KB ROM at E000-FFFF
private static final int ROM_BASE = 0xD000;
// The delay in microseconds between steps. private static final int ROM_SIZE = 0x3000;
private static final int DELAY_BETWEEN_STEPS_NS = 1000;
// Since it is very expensive to update the UI with Swing's Event Dispatch Thread, we can't afford // Since it is very expensive to update the UI with Swing's Event Dispatch Thread, we can't afford
// to refresh the view on every simulated clock cycle. Instead, we will only refresh the view after this // to refresh the view on every simulated clock cycle. Instead, we will only refresh the view after this
@@ -84,18 +84,26 @@ public class Simulator implements ActionListener, Observer {
private JFileChooser fileChooser; private JFileChooser fileChooser;
private PreferencesDialog preferences; private PreferencesDialog preferences;
public Simulator() throws MemoryRangeException { public Simulator() throws MemoryRangeException, IOException {
this.acia = new Acia(ACIA_BASE); this.acia = new Acia(ACIA_BASE);
this.bus = new Bus(BUS_BOTTOM, BUS_TOP); this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
this.cpu = new Cpu(); this.cpu = new Cpu();
this.ram = new Memory(MEMORY_BASE, MEMORY_SIZE, false); this.ram = new Memory(MEMORY_BASE, MEMORY_SIZE, false);
// TODO: Load this ROM from a file, of course! // TODO: Make this configurable, of course.
this.rom = new Memory(ROM_BASE, ROM_SIZE, false); File romImage = new File("rom.bin");
if (romImage.canRead()) {
logger.info("Loading ROM image from file " + romImage);
this.rom = Memory.makeROM(ROM_BASE, ROM_SIZE, romImage);
} else {
logger.info("No ROM file 'rom.bin' found. ROM image will be empty R/W memory.");
// Just make it a normal RAM image.
this.rom = Memory.makeRAM(ROM_BASE, ROM_SIZE);
}
bus.addCpu(cpu); bus.addCpu(cpu);
bus.addDevice(acia);
bus.addDevice(ram); bus.addDevice(ram);
bus.addDevice(acia);
bus.addDevice(rom); bus.addDevice(rom);
} }
@@ -212,7 +220,7 @@ public class Simulator implements ActionListener, Observer {
*/ */
public void actionPerformed(ActionEvent actionEvent) { public void actionPerformed(ActionEvent actionEvent) {
if (actionEvent.getSource() == resetButton) { if (actionEvent.getSource() == resetButton) {
handleReset(); coldReset();
} else if (actionEvent.getSource() == stepButton) { } else if (actionEvent.getSource() == stepButton) {
handleStep(); handleStep();
} else if (actionEvent.getSource() == runStopButton) { } else if (actionEvent.getSource() == runStopButton) {
@@ -274,7 +282,7 @@ public class Simulator implements ActionListener, Observer {
} }
} }
private void handleReset() { private void coldReset() {
if (runLoop != null && runLoop.isRunning()) { if (runLoop != null && runLoop.isRunning()) {
runLoop.requestStop(); runLoop.requestStop();
runLoop.interrupt(); runLoop.interrupt();
@@ -282,7 +290,7 @@ public class Simulator implements ActionListener, Observer {
} }
try { try {
logger.log(Level.INFO, "Reset requested. Resetting CPU and clearing memory."); logger.log(Level.INFO, "Cold reset requested. Resetting CPU and clearing memory.");
// Reset and clear memory // Reset and clear memory
cpu.reset(); cpu.reset();
ram.fill(0x00); ram.fill(0x00);
@@ -307,6 +315,7 @@ public class Simulator implements ActionListener, Observer {
private void handleStep() { private void handleStep() {
try { try {
step(); step();
// The simulator is lazy about updating the UI for performance reasons, so always request an // The simulator is lazy about updating the UI for performance reasons, so always request an
// immediate update after stepping manually. // immediate update after stepping manually.
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
@@ -337,8 +346,6 @@ public class Simulator implements ActionListener, Observer {
*/ */
private void step() throws MemoryAccessException { private void step() throws MemoryAccessException {
delayLoop();
cpu.step(); cpu.step();
// Read from the ACIA and immediately update the console if there's // Read from the ACIA and immediately update the console if there's
@@ -351,9 +358,13 @@ public class Simulator implements ActionListener, Observer {
// If a key has been pressed, fill the ACIA. // If a key has been pressed, fill the ACIA.
// TODO: Interrupt handling. // TODO: Interrupt handling.
try {
if (console.hasInput()) { if (console.hasInput()) {
acia.rxWrite((int)console.readInputChar()); acia.rxWrite((int)console.readInputChar());
} }
} catch (FifoUnderrunException ex) {
logger.severe("Console type-ahead buffer underrun!");
}
// This is a very expensive update, and we're doing it without // This is a very expensive update, and we're doing it without
// a delay, so we don't want to overwhelm the Swing event processing thread // a delay, so we don't want to overwhelm the Swing event processing thread
@@ -374,8 +385,6 @@ public class Simulator implements ActionListener, Observer {
* Load a program into memory at the simulatorDidStart address. * Load a program into memory at the simulatorDidStart address.
*/ */
private void loadProgram(byte[] program, int startAddress) throws MemoryAccessException { private void loadProgram(byte[] program, int startAddress) throws MemoryAccessException {
cpu.setResetVector(startAddress);
int addr = startAddress, i; int addr = startAddress, i;
for (i = 0; i < program.length; i++) { for (i = 0; i < program.length; i++) {
bus.write(addr++, program[i] & 0xff); bus.write(addr++, program[i] & 0xff);
@@ -388,6 +397,9 @@ public class Simulator implements ActionListener, Observer {
// Reset (but don't clear memory, naturally) // Reset (but don't clear memory, naturally)
cpu.reset(); cpu.reset();
// Reset the stack program counter
cpu.setProgramCounter(preferences.getProgramStartAddress());
// Immediately update the UI. // Immediately update the UI.
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
public void run() { public void run() {
@@ -405,8 +417,8 @@ public class Simulator implements ActionListener, Observer {
Simulator simulator = new Simulator(); Simulator simulator = new Simulator();
simulator.createAndShowUi(); simulator.createAndShowUi();
// Reset the simulator. // Reset the simulator.
simulator.handleReset(); simulator.coldReset();
} catch (MemoryRangeException e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@@ -433,15 +445,6 @@ public class Simulator implements ActionListener, Observer {
} }
} }
/*
* Perform a busy-loop for DELAY_BETWEEN_STEPS_NS nanoseconds
*/
private void delayLoop() {
long startTime = System.nanoTime();
long stopTime = startTime + DELAY_BETWEEN_STEPS_NS;
// Busy loop
while (System.nanoTime() < stopTime) { ; }
}
/** /**
* The main run thread. * The main run thread.
@@ -1,9 +1,12 @@
package com.loomcom.symon.devices; package com.loomcom.symon.devices;
import java.io.*;
import java.util.*; import java.util.*;
import com.loomcom.symon.exceptions.*; import com.loomcom.symon.exceptions.*;
import javax.swing.*;
public class Memory extends Device { public class Memory extends Device {
private boolean readOnly; private boolean readOnly;
@@ -24,6 +27,17 @@ public class Memory extends Device {
this(address, size, false); this(address, size, false);
} }
public static Memory makeROM(int address, int size, File f) throws MemoryRangeException, IOException {
Memory memory = new Memory(address, size, true);
memory.loadFromFile(f);
return memory;
}
public static Memory makeRAM(int address, int size) throws MemoryRangeException {
Memory memory = new Memory(address, size, false);
return memory;
}
public void write(int address, int data) throws MemoryAccessException { public void write(int address, int data) throws MemoryAccessException {
if (readOnly) { if (readOnly) {
throw new MemoryAccessException("Cannot write to read-only memory at address " + address); throw new MemoryAccessException("Cannot write to read-only memory at address " + address);
@@ -32,6 +46,34 @@ public class Memory extends Device {
} }
} }
/**
* Load the memory from a file.
*
* @param file The file to read an array of bytes from.
* @throws MemoryRangeException if the file and memory size do not match.
* @throws IOException if the file read fails.
*/
public void loadFromFile(File file) throws MemoryRangeException, IOException {
if (file.canRead()) {
long fileSize = file.length();
if (fileSize > mem.length) {
throw new MemoryRangeException("File will not fit in available memory.");
} else {
int i = 0;
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
while (dis.available() != 0) {
mem[i++] = dis.readUnsignedByte();
}
}
} else {
throw new IOException("Cannot open file " + file);
}
}
public int read(int address) throws MemoryAccessException { public int read(int address) throws MemoryAccessException {
return this.mem[address]; return this.mem[address];
} }
@@ -40,7 +82,6 @@ public class Memory extends Device {
Arrays.fill(this.mem, val); Arrays.fill(this.mem, val);
} }
public String toString() { public String toString() {
return "Memory: " + getMemoryRange().toString(); return "Memory: " + getMemoryRange().toString();
} }
@@ -7,6 +7,8 @@ import java.awt.event.MouseListener;
import com.grahamedgecombe.jterminal.JTerminal; import com.grahamedgecombe.jterminal.JTerminal;
import com.grahamedgecombe.jterminal.vt100.Vt100TerminalModel; import com.grahamedgecombe.jterminal.vt100.Vt100TerminalModel;
import com.loomcom.symon.exceptions.FifoUnderrunException;
import com.loomcom.symon.util.FifoRingBuffer;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.BevelBorder; import javax.swing.border.BevelBorder;
@@ -22,9 +24,12 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
private static final int DEFAULT_COLUMNS = 80; private static final int DEFAULT_COLUMNS = 80;
private static final int DEFAULT_ROWS = 24; private static final int DEFAULT_ROWS = 24;
private static final int DEFAULT_BORDER_WIDTH = 10; private static final int DEFAULT_BORDER_WIDTH = 10;
// If true, swap CR and LF characters
private static final boolean SWAP_CR_AND_LF = true;
// If true, send CRLF (0x0d 0x0a) whenever CR is typed
private static final boolean SEND_CR_LF_FOR_CR = false;
private boolean hasInput = false; private FifoRingBuffer typeAheadBuffer;
private char keyBuffer;
public Console() { public Console() {
this(DEFAULT_COLUMNS, DEFAULT_ROWS); this(DEFAULT_COLUMNS, DEFAULT_ROWS);
@@ -32,6 +37,9 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
public Console(int columns, int rows) { public Console(int columns, int rows) {
super(new Vt100TerminalModel(columns, rows)); super(new Vt100TerminalModel(columns, rows));
// A small type-ahead buffer, as might be found in any real
// VT100-style serial terminal.
this.typeAheadBuffer = new FifoRingBuffer(128);
setBorderWidth(DEFAULT_BORDER_WIDTH); setBorderWidth(DEFAULT_BORDER_WIDTH);
addKeyListener(this); addKeyListener(this);
addMouseListener(this); addMouseListener(this);
@@ -47,11 +55,11 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
* *
*/ */
public void reset() { public void reset() {
typeAheadBuffer.reset();
getModel().clear(); getModel().clear();
getModel().setCursorColumn(0); getModel().setCursorColumn(0);
getModel().setCursorRow(0); getModel().setCursorRow(0);
repaint(); repaint();
this.hasInput = false;
} }
/** /**
@@ -60,7 +68,7 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
* @return * @return
*/ */
public boolean hasInput() { public boolean hasInput() {
return hasInput; return !typeAheadBuffer.isEmpty();
} }
/** /**
@@ -69,6 +77,24 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
* @param keyEvent The key event. * @param keyEvent The key event.
*/ */
public void keyTyped(KeyEvent keyEvent) { public void keyTyped(KeyEvent keyEvent) {
char keyTyped = keyEvent.getKeyChar();
if (SWAP_CR_AND_LF) {
if (keyTyped == 0x0a) {
keyTyped = 0x0d;
}
else if (keyTyped == 0x0d) {
keyTyped = 0x0a;
}
}
if (SEND_CR_LF_FOR_CR && keyTyped == 0x0d) {
typeAheadBuffer.push(0x0d);
typeAheadBuffer.push(0x0a);
} else {
typeAheadBuffer.push(keyTyped);
}
keyEvent.consume(); keyEvent.consume();
} }
@@ -78,8 +104,6 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
* @param keyEvent The key event. * @param keyEvent The key event.
*/ */
public void keyPressed(KeyEvent keyEvent) { public void keyPressed(KeyEvent keyEvent) {
keyBuffer = keyEvent.getKeyChar();
hasInput = true;
keyEvent.consume(); keyEvent.consume();
} }
@@ -88,9 +112,8 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
* *
* @return The character typed. * @return The character typed.
*/ */
public char readInputChar() { public char readInputChar() throws FifoUnderrunException {
hasInput = false; return (char)typeAheadBuffer.pop();
return this.keyBuffer;
} }
/** /**
@@ -79,27 +79,27 @@ public class CpuAbsoluteModeTest extends TestCase {
0x0d, 0x04, 0x35, // ORA $3504 0x0d, 0x04, 0x35, // ORA $3504
0x0d, 0x08, 0x35, // ORA $3508 0x0d, 0x08, 0x35, // ORA $3508
0x0d, 0x10, 0x12); // ORA $1210 0x0d, 0x10, 0x12); // ORA $1210
extracted(); cpu.step();
assertEquals(0x00, cpu.getAccumulator()); assertEquals(0x00, cpu.getAccumulator());
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x11, cpu.getAccumulator()); assertEquals(0x11, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x33, cpu.getAccumulator()); assertEquals(0x33, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x77, cpu.getAccumulator()); assertEquals(0x77, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0xff, cpu.getAccumulator()); assertEquals(0xff, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
@@ -120,31 +120,31 @@ public class CpuAbsoluteModeTest extends TestCase {
0x0e, 0x03, 0x35, // ASL $3503 0x0e, 0x03, 0x35, // ASL $3503
0x0e, 0x04, 0x12); // ASL $1204 0x0e, 0x04, 0x12); // ASL $1204
extracted(); cpu.step();
assertEquals(0x00, bus.read(0x7f00)); assertEquals(0x00, bus.read(0x7f00));
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x02, bus.read(0x7f01)); assertEquals(0x02, bus.read(0x7f01));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x04, bus.read(0x3502)); assertEquals(0x04, bus.read(0x3502));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x88, bus.read(0x3503)); assertEquals(0x88, bus.read(0x3503));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x00, bus.read(0x1204)); assertEquals(0x00, bus.read(0x1204));
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
@@ -197,23 +197,23 @@ public class CpuAbsoluteModeTest extends TestCase {
cpu.step(2); cpu.step(2);
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getOverflowFlag()); assertTrue(cpu.getOverflowFlag());
cpu.step(2); cpu.step(2);
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getOverflowFlag());
cpu.step(2);
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getOverflowFlag()); assertTrue(cpu.getOverflowFlag());
cpu.step(2); cpu.step(2);
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getOverflowFlag()); assertTrue(cpu.getOverflowFlag());
cpu.step(2);
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertTrue(cpu.getOverflowFlag());
cpu.step(2); cpu.step(2);
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
@@ -244,12 +244,12 @@ public class CpuAbsoluteModeTest extends TestCase {
0x2d, 0x04, 0x12, // AND $1204 0x2d, 0x04, 0x12, // AND $1204
0xa9, 0xff, // LDA #$ff 0xa9, 0xff, // LDA #$ff
0x2d, 0x05, 0x12); // AND $1205 0x2d, 0x05, 0x12); // AND $1205
extracted(); cpu.step();
assertEquals(0x00, cpu.getAccumulator()); assertEquals(0x00, cpu.getAccumulator());
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x00, cpu.getAccumulator()); assertEquals(0x00, cpu.getAccumulator());
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
@@ -259,12 +259,12 @@ public class CpuAbsoluteModeTest extends TestCase {
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x88, cpu.getAccumulator()); assertEquals(0x88, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x00, cpu.getAccumulator()); assertEquals(0x00, cpu.getAccumulator());
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
@@ -294,13 +294,13 @@ public class CpuAbsoluteModeTest extends TestCase {
0x2e, 0x01, 0x12, // ROL $1201 (m=%01000000, c=1) 0x2e, 0x01, 0x12, // ROL $1201 (m=%01000000, c=1)
0x2e, 0x01, 0x12); // ROL $1201 (m=%10000001, c=0) 0x2e, 0x01, 0x12); // ROL $1201 (m=%10000001, c=0)
extracted(); cpu.step();
assertEquals(0x00, bus.read(0x1200)); assertEquals(0x00, bus.read(0x1200));
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x02, bus.read(0x1201)); assertEquals(0x02, bus.read(0x1201));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
@@ -312,43 +312,43 @@ public class CpuAbsoluteModeTest extends TestCase {
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x0a, bus.read(0x1201)); assertEquals(0x0a, bus.read(0x1201));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x14, bus.read(0x1201)); assertEquals(0x14, bus.read(0x1201));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x28, bus.read(0x1201)); assertEquals(0x28, bus.read(0x1201));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x50, bus.read(0x1201)); assertEquals(0x50, bus.read(0x1201));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0xa0, bus.read(0x1201)); assertEquals(0xa0, bus.read(0x1201));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x40, bus.read(0x1201)); assertEquals(0x40, bus.read(0x1201));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x81, bus.read(0x1201)); assertEquals(0x81, bus.read(0x1201));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
@@ -359,16 +359,12 @@ public class CpuAbsoluteModeTest extends TestCase {
public void test_JMP() throws MemoryAccessException { public void test_JMP() throws MemoryAccessException {
bus.loadProgram(0x4c, 0x00, 0x34); bus.loadProgram(0x4c, 0x00, 0x34);
extracted(); cpu.step();
assertEquals(0x3400, cpu.getProgramCounter()); assertEquals(0x3400, cpu.getProgramCounter());
// No change to status flags. // No change to status flags.
assertEquals(0x20, cpu.getProcessorStatus()); assertEquals(0x20, cpu.getProcessorStatus());
} }
private void extracted() throws MemoryAccessException {
cpu.step();
}
/* EOR - Exclusive OR - $4d */ /* EOR - Exclusive OR - $4d */
public void test_EOR() throws MemoryAccessException { public void test_EOR() throws MemoryAccessException {
@@ -387,17 +383,17 @@ public class CpuAbsoluteModeTest extends TestCase {
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
extracted(); cpu.step();
assertEquals(0x77, cpu.getAccumulator()); assertEquals(0x77, cpu.getAccumulator());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
extracted(); cpu.step();
assertEquals(0x44, cpu.getAccumulator()); assertEquals(0x44, cpu.getAccumulator());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
extracted(); cpu.step();
assertEquals(0x00, cpu.getAccumulator()); assertEquals(0x00, cpu.getAccumulator());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
@@ -421,31 +417,31 @@ public class CpuAbsoluteModeTest extends TestCase {
0x38, // SEC 0x38, // SEC
0x4e, 0x05, 0x12); // LSR $1205 0x4e, 0x05, 0x12); // LSR $1205
extracted(); cpu.step();
assertEquals(0x00, bus.read(0x1200)); assertEquals(0x00, bus.read(0x1200));
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x00, bus.read(0x1201)); assertEquals(0x00, bus.read(0x1201));
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x01, bus.read(0x1202)); assertEquals(0x01, bus.read(0x1202));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x22, bus.read(0x1203)); assertEquals(0x22, bus.read(0x1203));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x40, bus.read(0x1204)); assertEquals(0x40, bus.read(0x1204));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
@@ -657,61 +653,61 @@ public class CpuAbsoluteModeTest extends TestCase {
0x6e, 0x11, 0x12, // ROR $1201 (m=%00100000, c=0) 0x6e, 0x11, 0x12, // ROR $1201 (m=%00100000, c=0)
0x6e, 0x11, 0x12); // ROR $1201 (m=%00010000, c=0) 0x6e, 0x11, 0x12); // ROR $1201 (m=%00010000, c=0)
extracted(); cpu.step();
assertEquals(0x00, bus.read(0x1210)); assertEquals(0x00, bus.read(0x1210));
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x08, bus.read(0x1211)); assertEquals(0x08, bus.read(0x1211));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x04, bus.read(0x1211)); assertEquals(0x04, bus.read(0x1211));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x02, bus.read(0x1211)); assertEquals(0x02, bus.read(0x1211));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x01, bus.read(0x1211)); assertEquals(0x01, bus.read(0x1211));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x00, bus.read(0x1211)); assertEquals(0x00, bus.read(0x1211));
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x80, bus.read(0x1211)); assertEquals(0x80, bus.read(0x1211));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x40, bus.read(0x1211)); assertEquals(0x40, bus.read(0x1211));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x20, bus.read(0x1211)); assertEquals(0x20, bus.read(0x1211));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
extracted(); cpu.step();
assertEquals(0x10, bus.read(0x1211)); assertEquals(0x10, bus.read(0x1211));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
@@ -723,16 +719,17 @@ public class CpuAbsoluteModeTest extends TestCase {
public void test_STY() throws MemoryAccessException { public void test_STY() throws MemoryAccessException {
cpu.setYRegister(0x00); cpu.setYRegister(0x00);
bus.loadProgram(0x8c, 0x10, 0x12); bus.loadProgram(0x8c, 0x10, 0x12);
extracted(); cpu.step();
assertEquals(0x00, bus.read(0x1210)); assertEquals(0x00, bus.read(0x1210));
assertTrue(cpu.getZeroFlag()); // STY should have NO effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
cpu.setYRegister(0x0f); cpu.setYRegister(0x0f);
bus.loadProgram(0x8c, 0x10, 0x12); bus.loadProgram(0x8c, 0x10, 0x12);
extracted(); cpu.step();
assertEquals(0x0f, bus.read(0x1210)); assertEquals(0x0f, bus.read(0x1210));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
@@ -741,10 +738,10 @@ public class CpuAbsoluteModeTest extends TestCase {
cpu.setYRegister(0x80); cpu.setYRegister(0x80);
bus.loadProgram(0x8c, 0x10, 0x12); bus.loadProgram(0x8c, 0x10, 0x12);
extracted(); cpu.step();
assertEquals(0x80, bus.read(0x1210)); assertEquals(0x80, bus.read(0x1210));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
/* STA - Store Accumulator - $8d */ /* STA - Store Accumulator - $8d */
@@ -752,16 +749,17 @@ public class CpuAbsoluteModeTest extends TestCase {
public void test_STA() throws MemoryAccessException { public void test_STA() throws MemoryAccessException {
cpu.setAccumulator(0x00); cpu.setAccumulator(0x00);
bus.loadProgram(0x8d, 0x10, 0x12); bus.loadProgram(0x8d, 0x10, 0x12);
extracted(); cpu.step();
assertEquals(0x00, bus.read(0x1210)); assertEquals(0x00, bus.read(0x1210));
assertTrue(cpu.getZeroFlag()); // STA should have NO effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
cpu.setAccumulator(0x0f); cpu.setAccumulator(0x0f);
bus.loadProgram(0x8d, 0x10, 0x12); bus.loadProgram(0x8d, 0x10, 0x12);
extracted(); cpu.step();
assertEquals(0x0f, bus.read(0x1210)); assertEquals(0x0f, bus.read(0x1210));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
@@ -770,10 +768,10 @@ public class CpuAbsoluteModeTest extends TestCase {
cpu.setAccumulator(0x80); cpu.setAccumulator(0x80);
bus.loadProgram(0x8d, 0x10, 0x12); bus.loadProgram(0x8d, 0x10, 0x12);
extracted(); cpu.step();
assertEquals(0x80, bus.read(0x1210)); assertEquals(0x80, bus.read(0x1210));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
/* STX - Store X Register - $8e */ /* STX - Store X Register - $8e */
@@ -781,16 +779,17 @@ public class CpuAbsoluteModeTest extends TestCase {
public void test_STX() throws MemoryAccessException { public void test_STX() throws MemoryAccessException {
cpu.setXRegister(0x00); cpu.setXRegister(0x00);
bus.loadProgram(0x8e, 0x10, 0x12); bus.loadProgram(0x8e, 0x10, 0x12);
extracted(); cpu.step();
assertEquals(0x00, bus.read(0x1210)); assertEquals(0x00, bus.read(0x1210));
assertTrue(cpu.getZeroFlag()); // STX should have NO effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
cpu.setXRegister(0x0f); cpu.setXRegister(0x0f);
bus.loadProgram(0x8e, 0x10, 0x12); bus.loadProgram(0x8e, 0x10, 0x12);
extracted(); cpu.step();
assertEquals(0x0f, bus.read(0x1210)); assertEquals(0x0f, bus.read(0x1210));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
@@ -799,10 +798,10 @@ public class CpuAbsoluteModeTest extends TestCase {
cpu.setXRegister(0x80); cpu.setXRegister(0x80);
bus.loadProgram(0x8e, 0x10, 0x12); bus.loadProgram(0x8e, 0x10, 0x12);
extracted(); cpu.step();
assertEquals(0x80, bus.read(0x1210)); assertEquals(0x80, bus.read(0x1210));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
/* LDA - Load Accumulator - $ad */ /* LDA - Load Accumulator - $ad */
@@ -816,17 +815,17 @@ public class CpuAbsoluteModeTest extends TestCase {
0xad, 0x11, 0x12, // LDA $1211 0xad, 0x11, 0x12, // LDA $1211
0xad, 0x12, 0x12); // LDA $1212 0xad, 0x12, 0x12); // LDA $1212
extracted(); cpu.step();
assertEquals(0x00, cpu.getAccumulator()); assertEquals(0x00, cpu.getAccumulator());
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x0f, cpu.getAccumulator()); assertEquals(0x0f, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x80, cpu.getAccumulator()); assertEquals(0x80, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
@@ -843,17 +842,17 @@ public class CpuAbsoluteModeTest extends TestCase {
0xae, 0x11, 0x12, // LDX $1211 0xae, 0x11, 0x12, // LDX $1211
0xae, 0x12, 0x12); // LDX $1212 0xae, 0x12, 0x12); // LDX $1212
extracted(); cpu.step();
assertEquals(0x00, cpu.getXRegister()); assertEquals(0x00, cpu.getXRegister());
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x0f, cpu.getXRegister()); assertEquals(0x0f, cpu.getXRegister());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x80, cpu.getXRegister()); assertEquals(0x80, cpu.getXRegister());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
@@ -870,17 +869,17 @@ public class CpuAbsoluteModeTest extends TestCase {
0xbc, 0x11, 0x12, // LDY $1211 0xbc, 0x11, 0x12, // LDY $1211
0xbc, 0x12, 0x12); // LDY $1212 0xbc, 0x12, 0x12); // LDY $1212
extracted(); cpu.step();
assertEquals(0x00, cpu.getYRegister()); assertEquals(0x00, cpu.getYRegister());
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x0f, cpu.getYRegister()); assertEquals(0x0f, cpu.getYRegister());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x80, cpu.getYRegister()); assertEquals(0x80, cpu.getYRegister());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
@@ -899,20 +898,20 @@ public class CpuAbsoluteModeTest extends TestCase {
0xcd, 0x11, 0x12, // CMP $1211 0xcd, 0x11, 0x12, // CMP $1211
0xcd, 0x12, 0x12); // CMP $1212 0xcd, 0x12, 0x12); // CMP $1212
extracted(); cpu.step();
assertTrue(cpu.getCarryFlag()); // m > y assertTrue(cpu.getCarryFlag()); // m > y
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); // m - y < 0 assertTrue(cpu.getNegativeFlag()); // m - y < 0
extracted(); cpu.step();
assertTrue(cpu.getCarryFlag()); // m = y assertTrue(cpu.getCarryFlag()); // m = y
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // m - y == 0 assertFalse(cpu.getNegativeFlag()); // m - y == 0
extracted(); cpu.step();
assertFalse(cpu.getCarryFlag()); // m < y assertFalse(cpu.getCarryFlag()); // m < y
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // m - y > 0 assertTrue(cpu.getNegativeFlag()); // m - y > 0
} }
/* CPY - Compare Y Register - $cc */ /* CPY - Compare Y Register - $cc */
@@ -928,20 +927,20 @@ public class CpuAbsoluteModeTest extends TestCase {
0xcc, 0x11, 0x12, // CPY $1211 0xcc, 0x11, 0x12, // CPY $1211
0xcc, 0x12, 0x12); // CPY $1212 0xcc, 0x12, 0x12); // CPY $1212
extracted(); cpu.step();
assertTrue(cpu.getCarryFlag()); // m > y assertTrue(cpu.getCarryFlag()); // m > y
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); // m - y < 0 assertTrue(cpu.getNegativeFlag()); // m - y < 0
extracted(); cpu.step();
assertTrue(cpu.getCarryFlag()); // m = y assertTrue(cpu.getCarryFlag()); // m = y
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // m - y == 0 assertFalse(cpu.getNegativeFlag()); // m - y == 0
extracted(); cpu.step();
assertFalse(cpu.getCarryFlag()); // m < y assertFalse(cpu.getCarryFlag()); // m < y
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // m - y > 0 assertTrue(cpu.getNegativeFlag()); // m - y = $81
} }
/* DEC - Decrement Memory Location - $ce */ /* DEC - Decrement Memory Location - $ce */
@@ -957,22 +956,22 @@ public class CpuAbsoluteModeTest extends TestCase {
0xce, 0x12, 0x12, // DEC $1212 0xce, 0x12, 0x12, // DEC $1212
0xce, 0x13, 0x12); // DEC $1213 0xce, 0x13, 0x12); // DEC $1213
extracted(); cpu.step();
assertEquals(0xff, bus.read(0x1210)); assertEquals(0xff, bus.read(0x1210));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x00, bus.read(0x1211)); assertEquals(0x00, bus.read(0x1211));
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x7f, bus.read(0x1212)); assertEquals(0x7f, bus.read(0x1212));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0xfe, bus.read(0x1213)); assertEquals(0xfe, bus.read(0x1213));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
@@ -991,20 +990,20 @@ public class CpuAbsoluteModeTest extends TestCase {
0xec, 0x11, 0x12, // CPX $1211 0xec, 0x11, 0x12, // CPX $1211
0xec, 0x12, 0x12); // CPX $1212 0xec, 0x12, 0x12); // CPX $1212
extracted(); cpu.step();
assertTrue(cpu.getCarryFlag()); // m > y assertTrue(cpu.getCarryFlag()); // m > y
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); // m - y < 0 assertTrue(cpu.getNegativeFlag()); // m - y < 0
extracted(); cpu.step();
assertTrue(cpu.getCarryFlag()); // m = y assertTrue(cpu.getCarryFlag()); // m = y
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // m - y == 0 assertFalse(cpu.getNegativeFlag()); // m - y == 0
extracted(); cpu.step();
assertFalse(cpu.getCarryFlag()); // m < y assertFalse(cpu.getCarryFlag()); // m < y
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // m - y > 0 assertTrue(cpu.getNegativeFlag()); // $80 - $ff = 81
} }
/* SBC - Subtract with Carry (borrow) - $ed */ /* SBC - Subtract with Carry (borrow) - $ed */
@@ -1213,17 +1212,17 @@ public class CpuAbsoluteModeTest extends TestCase {
0xee, 0x11, 0x12, // INC $1211 0xee, 0x11, 0x12, // INC $1211
0xee, 0x12, 0x12); // INC $1212 0xee, 0x12, 0x12); // INC $1212
extracted(); cpu.step();
assertEquals(0x01, bus.read(0x1210)); assertEquals(0x01, bus.read(0x1210));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x80, bus.read(0x1211)); assertEquals(0x80, bus.read(0x1211));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
extracted(); cpu.step();
assertEquals(0x00, bus.read(0x1212)); assertEquals(0x00, bus.read(0x1212));
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
@@ -678,7 +678,8 @@ public class CpuAbsoluteXModeTest extends TestCase {
bus.loadProgram(0x9d, 0x10, 0xab); // STA $ab10,X bus.loadProgram(0x9d, 0x10, 0xab); // STA $ab10,X
cpu.step(); cpu.step();
assertEquals(0x00, bus.read(0xab40)); assertEquals(0x00, bus.read(0xab40));
assertTrue(cpu.getZeroFlag()); // STA should have NO affect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
@@ -699,7 +700,7 @@ public class CpuAbsoluteXModeTest extends TestCase {
cpu.step(); cpu.step();
assertEquals(0x80, bus.read(0xab40)); assertEquals(0x80, bus.read(0xab40));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
/* LDY - Load Y Register - $bc */ /* LDY - Load Y Register - $bc */
@@ -788,7 +789,7 @@ public class CpuAbsoluteXModeTest extends TestCase {
cpu.step(); cpu.step();
assertFalse(cpu.getCarryFlag()); // m < y assertFalse(cpu.getCarryFlag()); // m < y
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // m - y > 0 assertTrue(cpu.getNegativeFlag()); // $80 - $ff = $81
} }
/* DEC - Decrement Memory Location - $de */ /* DEC - Decrement Memory Location - $de */
@@ -412,7 +412,8 @@ public class CpuAbsoluteYModeTest extends TestCase {
bus.loadProgram(0x99, 0x10, 0xab); // STA $ab10,Y bus.loadProgram(0x99, 0x10, 0xab); // STA $ab10,Y
cpu.step(); cpu.step();
assertEquals(0x00, bus.read(0xab40)); assertEquals(0x00, bus.read(0xab40));
assertTrue(cpu.getZeroFlag()); // STA should have NO effect on flags
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
@@ -434,7 +435,7 @@ public class CpuAbsoluteYModeTest extends TestCase {
cpu.step(); cpu.step();
assertEquals(0x80, bus.read(0xab40)); assertEquals(0x80, bus.read(0xab40));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
/* LDX - Load X Register - $be */ /* LDX - Load X Register - $be */
@@ -523,7 +524,7 @@ public class CpuAbsoluteYModeTest extends TestCase {
cpu.step(); cpu.step();
assertFalse(cpu.getCarryFlag()); // m < y assertFalse(cpu.getCarryFlag()); // m < y
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // m - y > 0 assertTrue(cpu.getNegativeFlag()); // $80 - $ff = $81
} }
/* SBC - Subtract with Carry - $f9 */ /* SBC - Subtract with Carry - $f9 */
@@ -200,6 +200,7 @@ public class CpuImmediateModeTest extends TestCase {
0x69, 0x01); // ADC #$01 0x69, 0x01); // ADC #$01
cpu.step(2); cpu.step(2);
assertEquals(0x80, cpu.getAccumulator()); assertEquals(0x80, cpu.getAccumulator());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
assertTrue(cpu.getOverflowFlag()); assertTrue(cpu.getOverflowFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
@@ -493,8 +494,8 @@ public class CpuImmediateModeTest extends TestCase {
cpu.step(2); cpu.step(2);
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
// $08 - $0a = negative // $0a - $08 = positive
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
bus.loadProgram(0xa0, 0xfa, // LDY #$FA bus.loadProgram(0xa0, 0xfa, // LDY #$FA
@@ -502,8 +503,8 @@ public class CpuImmediateModeTest extends TestCase {
cpu.step(2); cpu.step(2);
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
// $80 - $FA = negative // $fa - 80 = positive
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
public void test_CPY_DoesNotSetCarryFlagIfYLessThanThanMemory() throws MemoryAccessException { public void test_CPY_DoesNotSetCarryFlagIfYLessThanThanMemory() throws MemoryAccessException {
@@ -512,7 +513,8 @@ public class CpuImmediateModeTest extends TestCase {
cpu.step(2); cpu.step(2);
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // 08 - 0a = negative
assertTrue(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
bus.loadProgram(0xa0, 0x70, // LDY #$70 bus.loadProgram(0xa0, 0x70, // LDY #$70
@@ -520,7 +522,8 @@ public class CpuImmediateModeTest extends TestCase {
cpu.step(2); cpu.step(2);
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // $70 - $80 = negative
assertTrue(cpu.getNegativeFlag());
} }
/* CMP Immediate Mode Tests - 0xc9 */ /* CMP Immediate Mode Tests - 0xc9 */
@@ -564,8 +567,8 @@ public class CpuImmediateModeTest extends TestCase {
cpu.step(2); cpu.step(2);
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
// $08 - $0a = negative // $0a - $08 = positive
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
bus.loadProgram(0xa9, 0xfa, // LDA #$FA bus.loadProgram(0xa9, 0xfa, // LDA #$FA
@@ -573,8 +576,8 @@ public class CpuImmediateModeTest extends TestCase {
cpu.step(2); cpu.step(2);
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
// $80 - $FA = negative // $fa - $80 = positive
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
public void test_CMP_DoesNotSetCarryFlagIfYGreaterThanMemory() throws MemoryAccessException { public void test_CMP_DoesNotSetCarryFlagIfYGreaterThanMemory() throws MemoryAccessException {
@@ -583,7 +586,8 @@ public class CpuImmediateModeTest extends TestCase {
cpu.step(2); cpu.step(2);
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // 08 - 0a = negative
assertTrue(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
bus.loadProgram(0xa9, 0x70, // LDA #$70 bus.loadProgram(0xa9, 0x70, // LDA #$70
@@ -591,7 +595,8 @@ public class CpuImmediateModeTest extends TestCase {
cpu.step(2); cpu.step(2);
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // 70 - 80 = negative
assertTrue(cpu.getNegativeFlag());
} }
/* CPX Immediate Mode Tests - 0xe0 */ /* CPX Immediate Mode Tests - 0xe0 */
@@ -635,8 +640,8 @@ public class CpuImmediateModeTest extends TestCase {
cpu.step(2); cpu.step(2);
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
// $08 - $0a = negative // $0a - $08 = positive
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
bus.loadProgram(0xa2, 0xfa, // LDX #$FA bus.loadProgram(0xa2, 0xfa, // LDX #$FA
@@ -644,8 +649,8 @@ public class CpuImmediateModeTest extends TestCase {
cpu.step(2); cpu.step(2);
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
// $80 - $FA = negative // $fa - $80 = positive
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
public void test_CPX_DoesNotSetCarryFlagIfYGreaterThanMemory() throws MemoryAccessException { public void test_CPX_DoesNotSetCarryFlagIfYGreaterThanMemory() throws MemoryAccessException {
@@ -654,7 +659,8 @@ public class CpuImmediateModeTest extends TestCase {
cpu.step(2); cpu.step(2);
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // $08 - $0a = negative
assertTrue(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
bus.loadProgram(0xa2, 0x70, // LDX #$70 bus.loadProgram(0xa2, 0x70, // LDX #$70
@@ -662,7 +668,8 @@ public class CpuImmediateModeTest extends TestCase {
cpu.step(2); cpu.step(2);
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // $70 - $80 = negative
assertTrue(cpu.getNegativeFlag());
} }
/* SBC Immediate Mode Tests - 0xe9 */ /* SBC Immediate Mode Tests - 0xe9 */
@@ -412,7 +412,7 @@ public class CpuIndirectXModeTest extends TestCase {
bus.loadProgram(0x9d, 0x10, 0xab); // STA $ab10,X bus.loadProgram(0x9d, 0x10, 0xab); // STA $ab10,X
cpu.step(); cpu.step();
assertEquals(0x00, bus.read(0xab40)); assertEquals(0x00, bus.read(0xab40));
assertTrue(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
@@ -434,7 +434,7 @@ public class CpuIndirectXModeTest extends TestCase {
cpu.step(); cpu.step();
assertEquals(0x80, bus.read(0xab40)); assertEquals(0x80, bus.read(0xab40));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
/* LDA - Load Accumulator - $bd */ /* LDA - Load Accumulator - $bd */
@@ -494,7 +494,7 @@ public class CpuIndirectXModeTest extends TestCase {
cpu.step(); cpu.step();
assertFalse(cpu.getCarryFlag()); // m < y assertFalse(cpu.getCarryFlag()); // m < y
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // m - y > 0 assertTrue(cpu.getNegativeFlag()); // m - y < 0
} }
/* SBC - Subtract with Carry - $fd */ /* SBC - Subtract with Carry - $fd */
@@ -153,6 +153,8 @@ public class CpuZeroPageModeTest extends TestCase {
public void test_BIT() throws MemoryAccessException { public void test_BIT() throws MemoryAccessException {
bus.write(0x0000, 0xc0); bus.write(0x0000, 0xc0);
bus.write(0x0010, 0x40);
bus.write(0x0020, 0x80);
bus.loadProgram(0xa9, 0x01, // LDA #$01 bus.loadProgram(0xa9, 0x01, // LDA #$01
0x24, 0x00, // BIT $00 0x24, 0x00, // BIT $00
@@ -161,10 +163,10 @@ public class CpuZeroPageModeTest extends TestCase {
0x24, 0x00, // BIT $00 0x24, 0x00, // BIT $00
0xa9, 0x40, // LDA #$40 0xa9, 0x40, // LDA #$40
0x24, 0x00, // BIT $00 0x24, 0x20, // BIT $20
0xa9, 0x80, // LDA #$80 0xa9, 0x80, // LDA #$80
0x24, 0x00, // BIT $00 0x24, 0x10, // BIT $10
0xa9, 0xc0, // LDA #$c0 0xa9, 0xc0, // LDA #$c0
0x24, 0x00, // BIT $00 0x24, 0x00, // BIT $00
@@ -174,24 +176,24 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.step(2); cpu.step(2);
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
assertTrue(cpu.getOverflowFlag());
cpu.step(2);
assertTrue(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertTrue(cpu.getOverflowFlag());
cpu.step(2);
assertTrue(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getOverflowFlag()); assertFalse(cpu.getOverflowFlag());
cpu.step(2); cpu.step(2);
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getOverflowFlag());
cpu.step(2);
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getOverflowFlag()); assertTrue(cpu.getOverflowFlag());
cpu.step(2);
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getOverflowFlag());
cpu.step(2); cpu.step(2);
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
@@ -688,7 +690,8 @@ public class CpuZeroPageModeTest extends TestCase {
bus.loadProgram(0x84, 0x10); bus.loadProgram(0x84, 0x10);
cpu.step(); cpu.step();
assertEquals(0x00, bus.read(0x10)); assertEquals(0x00, bus.read(0x10));
assertTrue(cpu.getZeroFlag()); // Should have no effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
@@ -707,7 +710,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.step(); cpu.step();
assertEquals(0x80, bus.read(0x10)); assertEquals(0x80, bus.read(0x10));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
/* STA - Store Accumulator - $85 */ /* STA - Store Accumulator - $85 */
@@ -717,7 +720,8 @@ public class CpuZeroPageModeTest extends TestCase {
bus.loadProgram(0x85, 0x10); bus.loadProgram(0x85, 0x10);
cpu.step(); cpu.step();
assertEquals(0x00, bus.read(0x10)); assertEquals(0x00, bus.read(0x10));
assertTrue(cpu.getZeroFlag()); // Should have no effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
@@ -736,7 +740,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.step(); cpu.step();
assertEquals(0x80, bus.read(0x10)); assertEquals(0x80, bus.read(0x10));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
/* STX - Store X Register - $86 */ /* STX - Store X Register - $86 */
@@ -746,7 +750,8 @@ public class CpuZeroPageModeTest extends TestCase {
bus.loadProgram(0x86, 0x10); bus.loadProgram(0x86, 0x10);
cpu.step(); cpu.step();
assertEquals(0x00, bus.read(0x10)); assertEquals(0x00, bus.read(0x10));
assertTrue(cpu.getZeroFlag()); // Should have no effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
@@ -765,7 +770,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.step(); cpu.step();
assertEquals(0x80, bus.read(0x10)); assertEquals(0x80, bus.read(0x10));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
/* LDY - Load Y Register - $a4 */ /* LDY - Load Y Register - $a4 */
@@ -875,7 +880,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.step(); cpu.step();
assertFalse(cpu.getCarryFlag()); // m < y assertFalse(cpu.getCarryFlag()); // m < y
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // m - y > 0 assertTrue(cpu.getNegativeFlag()); // m - y < 0
} }
/* CMP - Compare Accumulator - $c5 */ /* CMP - Compare Accumulator - $c5 */
@@ -904,7 +909,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.step(); cpu.step();
assertFalse(cpu.getCarryFlag()); // m < y assertFalse(cpu.getCarryFlag()); // m < y
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // m - y > 0 assertTrue(cpu.getNegativeFlag()); // m - y < 0
} }
/* DEC - Decrement Memory Location - $c6 */ /* DEC - Decrement Memory Location - $c6 */
@@ -967,7 +972,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.step(); cpu.step();
assertFalse(cpu.getCarryFlag()); // m < y assertFalse(cpu.getCarryFlag()); // m < y
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // m - y > 0 assertTrue(cpu.getNegativeFlag()); // m - y < 0
} }
/* SBC - Subtract with Carry - $e5 */ /* SBC - Subtract with Carry - $e5 */
@@ -709,7 +709,8 @@ public class CpuZeroPageXModeTest extends TestCase {
bus.loadProgram(0x94, 0x10); // STY $10,X bus.loadProgram(0x94, 0x10); // STY $10,X
cpu.step(); cpu.step();
assertEquals(0x00, bus.read(0x40)); assertEquals(0x00, bus.read(0x40));
assertTrue(cpu.getZeroFlag()); // Should have no effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
@@ -730,7 +731,7 @@ public class CpuZeroPageXModeTest extends TestCase {
cpu.step(); cpu.step();
assertEquals(0x80, bus.read(0x40)); assertEquals(0x80, bus.read(0x40));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
/* STA - Store Accumulator - $95 */ /* STA - Store Accumulator - $95 */
@@ -742,7 +743,8 @@ public class CpuZeroPageXModeTest extends TestCase {
bus.loadProgram(0x95, 0x10); // STA $10,X bus.loadProgram(0x95, 0x10); // STA $10,X
cpu.step(); cpu.step();
assertEquals(0x00, bus.read(0x40)); assertEquals(0x00, bus.read(0x40));
assertTrue(cpu.getZeroFlag()); // Should have no effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
@@ -763,7 +765,7 @@ public class CpuZeroPageXModeTest extends TestCase {
cpu.step(); cpu.step();
assertEquals(0x80, bus.read(0x40)); assertEquals(0x80, bus.read(0x40));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
/* LDY - Load Y Register - $b4 */ /* LDY - Load Y Register - $b4 */
@@ -852,7 +854,7 @@ public class CpuZeroPageXModeTest extends TestCase {
cpu.step(); cpu.step();
assertFalse(cpu.getCarryFlag()); // m < y assertFalse(cpu.getCarryFlag()); // m < y
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); // m - y > 0 assertTrue(cpu.getNegativeFlag()); // m - y < 0
} }
/* DEC - Decrement Memory Location - $d6 */ /* DEC - Decrement Memory Location - $d6 */
@@ -48,7 +48,8 @@ public class CpuZeroPageYModeTest extends TestCase {
bus.loadProgram(0x96, 0x10); // STX $10,Y bus.loadProgram(0x96, 0x10); // STX $10,Y
cpu.step(); cpu.step();
assertEquals(0x00, bus.read(0x40)); assertEquals(0x00, bus.read(0x40));
assertTrue(cpu.getZeroFlag()); // Should have no effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.reset(); cpu.reset();
@@ -67,7 +68,7 @@ public class CpuZeroPageYModeTest extends TestCase {
cpu.step(); cpu.step();
assertEquals(0x80, bus.read(0x40)); assertEquals(0x80, bus.read(0x40));
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
/* LDX - Load X Register - $b6 */ /* LDX - Load X Register - $b6 */