mirror of
https://github.com/felipecsl/6502Android.git
synced 2024-09-30 06:55:32 +00:00
Merge pull request #1 from felipecsl/nes-emulation
Initial commit for INES file/cartridge parser
This commit is contained in:
commit
4a9bc14659
33
.travis.yml
Normal file
33
.travis.yml
Normal file
@ -0,0 +1,33 @@
|
||||
language: android
|
||||
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- platform-tools
|
||||
- build-tools-25.0.2
|
||||
- extra-android-m2repository
|
||||
- android-25
|
||||
licenses:
|
||||
- 'android-sdk-license-.+'
|
||||
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
|
||||
branches:
|
||||
except:
|
||||
- gh-pages
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
sudo: false
|
||||
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
- $HOME/.android/build-cache
|
@ -31,8 +31,6 @@ dependencies {
|
||||
compile 'com.google.guava:guava:20.0'
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'com.google.truth:truth:0.32'
|
||||
testCompile 'org.mockito:mockito-core:1.10.19'
|
||||
testCompile 'org.hamcrest:hamcrest-integration:1.3'
|
||||
testCompile 'org.hamcrest:hamcrest-core:1.3'
|
||||
testCompile 'org.hamcrest:hamcrest-library:1.3'
|
||||
}
|
||||
|
41
app/src/main/kotlin/android/emu6502/nes/Cartridge.kt
Normal file
41
app/src/main/kotlin/android/emu6502/nes/Cartridge.kt
Normal file
@ -0,0 +1,41 @@
|
||||
package android.emu6502.nes
|
||||
|
||||
import java.util.*
|
||||
|
||||
data class Cartridge(
|
||||
// @formatter:off
|
||||
val pgr: ByteArray, // PRG-ROM banks
|
||||
val chr: ByteArray, // CHR-ROM banks
|
||||
val mapper: Byte, // mapper type
|
||||
val mirror: Byte, // mirroring mode
|
||||
val battery: Byte // battery present
|
||||
// @formatter:on
|
||||
) {
|
||||
private val sram: ByteArray = ByteArray(0x2000) // Save RAM
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other?.javaClass != javaClass) return false
|
||||
|
||||
other as Cartridge
|
||||
|
||||
if (!Arrays.equals(pgr, other.pgr)) return false
|
||||
if (!Arrays.equals(chr, other.chr)) return false
|
||||
if (!Arrays.equals(sram, other.sram)) return false
|
||||
if (mapper != other.mapper) return false
|
||||
if (mirror != other.mirror) return false
|
||||
if (battery != other.battery) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = Arrays.hashCode(pgr)
|
||||
result = 31 * result + Arrays.hashCode(chr)
|
||||
result = 31 * result + Arrays.hashCode(sram)
|
||||
result = 31 * result + mapper
|
||||
result = 31 * result + mirror
|
||||
result = 31 * result + battery
|
||||
return result
|
||||
}
|
||||
}
|
51
app/src/main/kotlin/android/emu6502/nes/INESFileHeader.kt
Normal file
51
app/src/main/kotlin/android/emu6502/nes/INESFileHeader.kt
Normal file
@ -0,0 +1,51 @@
|
||||
package android.emu6502.nes
|
||||
|
||||
import android.emu6502.nes.INESFileParser.Companion.INES_FILE_MAGIC
|
||||
import android.emu6502.nes.INESFileParser.Companion.PADDING
|
||||
import java.util.*
|
||||
|
||||
// http://wiki.nesdev.com/w/index.php/INES
|
||||
// Sample header:
|
||||
// 4e 45 53 1a 10 10 40 00 00 00 00 00 00 00 00 00
|
||||
data class INESFileHeader(
|
||||
// @formatter:off
|
||||
val magic: ByteArray, // Constant $4E $45 $53 $1A ("NES" followed by MS-DOS end-of-file)
|
||||
val numPRG: Byte, // Size of PRG ROM in 16 KB units
|
||||
val numCHR: Byte, // Size of CHR ROM in 8 KB units (Value 0 means the board uses CHR RAM)
|
||||
val control1: Byte, // Flags 6
|
||||
val control2: Byte, // Flags 7
|
||||
val numRAM: Byte, // Size of PRG RAM in 8 KB units
|
||||
val padding: ByteArray // 7 bytes, unused
|
||||
// @formatter:on
|
||||
) {
|
||||
fun isValid() =
|
||||
Arrays.equals(magic, INES_FILE_MAGIC) && Arrays.equals(padding, PADDING)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other?.javaClass != javaClass) return false
|
||||
|
||||
other as INESFileHeader
|
||||
|
||||
if (!Arrays.equals(magic, other.magic)) return false
|
||||
if (numPRG != other.numPRG) return false
|
||||
if (numCHR != other.numCHR) return false
|
||||
if (control1 != other.control1) return false
|
||||
if (control2 != other.control2) return false
|
||||
if (numRAM != other.numRAM) return false
|
||||
if (!Arrays.equals(padding, other.padding)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = Arrays.hashCode(magic)
|
||||
result = 31 * result + numPRG
|
||||
result = 31 * result + numCHR
|
||||
result = 31 * result + control1
|
||||
result = 31 * result + control2
|
||||
result = 31 * result + numRAM
|
||||
result = 31 * result + Arrays.hashCode(padding)
|
||||
return result
|
||||
}
|
||||
}
|
49
app/src/main/kotlin/android/emu6502/nes/INESFileParser.kt
Normal file
49
app/src/main/kotlin/android/emu6502/nes/INESFileParser.kt
Normal file
@ -0,0 +1,49 @@
|
||||
package android.emu6502.nes
|
||||
|
||||
import java.io.DataInputStream
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
internal class INESFileParser {
|
||||
companion object {
|
||||
internal val INES_FILE_MAGIC = byteArrayOf(0x4e, 0x45, 0x53, 0x1a)/*0x4e45531a*/
|
||||
internal val PADDING = byteArrayOf(0, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
internal fun parseFileHeader(stream: InputStream): INESFileHeader {
|
||||
val dataStream = DataInputStream(stream.buffered())
|
||||
return INESFileHeader(
|
||||
(0..3).map { dataStream.readByte() }.toByteArray(),
|
||||
dataStream.readByte(),
|
||||
dataStream.readByte(),
|
||||
dataStream.readByte(),
|
||||
dataStream.readByte(),
|
||||
dataStream.readByte(),
|
||||
(0..6).map { dataStream.readByte() }.toByteArray())
|
||||
}
|
||||
|
||||
internal fun parseCartridge(file: File): Cartridge {
|
||||
val stream = file.inputStream()
|
||||
stream.use {
|
||||
val inesFileHeader = parseFileHeader(stream)
|
||||
// mapper type
|
||||
val control1 = inesFileHeader.control1.toInt()
|
||||
val mapper1 = control1.shr(4)
|
||||
val mapper2 = inesFileHeader.control2.toInt().shr(4)
|
||||
val mapper = if (mapper1 != 0) mapper1 else mapper2
|
||||
// mirroring type
|
||||
val mirror1 = control1.and(1)
|
||||
val mirror2 = control1.shr(3).and(1)
|
||||
val mirror = if (mirror1 != 0) mirror1 else mirror2.shl(1)
|
||||
// battery-backed RAM
|
||||
val battery = control1.shr(1).and(1).toByte()
|
||||
// read prg-rom bank(s)
|
||||
val pgr = ByteArray(inesFileHeader.numPRG.toInt() * 16384)
|
||||
stream.read(pgr)
|
||||
// read chr-rom bank(s)
|
||||
val chr = ByteArray(inesFileHeader.numCHR.toInt() * 8192)
|
||||
stream.read(chr)
|
||||
return Cartridge(pgr, chr, mapper.toByte(), mirror.toByte(), battery)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package android.emu6502;
|
||||
|
||||
import android.emu6502.instructions.Symbols;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class LabelsTest {
|
||||
private final Symbols symbols = new Symbols();
|
||||
private final Assembler assembler = new Assembler(new Memory(mock(Display.class)), symbols);
|
||||
private final Labels labels = new Labels(assembler, symbols);
|
||||
|
||||
@Test public void testAddLabel() {
|
||||
labels.indexLines(Collections.singletonList("test:"));
|
||||
assertThat(labels.get("test"), equalTo(assembler.getDefaultCodePC()));
|
||||
}
|
||||
}
|
@ -1,46 +1,39 @@
|
||||
package android.emu6502;
|
||||
package android.emu6502
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import android.emu6502.instructions.Symbols
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito.mock
|
||||
|
||||
import android.emu6502.instructions.Symbols;
|
||||
class AssemblerTest {
|
||||
private val assembler = Assembler(Memory(mock(Display::class.java)), Symbols())
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.text.IsEqualIgnoringCase.equalToIgnoringCase;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class AssemblerTest {
|
||||
private final Assembler assembler = new Assembler(new Memory(mock(Display.class)), new Symbols());
|
||||
|
||||
@Test public void testSimple() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
@Test fun testSimple() {
|
||||
val lines = ImmutableList.of(
|
||||
"LDA #$01",
|
||||
"STA $0200",
|
||||
"LDA #$05",
|
||||
"STA $0201",
|
||||
"LDA #$08",
|
||||
"STA $0202");
|
||||
assembler.assembleCode(lines);
|
||||
assertThat(assembler.hexdump(), equalTo("0600: A9 01 8D 00 02 A9 05 8D 01 02 A9 08 8D 02 02"));
|
||||
"STA $0202")
|
||||
assembler.assembleCode(lines)
|
||||
assertThat(assembler.hexdump()).isEqualTo("0600: A9 01 8D 00 02 A9 05 8D 01 02 A9 08 8D 02 02")
|
||||
}
|
||||
|
||||
@Test public void testWithComments() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
"LDA #$c0 ;Load the hex value $c0 into the A register",
|
||||
@Test fun testWithComments() {
|
||||
val lines = ImmutableList.of(
|
||||
"LDA #\$c0 ;Load the hex value \$c0 into the A register",
|
||||
"TAX ;Transfer the value in the A register to X",
|
||||
"INX ;Increment the value in the X register",
|
||||
"ADC #$c4 ;Add the hex value $c4 to the A register",
|
||||
"BRK ;Break - we're done");
|
||||
assembler.assembleCode(lines);
|
||||
assertThat(assembler.hexdump(), equalTo("0600: A9 C0 AA E8 69 C4 00"));
|
||||
"ADC #\$c4 ;Add the hex value \$c4 to the A register",
|
||||
"BRK ;Break - we're done")
|
||||
assembler.assembleCode(lines)
|
||||
assertThat(assembler.hexdump()).isEqualTo("0600: A9 C0 AA E8 69 C4 00")
|
||||
}
|
||||
|
||||
@Test public void testBranchAndLabel() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
@Test fun testBranchAndLabel() {
|
||||
val lines = ImmutableList.of(
|
||||
"LDX #$08",
|
||||
"decrement:",
|
||||
"DEX",
|
||||
@ -48,38 +41,38 @@ public class AssemblerTest {
|
||||
"CPX #$03",
|
||||
"BNE decrement",
|
||||
"STX $0201",
|
||||
"BRK");
|
||||
assembler.assembleCode(lines);
|
||||
assertThat(assembler.hexdump(), equalTo("0600: A2 08 CA 8E 00 02 E0 03 D0 F8 8E 01 02 00"));
|
||||
"BRK")
|
||||
assembler.assembleCode(lines)
|
||||
assertThat(assembler.hexdump()).isEqualTo("0600: A2 08 CA 8E 00 02 E0 03 D0 F8 8E 01 02 00")
|
||||
}
|
||||
|
||||
@Test public void testRelative() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
@Test fun testRelative() {
|
||||
val lines = ImmutableList.of(
|
||||
"LDA #$01",
|
||||
"CMP #$02",
|
||||
"BNE notequal",
|
||||
"STA $22",
|
||||
"notequal:",
|
||||
"BRK");
|
||||
"BRK")
|
||||
|
||||
assembler.assembleCode(lines);
|
||||
assertThat(assembler.hexdump(), equalTo("0600: A9 01 C9 02 D0 02 85 22 00"));
|
||||
assembler.assembleCode(lines)
|
||||
assertThat(assembler.hexdump()).isEqualTo("0600: A9 01 C9 02 D0 02 85 22 00")
|
||||
}
|
||||
|
||||
@Test public void testIndirect() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
@Test fun testIndirect() {
|
||||
val lines = ImmutableList.of(
|
||||
"LDA #$01",
|
||||
"STA $f0",
|
||||
"LDA #$cc",
|
||||
"STA $f1",
|
||||
"JMP ($00f0) ;dereferences to $cc01");
|
||||
"STA \$f0",
|
||||
"LDA #\$cc",
|
||||
"STA \$f1",
|
||||
"JMP ($00f0) ;dereferences to \$cc01")
|
||||
|
||||
assembler.assembleCode(lines);
|
||||
assertThat(assembler.hexdump(), equalTo("0600: A9 01 85 F0 A9 CC 85 F1 6C F0 00"));
|
||||
assembler.assembleCode(lines)
|
||||
assertThat(assembler.hexdump()).isEqualTo("0600: A9 01 85 F0 A9 CC 85 F1 6C F0 00")
|
||||
}
|
||||
|
||||
@Test public void testIndirectX() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
@Test fun testIndirectX() {
|
||||
val lines = ImmutableList.of(
|
||||
"LDX #$01",
|
||||
"LDA #$05",
|
||||
"STA $01",
|
||||
@ -87,15 +80,15 @@ public class AssemblerTest {
|
||||
"STA $02",
|
||||
"LDY #$0a",
|
||||
"STY $0605",
|
||||
"LDA ($00,X)");
|
||||
"LDA ($00,X)")
|
||||
|
||||
assembler.assembleCode(lines);
|
||||
assertThat(assembler.hexdump(),
|
||||
equalTo("0600: A2 01 A9 05 85 01 A9 06 85 02 A0 0A 8C 05 06 A1 \n0610: 00"));
|
||||
assembler.assembleCode(lines)
|
||||
assertThat(assembler.hexdump())
|
||||
.isEqualTo("0600: A2 01 A9 05 85 01 A9 06 85 02 A0 0A 8C 05 06 A1 \n0610: 00")
|
||||
}
|
||||
|
||||
@Test public void testIndirectY() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
@Test fun testIndirectY() {
|
||||
val lines = ImmutableList.of(
|
||||
"LDY #$01",
|
||||
"LDA #$03",
|
||||
"STA $01",
|
||||
@ -103,39 +96,39 @@ public class AssemblerTest {
|
||||
"STA $02",
|
||||
"LDX #$0a",
|
||||
"STX $0704",
|
||||
"LDA ($01),Y");
|
||||
"LDA ($01),Y")
|
||||
|
||||
assembler.assembleCode(lines);
|
||||
assertThat(assembler.hexdump(),
|
||||
equalTo("0600: A0 01 A9 03 85 01 A9 07 85 02 A2 0A 8E 04 07 B1 \n0610: 01"));
|
||||
assembler.assembleCode(lines)
|
||||
assertThat(assembler.hexdump())
|
||||
.isEqualTo("0600: A0 01 A9 03 85 01 A9 07 85 02 A2 0A 8E 04 07 B1 \n0610: 01")
|
||||
}
|
||||
|
||||
@Test public void testJump() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
@Test fun testJump() {
|
||||
val lines = ImmutableList.of(
|
||||
"LDA #$03",
|
||||
"JMP there",
|
||||
"BRK",
|
||||
"BRK",
|
||||
"BRK",
|
||||
"there:",
|
||||
"STA $0200");
|
||||
"STA $0200")
|
||||
|
||||
assembler.assembleCode(lines);
|
||||
assertThat(assembler.hexdump(), equalTo("0600: A9 03 4C 08 06 00 00 00 8D 00 02"));
|
||||
assembler.assembleCode(lines)
|
||||
assertThat(assembler.hexdump()).isEqualTo("0600: A9 03 4C 08 06 00 00 00 8D 00 02")
|
||||
}
|
||||
|
||||
@Test public void testSymbols() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
"define sysRandom $fe ; an adress",
|
||||
@Test fun testSymbols() {
|
||||
val lines = ImmutableList.of(
|
||||
"define sysRandom \$fe ; an adress",
|
||||
"define a_dozen $0c ; a constant",
|
||||
"LDA sysRandom ; equivalent to \"LDA $fe\"",
|
||||
"LDX #a_dozen ; equivalent to \"LDX #$0c\"");
|
||||
assembler.assembleCode(lines);
|
||||
assertThat(assembler.hexdump(), equalTo("0600: A5 FE A2 0C"));
|
||||
"LDA sysRandom ; equivalent to \"LDA \$fe\"",
|
||||
"LDX #a_dozen ; equivalent to \"LDX #$0c\"")
|
||||
assembler.assembleCode(lines)
|
||||
assertThat(assembler.hexdump()).isEqualTo("0600: A5 FE A2 0C")
|
||||
}
|
||||
|
||||
@Test public void testSnake() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
@Test fun testSnake() {
|
||||
val lines = ImmutableList.of(
|
||||
"define appleL $00 ; screen location of apple, low byte",
|
||||
"define appleH $01 ; screen location of apple, high byte",
|
||||
"define snakeHeadL $10 ; screen location of snake head, low byte",
|
||||
@ -154,8 +147,8 @@ public class AssemblerTest {
|
||||
"define ASCII_s $73",
|
||||
"define ASCII_d $64",
|
||||
"; System variables",
|
||||
"define sysRandom $fe",
|
||||
"define sysLastKey $ff",
|
||||
"define sysRandom \$fe",
|
||||
"define sysLastKey \$ff",
|
||||
" jsr init",
|
||||
" jsr loop",
|
||||
"init:",
|
||||
@ -361,28 +354,28 @@ public class AssemblerTest {
|
||||
" dex",
|
||||
" bne spinloop",
|
||||
" rts",
|
||||
"gameOver:", "\n");
|
||||
assembler.assembleCode(lines);
|
||||
assertThat(assembler.hexdump(), equalToIgnoringCase(
|
||||
"gameOver:", "\n")
|
||||
assembler.assembleCode(lines)
|
||||
assertThat(assembler.hexdump().toLowerCase()).isEqualTo(
|
||||
"0600: 20 06 06 20 38 06 20 0d 06 20 2a 06 60 a9 02 85 \n" +
|
||||
"0610: 02 a9 04 85 03 a9 11 85 10 a9 10 85 12 a9 0f 85 \n" +
|
||||
"0620: 14 a9 04 85 11 85 13 85 15 60 a5 fe 85 00 a5 fe \n" +
|
||||
"0630: 29 03 18 69 02 85 01 60 20 4d 06 20 8d 06 20 c3 \n" +
|
||||
"0640: 06 20 19 07 20 20 07 20 2d 07 4c 38 06 a5 ff c9 \n" +
|
||||
"0650: 77 f0 0d c9 64 f0 14 c9 73 f0 1b c9 61 f0 22 60 \n" +
|
||||
"0660: a9 04 24 02 d0 26 a9 01 85 02 60 a9 08 24 02 d0 \n" +
|
||||
"0670: 1b a9 02 85 02 60 a9 01 24 02 d0 10 a9 04 85 02 \n" +
|
||||
"0680: 60 a9 02 24 02 d0 05 a9 08 85 02 60 60 20 94 06 \n" +
|
||||
"0690: 20 a8 06 60 a5 00 c5 10 d0 0d a5 01 c5 11 d0 07 \n" +
|
||||
"06a0: e6 03 e6 03 20 2a 06 60 a2 02 b5 10 c5 10 d0 06 \n" +
|
||||
"06b0: b5 11 c5 11 f0 09 e8 e8 e4 03 f0 06 4c aa 06 4c \n" +
|
||||
"06c0: 35 07 60 a6 03 ca 8a b5 10 95 12 ca 10 f9 a5 02 \n" +
|
||||
"06d0: 4a b0 09 4a b0 19 4a b0 1f 4a b0 2f a5 10 38 e9 \n" +
|
||||
"06e0: 20 85 10 90 01 60 c6 11 a9 01 c5 11 f0 28 60 e6 \n" +
|
||||
"06f0: 10 a9 1f 24 10 f0 1f 60 a5 10 18 69 20 85 10 b0 \n" +
|
||||
"0700: 01 60 e6 11 a9 06 c5 11 f0 0c 60 c6 10 a5 10 29 \n" +
|
||||
"0710: 1f c9 1f f0 01 60 4c 35 07 a0 00 a5 fe 91 00 60 \n" +
|
||||
"0720: a2 00 a9 01 81 10 a6 03 a9 00 81 10 60 a2 00 ea \n" +
|
||||
"0730: ea ca d0 fb 60"));
|
||||
"0610: 02 a9 04 85 03 a9 11 85 10 a9 10 85 12 a9 0f 85 \n" +
|
||||
"0620: 14 a9 04 85 11 85 13 85 15 60 a5 fe 85 00 a5 fe \n" +
|
||||
"0630: 29 03 18 69 02 85 01 60 20 4d 06 20 8d 06 20 c3 \n" +
|
||||
"0640: 06 20 19 07 20 20 07 20 2d 07 4c 38 06 a5 ff c9 \n" +
|
||||
"0650: 77 f0 0d c9 64 f0 14 c9 73 f0 1b c9 61 f0 22 60 \n" +
|
||||
"0660: a9 04 24 02 d0 26 a9 01 85 02 60 a9 08 24 02 d0 \n" +
|
||||
"0670: 1b a9 02 85 02 60 a9 01 24 02 d0 10 a9 04 85 02 \n" +
|
||||
"0680: 60 a9 02 24 02 d0 05 a9 08 85 02 60 60 20 94 06 \n" +
|
||||
"0690: 20 a8 06 60 a5 00 c5 10 d0 0d a5 01 c5 11 d0 07 \n" +
|
||||
"06a0: e6 03 e6 03 20 2a 06 60 a2 02 b5 10 c5 10 d0 06 \n" +
|
||||
"06b0: b5 11 c5 11 f0 09 e8 e8 e4 03 f0 06 4c aa 06 4c \n" +
|
||||
"06c0: 35 07 60 a6 03 ca 8a b5 10 95 12 ca 10 f9 a5 02 \n" +
|
||||
"06d0: 4a b0 09 4a b0 19 4a b0 1f 4a b0 2f a5 10 38 e9 \n" +
|
||||
"06e0: 20 85 10 90 01 60 c6 11 a9 01 c5 11 f0 28 60 e6 \n" +
|
||||
"06f0: 10 a9 1f 24 10 f0 1f 60 a5 10 18 69 20 85 10 b0 \n" +
|
||||
"0700: 01 60 e6 11 a9 06 c5 11 f0 0c 60 c6 10 a5 10 29 \n" +
|
||||
"0710: 1f c9 1f f0 01 60 4c 35 07 a0 00 a5 fe 91 00 60 \n" +
|
||||
"0720: a2 00 a9 01 81 10 a6 03 a9 00 81 10 60 a2 00 ea \n" +
|
||||
"0730: ea ca d0 fb 60")
|
||||
}
|
||||
}
|
@ -1,59 +1,54 @@
|
||||
package android.emu6502;
|
||||
package android.emu6502
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import android.emu6502.instructions.Symbols
|
||||
import com.google.common.collect.ImmutableList
|
||||
import org.hamcrest.CoreMatchers.equalTo
|
||||
import org.junit.Assert.assertThat
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito.mock
|
||||
|
||||
import android.emu6502.instructions.Symbols;
|
||||
class CPUTest {
|
||||
private val memory = Memory(mock(Display::class.java))
|
||||
private val assembler = Assembler(memory, Symbols())
|
||||
private val cpu = CPU(memory)
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class CPUTest {
|
||||
private final Memory memory = new Memory(mock(Display.class));
|
||||
private final Assembler assembler = new Assembler(memory, new Symbols());
|
||||
private final CPU cpu = new CPU(memory);
|
||||
|
||||
@Test public void testSimple() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
@Test fun testSimple() {
|
||||
val lines = ImmutableList.of(
|
||||
"LDA #$01",
|
||||
"STA $0200",
|
||||
"LDA #$05",
|
||||
"STA $0201",
|
||||
"LDA #$08",
|
||||
"STA $0202");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.testRun();
|
||||
assertThat(cpu.getA(), equalTo(0x08));
|
||||
assertThat(cpu.getX(), equalTo(0x00));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
assertThat(cpu.getSP(), equalTo(0xFF));
|
||||
assertThat(cpu.getPC(), equalTo(0x0610));
|
||||
assertThat(cpu.flags(), equalTo("00110000"));
|
||||
"STA $0202")
|
||||
assembler.assembleCode(lines)
|
||||
cpu.testRun()
|
||||
assertThat(cpu.A, equalTo(0x08))
|
||||
assertThat(cpu.X, equalTo(0x00))
|
||||
assertThat(cpu.Y, equalTo(0x00))
|
||||
assertThat(cpu.SP, equalTo(0xFF))
|
||||
assertThat(cpu.PC, equalTo(0x0610))
|
||||
assertThat(cpu.flags(), equalTo("00110000"))
|
||||
}
|
||||
|
||||
@Test public void testWithComments() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
"LDA #$c0 ;Load the hex value $c0 into the A register",
|
||||
@Test fun testWithComments() {
|
||||
val lines = ImmutableList.of(
|
||||
"LDA #\$c0 ;Load the hex value \$c0 into the A register",
|
||||
"TAX ;Transfer the value in the A register to X",
|
||||
"INX ;Increment the value in the X register",
|
||||
"ADC #$c4 ;Add the hex value $c4 to the A register",
|
||||
"BRK ;Break - we're done");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.testRun();
|
||||
assertThat(cpu.getA(), equalTo(0x84));
|
||||
assertThat(cpu.getX(), equalTo(0xC1));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
assertThat(cpu.getSP(), equalTo(0xFF));
|
||||
assertThat(cpu.getPC(), equalTo(0x0607));
|
||||
assertThat(cpu.flags(), equalTo("10110001"));
|
||||
"ADC #\$c4 ;Add the hex value \$c4 to the A register",
|
||||
"BRK ;Break - we're done")
|
||||
assembler.assembleCode(lines)
|
||||
cpu.testRun()
|
||||
assertThat(cpu.A, equalTo(0x84))
|
||||
assertThat(cpu.X, equalTo(0xC1))
|
||||
assertThat(cpu.Y, equalTo(0x00))
|
||||
assertThat(cpu.SP, equalTo(0xFF))
|
||||
assertThat(cpu.PC, equalTo(0x0607))
|
||||
assertThat(cpu.flags(), equalTo("10110001"))
|
||||
}
|
||||
|
||||
@Test public void testBranchAndLabel() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
@Test fun testBranchAndLabel() {
|
||||
val lines = ImmutableList.of(
|
||||
"LDX #$08",
|
||||
|
||||
"decrement:",
|
||||
@ -62,19 +57,19 @@ public class CPUTest {
|
||||
"CPX #$03",
|
||||
"BNE decrement",
|
||||
"STX $0201",
|
||||
"BRK");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.testRun();
|
||||
assertThat(cpu.getA(), equalTo(0x00));
|
||||
assertThat(cpu.getX(), equalTo(0x03));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
assertThat(cpu.getSP(), equalTo(0xFF));
|
||||
assertThat(cpu.getPC(), equalTo(0x060e));
|
||||
assertThat(cpu.flags(), equalTo("00110011"));
|
||||
"BRK")
|
||||
assembler.assembleCode(lines)
|
||||
cpu.testRun()
|
||||
assertThat(cpu.A, equalTo(0x00))
|
||||
assertThat(cpu.X, equalTo(0x03))
|
||||
assertThat(cpu.Y, equalTo(0x00))
|
||||
assertThat(cpu.SP, equalTo(0xFF))
|
||||
assertThat(cpu.PC, equalTo(0x060e))
|
||||
assertThat(cpu.flags(), equalTo("00110011"))
|
||||
}
|
||||
|
||||
@Test public void testJump() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
@Test fun testJump() {
|
||||
val lines = ImmutableList.of(
|
||||
"LDA #$03",
|
||||
"JMP there",
|
||||
"BRK",
|
||||
@ -82,19 +77,19 @@ public class CPUTest {
|
||||
"BRK",
|
||||
|
||||
"there:",
|
||||
"STA $0200");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.testRun();
|
||||
assertThat(cpu.getA(), equalTo(0x03));
|
||||
assertThat(cpu.getX(), equalTo(0x00));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
assertThat(cpu.getSP(), equalTo(0xFF));
|
||||
assertThat(cpu.getPC(), equalTo(0x060c));
|
||||
assertThat(cpu.flags(), equalTo("00110000"));
|
||||
"STA $0200")
|
||||
assembler.assembleCode(lines)
|
||||
cpu.testRun()
|
||||
assertThat(cpu.A, equalTo(0x03))
|
||||
assertThat(cpu.X, equalTo(0x00))
|
||||
assertThat(cpu.Y, equalTo(0x00))
|
||||
assertThat(cpu.SP, equalTo(0xFF))
|
||||
assertThat(cpu.PC, equalTo(0x060c))
|
||||
assertThat(cpu.flags(), equalTo("00110000"))
|
||||
}
|
||||
|
||||
@Test public void testJumpToSubroutines() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
@Test fun testJumpToSubroutines() {
|
||||
val lines = ImmutableList.of(
|
||||
"JSR init",
|
||||
"JSR loop",
|
||||
"JSR end",
|
||||
@ -110,33 +105,33 @@ public class CPUTest {
|
||||
"RTS",
|
||||
|
||||
"end:",
|
||||
"BRK");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.testRun();
|
||||
assertThat(cpu.getA(), equalTo(0x00));
|
||||
assertThat(cpu.getX(), equalTo(0x05));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
assertThat(cpu.getSP(), equalTo(0xFD));
|
||||
assertThat(cpu.getPC(), equalTo(0x0613));
|
||||
assertThat(cpu.flags(), equalTo("00110011"));
|
||||
"BRK")
|
||||
assembler.assembleCode(lines)
|
||||
cpu.testRun()
|
||||
assertThat(cpu.A, equalTo(0x00))
|
||||
assertThat(cpu.X, equalTo(0x05))
|
||||
assertThat(cpu.Y, equalTo(0x00))
|
||||
assertThat(cpu.SP, equalTo(0xFD))
|
||||
assertThat(cpu.PC, equalTo(0x0613))
|
||||
assertThat(cpu.flags(), equalTo("00110011"))
|
||||
}
|
||||
|
||||
@Test public void testSymbols() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
@Test fun testSymbols() {
|
||||
val lines = ImmutableList.of(
|
||||
"define a_dozen $0c ; a constant",
|
||||
"LDX #a_dozen ; equivalent to \"LDX #$0c\"");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.testRun();
|
||||
assertThat(cpu.getA(), equalTo(0x00));
|
||||
assertThat(cpu.getX(), equalTo(0x0C));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
assertThat(cpu.getSP(), equalTo(0xFF));
|
||||
assertThat(cpu.getPC(), equalTo(0x0603));
|
||||
assertThat(cpu.flags(), equalTo("00110000"));
|
||||
"LDX #a_dozen ; equivalent to \"LDX #$0c\"")
|
||||
assembler.assembleCode(lines)
|
||||
cpu.testRun()
|
||||
assertThat(cpu.A, equalTo(0x00))
|
||||
assertThat(cpu.X, equalTo(0x0C))
|
||||
assertThat(cpu.Y, equalTo(0x00))
|
||||
assertThat(cpu.SP, equalTo(0xFF))
|
||||
assertThat(cpu.PC, equalTo(0x0603))
|
||||
assertThat(cpu.flags(), equalTo("00110000"))
|
||||
}
|
||||
|
||||
@Test public void testSnake() {
|
||||
List<String> lines = ImmutableList.of(
|
||||
@Test fun testSnake() {
|
||||
val lines = ImmutableList.of(
|
||||
"define appleL $00 ; screen location of apple, low byte",
|
||||
"define appleH $01 ; screen location of apple, high byte",
|
||||
"define snakeHeadL $10 ; screen location of snake head, low byte",
|
||||
@ -155,8 +150,8 @@ public class CPUTest {
|
||||
"define ASCII_s $73",
|
||||
"define ASCII_d $64",
|
||||
"; System variables",
|
||||
"define sysRandom $fe",
|
||||
"define sysLastKey $ff",
|
||||
"define sysRandom \$fe",
|
||||
"define sysLastKey \$ff",
|
||||
" jsr init",
|
||||
" jsr loop",
|
||||
"init:",
|
||||
@ -362,14 +357,14 @@ public class CPUTest {
|
||||
" dex",
|
||||
" bne spinloop",
|
||||
" rts",
|
||||
"gameOver:", "\n");
|
||||
assembler.assembleCode(lines);
|
||||
cpu.testRun();
|
||||
assertThat(cpu.getA(), equalTo(0x1f));
|
||||
assertThat(cpu.getX(), equalTo(0xff));
|
||||
assertThat(cpu.getY(), equalTo(0x00));
|
||||
assertThat(cpu.getSP(), equalTo(0xfb));
|
||||
assertThat(cpu.getPC(), equalTo(0x0736));
|
||||
assertThat(cpu.flags(), equalTo("00110011"));
|
||||
"gameOver:", "\n")
|
||||
assembler.assembleCode(lines)
|
||||
cpu.testRun()
|
||||
assertThat(cpu.A, equalTo(0x1f))
|
||||
assertThat(cpu.X, equalTo(0xff))
|
||||
assertThat(cpu.Y, equalTo(0x00))
|
||||
assertThat(cpu.SP, equalTo(0xfb))
|
||||
assertThat(cpu.PC, equalTo(0x0736))
|
||||
assertThat(cpu.flags(), equalTo("00110011"))
|
||||
}
|
||||
}
|
18
app/src/test/kotlin/android/emu6502/LabelsTest.kt
Normal file
18
app/src/test/kotlin/android/emu6502/LabelsTest.kt
Normal file
@ -0,0 +1,18 @@
|
||||
package android.emu6502
|
||||
|
||||
import android.emu6502.instructions.Symbols
|
||||
import org.hamcrest.CoreMatchers.equalTo
|
||||
import org.junit.Assert.assertThat
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito.mock
|
||||
|
||||
class LabelsTest {
|
||||
private val symbols = Symbols()
|
||||
private val assembler = Assembler(Memory(mock(Display::class.java)), symbols)
|
||||
private val labels = Labels(assembler, symbols)
|
||||
|
||||
@Test fun testAddLabel() {
|
||||
labels.indexLines(listOf("test:"))
|
||||
assertThat(labels["test"], equalTo(assembler.defaultCodePC))
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package android.emu6502.nes
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
class INESFileParserTest {
|
||||
val tempFile: File = File.createTempFile("foo", "bar")
|
||||
|
||||
@Before fun setUp() {
|
||||
tempFile.writeBytes((0..40).map(Int::toByte).toByteArray())
|
||||
}
|
||||
|
||||
@After fun tearDown() {
|
||||
tempFile.delete()
|
||||
}
|
||||
|
||||
@Test fun invalidHeader() {
|
||||
assertThat(INESFileParser.parseFileHeader(tempFile.inputStream()).isValid()).isFalse()
|
||||
}
|
||||
|
||||
@Test fun validHeader() {
|
||||
val testRom = javaClass.classLoader.getResource("roms/testrom.nes").toURI()
|
||||
val header = INESFileParser.parseFileHeader(File(testRom).inputStream())
|
||||
assertThat(header).isEqualTo(INESFileHeader(
|
||||
INESFileParser.INES_FILE_MAGIC, 0x10, 0x10, 0x40, 0x0, 0x0, INESFileParser.PADDING))
|
||||
assertThat(header.isValid()).isTrue()
|
||||
}
|
||||
}
|
BIN
app/src/test/resources/roms/testrom.nes
Executable file
BIN
app/src/test/resources/roms/testrom.nes
Executable file
Binary file not shown.
Loading…
Reference in New Issue
Block a user