1
0
mirror of https://github.com/irmen/ksim65.git synced 2024-06-01 06:41:34 +00:00

fixed the 65c02 differences in bcd mode, all tests now pass

This commit is contained in:
Irmen de Jong 2019-09-14 15:46:55 +02:00
parent b4d4d1b381
commit 655e106a1d
9 changed files with 391 additions and 107 deletions

View File

@ -1,11 +1,10 @@
package razorvine.ksim65.components
// TODO: implement the illegal opcodes, see http://www.ffd2.com/fridge/docs/6502-NMOS.extra.opcodes
// TODO: add the optional additional cycles to certain instructions and addressing modes
// TODO: add 6510 behavior mode (is there even a difference to be simulated here?)
/**
* 6502 cpu simulation (the NMOS version) including the 'illegal' opcodes.
* TODO: actually implement the illegal opcodes, see http://www.ffd2.com/fridge/docs/6502-NMOS.extra.opcodes
* TODO: add the optional additional cycles to certain instructions and addressing modes
*/
open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
var tracing: Boolean = false
@ -330,7 +329,7 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
)
}
private fun getFetched() =
protected fun getFetched() =
if (currentInstruction.mode == AddrMode.Imm ||
currentInstruction.mode == AddrMode.Acc ||
currentInstruction.mode == AddrMode.Imp
@ -978,7 +977,7 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
// official instructions
protected fun iAdc() {
protected open fun iAdc() {
val operand = getFetched()
if (Status.D) {
// BCD add
@ -1044,7 +1043,7 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
if (Status.Z) PC = fetchedAddress
}
protected fun iBit() {
protected open fun iBit() {
val operand = getFetched()
Status.Z = (A and operand) == 0
Status.V = (operand and 0b01000000) != 0
@ -1288,7 +1287,7 @@ open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
PC = (PC + 1) and 0xffff
}
protected fun iSbc() {
protected open fun iSbc() {
val operand = getFetched()
val tmp = (A - operand - if (Status.C) 0 else 1) and 0xffff
Status.V = (A xor operand) and (A xor tmp) and 0b10000000 != 0

View File

@ -2,6 +2,7 @@ package razorvine.ksim65.components
/**
* 65C02 cpu simulation (the CMOS version of the 6502).
* TODO: add the optional additional cycles to certain instructions and addressing modes
*/
class Cpu65C02(stopOnBrk: Boolean = false) : Cpu6502(stopOnBrk) {
@ -366,7 +367,6 @@ class Cpu65C02(stopOnBrk: Boolean = false) : Cpu6502(stopOnBrk) {
}
// opcode list: http://www.oxyron.de/html/opcodesc02.html
// TODO add optional additional cycles
override val instructions: Array<Instruction> by lazy {
listOf(
/* 00 */ Instruction("brk", AddrMode.Imp, 7),
@ -646,8 +646,66 @@ class Cpu65C02(stopOnBrk: Boolean = false) : Cpu6502(stopOnBrk) {
pendingInterrupt = null
}
override fun iBit() {
val data = getFetched()
Status.Z = (A and data) == 0
if (currentInstruction.mode != AddrMode.Imm) {
Status.V = (data and 0b01000000) != 0
Status.N = (data and 0b10000000) != 0
}
}
override fun iAdc() {
val value = getFetched()
if (Status.D) {
// BCD add
// see http://www.6502.org/tutorials/decimal_mode.html
// and https://sourceforge.net/p/vice-emu/code/HEAD/tree/trunk/vice/src/65c02core.c#l542
// (the implementation below is based on the code used by Vice)
var tmp = (A and 0x0f) + (value and 0x0f) + if (Status.C) 1 else 0
var tmp2 = (A and 0xf0) + (value and 0xf0)
if (tmp > 9) {
tmp2 += 0x10
tmp += 6
}
Status.V = (A xor value).inv() and (A xor tmp2) and 0b10000000 != 0
if (tmp2 > 0x90) tmp2 += 0x60
Status.C = tmp2 >= 0x100
tmp = (tmp and 0x0f) + (tmp2 and 0xf0)
Status.N = (tmp and 0b10000000) != 0
Status.Z = tmp == 0
A = tmp and 0xff
} else {
// normal add (identical to 6502)
val tmp = value + A + if (Status.C) 1 else 0
Status.N = (tmp and 0b10000000) != 0
Status.Z = (tmp and 0xff) == 0
Status.V = (A xor value).inv() and (A xor tmp) and 0b10000000 != 0
Status.C = tmp > 0xff
A = tmp and 0xff
}
}
override fun iSbc() {
// see http://www.6502.org/tutorials/decimal_mode.html
// and https://sourceforge.net/p/vice-emu/code/HEAD/tree/trunk/vice/src/65c02core.c#l1205
// (the implementation below is based on the code used by Vice)
val value = getFetched()
var tmp = (A - value - if (Status.C) 0 else 1) and 0xffff
Status.V = (A xor tmp) and (A xor value) and 0b10000000 != 0
if (Status.D) {
if (tmp > 0xff) tmp = (tmp - 0x60) and 0xffff
val tmp2 = ((A and 0x0f) - (value and 0x0f) - if (Status.C) 0 else 1) and 0xffff
if (tmp2 > 0xff) tmp -= 6
}
Status.C = (A - if (Status.C) 0 else 1) >= value
Status.Z = (tmp and 0xff) == 0
Status.N = (tmp and 0b10000000) != 0
A = tmp and 0xff
}
override fun iDec() {
if(currentInstruction.mode==AddrMode.Acc) {
if (currentInstruction.mode == AddrMode.Acc) {
A = (A - 1) and 0xff
Status.Z = A == 0
Status.N = (A and 0b10000000) != 0
@ -655,7 +713,7 @@ class Cpu65C02(stopOnBrk: Boolean = false) : Cpu6502(stopOnBrk) {
}
override fun iInc() {
if(currentInstruction.mode==AddrMode.Acc) {
if (currentInstruction.mode == AddrMode.Acc) {
A = (A + 1) and 0xff
Status.Z = A == 0
Status.N = (A and 0b10000000) != 0
@ -668,15 +726,15 @@ class Cpu65C02(stopOnBrk: Boolean = false) : Cpu6502(stopOnBrk) {
}
private fun iTrb() {
val m = read(fetchedAddress)
Status.Z = m and A ==0
write(fetchedAddress, m and A.inv())
val data = getFetched()
Status.Z = data and A == 0
write(fetchedAddress, data and A.inv())
}
private fun iTsb() {
val m = read(fetchedAddress)
Status.Z = m and A ==0
write(fetchedAddress, m or A)
val data = getFetched()
Status.Z = data and A == 0
write(fetchedAddress, data or A)
}
private fun iStz() {
@ -712,178 +770,178 @@ class Cpu65C02(stopOnBrk: Boolean = false) : Cpu6502(stopOnBrk) {
}
private fun iBbr0() {
val data = read(fetchedAddress)
if(data and 1 == 0)
val data = getFetched()
if (data and 1 == 0)
PC = fetchedAddressZpr
}
private fun iBbr1() {
val data = read(fetchedAddress)
if(data and 2 == 0)
val data = getFetched()
if (data and 2 == 0)
PC = fetchedAddressZpr
}
private fun iBbr2() {
val data = read(fetchedAddress)
if(data and 4 == 0)
val data = getFetched()
if (data and 4 == 0)
PC = fetchedAddressZpr
}
private fun iBbr3() {
val data = read(fetchedAddress)
if(data and 8 == 0)
val data = getFetched()
if (data and 8 == 0)
PC = fetchedAddressZpr
}
private fun iBbr4() {
val data = read(fetchedAddress)
if(data and 16 == 0)
val data = getFetched()
if (data and 16 == 0)
PC = fetchedAddressZpr
}
private fun iBbr5() {
val data = read(fetchedAddress)
if(data and 32 == 0)
val data = getFetched()
if (data and 32 == 0)
PC = fetchedAddressZpr
}
private fun iBbr6() {
val data = read(fetchedAddress)
if(data and 64 == 0)
val data = getFetched()
if (data and 64 == 0)
PC = fetchedAddressZpr
}
private fun iBbr7() {
val data = read(fetchedAddress)
if(data and 128 == 0)
val data = getFetched()
if (data and 128 == 0)
PC = fetchedAddressZpr
}
private fun iBbs0() {
val data = read(fetchedAddress)
if(data and 1 != 0)
val data = getFetched()
if (data and 1 != 0)
PC = fetchedAddressZpr
}
private fun iBbs1() {
val data = read(fetchedAddress)
if(data and 2 != 0)
val data = getFetched()
if (data and 2 != 0)
PC = fetchedAddressZpr
}
private fun iBbs2() {
val data = read(fetchedAddress)
if(data and 4 != 0)
val data = getFetched()
if (data and 4 != 0)
PC = fetchedAddressZpr
}
private fun iBbs3() {
val data = read(fetchedAddress)
if(data and 8 != 0)
val data = getFetched()
if (data and 8 != 0)
PC = fetchedAddressZpr
}
private fun iBbs4() {
val data = read(fetchedAddress)
if(data and 16 != 0)
val data = getFetched()
if (data and 16 != 0)
PC = fetchedAddressZpr
}
private fun iBbs5() {
val data = read(fetchedAddress)
if(data and 32 != 0)
val data = getFetched()
if (data and 32 != 0)
PC = fetchedAddressZpr
}
private fun iBbs6() {
val data = read(fetchedAddress)
if(data and 64 != 0)
val data = getFetched()
if (data and 64 != 0)
PC = fetchedAddressZpr
}
private fun iBbs7() {
val data = read(fetchedAddress)
if(data and 128 != 0)
val data = getFetched()
if (data and 128 != 0)
PC = fetchedAddressZpr
}
private fun iSmb0() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data or 1)
}
private fun iSmb1() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data or 2)
}
private fun iSmb2() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data or 4)
}
private fun iSmb3() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data or 8)
}
private fun iSmb4() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data or 16)
}
private fun iSmb5() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data or 32)
}
private fun iSmb6() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data or 64)
}
private fun iSmb7() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data or 128)
}
private fun iRmb0() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data and 0b11111110)
}
private fun iRmb1() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data and 0b11111101)
}
private fun iRmb2() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data and 0b11111011)
}
private fun iRmb3() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data and 0b11110111)
}
private fun iRmb4() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data and 0b11101111)
}
private fun iRmb5() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data and 0b11011111)
}
private fun iRmb6() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data and 0b10111111)
}
private fun iRmb7() {
val data = read(fetchedAddress)
val data = getFetched()
write(fetchedAddress, data and 0b01111111)
}
}

View File

@ -1,5 +1,9 @@
#include <stdio.h>
/*
testing 6502/6510 core ADC and SBC from Vice
*/
static int flag_Z;
static int flag_C;
static int flag_N;
@ -60,13 +64,13 @@ unsigned int SBC(unsigned int A, unsigned int value, unsigned int bcd_mode)
flag_Z = (tmp&0xff)==0;
flag_N = (tmp&0x80)!=0;
flag_V = ((A ^ tmp) & 0x80) && ((A ^ src) & 0x80);
return tmp_a && 255;
return tmp_a & 0xff;
} else {
flag_Z = (tmp&0xff)==0;
flag_N = (tmp&0x80)!=0;
flag_C = tmp < 0x100;
flag_V = ((A ^ tmp) & 0x80) && ((A ^ src) & 0x80);
return tmp && 255;
return tmp & 0xff;
}
}
@ -81,6 +85,8 @@ void print_result(int result) {
}
int main(char* argv) {
printf("6502 adc/sbc simulation\n");
flag_C = flag_N = flag_V = flag_Z = 0;
for(unsigned int A=0; A<256; ++A) {

View File

@ -0,0 +1,151 @@
#include <stdio.h>
/*
testing 65c02 core ADC and SBC from Vice
*/
static int flag_Z;
static int flag_C;
static int flag_N;
static int flag_V;
unsigned int ADC(unsigned int A, unsigned int value, unsigned int bcd_mode)
{
unsigned int tmp, tmp2;
unsigned int tmp_value = value;
if (bcd_mode) {
tmp = (A & 0xf) + (tmp_value & 0xf) + flag_C;
tmp2 = (A & 0xf0) + (tmp_value & 0xf0);
if (tmp > 9) {
tmp2 += 0x10;
tmp += 6;
}
flag_V = ~(A ^ tmp_value) & (A ^ tmp2) & 0x80;
if (tmp2 > 0x90) {
tmp2 += 0x60;
}
flag_C = (tmp2 & 0xff00)!=0;
tmp = (tmp & 0xf) + (tmp2 & 0xf0);
flag_Z = tmp==0;
flag_N = tmp&0x80!=0;
} else {
tmp = tmp_value + A + flag_C;
flag_Z = (tmp&0xff)==0;
flag_N = (tmp&0x80)!=0;
flag_V = !((A ^ tmp_value) & 0x80) && ((A ^ tmp) & 0x80);
flag_C = tmp > 0xff;
}
return tmp & 0xff;
}
unsigned int SBC(unsigned int A, unsigned int value, unsigned int bcd_mode)
{
unsigned int src = value;
unsigned int tmp = A - src + flag_C - 1;
flag_V = (((A ^ tmp) & 0x80) && ((A ^ src) & 0x80));
if (bcd_mode) {
if (tmp > 0xff) {
tmp -= 0x60;
}
unsigned int tmp2 = (A & 0xf) - (src & 0xf) + flag_C - 1;
if (tmp2 > 0xff) {
tmp -= 6;
}
}
flag_C = (A + flag_C - 1 >= src);
flag_Z = (tmp&0xff)==0;
flag_N = (tmp&0x80)!=0;
return tmp & 0xff;
}
void print_result(int result) {
printf(" %02x", result);
printf(" N=%d", flag_N? 1:0);
printf(" V=%d", flag_V? 1:0);
printf(" Z=%d", flag_Z? 1:0);
printf(" C=%d", flag_C? 1:0);
printf("\n");
}
int main(char* argv) {
printf("65c02 adc/sbc simulation\n");
flag_C = flag_N = flag_V = flag_Z = 0;
for(unsigned int A=0; A<256; ++A) {
for(unsigned int v=0; v<256; ++v) {
flag_C = 0;
printf("adc,normal,carry0: %02x + %02x = ", A, v);
unsigned int result = ADC(A, v, 0);
print_result(result);
}
}
printf("\n");
for(unsigned int A=0; A<256; ++A) {
for(unsigned int v=0; v<256; ++v) {
flag_C = 1;
printf("adc,normal,carry1: %02x + %02x = ", A, v);
unsigned int result = ADC(A, v, 0);
print_result(result);
}
}
printf("\n");
for(unsigned int A=0; A<256; ++A) {
for(unsigned int v=0; v<256; ++v) {
flag_C = 0;
printf("adc,bcd,carry0: %02x + %02x = ", A, v);
unsigned int result = ADC(A, v, 1);
print_result(result);
}
}
printf("\n");
for(unsigned int A=0; A<256; ++A) {
for(unsigned int v=0; v<256; ++v) {
flag_C = 1;
printf("adc,bcd,carry1: %02x + %02x = ", A, v);
unsigned int result = ADC(A, v, 1);
print_result(result);
}
}
for(unsigned int A=0; A<256; ++A) {
for(unsigned int v=0; v<256; ++v) {
flag_C = 0;
printf("sbc,normal,carry0: %02x - %02x = ", A, v);
unsigned int result = SBC(A, v, 0);
print_result(result);
}
}
printf("\n");
for(unsigned int A=0; A<256; ++A) {
for(unsigned int v=0; v<256; ++v) {
flag_C = 1;
printf("sbc,normal,carry1: %02x - %02x = ", A, v);
unsigned int result = SBC(A, v, 0);
print_result(result);
}
}
printf("\n");
for(unsigned int A=0; A<256; ++A) {
for(unsigned int v=0; v<256; ++v) {
flag_C = 0;
printf("sbc,bcd,carry0: %02x - %02x = ", A, v);
unsigned int result = SBC(A, v, 1);
print_result(result);
}
}
printf("\n");
for(unsigned int A=0; A<256; ++A) {
for(unsigned int v=0; v<256; ++v) {
flag_C = 1;
printf("sbc,bcd,carry1: %02x - %02x = ", A, v);
unsigned int result = SBC(A, v, 1);
print_result(result);
}
}
}

View File

@ -320,4 +320,42 @@ class Test6502 : TestCommon6502() {
mpu.step()
assertEquals(0x3f, mpu.A)
}
@Test
fun test_sbc_bcd_on_immediate_20_minus_0a_carry_unset() {
mpu.Status.D = true
mpu.Status.C = false
mpu.A = 0x20
// $0000 SBC #$0a
writeMem(memory, 0x0000, listOf(0xe9, 0x0a))
mpu.step()
assertEquals(0x0002, mpu.PC)
assertEquals(0x1f, mpu.A) // 0x1f on 6502, 0x0f on 65c02
assertFalse(mpu.Status.Z)
assertTrue(mpu.Status.C)
assertFalse(mpu.Status.N)
assertFalse(mpu.Status.V)
}
@Test
fun test_adc_bcd_on_immediate_9c_plus_9d() {
mpu.Status.D = true
mpu.Status.C = false
mpu.Status.N = true
mpu.A = 0x9c
// $0000 ADC #$9d
// $0002 ADC #$9d
writeMem(memory, 0x0000, listOf(0x69, 0x9d))
writeMem(memory, 0x0002, listOf(0x69, 0x9d))
mpu.step()
assertEquals(0x9f, mpu.A)
assertTrue(mpu.Status.C)
mpu.step()
assertEquals(0x0004, mpu.PC)
assertEquals(0x93, mpu.A)
assertFalse(mpu.Status.Z)
assertTrue(mpu.Status.C)
assertTrue(mpu.Status.V)
assertFalse(mpu.Status.N) // False on 6502, True on 65C02
}
}

View File

@ -1,6 +1,7 @@
import razorvine.ksim65.components.Bus
import razorvine.ksim65.components.Ram
import razorvine.ksim65.components.Cpu6502
import razorvine.ksim65.components.Cpu65C02
import java.lang.Exception
import kotlin.test.*
@ -10,7 +11,7 @@ class Test6502Functional {
private class SuccessfulTestResult: Exception()
@Test
fun testFunctional() {
fun testFunctional6502() {
val cpu = Cpu6502(false)
val bus = Bus()
val ram = Ram(0, 0xffff)
@ -26,7 +27,38 @@ class Test6502Functional {
}
try {
while (cpu.totalCycles < 900000000) {
while (cpu.totalCycles < 100000000) {
cpu.clock()
}
} catch (sx: SuccessfulTestResult) {
println("test successful ${cpu.totalCycles}")
return
}
cpu.printState()
val d = cpu.disassemble(ram, cpu.PC-20, cpu.PC+20)
println(d.joinToString ("\n"))
fail("test failed")
}
@Test
fun testFunctional65C02() {
val cpu = Cpu65C02(false)
val bus = Bus()
val ram = Ram(0, 0xffff)
ram.load("src/test/kotlin/6502_functional_tests/bin_files/65C02_extended_opcodes_test.bin", 0)
bus.add(cpu)
bus.add(ram)
cpu.reset()
cpu.PC = 0x0400
cpu.breakpoint(0x24f1) { _, _ ->
// reaching this address means successful test result
if(cpu.currentOpcode==0x4c)
throw SuccessfulTestResult()
}
try {
while (cpu.totalCycles < 100000000) {
cpu.clock()
}
} catch (sx: SuccessfulTestResult) {

View File

@ -7,7 +7,7 @@ import kotlin.test.*
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
//@Disabled("this test suite takes a long time")
@Disabled("this test suite takes a long time")
class Test6502TestSuite {
val cpu: Cpu6502 = Cpu6502(stopOnBrk = false)

View File

@ -1604,4 +1604,42 @@ class Test65C02 : TestCommon6502() {
mpu.step()
assertEquals(0x0043, mpu.PC)
}
@Test
fun test_sbc_bcd_on_immediate_20_minus_0a_carry_unset() {
mpu.Status.D = true
mpu.Status.C = false
mpu.A = 0x20
// $0000 SBC #$0a
writeMem(memory, 0x0000, listOf(0xe9, 0x0a))
mpu.step()
assertEquals(0x0002, mpu.PC)
assertEquals(0x0f, mpu.A) // 0x1f on 6502, 0x0f on 65c02
assertFalse(mpu.Status.Z)
assertTrue(mpu.Status.C)
assertFalse(mpu.Status.N)
assertFalse(mpu.Status.V)
}
@Test
fun test_adc_bcd_on_immediate_9c_plus_9d() {
mpu.Status.D = true
mpu.Status.C = false
mpu.Status.N = true
mpu.A = 0x9c
// $0000 ADC #$9d
// $0002 ADC #$9d
writeMem(memory, 0x0000, listOf(0x69, 0x9d))
writeMem(memory, 0x0002, listOf(0x69, 0x9d))
mpu.step()
assertEquals(0x9f, mpu.A)
assertTrue(mpu.Status.C)
mpu.step()
assertEquals(0x0004, mpu.PC)
assertEquals(0x93, mpu.A)
assertFalse(mpu.Status.Z)
assertTrue(mpu.Status.C)
assertTrue(mpu.Status.V)
assertTrue(mpu.Status.N) // False on 6502, True on 65C02
}
}

View File

@ -491,28 +491,6 @@ abstract class TestCommon6502 {
assertFalse(mpu.Status.C)
}
@Test
fun test_adc_bcd_on_immediate_9c_plus_9d() {
mpu.Status.D = true
mpu.Status.C = false
mpu.Status.N = true
mpu.A = 0x9c
// $0000 ADC #$9d
// $0002 ADC #$9d
writeMem(memory, 0x0000, listOf(0x69, 0x9d))
writeMem(memory, 0x0002, listOf(0x69, 0x9d))
mpu.step()
assertEquals(0x9f, mpu.A)
assertTrue(mpu.Status.C)
mpu.step()
assertEquals(0x0004, mpu.PC)
assertEquals(0x93, mpu.A)
assertFalse(mpu.Status.Z)
assertTrue(mpu.Status.C)
assertTrue(mpu.Status.V)
assertFalse(mpu.Status.N)
}
// ADC Absolute, X-Indexed
@Test
@ -5142,22 +5120,6 @@ abstract class TestCommon6502 {
assertFalse(mpu.Status.C)
}
@Test
fun test_sbc_bcd_on_immediate_20_minus_0a_carry_unset() {
mpu.Status.D = true
mpu.Status.C = false
mpu.A = 0x20
// $0000 SBC #$0a
writeMem(memory, 0x0000, listOf(0xe9, 0x0a))
mpu.step()
assertEquals(0x0002, mpu.PC)
assertEquals(0x1f, mpu.A)
assertFalse(mpu.Status.Z)
assertTrue(mpu.Status.C)
assertFalse(mpu.Status.N)
assertFalse(mpu.Status.V)
}
// SBC Absolute, X-Indexed
@Test