From 7fec386d42ea53c9ccc8ba65008046821423a82b Mon Sep 17 00:00:00 2001 From: kris Date: Wed, 10 May 2017 23:16:23 +0100 Subject: [PATCH 01/18] Test harness for executing Klaus Dormann's 6502-based test suites, see https://github.com/Klaus2m5/6502_65C02_functional_tests for source and binary images (GPL'ed so not committing here) The 65C02 one currently is not passing. --- py65/tests/devices/test_klaus.py | 64 ++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 py65/tests/devices/test_klaus.py diff --git a/py65/tests/devices/test_klaus.py b/py65/tests/devices/test_klaus.py new file mode 100644 index 0000000..d59ab8f --- /dev/null +++ b/py65/tests/devices/test_klaus.py @@ -0,0 +1,64 @@ +import unittest +import sys +import py65.devices.mpu65c02 + +class KlausDormannTests(unittest.TestCase): + """Runs Klaus Dormann's 6502-based test suites""" + + def klausTestCase(self, filename, load_addr, pc, success_addr): + mpu = self._make_mpu() + mpu.pc = pc + + object_code = bytearray(open(filename, "r").read()) + self._write(mpu.memory, load_addr, object_code) + + while True: + old_pc = mpu.pc + mpu.step() + if mpu.pc == old_pc: + break + + assert mpu.pc == success_addr, mpu + + def test6502FunctionalTest(self): + self.klausTestCase("6502_functional_test.bin", 0x0, 0x400, 0x3399) + + def test65C02ExtendedOpcodesTest(self): + self.klausTestCase("65C02_extended_opcodes_test.bin", 0x0, 0x400, 0x24a8) + + def test6502DecimalTest(self): + mpu = self._make_mpu() + mpu.pc = 0x1000 + + object_code = bytearray(open("6502_decimal_test.bin", "r").read()) + self._write(mpu.memory, 0x200, object_code) + + # $1000: JSR $0200 + self._write(mpu.memory, 0x1000, [0x20, 0x00, 0x02]) + + while True: + mpu.step() + if mpu.pc == 0x1003: + break + assert mpu.memory[0x0b] == 0 + + # Test Helpers + + def _write(self, memory, start_address, bytes): + memory[start_address:start_address + len(bytes)] = bytes + + def _make_mpu(self, *args, **kargs): + klass = self._get_target_class() + mpu = klass(*args, **kargs) + if 'memory' not in kargs: + mpu.memory = 0x10000 * [0xAA] + return mpu + + def _get_target_class(self): + return py65.devices.mpu65c02.MPU + +def test_suite(): + return unittest.findTestCases(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') From 5e7ade9cb939899980c67990611571920118e278 Mon Sep 17 00:00:00 2001 From: kris Date: Thu, 11 May 2017 21:55:29 +0100 Subject: [PATCH 02/18] Add support for optionally tracing execution --- py65/devices/mpu6502.py | 9 +++++++-- py65/devices/mpu65c02.py | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/py65/devices/mpu6502.py b/py65/devices/mpu6502.py index d72973e..62a0647 100644 --- a/py65/devices/mpu6502.py +++ b/py65/devices/mpu6502.py @@ -1,6 +1,6 @@ from py65.utils.conversions import itoa from py65.utils.devices import make_instruction_decorator - +from py65 import disassembler class MPU: # vectors @@ -41,6 +41,8 @@ class MPU: self.memory = memory self.start_pc = pc + self.disassembler = disassembler.Disassembler(self) + # init self.reset() @@ -55,8 +57,11 @@ class MPU: return self.reprformat() % (indent, self.name, self.pc, self.a, self.x, self.y, self.sp, flags) - def step(self): + def step(self, trace=False): instructCode = self.memory[self.pc] + if trace: + print self, "$%04X: %s" % ( + self.pc, self.disassembler.instruction_at(self.pc)[1]) self.pc = (self.pc + 1) & self.addrMask self.excycles = 0 self.addcycles = self.extracycles[instructCode] diff --git a/py65/devices/mpu65c02.py b/py65/devices/mpu65c02.py index ef37f74..2ac5a9d 100644 --- a/py65/devices/mpu65c02.py +++ b/py65/devices/mpu65c02.py @@ -8,11 +8,11 @@ class MPU(mpu6502.MPU): self.name = '65C02' self.waiting = False - def step(self): + def step(self, trace=False): if self.waiting: self.processorCycles += 1 else: - mpu6502.MPU.step(self) + mpu6502.MPU.step(self, trace) return self # Make copies of the lists From 32896cc13955518ca191884671116e0cfc6c2db1 Mon Sep 17 00:00:00 2001 From: kris Date: Thu, 11 May 2017 22:01:33 +0100 Subject: [PATCH 03/18] Support tracing execution for particular PC values. Use this to trace the execution of the currently failing decimal-mode ADC test, see https://github.com/mnaberez/py65/issues/33 --- py65/tests/devices/test_klaus.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/py65/tests/devices/test_klaus.py b/py65/tests/devices/test_klaus.py index d59ab8f..70f8214 100644 --- a/py65/tests/devices/test_klaus.py +++ b/py65/tests/devices/test_klaus.py @@ -5,26 +5,31 @@ import py65.devices.mpu65c02 class KlausDormannTests(unittest.TestCase): """Runs Klaus Dormann's 6502-based test suites""" - def klausTestCase(self, filename, load_addr, pc, success_addr): + def klausTestCase(self, filename, load_addr, pc, success_addr, should_trace=None): mpu = self._make_mpu() mpu.pc = pc object_code = bytearray(open(filename, "r").read()) self._write(mpu.memory, load_addr, object_code) + if not should_trace: + should_trace = lambda pc: False + while True: old_pc = mpu.pc - mpu.step() + mpu.step(trace=should_trace(mpu.pc)) if mpu.pc == old_pc: break - assert mpu.pc == success_addr, mpu + assert mpu.pc == success_addr, "%s 0xb=%02x 0xc=%02x 0xd=%02x 0xf=%02x" % ( + mpu, mpu.memory[0xb], mpu.memory[0xc], mpu.memory[0xd], mpu.memory[0xf]) def test6502FunctionalTest(self): self.klausTestCase("6502_functional_test.bin", 0x0, 0x400, 0x3399) def test65C02ExtendedOpcodesTest(self): - self.klausTestCase("65C02_extended_opcodes_test.bin", 0x0, 0x400, 0x24a8) + tracer = lambda pc: (0x1484 <= pc <= 0x16cc) + self.klausTestCase("65C02_extended_opcodes_test_modified.bin", 0xa, 0x400, 0x24a8, tracer) def test6502DecimalTest(self): mpu = self._make_mpu() From 666cd9cd99484f769b563218214433d37faa1d87 Mon Sep 17 00:00:00 2001 From: kris Date: Thu, 11 May 2017 22:43:08 +0100 Subject: [PATCH 04/18] Use the decimally adjusted aluresult to compute the value of flags. This fixes various bugs with ADC/SBC in decimal mode found by Klaus Dormann's test suite. --- py65/devices/mpu6502.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/py65/devices/mpu6502.py b/py65/devices/mpu6502.py index 62a0647..3ca4cef 100644 --- a/py65/devices/mpu6502.py +++ b/py65/devices/mpu6502.py @@ -315,11 +315,14 @@ class MPU: # the ALU outputs are not decimally adjusted nibble0 = nibble0 & 0xf nibble1 = nibble1 & 0xf - aluresult = (nibble1 << 4) + nibble0 # the final A contents will be decimally adjusted nibble0 = (nibble0 + adjust0) & 0xf nibble1 = (nibble1 + adjust1) & 0xf + + # Update result for use in setting flags below + aluresult = (nibble1 << 4) + nibble0 + self.p &= ~(self.CARRY | self.OVERFLOW | self.NEGATIVE | self.ZERO) if aluresult == 0: self.p |= self.ZERO @@ -420,6 +423,9 @@ class MPU: nibble0 = (aluresult + adjust0) & 0xf nibble1 = ((aluresult + adjust1) >> 4) & 0xf + # Update result for use in setting flags below + aluresult = (nibble1 << 4) + nibble0 + self.p &= ~(self.CARRY | self.ZERO | self.NEGATIVE | self.OVERFLOW) if aluresult == 0: self.p |= self.ZERO From 4f52c955119d38ff627a44eb15947c9e63c07a84 Mon Sep 17 00:00:00 2001 From: kris Date: Sat, 22 Jul 2017 23:40:05 +0100 Subject: [PATCH 05/18] Add functional test cases that exhaustively test BCD mode on 6502 and 65C02 including invalid BCD arguments. These are ; Written by Bruce Clark. This code is public domain. ; see http://www.6502.org/tutorials/decimal_mode.html and obtained via Klaus Dormann's https://github.com/Klaus2m5/6502_65C02_functional_tests --- py65/tests/devices/bcd/6502_decimal_test.a65 | 355 +++++++++++++++++ py65/tests/devices/bcd/6502_decimal_test.bin | Bin 0 -> 258 bytes py65/tests/devices/bcd/6502_decimal_test.lst | 367 ++++++++++++++++++ py65/tests/devices/bcd/65C02_decimal_test.a65 | 355 +++++++++++++++++ py65/tests/devices/bcd/65C02_decimal_test.bin | Bin 0 -> 266 bytes py65/tests/devices/bcd/65C02_decimal_test.lst | 367 ++++++++++++++++++ py65/tests/devices/test_binary_object.py | 96 +++++ 7 files changed, 1540 insertions(+) create mode 100644 py65/tests/devices/bcd/6502_decimal_test.a65 create mode 100644 py65/tests/devices/bcd/6502_decimal_test.bin create mode 100644 py65/tests/devices/bcd/6502_decimal_test.lst create mode 100644 py65/tests/devices/bcd/65C02_decimal_test.a65 create mode 100644 py65/tests/devices/bcd/65C02_decimal_test.bin create mode 100644 py65/tests/devices/bcd/65C02_decimal_test.lst create mode 100644 py65/tests/devices/test_binary_object.py diff --git a/py65/tests/devices/bcd/6502_decimal_test.a65 b/py65/tests/devices/bcd/6502_decimal_test.a65 new file mode 100644 index 0000000..0d076bf --- /dev/null +++ b/py65/tests/devices/bcd/6502_decimal_test.a65 @@ -0,0 +1,355 @@ +; Verify decimal mode behavior +; Written by Bruce Clark. This code is public domain. +; see http://www.6502.org/tutorials/decimal_mode.html +; +; Returns: +; ERROR = 0 if the test passed +; ERROR = 1 if the test failed +; modify the code at the DONE label for desired program end +; +; This routine requires 17 bytes of RAM -- 1 byte each for: +; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF +; and 2 bytes for N2H +; +; Variables: +; N1 and N2 are the two numbers to be added or subtracted +; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2 +; DA and DNVZC are the actual accumulator and flag results in decimal mode +; HA and HNVZC are the accumulator and flag results when N1 and N2 are +; added or subtracted using binary arithmetic +; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and +; flag results, calculated using binary arithmetic +; +; This program takes approximately 1 minute at 1 MHz (a few seconds more on +; a 65C02 than a 6502 or 65816) +; + +; Configuration: +cputype = 0 ; 0 = 6502, 1 = 65C02, 2 = 65C816 +vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only +chk_a = 1 ; check accumulator +chk_n = 1 ; check sign (negative) flag +chk_v = 1 ; check overflow flag +chk_z = 1 ; check zero flag +chk_c = 1 ; check carry flag + +end_of_test macro + db $db ;execute 65C02 stop instruction + endm + + bss + org 0 +; operands - register Y = carry in +N1 ds 1 +N2 ds 1 +; binary result +HA ds 1 +HNVZC ds 1 + ;04 +; decimal result +DA ds 1 +DNVZC ds 1 +; predicted results +AR ds 1 +NF ds 1 + ;08 +VF ds 1 +ZF ds 1 +CF ds 1 +ERROR ds 1 + ;0C +; workspace +N1L ds 1 +N1H ds 1 +N2L ds 1 +N2H ds 2 + + code + org $200 +TEST ldy #1 ; initialize Y (used to loop through carry flag values) + sty ERROR ; store 1 in ERROR until the test passes + lda #0 ; initialize N1 and N2 + sta N1 + sta N2 +LOOP1 lda N2 ; N2L = N2 & $0F + and #$0F ; [1] see text + if vld_bcd = 1 + cmp #$0a + bcs NEXT2 + endif + sta N2L + lda N2 ; N2H = N2 & $F0 + and #$F0 ; [2] see text + if vld_bcd = 1 + cmp #$a0 + bcs NEXT2 + endif + sta N2H + ora #$0F ; N2H+1 = (N2 & $F0) + $0F + sta N2H+1 +LOOP2 lda N1 ; N1L = N1 & $0F + and #$0F ; [3] see text + if vld_bcd = 1 + cmp #$0a + bcs NEXT1 + endif + sta N1L + lda N1 ; N1H = N1 & $F0 + and #$F0 ; [4] see text + if vld_bcd = 1 + cmp #$a0 + bcs NEXT1 + endif + sta N1H + jsr ADD + jsr A6502 + jsr COMPARE + bne * + jsr SUB + jsr S6502 + jsr COMPARE + bne * +NEXT1 inc N1 ; [5] see text + bne LOOP2 ; loop through all 256 values of N1 +NEXT2 inc N2 ; [6] see text + bne LOOP1 ; loop through all 256 values of N2 + dey + bpl LOOP1 ; loop through both values of the carry flag + lda #0 ; test passed, so store 0 in ERROR + sta ERROR +DONE + end_of_test + +; Calculate the actual decimal mode accumulator and flags, the accumulator +; and flag results when N1 is added to N2 using binary arithmetic, the +; predicted accumulator result, the predicted carry flag, and the predicted +; V flag +; +ADD sed ; decimal mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + adc N2 + sta DA ; actual accumulator result in decimal mode + php + pla + sta DNVZC ; actual flags result in decimal mode + cld ; binary mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + adc N2 + sta HA ; accumulator result of N1+N2 using binary arithmetic + + php + pla + sta HNVZC ; flags result of N1+N2 using binary arithmetic + cpy #1 + lda N1L + adc N2L + cmp #$0A + ldx #0 + bcc A1 + inx + adc #5 ; add 6 (carry is set) + and #$0F + sec +A1 ora N1H +; +; if N1L + N2L < $0A, then add N2 & $F0 +; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set) +; + adc N2H,x + php + bcs A2 + cmp #$A0 + bcc A3 +A2 adc #$5F ; add $60 (carry is set) + sec +A3 sta AR ; predicted accumulator result + php + pla + sta CF ; predicted carry result + pla +; +; note that all 8 bits of the P register are stored in VF +; + sta VF ; predicted V flags + rts + +; Calculate the actual decimal mode accumulator and flags, and the +; accumulator and flag results when N2 is subtracted from N1 using binary +; arithmetic +; +SUB sed ; decimal mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + sbc N2 + sta DA ; actual accumulator result in decimal mode + php + pla + sta DNVZC ; actual flags result in decimal mode + cld ; binary mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + sbc N2 + sta HA ; accumulator result of N1-N2 using binary arithmetic + + php + pla + sta HNVZC ; flags result of N1-N2 using binary arithmetic + rts + + if cputype != 1 +; Calculate the predicted SBC accumulator result for the 6502 and 65816 +; +SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1L + sbc N2L + ldx #0 + bcs S11 + inx + sbc #5 ; subtract 6 (carry is clear) + and #$0F + clc +S11 ora N1H +; +; if N1L - N2L >= 0, then subtract N2 & $F0 +; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) +; + sbc N2H,x + bcs S12 + sbc #$5F ; subtract $60 (carry is clear) +S12 sta AR + rts + endif + + if cputype = 1 +; Calculate the predicted SBC accumulator result for the 6502 and 65C02 +; +SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1L + sbc N2L + ldx #0 + bcs S21 + inx + and #$0F + clc +S21 ora N1H +; +; if N1L - N2L >= 0, then subtract N2 & $F0 +; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) +; + sbc N2H,x + bcs S22 + sbc #$5F ; subtract $60 (carry is clear) +S22 cpx #0 + beq S23 + sbc #6 +S23 sta AR ; predicted accumulator result + rts + endif + +; Compare accumulator actual results to predicted results +; +; Return: +; Z flag = 1 (BEQ branch) if same +; Z flag = 0 (BNE branch) if different +; +COMPARE + if chk_a = 1 + lda DA + cmp AR + bne C1 + endif + if chk_n = 1 + lda DNVZC ; [7] see text + eor NF + and #$80 ; mask off N flag + bne C1 + endif + if chk_v = 1 + lda DNVZC ; [8] see text + eor VF + and #$40 ; mask off V flag + bne C1 ; [9] see text + endif + if chk_z = 1 + lda DNVZC + eor ZF ; mask off Z flag + and #2 + bne C1 ; [10] see text + endif + if chk_c = 1 + lda DNVZC + eor CF + and #1 ; mask off C flag + endif +C1 rts + +; These routines store the predicted values for ADC and SBC for the 6502, +; 65C02, and 65816 in AR, CF, NF, VF, and ZF + + if cputype = 0 + +A6502 lda VF ; 6502 +; +; since all 8 bits of the P register were stored in VF, bit 7 of VF contains +; the N flag for NF +; + sta NF + lda HNVZC + sta ZF + rts + +S6502 jsr SUB1 + lda HNVZC + sta NF + sta VF + sta ZF + sta CF + rts + + endif + if cputype = 1 + +A6502 lda AR ; 65C02 + php + pla + sta NF + sta ZF + rts + +S6502 jsr SUB2 + lda AR + php + pla + sta NF + sta ZF + lda HNVZC + sta VF + sta CF + rts + + endif + if cputype = 2 + +A6502 lda AR ; 65C816 + php + pla + sta NF + sta ZF + rts + +S6502 jsr SUB1 + lda AR + php + pla + sta NF + sta ZF + lda HNVZC + sta VF + sta CF + rts + + endif + + end TEST diff --git a/py65/tests/devices/bcd/6502_decimal_test.bin b/py65/tests/devices/bcd/6502_decimal_test.bin new file mode 100644 index 0000000000000000000000000000000000000000..9f959425aff5b4d2218d7f085cc4d1fc591f6409 GIT binary patch literal 258 zcmYj}JqiLb5Jr>iCYxQ|jg?qQ+T6g>#?CvKM$iM;TR333M-U-}*z6GmQLsxXS}Tf; zg@Tnh{uDOfn+L<2&{1hdfB<#WD9CWHpkf6TN6^c-S4ef6=?l|q=ASxe`u07K#P5#O zug8@oCQAFqf;!Tr5Xv6#?N1m=fOol@ZIUU;#i`GYYVdTRWD#yAdC>1PK;T!>18F~v z!+!(i)3EQzA)AsyoX(i2@${|=cJ6?%E?fy;cf1vup|2vPY5EKq8|DQvG1NK db $db ;execute 65C02 stop instruction + + + ; Calculate the actual decimal mode accumulator and flags, the accumulator + ; and flag results when N1 is added to N2 using binary arithmetic, the + ; predicted accumulator result, the predicted carry flag, and the predicted + ; V flag + ; +024c : f8 ADD sed ; decimal mode +024d : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +024f : a500 lda N1 +0251 : 6501 adc N2 +0253 : 8504 sta DA ; actual accumulator result in decimal mode +0255 : 08 php +0256 : 68 pla +0257 : 8505 sta DNVZC ; actual flags result in decimal mode +0259 : d8 cld ; binary mode +025a : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +025c : a500 lda N1 +025e : 6501 adc N2 +0260 : 8502 sta HA ; accumulator result of N1+N2 using binary arithmetic + +0262 : 08 php +0263 : 68 pla +0264 : 8503 sta HNVZC ; flags result of N1+N2 using binary arithmetic +0266 : c001 cpy #1 +0268 : a50c lda N1L +026a : 650e adc N2L +026c : c90a cmp #$0A +026e : a200 ldx #0 +0270 : 9006 bcc A1 +0272 : e8 inx +0273 : 6905 adc #5 ; add 6 (carry is set) +0275 : 290f and #$0F +0277 : 38 sec +0278 : 050d A1 ora N1H + ; + ; if N1L + N2L < $0A, then add N2 & $F0 + ; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set) + ; +027a : 750f adc N2H,x +027c : 08 php +027d : b004 bcs A2 +027f : c9a0 cmp #$A0 +0281 : 9003 bcc A3 +0283 : 695f A2 adc #$5F ; add $60 (carry is set) +0285 : 38 sec +0286 : 8506 A3 sta AR ; predicted accumulator result +0288 : 08 php +0289 : 68 pla +028a : 850a sta CF ; predicted carry result +028c : 68 pla + ; + ; note that all 8 bits of the P register are stored in VF + ; +028d : 8508 sta VF ; predicted V flags +028f : 60 rts + + ; Calculate the actual decimal mode accumulator and flags, and the + ; accumulator and flag results when N2 is subtracted from N1 using binary + ; arithmetic + ; +0290 : f8 SUB sed ; decimal mode +0291 : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +0293 : a500 lda N1 +0295 : e501 sbc N2 +0297 : 8504 sta DA ; actual accumulator result in decimal mode +0299 : 08 php +029a : 68 pla +029b : 8505 sta DNVZC ; actual flags result in decimal mode +029d : d8 cld ; binary mode +029e : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +02a0 : a500 lda N1 +02a2 : e501 sbc N2 +02a4 : 8502 sta HA ; accumulator result of N1-N2 using binary arithmetic + +02a6 : 08 php +02a7 : 68 pla +02a8 : 8503 sta HNVZC ; flags result of N1-N2 using binary arithmetic +02aa : 60 rts + + if cputype != 1 + ; Calculate the predicted SBC accumulator result for the 6502 and 65816 + ; +02ab : c001 SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +02ad : a50c lda N1L +02af : e50e sbc N2L +02b1 : a200 ldx #0 +02b3 : b006 bcs S11 +02b5 : e8 inx +02b6 : e905 sbc #5 ; subtract 6 (carry is clear) +02b8 : 290f and #$0F +02ba : 18 clc +02bb : 050d S11 ora N1H + ; + ; if N1L - N2L >= 0, then subtract N2 & $F0 + ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) + ; +02bd : f50f sbc N2H,x +02bf : b002 bcs S12 +02c1 : e95f sbc #$5F ; subtract $60 (carry is clear) +02c3 : 8506 S12 sta AR +02c5 : 60 rts + endif + + if cputype = 1 + ; Calculate the predicted SBC accumulator result for the 6502 and 65C02 + ; + SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1L + sbc N2L + ldx #0 + bcs S21 + inx + and #$0F + clc + S21 ora N1H + ; + ; if N1L - N2L >= 0, then subtract N2 & $F0 + ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) + ; + sbc N2H,x + bcs S22 + sbc #$5F ; subtract $60 (carry is clear) + S22 cpx #0 + beq S23 + sbc #6 + S23 sta AR ; predicted accumulator result + rts + endif + + ; Compare accumulator actual results to predicted results + ; + ; Return: + ; Z flag = 1 (BEQ branch) if same + ; Z flag = 0 (BNE branch) if different + ; +02c6 : COMPARE + if chk_a = 1 +02c6 : a504 lda DA +02c8 : c506 cmp AR +02ca : d01e bne C1 + endif + if chk_n = 1 +02cc : a505 lda DNVZC ; [7] see text +02ce : 4507 eor NF +02d0 : 2980 and #$80 ; mask off N flag +02d2 : d016 bne C1 + endif + if chk_v = 1 +02d4 : a505 lda DNVZC ; [8] see text +02d6 : 4508 eor VF +02d8 : 2940 and #$40 ; mask off V flag +02da : d00e bne C1 ; [9] see text + endif + if chk_z = 1 +02dc : a505 lda DNVZC +02de : 4509 eor ZF ; mask off Z flag +02e0 : 2902 and #2 +02e2 : d006 bne C1 ; [10] see text + endif + if chk_c = 1 +02e4 : a505 lda DNVZC +02e6 : 450a eor CF +02e8 : 2901 and #1 ; mask off C flag + endif +02ea : 60 C1 rts + + ; These routines store the predicted values for ADC and SBC for the 6502, + ; 65C02, and 65816 in AR, CF, NF, VF, and ZF + + if cputype = 0 + +02eb : a508 A6502 lda VF ; 6502 + ; + ; since all 8 bits of the P register were stored in VF, bit 7 of VF contains + ; the N flag for NF + ; +02ed : 8507 sta NF +02ef : a503 lda HNVZC +02f1 : 8509 sta ZF +02f3 : 60 rts + +02f4 : 20ab02 S6502 jsr SUB1 +02f7 : a503 lda HNVZC +02f9 : 8507 sta NF +02fb : 8508 sta VF +02fd : 8509 sta ZF +02ff : 850a sta CF +0301 : 60 rts + + endif + if cputype = 1 + + A6502 lda AR ; 65C02 + php + pla + sta NF + sta ZF + rts + + S6502 jsr SUB2 + lda AR + php + pla + sta NF + sta ZF + lda HNVZC + sta VF + sta CF + rts + + endif + if cputype = 2 + + A6502 lda AR ; 65C816 + php + pla + sta NF + sta ZF + rts + + S6502 jsr SUB1 + lda AR + php + pla + sta NF + sta ZF + lda HNVZC + sta VF + sta CF + rts + + endif + +02f4 = end TEST + +No errors in pass 2. +Wrote binary from address $0200 through $0301. +Total size 258 bytes. +Program start address is at $0200 (512). + \ No newline at end of file diff --git a/py65/tests/devices/bcd/65C02_decimal_test.a65 b/py65/tests/devices/bcd/65C02_decimal_test.a65 new file mode 100644 index 0000000..37dfa4b --- /dev/null +++ b/py65/tests/devices/bcd/65C02_decimal_test.a65 @@ -0,0 +1,355 @@ +; Verify decimal mode behavior +; Written by Bruce Clark. This code is public domain. +; see http://www.6502.org/tutorials/decimal_mode.html +; +; Returns: +; ERROR = 0 if the test passed +; ERROR = 1 if the test failed +; modify the code at the DONE label for desired program end +; +; This routine requires 17 bytes of RAM -- 1 byte each for: +; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF +; and 2 bytes for N2H +; +; Variables: +; N1 and N2 are the two numbers to be added or subtracted +; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2 +; DA and DNVZC are the actual accumulator and flag results in decimal mode +; HA and HNVZC are the accumulator and flag results when N1 and N2 are +; added or subtracted using binary arithmetic +; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and +; flag results, calculated using binary arithmetic +; +; This program takes approximately 1 minute at 1 MHz (a few seconds more on +; a 65C02 than a 6502 or 65816) +; + +; Configuration: +cputype = 1 ; 0 = 6502, 1 = 65C02, 2 = 65C816 +vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only +chk_a = 1 ; check accumulator +chk_n = 1 ; check sign (negative) flag +chk_v = 1 ; check overflow flag +chk_z = 1 ; check zero flag +chk_c = 1 ; check carry flag + +end_of_test macro + db $db ;execute 65C02 stop instruction + endm + + bss + org 0 +; operands - register Y = carry in +N1 ds 1 +N2 ds 1 +; binary result +HA ds 1 +HNVZC ds 1 + ;04 +; decimal result +DA ds 1 +DNVZC ds 1 +; predicted results +AR ds 1 +NF ds 1 + ;08 +VF ds 1 +ZF ds 1 +CF ds 1 +ERROR ds 1 + ;0C +; workspace +N1L ds 1 +N1H ds 1 +N2L ds 1 +N2H ds 2 + + code + org $200 +TEST ldy #1 ; initialize Y (used to loop through carry flag values) + sty ERROR ; store 1 in ERROR until the test passes + lda #0 ; initialize N1 and N2 + sta N1 + sta N2 +LOOP1 lda N2 ; N2L = N2 & $0F + and #$0F ; [1] see text + if vld_bcd = 1 + cmp #$0a + bcs NEXT2 + endif + sta N2L + lda N2 ; N2H = N2 & $F0 + and #$F0 ; [2] see text + if vld_bcd = 1 + cmp #$a0 + bcs NEXT2 + endif + sta N2H + ora #$0F ; N2H+1 = (N2 & $F0) + $0F + sta N2H+1 +LOOP2 lda N1 ; N1L = N1 & $0F + and #$0F ; [3] see text + if vld_bcd = 1 + cmp #$0a + bcs NEXT1 + endif + sta N1L + lda N1 ; N1H = N1 & $F0 + and #$F0 ; [4] see text + if vld_bcd = 1 + cmp #$a0 + bcs NEXT1 + endif + sta N1H + jsr ADD + jsr A6502 + jsr COMPARE + bne * + jsr SUB + jsr S6502 + jsr COMPARE + bne * +NEXT1 inc N1 ; [5] see text + bne LOOP2 ; loop through all 256 values of N1 +NEXT2 inc N2 ; [6] see text + bne LOOP1 ; loop through all 256 values of N2 + dey + bpl LOOP1 ; loop through both values of the carry flag + lda #0 ; test passed, so store 0 in ERROR + sta ERROR +DONE + end_of_test + +; Calculate the actual decimal mode accumulator and flags, the accumulator +; and flag results when N1 is added to N2 using binary arithmetic, the +; predicted accumulator result, the predicted carry flag, and the predicted +; V flag +; +ADD sed ; decimal mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + adc N2 + sta DA ; actual accumulator result in decimal mode + php + pla + sta DNVZC ; actual flags result in decimal mode + cld ; binary mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + adc N2 + sta HA ; accumulator result of N1+N2 using binary arithmetic + + php + pla + sta HNVZC ; flags result of N1+N2 using binary arithmetic + cpy #1 + lda N1L + adc N2L + cmp #$0A + ldx #0 + bcc A1 + inx + adc #5 ; add 6 (carry is set) + and #$0F + sec +A1 ora N1H +; +; if N1L + N2L < $0A, then add N2 & $F0 +; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set) +; + adc N2H,x + php + bcs A2 + cmp #$A0 + bcc A3 +A2 adc #$5F ; add $60 (carry is set) + sec +A3 sta AR ; predicted accumulator result + php + pla + sta CF ; predicted carry result + pla +; +; note that all 8 bits of the P register are stored in VF +; + sta VF ; predicted V flags + rts + +; Calculate the actual decimal mode accumulator and flags, and the +; accumulator and flag results when N2 is subtracted from N1 using binary +; arithmetic +; +SUB sed ; decimal mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + sbc N2 + sta DA ; actual accumulator result in decimal mode + php + pla + sta DNVZC ; actual flags result in decimal mode + cld ; binary mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + sbc N2 + sta HA ; accumulator result of N1-N2 using binary arithmetic + + php + pla + sta HNVZC ; flags result of N1-N2 using binary arithmetic + rts + + if cputype != 1 +; Calculate the predicted SBC accumulator result for the 6502 and 65816 +; +SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1L + sbc N2L + ldx #0 + bcs S11 + inx + sbc #5 ; subtract 6 (carry is clear) + and #$0F + clc +S11 ora N1H +; +; if N1L - N2L >= 0, then subtract N2 & $F0 +; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) +; + sbc N2H,x + bcs S12 + sbc #$5F ; subtract $60 (carry is clear) +S12 sta AR + rts + endif + + if cputype = 1 +; Calculate the predicted SBC accumulator result for the 6502 and 65C02 +; +SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1L + sbc N2L + ldx #0 + bcs S21 + inx + and #$0F + clc +S21 ora N1H +; +; if N1L - N2L >= 0, then subtract N2 & $F0 +; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) +; + sbc N2H,x + bcs S22 + sbc #$5F ; subtract $60 (carry is clear) +S22 cpx #0 + beq S23 + sbc #6 +S23 sta AR ; predicted accumulator result + rts + endif + +; Compare accumulator actual results to predicted results +; +; Return: +; Z flag = 1 (BEQ branch) if same +; Z flag = 0 (BNE branch) if different +; +COMPARE + if chk_a = 1 + lda DA + cmp AR + bne C1 + endif + if chk_n = 1 + lda DNVZC ; [7] see text + eor NF + and #$80 ; mask off N flag + bne C1 + endif + if chk_v = 1 + lda DNVZC ; [8] see text + eor VF + and #$40 ; mask off V flag + bne C1 ; [9] see text + endif + if chk_z = 1 + lda DNVZC + eor ZF ; mask off Z flag + and #2 + bne C1 ; [10] see text + endif + if chk_c = 1 + lda DNVZC + eor CF + and #1 ; mask off C flag + endif +C1 rts + +; These routines store the predicted values for ADC and SBC for the 6502, +; 65C02, and 65816 in AR, CF, NF, VF, and ZF + + if cputype = 0 + +A6502 lda VF ; 6502 +; +; since all 8 bits of the P register were stored in VF, bit 7 of VF contains +; the N flag for NF +; + sta NF + lda HNVZC + sta ZF + rts + +S6502 jsr SUB1 + lda HNVZC + sta NF + sta VF + sta ZF + sta CF + rts + + endif + if cputype = 1 + +A6502 lda AR ; 65C02 + php + pla + sta NF + sta ZF + rts + +S6502 jsr SUB2 + lda AR + php + pla + sta NF + sta ZF + lda HNVZC + sta VF + sta CF + rts + + endif + if cputype = 2 + +A6502 lda AR ; 65C816 + php + pla + sta NF + sta ZF + rts + +S6502 jsr SUB1 + lda AR + php + pla + sta NF + sta ZF + lda HNVZC + sta VF + sta CF + rts + + endif + + end TEST diff --git a/py65/tests/devices/bcd/65C02_decimal_test.bin b/py65/tests/devices/bcd/65C02_decimal_test.bin new file mode 100644 index 0000000000000000000000000000000000000000..e4b80b1d1d384e971216bbb3651edbfbaec6d6e5 GIT binary patch literal 266 zcmYj~v1$TA6h&v=?u@gmyJHnn$+YtaTc)wgKRAt`A7Gy%4^r+Yh%iO4*-sF}z^0lq zm?n*)RkjhV#HWd1bMHOyIB&{d+wTk+a?h>CoU%ckXzY4M+@WkRGtXdt!F)nEnG($L zZ*EywZ+ZB5>n=&r{yr|aXG4y$8lm|52Ox-OcZPYL&R8jTV_|h6vWZqzTu;krJSb4o zRXRfDu8z&UgW&3L7s)1{u`1qK{V1}7u3$g-VGZmhO6TL13_UOUE&CE4sFi&RIdx(o WNb1yb=P!?1m^d?s+x8JEOx+nFSy^!a literal 0 HcmV?d00001 diff --git a/py65/tests/devices/bcd/65C02_decimal_test.lst b/py65/tests/devices/bcd/65C02_decimal_test.lst new file mode 100644 index 0000000..352ee3b --- /dev/null +++ b/py65/tests/devices/bcd/65C02_decimal_test.lst @@ -0,0 +1,367 @@ +AS65 Assembler for R6502 [1.42]. Copyright 1994-2007, Frank A. Kingswood Page 1 +----------------------------------------------------- 65C02_decimal_test.a65 ----------------------------------------------------- + +355 lines read, no errors in pass 1. + ; Verify decimal mode behavior + ; Written by Bruce Clark. This code is public domain. + ; see http://www.6502.org/tutorials/decimal_mode.html + ; + ; Returns: + ; ERROR = 0 if the test passed + ; ERROR = 1 if the test failed + ; modify the code at the DONE label for desired program end + ; + ; This routine requires 17 bytes of RAM -- 1 byte each for: + ; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF + ; and 2 bytes for N2H + ; + ; Variables: + ; N1 and N2 are the two numbers to be added or subtracted + ; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2 + ; DA and DNVZC are the actual accumulator and flag results in decimal mode + ; HA and HNVZC are the accumulator and flag results when N1 and N2 are + ; added or subtracted using binary arithmetic + ; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and + ; flag results, calculated using binary arithmetic + ; + ; This program takes approximately 1 minute at 1 MHz (a few seconds more on + ; a 65C02 than a 6502 or 65816) + ; + + ; Configuration: +0001 = cputype = 1 ; 0 = 6502, 1 = 65C02, 2 = 65C816 +0000 = vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only +0001 = chk_a = 1 ; check accumulator +0001 = chk_n = 1 ; check sign (negative) flag +0001 = chk_v = 1 ; check overflow flag +0001 = chk_z = 1 ; check zero flag +0001 = chk_c = 1 ; check carry flag + + end_of_test macro + db $db ;execute 65C02 stop instruction + endm + + bss +0000 = org 0 + ; operands - register Y = carry in +0000 = N1 ds 1 +0001 = N2 ds 1 + ; binary result +0002 = HA ds 1 +0003 = HNVZC ds 1 + ;04 + ; decimal result +0004 = DA ds 1 +0005 = DNVZC ds 1 + ; predicted results +0006 = AR ds 1 +0007 = NF ds 1 + ;08 +0008 = VF ds 1 +0009 = ZF ds 1 +000a = CF ds 1 +000b = ERROR ds 1 + ;0C + ; workspace +000c = N1L ds 1 +000d = N1H ds 1 +000e = N2L ds 1 +000f = N2H ds 2 + + code +0200 = org $200 +0200 : a001 TEST ldy #1 ; initialize Y (used to loop through carry flag values) +0202 : 840b sty ERROR ; store 1 in ERROR until the test passes +0204 : a900 lda #0 ; initialize N1 and N2 +0206 : 8500 sta N1 +0208 : 8501 sta N2 +020a : a501 LOOP1 lda N2 ; N2L = N2 & $0F +020c : 290f and #$0F ; [1] see text + if vld_bcd = 1 + cmp #$0a + bcs NEXT2 + endif +020e : 850e sta N2L +0210 : a501 lda N2 ; N2H = N2 & $F0 +0212 : 29f0 and #$F0 ; [2] see text + if vld_bcd = 1 + cmp #$a0 + bcs NEXT2 + endif +0214 : 850f sta N2H +0216 : 090f ora #$0F ; N2H+1 = (N2 & $F0) + $0F +0218 : 8510 sta N2H+1 +021a : a500 LOOP2 lda N1 ; N1L = N1 & $0F +021c : 290f and #$0F ; [3] see text + if vld_bcd = 1 + cmp #$0a + bcs NEXT1 + endif +021e : 850c sta N1L +0220 : a500 lda N1 ; N1H = N1 & $F0 +0222 : 29f0 and #$F0 ; [4] see text + if vld_bcd = 1 + cmp #$a0 + bcs NEXT1 + endif +0224 : 850d sta N1H +0226 : 204c02 jsr ADD +0229 : 20ef02 jsr A6502 +022c : 20ca02 jsr COMPARE +022f : d0fe bne * +0231 : 209002 jsr SUB +0234 : 20f802 jsr S6502 +0237 : 20ca02 jsr COMPARE +023a : d0fe bne * +023c : e600 NEXT1 inc N1 ; [5] see text +023e : d0da bne LOOP2 ; loop through all 256 values of N1 +0240 : e601 NEXT2 inc N2 ; [6] see text +0242 : d0c6 bne LOOP1 ; loop through all 256 values of N2 +0244 : 88 dey +0245 : 10c3 bpl LOOP1 ; loop through both values of the carry flag +0247 : a900 lda #0 ; test passed, so store 0 in ERROR +0249 : 850b sta ERROR +024b : DONE + end_of_test +024b : db > db $db ;execute 65C02 stop instruction + + + ; Calculate the actual decimal mode accumulator and flags, the accumulator + ; and flag results when N1 is added to N2 using binary arithmetic, the + ; predicted accumulator result, the predicted carry flag, and the predicted + ; V flag + ; +024c : f8 ADD sed ; decimal mode +024d : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +024f : a500 lda N1 +0251 : 6501 adc N2 +0253 : 8504 sta DA ; actual accumulator result in decimal mode +0255 : 08 php +0256 : 68 pla +0257 : 8505 sta DNVZC ; actual flags result in decimal mode +0259 : d8 cld ; binary mode +025a : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +025c : a500 lda N1 +025e : 6501 adc N2 +0260 : 8502 sta HA ; accumulator result of N1+N2 using binary arithmetic + +0262 : 08 php +0263 : 68 pla +0264 : 8503 sta HNVZC ; flags result of N1+N2 using binary arithmetic +0266 : c001 cpy #1 +0268 : a50c lda N1L +026a : 650e adc N2L +026c : c90a cmp #$0A +026e : a200 ldx #0 +0270 : 9006 bcc A1 +0272 : e8 inx +0273 : 6905 adc #5 ; add 6 (carry is set) +0275 : 290f and #$0F +0277 : 38 sec +0278 : 050d A1 ora N1H + ; + ; if N1L + N2L < $0A, then add N2 & $F0 + ; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set) + ; +027a : 750f adc N2H,x +027c : 08 php +027d : b004 bcs A2 +027f : c9a0 cmp #$A0 +0281 : 9003 bcc A3 +0283 : 695f A2 adc #$5F ; add $60 (carry is set) +0285 : 38 sec +0286 : 8506 A3 sta AR ; predicted accumulator result +0288 : 08 php +0289 : 68 pla +028a : 850a sta CF ; predicted carry result +028c : 68 pla + ; + ; note that all 8 bits of the P register are stored in VF + ; +028d : 8508 sta VF ; predicted V flags +028f : 60 rts + + ; Calculate the actual decimal mode accumulator and flags, and the + ; accumulator and flag results when N2 is subtracted from N1 using binary + ; arithmetic + ; +0290 : f8 SUB sed ; decimal mode +0291 : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +0293 : a500 lda N1 +0295 : e501 sbc N2 +0297 : 8504 sta DA ; actual accumulator result in decimal mode +0299 : 08 php +029a : 68 pla +029b : 8505 sta DNVZC ; actual flags result in decimal mode +029d : d8 cld ; binary mode +029e : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +02a0 : a500 lda N1 +02a2 : e501 sbc N2 +02a4 : 8502 sta HA ; accumulator result of N1-N2 using binary arithmetic + +02a6 : 08 php +02a7 : 68 pla +02a8 : 8503 sta HNVZC ; flags result of N1-N2 using binary arithmetic +02aa : 60 rts + + if cputype != 1 + ; Calculate the predicted SBC accumulator result for the 6502 and 65816 + ; + SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1L + sbc N2L + ldx #0 + bcs S11 + inx + sbc #5 ; subtract 6 (carry is clear) + and #$0F + clc + S11 ora N1H + ; + ; if N1L - N2L >= 0, then subtract N2 & $F0 + ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) + ; + sbc N2H,x + bcs S12 + sbc #$5F ; subtract $60 (carry is clear) + S12 sta AR + rts + endif + + if cputype = 1 + ; Calculate the predicted SBC accumulator result for the 6502 and 65C02 + ; +02ab : c001 SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +02ad : a50c lda N1L +02af : e50e sbc N2L +02b1 : a200 ldx #0 +02b3 : b004 bcs S21 +02b5 : e8 inx +02b6 : 290f and #$0F +02b8 : 18 clc +02b9 : 050d S21 ora N1H + ; + ; if N1L - N2L >= 0, then subtract N2 & $F0 + ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) + ; +02bb : f50f sbc N2H,x +02bd : b002 bcs S22 +02bf : e95f sbc #$5F ; subtract $60 (carry is clear) +02c1 : e000 S22 cpx #0 +02c3 : f002 beq S23 +02c5 : e906 sbc #6 +02c7 : 8506 S23 sta AR ; predicted accumulator result +02c9 : 60 rts + endif + + ; Compare accumulator actual results to predicted results + ; + ; Return: + ; Z flag = 1 (BEQ branch) if same + ; Z flag = 0 (BNE branch) if different + ; +02ca : COMPARE + if chk_a = 1 +02ca : a504 lda DA +02cc : c506 cmp AR +02ce : d01e bne C1 + endif + if chk_n = 1 +02d0 : a505 lda DNVZC ; [7] see text +02d2 : 4507 eor NF +02d4 : 2980 and #$80 ; mask off N flag +02d6 : d016 bne C1 + endif + if chk_v = 1 +02d8 : a505 lda DNVZC ; [8] see text +02da : 4508 eor VF +02dc : 2940 and #$40 ; mask off V flag +02de : d00e bne C1 ; [9] see text + endif + if chk_z = 1 +02e0 : a505 lda DNVZC +02e2 : 4509 eor ZF ; mask off Z flag +02e4 : 2902 and #2 +02e6 : d006 bne C1 ; [10] see text + endif + if chk_c = 1 +02e8 : a505 lda DNVZC +02ea : 450a eor CF +02ec : 2901 and #1 ; mask off C flag + endif +02ee : 60 C1 rts + + ; These routines store the predicted values for ADC and SBC for the 6502, + ; 65C02, and 65816 in AR, CF, NF, VF, and ZF + + if cputype = 0 + + A6502 lda VF ; 6502 + ; + ; since all 8 bits of the P register were stored in VF, bit 7 of VF contains + ; the N flag for NF + ; + sta NF + lda HNVZC + sta ZF + rts + + S6502 jsr SUB1 + lda HNVZC + sta NF + sta VF + sta ZF + sta CF + rts + + endif + if cputype = 1 + +02ef : a506 A6502 lda AR ; 65C02 +02f1 : 08 php +02f2 : 68 pla +02f3 : 8507 sta NF +02f5 : 8509 sta ZF +02f7 : 60 rts + +02f8 : 20ab02 S6502 jsr SUB2 +02fb : a506 lda AR +02fd : 08 php +02fe : 68 pla +02ff : 8507 sta NF +0301 : 8509 sta ZF +0303 : a503 lda HNVZC +0305 : 8508 sta VF +0307 : 850a sta CF +0309 : 60 rts + + endif + if cputype = 2 + + A6502 lda AR ; 65C816 + php + pla + sta NF + sta ZF + rts + + S6502 jsr SUB1 + lda AR + php + pla + sta NF + sta ZF + lda HNVZC + sta VF + sta CF + rts + + endif + +02f8 = end TEST + +No errors in pass 2. +Wrote binary from address $0200 through $0309. +Total size 266 bytes. +Program start address is at $0200 (512). + \ No newline at end of file diff --git a/py65/tests/devices/test_binary_object.py b/py65/tests/devices/test_binary_object.py new file mode 100644 index 0000000..de871e9 --- /dev/null +++ b/py65/tests/devices/test_binary_object.py @@ -0,0 +1,96 @@ +import unittest +import sys +import py65.devices.mpu65c02 + + +class BinaryObjectTests(unittest.TestCase): + """Test cases based on executing 65x02 object code.""" + + def binaryObjectTestCase(self, filename, load_addr, pc, success_addr, should_trace=None): + mpu = self._make_mpu() + mpu.pc = pc + + object_code = bytearray(open(filename, "r").read()) + self._write(mpu.memory, load_addr, object_code) + + if not should_trace: + should_trace = lambda pc: False + + while True: + old_pc = mpu.pc + mpu.step(trace=should_trace(mpu.pc)) + if mpu.pc == old_pc: + break + + assert mpu.pc == success_addr, "%s 0xb=%02x 0xc=%02x 0xd=%02x 0xf=%02x" % ( + mpu, mpu.memory[0xb], mpu.memory[0xc], mpu.memory[0xd], mpu.memory[0xf]) + + # Test Helpers + + def _write(self, memory, start_address, bytes): + memory[start_address:start_address + len(bytes)] = bytes + + def _make_mpu(self, *args, **kargs): + klass = self._get_target_class() + mpu = klass(*args, **kargs) + if 'memory' not in kargs: + mpu.memory = 0x10000 * [0xAA] + return mpu + + # XXX common test case + def decimalTest(self, filename): + try: + return self._decimalTest(filename) + except AssertionError: + # Rerun with tracing + return self._decimalTest(filename, trace=True) + + def _decimalTest(self, filename, trace=False): + mpu = self._make_mpu() + mpu.pc = 0x1000 + + object_code = bytearray(open(filename, "r").read()) + self._write(mpu.memory, 0x200, object_code) + + # $1000: JSR $0200 + self._write(mpu.memory, 0x1000, [0x20, 0x00, 0x02]) + + should_trace = None + if not should_trace: + should_trace = lambda pc: trace + + while True: + old_pc = mpu.pc + mpu.step(trace=should_trace(mpu.pc)) + # If we are looping at the same PC, or we return + # from the JSR, then we are done. + if mpu.pc == old_pc or mpu.pc == 0x1003: + break + + assert mpu.memory[0x0b] == 0 + + +class Functional6502Tests(BinaryObjectTests): + + def test6502DecimalTest(self): + self._decimalTest("devices/bcd/6502_decimal_test.bin") + + def _get_target_class(self): + return py65.devices.mpu6502.MPU + + +class Functional65C02Tests(BinaryObjectTests): + + def test65C02DecimalTest(self): + self._decimalTest("devices/bcd/65C02_decimal_test.bin") + + def _get_target_class(self): + return py65.devices.mpu65c02.MPU + + +def test_suite(): + return unittest.findTestCases(sys.modules[__name__]) + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') From 9b5e1f394518b4cf136427fcbf6af5af55a76b0c Mon Sep 17 00:00:00 2001 From: kris Date: Sat, 22 Jul 2017 23:42:29 +0100 Subject: [PATCH 06/18] Move two BCD test cases from the common 65x02 test cases to 6502-specific. The handling of invalid BCD arguments differs between 6502 and 65C02 and these are testing 6502-specific behaviour. --- py65/tests/devices/test_mpu6502.py | 70 +++++++++++++++--------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/py65/tests/devices/test_mpu6502.py b/py65/tests/devices/test_mpu6502.py index 235adea..d83aab5 100644 --- a/py65/tests/devices/test_mpu6502.py +++ b/py65/tests/devices/test_mpu6502.py @@ -391,26 +391,6 @@ class Common6502Tests: self.assertEqual(0, mpu.p & mpu.ZERO) self.assertEqual(0, mpu.p & mpu.CARRY) - def test_adc_bcd_on_immediate_9c_plus_9d(self): - mpu = self._make_mpu() - mpu.p |= mpu.DECIMAL - mpu.p &= ~(mpu.CARRY) - mpu.a = 0x9c - # $0000 ADC #$9d - # $0002 ADC #$9d - self._write(mpu.memory, 0x0000, (0x69, 0x9d)) - self._write(mpu.memory, 0x0002, (0x69, 0x9d)) - mpu.step() - self.assertEqual(0x9f, mpu.a) - self.assertEqual(mpu.CARRY, mpu.p & mpu.CARRY) - mpu.step() - self.assertEqual(0x0004, mpu.pc) - self.assertEqual(0x93, mpu.a) - self.assertEqual(0, mpu.p & mpu.NEGATIVE) - self.assertEqual(mpu.OVERFLOW, mpu.p & mpu.OVERFLOW) - self.assertEqual(0, mpu.p & mpu.ZERO) - self.assertEqual(mpu.CARRY, mpu.p & mpu.CARRY) - # ADC Absolute, X-Indexed def test_adc_bcd_off_abs_x_carry_clear_in_accumulator_zeroes(self): @@ -4560,20 +4540,6 @@ class Common6502Tests: self.assertEqual(0, mpu.p & mpu.ZERO) self.assertEqual(0, mpu.p & mpu.CARRY) - def test_sbc_bcd_on_immediate_20_minus_0a_carry_unset(self): - mpu = self._make_mpu() - mpu.p |= mpu.DECIMAL - mpu.a = 0x20 - # $0000 SBC #$00 - self._write(mpu.memory, 0x0000, (0xe9, 0x0a)) - mpu.step() - self.assertEqual(0x0002, mpu.pc) - self.assertEqual(0x1f, mpu.a) - self.assertEqual(0, mpu.p & mpu.NEGATIVE) - self.assertEqual(0, mpu.p & mpu.OVERFLOW) - self.assertEqual(0, mpu.p & mpu.ZERO) - self.assertEqual(mpu.CARRY, mpu.p & mpu.CARRY) - # SBC Absolute, X-Indexed def test_sbc_abs_x_all_zeros_and_no_borrow_is_zero(self): @@ -5873,6 +5839,42 @@ class MPUTests(unittest.TestCase, Common6502Tests): mpu.step() self.assertEqual(0x3f, mpu.a) + # Different undefined BCD behaviour between 6502 and 65C02 + + def test_adc_bcd_on_immediate_9c_plus_9d(self): + mpu = self._make_mpu() + mpu.p |= mpu.DECIMAL + mpu.p &= ~(mpu.CARRY) + mpu.a = 0x9c + # $0000 ADC #$9d + # $0002 ADC #$9d + self._write(mpu.memory, 0x0000, (0x69, 0x9d)) + self._write(mpu.memory, 0x0002, (0x69, 0x9d)) + mpu.step() + self.assertEqual(0x9f, mpu.a) + self.assertEqual(mpu.CARRY, mpu.p & mpu.CARRY) + mpu.step() + self.assertEqual(0x0004, mpu.pc) + self.assertEqual(0x93, mpu.a) + self.assertEqual(0, mpu.p & mpu.NEGATIVE) + self.assertEqual(mpu.OVERFLOW, mpu.p & mpu.OVERFLOW) + self.assertEqual(0, mpu.p & mpu.ZERO) + self.assertEqual(mpu.CARRY, mpu.p & mpu.CARRY) + + def test_sbc_bcd_on_immediate_20_minus_0a_carry_unset(self): + mpu = self._make_mpu() + mpu.p |= mpu.DECIMAL + mpu.a = 0x20 + # $0000 SBC #$00 + self._write(mpu.memory, 0x0000, (0xe9, 0x0a)) + mpu.step() + self.assertEqual(0x0002, mpu.pc) + self.assertEqual(0x1f, mpu.a) + self.assertEqual(0, mpu.p & mpu.NEGATIVE) + self.assertEqual(0, mpu.p & mpu.OVERFLOW) + self.assertEqual(0, mpu.p & mpu.ZERO) + self.assertEqual(mpu.CARRY, mpu.p & mpu.CARRY) + def _get_target_class(self): return py65.devices.mpu6502.MPU From fab1174abb20f4b0dcc3e3bed16702fd13aba06d Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 20 Aug 2019 14:16:10 +0100 Subject: [PATCH 07/18] Revert "Add support for optionally tracing execution" This reverts commit 5e7ade9cb939899980c67990611571920118e278. --- py65/devices/mpu6502.py | 9 ++------- py65/devices/mpu65c02.py | 4 ++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/py65/devices/mpu6502.py b/py65/devices/mpu6502.py index b7954e5..d10c4e4 100644 --- a/py65/devices/mpu6502.py +++ b/py65/devices/mpu6502.py @@ -1,6 +1,6 @@ from py65.utils.conversions import itoa from py65.utils.devices import make_instruction_decorator -from py65 import disassembler + class MPU: # vectors @@ -41,8 +41,6 @@ class MPU: self.memory = memory self.start_pc = pc - self.disassembler = disassembler.Disassembler(self) - # init self.reset() @@ -57,11 +55,8 @@ class MPU: return self.reprformat() % (indent, self.name, self.pc, self.a, self.x, self.y, self.sp, flags) - def step(self, trace=False): + def step(self): instructCode = self.memory[self.pc] - if trace: - print self, "$%04X: %s" % ( - self.pc, self.disassembler.instruction_at(self.pc)[1]) self.pc = (self.pc + 1) & self.addrMask self.excycles = 0 self.addcycles = self.extracycles[instructCode] diff --git a/py65/devices/mpu65c02.py b/py65/devices/mpu65c02.py index 2ac5a9d..ef37f74 100644 --- a/py65/devices/mpu65c02.py +++ b/py65/devices/mpu65c02.py @@ -8,11 +8,11 @@ class MPU(mpu6502.MPU): self.name = '65C02' self.waiting = False - def step(self, trace=False): + def step(self): if self.waiting: self.processorCycles += 1 else: - mpu6502.MPU.step(self, trace) + mpu6502.MPU.step(self) return self # Make copies of the lists From 8b9cf7db69eabcbd3094f26f51827dff6bdbb62b Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 20 Aug 2019 14:22:57 +0100 Subject: [PATCH 08/18] Revert "Revert "Add support for optionally tracing execution"" This reverts commit fab1174abb20f4b0dcc3e3bed16702fd13aba06d. --- py65/devices/mpu6502.py | 9 +++++++-- py65/devices/mpu65c02.py | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/py65/devices/mpu6502.py b/py65/devices/mpu6502.py index d10c4e4..b7954e5 100644 --- a/py65/devices/mpu6502.py +++ b/py65/devices/mpu6502.py @@ -1,6 +1,6 @@ from py65.utils.conversions import itoa from py65.utils.devices import make_instruction_decorator - +from py65 import disassembler class MPU: # vectors @@ -41,6 +41,8 @@ class MPU: self.memory = memory self.start_pc = pc + self.disassembler = disassembler.Disassembler(self) + # init self.reset() @@ -55,8 +57,11 @@ class MPU: return self.reprformat() % (indent, self.name, self.pc, self.a, self.x, self.y, self.sp, flags) - def step(self): + def step(self, trace=False): instructCode = self.memory[self.pc] + if trace: + print self, "$%04X: %s" % ( + self.pc, self.disassembler.instruction_at(self.pc)[1]) self.pc = (self.pc + 1) & self.addrMask self.excycles = 0 self.addcycles = self.extracycles[instructCode] diff --git a/py65/devices/mpu65c02.py b/py65/devices/mpu65c02.py index ef37f74..2ac5a9d 100644 --- a/py65/devices/mpu65c02.py +++ b/py65/devices/mpu65c02.py @@ -8,11 +8,11 @@ class MPU(mpu6502.MPU): self.name = '65C02' self.waiting = False - def step(self): + def step(self, trace=False): if self.waiting: self.processorCycles += 1 else: - mpu6502.MPU.step(self) + mpu6502.MPU.step(self, trace) return self # Make copies of the lists From 254b2fb1677cd317a86a1ddf0549887be28de7a9 Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 20 Aug 2019 14:59:43 +0100 Subject: [PATCH 09/18] Test cases pass except for devices/65C02_extended_opcodes_test_modified.bin --- py65/devices/mpu6502.py | 140 ++++++++++++++++++------------- py65/devices/mpu65c02.py | 71 ++++++++++++++++ py65/tests/devices/test_klaus.py | 24 ++---- 3 files changed, 160 insertions(+), 75 deletions(-) diff --git a/py65/devices/mpu6502.py b/py65/devices/mpu6502.py index b7954e5..b14ffaf 100644 --- a/py65/devices/mpu6502.py +++ b/py65/devices/mpu6502.py @@ -1,3 +1,5 @@ +import sys + from py65.utils.conversions import itoa from py65.utils.devices import make_instruction_decorator from py65 import disassembler @@ -60,8 +62,9 @@ class MPU: def step(self, trace=False): instructCode = self.memory[self.pc] if trace: - print self, "$%04X: %s" % ( + out = str(self) + " $%04X: %s" % ( self.pc, self.disassembler.instruction_at(self.pc)[1]) + print >> sys.stderr, "\n".join(out.split('\n')) self.pc = (self.pc + 1) & self.addrMask self.excycles = 0 self.addcycles = self.extracycles[instructCode] @@ -296,6 +299,9 @@ class MPU: self.FlagsNZ(self.a) def opADC(self, x): + return self._opADC(x, flags_use_adjusted_result=False) + + def _opADC(self, x, flags_use_adjusted_result): data = self.ByteAt(x()) if self.p & self.DECIMAL: @@ -315,24 +321,35 @@ class MPU: # the ALU outputs are not decimally adjusted nibble0 = nibble0 & 0xf nibble1 = nibble1 & 0xf + aluresult = (nibble1 << 4) + nibble0 # the final A contents will be decimally adjusted nibble0 = (nibble0 + adjust0) & 0xf nibble1 = (nibble1 + adjust1) & 0xf + adjresult = (nibble1 << 4) + nibble0 - # Update result for use in setting flags below - aluresult = (nibble1 << 4) + nibble0 - self.p &= ~(self.CARRY | self.OVERFLOW | self.NEGATIVE | self.ZERO) - if aluresult == 0: - self.p |= self.ZERO + + if flags_use_adjusted_result: # 65C02 and 65816 + if adjresult == 0: + self.p |= self.ZERO + else: + self.p |= adjresult & self.NEGATIVE + if decimalcarry == 1: + self.p |= self.CARRY + if (~(self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE: + self.p |= self.OVERFLOW else: - self.p |= aluresult & self.NEGATIVE - if decimalcarry == 1: - self.p |= self.CARRY - if (~(self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE: - self.p |= self.OVERFLOW - self.a = (nibble1 << 4) + nibble0 + if aluresult == 0: + self.p |= self.ZERO + else: + self.p |= aluresult & self.NEGATIVE + if decimalcarry == 1: + self.p |= self.CARRY + if (~(self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE: + self.p |= self.OVERFLOW + + self.a = adjresult else: if self.p & self.CARRY: tmp = 1 @@ -395,59 +412,70 @@ class MPU: self.p |= (register_value - tbyte) & self.NEGATIVE def opSBC(self, x): + if self.p & self.DECIMAL: + self._opSBCDecimal(x) + return + data = self.ByteAt(x()) - if self.p & self.DECIMAL: - halfcarry = 1 - decimalcarry = 0 - adjust0 = 0 - adjust1 = 0 + result = self.a + (~data & self.byteMask) + (self.p & self.CARRY) + self.p &= ~(self.CARRY | self.ZERO | self.OVERFLOW | self.NEGATIVE) + if ((self.a ^ data) & (self.a ^ result)) & self.NEGATIVE: + self.p |= self.OVERFLOW + data = result & self.byteMask + if data == 0: + self.p |= self.ZERO + if result > self.byteMask: + self.p |= self.CARRY + self.p |= data & self.NEGATIVE + self.a = data - nibble0 = (self.a & 0xf) + (~data & 0xf) + (self.p & self.CARRY) - if nibble0 <= 0xf: - halfcarry = 0 - adjust0 = 10 - nibble1 = ((self.a >> 4) & 0xf) + ((~data >> 4) & 0xf) + halfcarry - if nibble1 <= 0xf: - adjust1 = 10 << 4 + def _opSBCDecimal(self, x): + """SBC opcode in BCD mode. - # the ALU outputs are not decimally adjusted - aluresult = self.a + (~data & self.byteMask) + \ - (self.p & self.CARRY) + This is sufficiently different on 6502 and 65C02 to warrant a separate + implementation, see e.g. http://6502.org/tutorials/decimal_mode.html#A + """ + data = self.ByteAt(x()) - if aluresult > self.byteMask: - decimalcarry = 1 - aluresult &= self.byteMask + halfcarry = 1 + decimalcarry = 0 + adjust0 = 0 + adjust1 = 0 - # but the final result will be adjusted - nibble0 = (aluresult + adjust0) & 0xf - nibble1 = ((aluresult + adjust1) >> 4) & 0xf + nibble0 = (self.a & 0xf) + (~data & 0xf) + (self.p & self.CARRY) + if nibble0 <= 0xf: + halfcarry = 0 + adjust0 = 10 + nibble1 = ((self.a >> 4) & 0xf) + ((~data >> 4) & 0xf) + halfcarry + if nibble1 <= 0xf: + adjust1 = 10 << 4 - # Update result for use in setting flags below - aluresult = (nibble1 << 4) + nibble0 + # the ALU outputs are not decimally adjusted + aluresult = self.a + (~data & self.byteMask) + \ + (self.p & self.CARRY) - self.p &= ~(self.CARRY | self.ZERO | self.NEGATIVE | self.OVERFLOW) - if aluresult == 0: - self.p |= self.ZERO - else: - self.p |= aluresult & self.NEGATIVE - if decimalcarry == 1: - self.p |= self.CARRY - if ((self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE: - self.p |= self.OVERFLOW - self.a = (nibble1 << 4) + nibble0 + if aluresult > self.byteMask: + decimalcarry = 1 + aluresult &= self.byteMask + + # but the final result will be adjusted + nibble0 = (aluresult + adjust0) & 0xf + nibble1 = ((aluresult + adjust1) >> 4) & 0xf + + # Update result for use in setting flags below + adjresult = (nibble1 << 4) + nibble0 + + self.p &= ~(self.CARRY | self.ZERO | self.NEGATIVE | self.OVERFLOW) + if aluresult == 0: + self.p |= self.ZERO else: - result = self.a + (~data & self.byteMask) + (self.p & self.CARRY) - self.p &= ~(self.CARRY | self.ZERO | self.OVERFLOW | self.NEGATIVE) - if ((self.a ^ data) & (self.a ^ result)) & self.NEGATIVE: - self.p |= self.OVERFLOW - data = result & self.byteMask - if data == 0: - self.p |= self.ZERO - if result > self.byteMask: - self.p |= self.CARRY - self.p |= data & self.NEGATIVE - self.a = data + self.p |= aluresult & self.NEGATIVE + if decimalcarry == 1: + self.p |= self.CARRY + if ((self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE: + self.p |= self.OVERFLOW + self.a = adjresult def opDECR(self, x): if x is None: diff --git a/py65/devices/mpu65c02.py b/py65/devices/mpu65c02.py index 2ac5a9d..ea6f800 100644 --- a/py65/devices/mpu65c02.py +++ b/py65/devices/mpu65c02.py @@ -63,6 +63,77 @@ class MPU(mpu6502.MPU): self.p |= self.ZERO self.memory[address] = m & ~self.a + def opADC(self, x): + return self._opADC(x, flags_use_adjusted_result=True) + + def _opSBCDecimal(self, x): + """SBC opcode in binary mode (non-BCD)""" + + data = self.ByteAt(x()) + + decimalcarry = 0 + #adjust0 = 0 + #adjust1 = 0 + + #nibble0 = (self.a & 0xf) + (~data & 0xf) + (self.p & self.CARRY) + #if nibble0 <= 0xf: + #halfcarry = 0 + #adjust0 = 10 # 0xa = -0x6 + #nibble1 = ((self.a >> 4) & 0xf) + ((~data >> 4) & 0xf) + halfcarry + #if nibble1 <= 0xf: + #adjust1 = 10 << 4 # -0x60 + + # the ALU outputs are not decimally adjusted + aluresult = self.a + (~data & self.byteMask) + \ + (self.p & self.CARRY) + + if aluresult > self.byteMask: + decimalcarry = 1 + aluresult &= self.byteMask + + #A2 = (self.a + ~data + (self.p & self.CARRY)) + #print "A = %02X, %02X, A2 = %02X, a=%02X, data=%02X, alu=%02X" % (A, (A & self.byteMask), A2, self.a, data, aluresult) + #assert A == A2 + + # but the final result will be adjusted + #nibble0 = (aluresult + adjust0) & 0xf + #nibble1 = ((aluresult + adjust1) >> 4) & 0xf + + # Update result for use in setting flags below + #adjresult = (nibble1 << 4) + nibble0 + + A = self.a - data + (self.p & self.CARRY) - 1 + AL = (self.a & 0xf) - (data & 0xf) + (self.p & self.CARRY) - 1 + assert (A & self.byteMask) == aluresult + + # XXX + if A < 0: + #print A, A2 + #assert adjust1, (A, adjust1) + A -= 0x60 + + if AL < 0: + #assert adjust0, (AL, adjust0) + A -= 0x6 + + #if (A & self.byteMask) != adjresult: + # print "a=%02X data=%02X carry=%02X, res=%02X, adj=%02X" % ( + # self.a, data, self.p & self.CARRY, A & self.byteMask, adjresult) + #assert False + + adjresult = A & self.byteMask + + self.p &= ~(self.CARRY | self.ZERO | self.NEGATIVE | self.OVERFLOW) + if adjresult == 0: + self.p |= self.ZERO + else: + self.p |= adjresult & self.NEGATIVE + if decimalcarry == 1: + self.p |= self.CARRY + if ((self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE: + self.p |= self.OVERFLOW + self.a = adjresult + # instructions @instruction(name="BRK", mode="imp", cycles=7) diff --git a/py65/tests/devices/test_klaus.py b/py65/tests/devices/test_klaus.py index 70f8214..4c7fb82 100644 --- a/py65/tests/devices/test_klaus.py +++ b/py65/tests/devices/test_klaus.py @@ -25,27 +25,13 @@ class KlausDormannTests(unittest.TestCase): mpu, mpu.memory[0xb], mpu.memory[0xc], mpu.memory[0xd], mpu.memory[0xf]) def test6502FunctionalTest(self): - self.klausTestCase("6502_functional_test.bin", 0x0, 0x400, 0x3399) + self.klausTestCase("devices/6502_functional_test.bin", 0x0, 0x400, 0x3399) - def test65C02ExtendedOpcodesTest(self): + # XXX fails + def Xtest65C02ExtendedOpcodesTest(self): tracer = lambda pc: (0x1484 <= pc <= 0x16cc) - self.klausTestCase("65C02_extended_opcodes_test_modified.bin", 0xa, 0x400, 0x24a8, tracer) - - def test6502DecimalTest(self): - mpu = self._make_mpu() - mpu.pc = 0x1000 - - object_code = bytearray(open("6502_decimal_test.bin", "r").read()) - self._write(mpu.memory, 0x200, object_code) - - # $1000: JSR $0200 - self._write(mpu.memory, 0x1000, [0x20, 0x00, 0x02]) - - while True: - mpu.step() - if mpu.pc == 0x1003: - break - assert mpu.memory[0x0b] == 0 + self.klausTestCase("devices/65C02_extended_opcodes_test_modified.bin" + "", 0xa, 0x400, 0x24a8) #, tracer) # Test Helpers From 0e7066a4e149264799cdbda1650e10457afa7741 Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 20 Aug 2019 19:58:31 +0100 Subject: [PATCH 10/18] Clean up code, tests still equally working (but still not yet the klaus 65C02 one) --- py65/devices/mpu6502.py | 119 ++++++++++++++--------- py65/devices/mpu65c02.py | 69 +------------ py65/tests/devices/test_binary_object.py | 16 ++- 3 files changed, 89 insertions(+), 115 deletions(-) diff --git a/py65/devices/mpu6502.py b/py65/devices/mpu6502.py index b14ffaf..0358c7b 100644 --- a/py65/devices/mpu6502.py +++ b/py65/devices/mpu6502.py @@ -318,36 +318,43 @@ class MPU: adjust1 = 6 decimalcarry = 1 - # the ALU outputs are not decimally adjusted + # The ALU outputs are not yet decimally adjusted nibble0 = nibble0 & 0xf nibble1 = nibble1 & 0xf aluresult = (nibble1 << 4) + nibble0 - # the final A contents will be decimally adjusted + # Partial result with only low nibble decimally adjusted nibble0 = (nibble0 + adjust0) & 0xf + halfadjresult = (nibble1 << 4) + nibble0 + + # the final A contents has both nibbles decimally adjusted nibble1 = (nibble1 + adjust1) & 0xf adjresult = (nibble1 << 4) + nibble0 self.p &= ~(self.CARRY | self.OVERFLOW | self.NEGATIVE | self.ZERO) if flags_use_adjusted_result: # 65C02 and 65816 - if adjresult == 0: - self.p |= self.ZERO - else: - self.p |= adjresult & self.NEGATIVE - if decimalcarry == 1: - self.p |= self.CARRY - if (~(self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE: - self.p |= self.OVERFLOW + # Z and N use adjusted (i.e. decimal) result + zerores = adjresult + negativeres = adjresult + else: # 6502 + # Z uses unadjusted (i.e. binary) ALU result + zerores = aluresult + # N effectively uses ALU result with only low nibble + # decimally adjusted - but see here for what is really going on + # https://atariage.com/forums/topic/163876-flags-on-decimal-mode-on-the-nmos-6502/ + negativeres = halfadjresult + + if zerores == 0: + self.p |= self.ZERO else: - if aluresult == 0: - self.p |= self.ZERO - else: - self.p |= aluresult & self.NEGATIVE - if decimalcarry == 1: - self.p |= self.CARRY - if (~(self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE: - self.p |= self.OVERFLOW + self.p |= negativeres & self.NEGATIVE + + if decimalcarry == 1: + self.p |= self.CARRY + + if (~(self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE: + self.p |= self.OVERFLOW self.a = adjresult else: @@ -412,8 +419,11 @@ class MPU: self.p |= (register_value - tbyte) & self.NEGATIVE def opSBC(self, x): + self._opSBC(x, decimal_flags_use_adjusted_result=False) + + def _opSBC(self, x, decimal_flags_use_adjusted_result): if self.p & self.DECIMAL: - self._opSBCDecimal(x) + self._opSBCDecimal(x, decimal_flags_use_adjusted_result) return data = self.ByteAt(x()) @@ -430,47 +440,68 @@ class MPU: self.p |= data & self.NEGATIVE self.a = data - def _opSBCDecimal(self, x): + def _opSBCDecimal(self, x, flags_use_adjusted_result=False): """SBC opcode in BCD mode. - This is sufficiently different on 6502 and 65C02 to warrant a separate - implementation, see e.g. http://6502.org/tutorials/decimal_mode.html#A + See e.g. http://6502.org/tutorials/decimal_mode.html#A for details """ data = self.ByteAt(x()) - - halfcarry = 1 + # + # halfcarry = 1 decimalcarry = 0 - adjust0 = 0 - adjust1 = 0 + # adjust0 = 0 + # adjust1 = 0 + # + # nibble0 = (self.a & 0xf) + (~data & 0xf) + (self.p & self.CARRY) + # if nibble0 <= 0xf: + # halfcarry = 0 + # adjust0 = 10 # -0x6 + # nibble1 = ((self.a >> 4) & 0xf) + ((~data >> 4) & 0xf) + halfcarry + # if nibble1 <= 0xf: + # adjust1 = 10 << 4 # -0x60 - nibble0 = (self.a & 0xf) + (~data & 0xf) + (self.p & self.CARRY) - if nibble0 <= 0xf: - halfcarry = 0 - adjust0 = 10 - nibble1 = ((self.a >> 4) & 0xf) + ((~data >> 4) & 0xf) + halfcarry - if nibble1 <= 0xf: - adjust1 = 10 << 4 - - # the ALU outputs are not decimally adjusted - aluresult = self.a + (~data & self.byteMask) + \ - (self.p & self.CARRY) + # the ALU outputs are not yet decimally adjusted + aluresult = self.a + (~data & self.byteMask) + (self.p & self.CARRY) if aluresult > self.byteMask: decimalcarry = 1 aluresult &= self.byteMask - # but the final result will be adjusted - nibble0 = (aluresult + adjust0) & 0xf - nibble1 = ((aluresult + adjust1) >> 4) & 0xf + AL = (self.a & 0xf) - (data & 0xf) + (self.p & self.CARRY) - 1 - # Update result for use in setting flags below - adjresult = (nibble1 << 4) + nibble0 + if flags_use_adjusted_result: # Note: 65C02 but not 65816 + A = self.a - data + (self.p & self.CARRY) - 1 + assert (A & self.byteMask) == aluresult + + if A < 0: + A -= 0x60 + + if AL < 0: + A -= 0x6 + else: + # Note: 65816 apparently uses this logic instead of 65C02 + if AL < 0: + AL = ((AL - 0x6) & 0xf) - 0x10 + A = (self.a & 0xf0) - (data & 0xf0) + AL + if A < 0: + A -= 0x60 + + adjresult = A & self.byteMask + + if flags_use_adjusted_result: # 65C02 and 65816 + # Z and N use adjusted (i.e. decimal) result + zerores = adjresult + negativeres = adjresult + else: # 6502 + # Z and N uses unadjusted (i.e. binary) ALU result + zerores = aluresult + negativeres = aluresult self.p &= ~(self.CARRY | self.ZERO | self.NEGATIVE | self.OVERFLOW) - if aluresult == 0: + if zerores == 0: self.p |= self.ZERO else: - self.p |= aluresult & self.NEGATIVE + self.p |= negativeres & self.NEGATIVE if decimalcarry == 1: self.p |= self.CARRY if ((self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE: diff --git a/py65/devices/mpu65c02.py b/py65/devices/mpu65c02.py index ea6f800..5993f47 100644 --- a/py65/devices/mpu65c02.py +++ b/py65/devices/mpu65c02.py @@ -66,73 +66,8 @@ class MPU(mpu6502.MPU): def opADC(self, x): return self._opADC(x, flags_use_adjusted_result=True) - def _opSBCDecimal(self, x): - """SBC opcode in binary mode (non-BCD)""" - - data = self.ByteAt(x()) - - decimalcarry = 0 - #adjust0 = 0 - #adjust1 = 0 - - #nibble0 = (self.a & 0xf) + (~data & 0xf) + (self.p & self.CARRY) - #if nibble0 <= 0xf: - #halfcarry = 0 - #adjust0 = 10 # 0xa = -0x6 - #nibble1 = ((self.a >> 4) & 0xf) + ((~data >> 4) & 0xf) + halfcarry - #if nibble1 <= 0xf: - #adjust1 = 10 << 4 # -0x60 - - # the ALU outputs are not decimally adjusted - aluresult = self.a + (~data & self.byteMask) + \ - (self.p & self.CARRY) - - if aluresult > self.byteMask: - decimalcarry = 1 - aluresult &= self.byteMask - - #A2 = (self.a + ~data + (self.p & self.CARRY)) - #print "A = %02X, %02X, A2 = %02X, a=%02X, data=%02X, alu=%02X" % (A, (A & self.byteMask), A2, self.a, data, aluresult) - #assert A == A2 - - # but the final result will be adjusted - #nibble0 = (aluresult + adjust0) & 0xf - #nibble1 = ((aluresult + adjust1) >> 4) & 0xf - - # Update result for use in setting flags below - #adjresult = (nibble1 << 4) + nibble0 - - A = self.a - data + (self.p & self.CARRY) - 1 - AL = (self.a & 0xf) - (data & 0xf) + (self.p & self.CARRY) - 1 - assert (A & self.byteMask) == aluresult - - # XXX - if A < 0: - #print A, A2 - #assert adjust1, (A, adjust1) - A -= 0x60 - - if AL < 0: - #assert adjust0, (AL, adjust0) - A -= 0x6 - - #if (A & self.byteMask) != adjresult: - # print "a=%02X data=%02X carry=%02X, res=%02X, adj=%02X" % ( - # self.a, data, self.p & self.CARRY, A & self.byteMask, adjresult) - #assert False - - adjresult = A & self.byteMask - - self.p &= ~(self.CARRY | self.ZERO | self.NEGATIVE | self.OVERFLOW) - if adjresult == 0: - self.p |= self.ZERO - else: - self.p |= adjresult & self.NEGATIVE - if decimalcarry == 1: - self.p |= self.CARRY - if ((self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE: - self.p |= self.OVERFLOW - self.a = adjresult + def opSBC(self, x): + return self._opSBC(x, decimal_flags_use_adjusted_result=True) # instructions diff --git a/py65/tests/devices/test_binary_object.py b/py65/tests/devices/test_binary_object.py index de871e9..b9a092d 100644 --- a/py65/tests/devices/test_binary_object.py +++ b/py65/tests/devices/test_binary_object.py @@ -67,13 +67,21 @@ class BinaryObjectTests(unittest.TestCase): if mpu.pc == old_pc or mpu.pc == 0x1003: break - assert mpu.memory[0x0b] == 0 + if mpu.memory[0x0b] != 0: + assert False, ("N1={:02x} N2={:02x} HA={:02x} HNVZC={:08b} DA={" + ":02x} DNVZC={:08b} AR={:02x} NF={:08b} VF={:08b} " + "ZF={:08b} CF={:08b}".format( + mpu.memory[0x00], mpu.memory[0x01], mpu.memory[0x02], + mpu.memory[0x03], mpu.memory[0x04],mpu.memory[0x05], mpu.memory[0x06], + mpu.memory[0x07],mpu.memory[0x08], mpu.memory[0x09], + mpu.memory[0x0a] + )) class Functional6502Tests(BinaryObjectTests): - def test6502DecimalTest(self): - self._decimalTest("devices/bcd/6502_decimal_test.bin") + def Xtest6502DecimalTest(self): + self.decimalTest("devices/bcd/6502_decimal_test.bin") def _get_target_class(self): return py65.devices.mpu6502.MPU @@ -82,7 +90,7 @@ class Functional6502Tests(BinaryObjectTests): class Functional65C02Tests(BinaryObjectTests): def test65C02DecimalTest(self): - self._decimalTest("devices/bcd/65C02_decimal_test.bin") + self.decimalTest("devices/bcd/65C02_decimal_test.bin") def _get_target_class(self): return py65.devices.mpu65c02.MPU From e0f56e48e03160b2a670cbee7d4188fb44f15c97 Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 20 Aug 2019 21:14:18 +0100 Subject: [PATCH 11/18] Switch to cc65 versions of these sources, courtesy of github user amb5l (https://github.com/Klaus2m5/6502_65C02_functional_tests/pull/8) Minor modification to BRK when test completes instead of using 65C02 STP. --- py65/tests/devices/bcd/6502_decimal_test.bin | Bin 258 -> 258 bytes ...decimal_test.a65 => 6502_decimal_test.c65} | 217 +++--- py65/tests/devices/bcd/6502_decimal_test.lst | 724 +++++++++--------- py65/tests/devices/bcd/65C02_decimal_test.bin | Bin 266 -> 266 bytes ...ecimal_test.a65 => 65C02_decimal_test.c65} | 217 +++--- py65/tests/devices/bcd/65C02_decimal_test.lst | 724 +++++++++--------- 6 files changed, 932 insertions(+), 950 deletions(-) rename py65/tests/devices/bcd/{6502_decimal_test.a65 => 6502_decimal_test.c65} (77%) rename py65/tests/devices/bcd/{65C02_decimal_test.a65 => 65C02_decimal_test.c65} (77%) diff --git a/py65/tests/devices/bcd/6502_decimal_test.bin b/py65/tests/devices/bcd/6502_decimal_test.bin index 9f959425aff5b4d2218d7f085cc4d1fc591f6409..b99d8d929cbebc835373a3ae27ec5b27931fa9dc 100644 GIT binary patch delta 38 wcmV+>0NMY70)hgNFdP~nkOCm|0wBf$&=2MS(Awq!(8h=m!>Is;3jmQ!wgmPL!T0NMY70)hgNFdY6MkOCm|0wBf$(EjEC(Awq!(8h=m!>Is;3)_)Qwl=B}Z~y=R diff --git a/py65/tests/devices/bcd/6502_decimal_test.a65 b/py65/tests/devices/bcd/6502_decimal_test.c65 similarity index 77% rename from py65/tests/devices/bcd/6502_decimal_test.a65 rename to py65/tests/devices/bcd/6502_decimal_test.c65 index 0d076bf..9868914 100644 --- a/py65/tests/devices/bcd/6502_decimal_test.a65 +++ b/py65/tests/devices/bcd/6502_decimal_test.c65 @@ -33,99 +33,100 @@ chk_v = 1 ; check overflow flag chk_z = 1 ; check zero flag chk_c = 1 ; check carry flag -end_of_test macro - db $db ;execute 65C02 stop instruction - endm +.macro end_of_test + BRK + ; .byte $db ;execute 65C02 stop instruction +.endmacro - bss - org 0 + .ZEROPAGE + .org 0 ; operands - register Y = carry in -N1 ds 1 -N2 ds 1 +N1: .res 1,0 +N2: .res 1,0 ; binary result -HA ds 1 -HNVZC ds 1 +HA: .res 1,0 +HNVZC: .res 1,0 ;04 ; decimal result -DA ds 1 -DNVZC ds 1 +DA: .res 1,0 +DNVZC: .res 1,0 ; predicted results -AR ds 1 -NF ds 1 +AR: .res 1,0 +NF: .res 1,0 ;08 -VF ds 1 -ZF ds 1 -CF ds 1 -ERROR ds 1 +VF: .res 1,0 +ZF: .res 1,0 +CF: .res 1,0 +ERROR: .res 1,0 ;0C ; workspace -N1L ds 1 -N1H ds 1 -N2L ds 1 -N2H ds 2 +N1L: .res 1,0 +N1H: .res 1,0 +N2L: .res 1,0 +N2H: .res 2,0 - code - org $200 -TEST ldy #1 ; initialize Y (used to loop through carry flag values) + .CODE + .org $200 +TEST: ldy #1 ; initialize Y (used to loop through carry flag values) sty ERROR ; store 1 in ERROR until the test passes lda #0 ; initialize N1 and N2 sta N1 sta N2 -LOOP1 lda N2 ; N2L = N2 & $0F +LOOP1: lda N2 ; N2L = N2 & $0F and #$0F ; [1] see text - if vld_bcd = 1 + .if vld_bcd = 1 cmp #$0a bcs NEXT2 - endif + .endif sta N2L lda N2 ; N2H = N2 & $F0 and #$F0 ; [2] see text - if vld_bcd = 1 + .if vld_bcd = 1 cmp #$a0 bcs NEXT2 - endif + .endif sta N2H ora #$0F ; N2H+1 = (N2 & $F0) + $0F sta N2H+1 -LOOP2 lda N1 ; N1L = N1 & $0F +LOOP2: lda N1 ; N1L = N1 & $0F and #$0F ; [3] see text - if vld_bcd = 1 + .if vld_bcd = 1 cmp #$0a bcs NEXT1 - endif + .endif sta N1L lda N1 ; N1H = N1 & $F0 and #$F0 ; [4] see text - if vld_bcd = 1 + .if vld_bcd = 1 cmp #$a0 bcs NEXT1 - endif + .endif sta N1H jsr ADD jsr A6502 jsr COMPARE - bne * + bne DONE jsr SUB jsr S6502 jsr COMPARE - bne * -NEXT1 inc N1 ; [5] see text + bne DONE +NEXT1: inc N1 ; [5] see text bne LOOP2 ; loop through all 256 values of N1 -NEXT2 inc N2 ; [6] see text +NEXT2: inc N2 ; [6] see text bne LOOP1 ; loop through all 256 values of N2 dey bpl LOOP1 ; loop through both values of the carry flag lda #0 ; test passed, so store 0 in ERROR sta ERROR -DONE +DONE: end_of_test - + ; Calculate the actual decimal mode accumulator and flags, the accumulator ; and flag results when N1 is added to N2 using binary arithmetic, the ; predicted accumulator result, the predicted carry flag, and the predicted -; V flag -; -ADD sed ; decimal mode +; V flag +; +ADD: sed ; decimal mode cpy #1 ; set carry if Y = 1, clear carry if Y = 0 lda N1 adc N2 @@ -138,7 +139,7 @@ ADD sed ; decimal mode lda N1 adc N2 sta HA ; accumulator result of N1+N2 using binary arithmetic - + php pla sta HNVZC ; flags result of N1+N2 using binary arithmetic @@ -152,34 +153,34 @@ ADD sed ; decimal mode adc #5 ; add 6 (carry is set) and #$0F sec -A1 ora N1H -; +A1: ora N1H +; ; if N1L + N2L < $0A, then add N2 & $F0 ; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set) -; +; adc N2H,x php bcs A2 cmp #$A0 bcc A3 -A2 adc #$5F ; add $60 (carry is set) +A2: adc #$5F ; add $60 (carry is set) sec -A3 sta AR ; predicted accumulator result +A3: sta AR ; predicted accumulator result php pla sta CF ; predicted carry result pla -; +; ; note that all 8 bits of the P register are stored in VF -; +; sta VF ; predicted V flags rts - + ; Calculate the actual decimal mode accumulator and flags, and the ; accumulator and flag results when N2 is subtracted from N1 using binary ; arithmetic -; -SUB sed ; decimal mode +; +SUB: sed ; decimal mode cpy #1 ; set carry if Y = 1, clear carry if Y = 0 lda N1 sbc N2 @@ -192,16 +193,16 @@ SUB sed ; decimal mode lda N1 sbc N2 sta HA ; accumulator result of N1-N2 using binary arithmetic - + php pla sta HNVZC ; flags result of N1-N2 using binary arithmetic rts - - if cputype != 1 + + .if cputype <> 1 ; Calculate the predicted SBC accumulator result for the 6502 and 65816 -; -SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +; +SUB1: cpy #1 ; set carry if Y = 1, clear carry if Y = 0 lda N1L sbc N2L ldx #0 @@ -210,22 +211,22 @@ SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 sbc #5 ; subtract 6 (carry is clear) and #$0F clc -S11 ora N1H -; +S11: ora N1H +; ; if N1L - N2L >= 0, then subtract N2 & $F0 ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) -; +; sbc N2H,x bcs S12 sbc #$5F ; subtract $60 (carry is clear) -S12 sta AR +S12: sta AR rts - endif - - if cputype = 1 + .endif + + .if cputype = 1 ; Calculate the predicted SBC accumulator result for the 6502 and 65C02 ; -SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +SUB2: cpy #1 ; set carry if Y = 1, clear carry if Y = 0 lda N1L sbc N2L ldx #0 @@ -233,74 +234,74 @@ SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 inx and #$0F clc -S21 ora N1H -; +S21: ora N1H +; ; if N1L - N2L >= 0, then subtract N2 & $F0 ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) -; +; sbc N2H,x bcs S22 sbc #$5F ; subtract $60 (carry is clear) -S22 cpx #0 +S22: cpx #0 beq S23 sbc #6 -S23 sta AR ; predicted accumulator result +S23: sta AR ; predicted accumulator result rts - endif - + .endif + ; Compare accumulator actual results to predicted results -; -; Return: +; +; Return: ; Z flag = 1 (BEQ branch) if same ; Z flag = 0 (BNE branch) if different -; -COMPARE - if chk_a = 1 +; +COMPARE: + .if chk_a = 1 lda DA cmp AR bne C1 - endif - if chk_n = 1 + .endif + .if chk_n = 1 lda DNVZC ; [7] see text eor NF and #$80 ; mask off N flag bne C1 - endif - if chk_v = 1 + .endif + .if chk_v = 1 lda DNVZC ; [8] see text eor VF and #$40 ; mask off V flag bne C1 ; [9] see text - endif - if chk_z = 1 + .endif + .if chk_z = 1 lda DNVZC eor ZF ; mask off Z flag and #2 bne C1 ; [10] see text - endif - if chk_c = 1 + .endif + .if chk_c = 1 lda DNVZC eor CF and #1 ; mask off C flag - endif -C1 rts - + .endif +C1: rts + ; These routines store the predicted values for ADC and SBC for the 6502, ; 65C02, and 65816 in AR, CF, NF, VF, and ZF - if cputype = 0 + .if cputype = 0 -A6502 lda VF ; 6502 -; +A6502: lda VF ; 6502 +; ; since all 8 bits of the P register were stored in VF, bit 7 of VF contains ; the N flag for NF -; +; sta NF lda HNVZC sta ZF rts - -S6502 jsr SUB1 + +S6502: jsr SUB1 lda HNVZC sta NF sta VF @@ -308,17 +309,17 @@ S6502 jsr SUB1 sta CF rts - endif - if cputype = 1 + .endif + .if cputype = 1 -A6502 lda AR ; 65C02 +A6502: lda AR ; 65C02 php pla sta NF sta ZF rts - -S6502 jsr SUB2 + +S6502: jsr SUB2 lda AR php pla @@ -329,17 +330,17 @@ S6502 jsr SUB2 sta CF rts - endif - if cputype = 2 + .endif + .if cputype = 2 -A6502 lda AR ; 65C816 +A6502: lda AR ; 65C816 php pla sta NF sta ZF rts - -S6502 jsr SUB1 + +S6502: jsr SUB1 lda AR php pla @@ -350,6 +351,4 @@ S6502 jsr SUB1 sta CF rts - endif - - end TEST + .endif \ No newline at end of file diff --git a/py65/tests/devices/bcd/6502_decimal_test.lst b/py65/tests/devices/bcd/6502_decimal_test.lst index 9d7190c..63581c2 100644 --- a/py65/tests/devices/bcd/6502_decimal_test.lst +++ b/py65/tests/devices/bcd/6502_decimal_test.lst @@ -1,367 +1,359 @@ -AS65 Assembler for R6502 [1.42]. Copyright 1994-2007, Frank A. Kingswood Page 1 ------------------------------------------------------ 6502_decimal_test.a65 ------------------------------------------------------ +ca65 V2.17 - Git N/A +Main file : 6502_decimal_test.c65 +Current file: 6502_decimal_test.c65 -355 lines read, no errors in pass 1. - ; Verify decimal mode behavior - ; Written by Bruce Clark. This code is public domain. - ; see http://www.6502.org/tutorials/decimal_mode.html - ; - ; Returns: - ; ERROR = 0 if the test passed - ; ERROR = 1 if the test failed - ; modify the code at the DONE label for desired program end - ; - ; This routine requires 17 bytes of RAM -- 1 byte each for: - ; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF - ; and 2 bytes for N2H - ; - ; Variables: - ; N1 and N2 are the two numbers to be added or subtracted - ; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2 - ; DA and DNVZC are the actual accumulator and flag results in decimal mode - ; HA and HNVZC are the accumulator and flag results when N1 and N2 are - ; added or subtracted using binary arithmetic - ; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and - ; flag results, calculated using binary arithmetic - ; - ; This program takes approximately 1 minute at 1 MHz (a few seconds more on - ; a 65C02 than a 6502 or 65816) - ; - - ; Configuration: -0000 = cputype = 0 ; 0 = 6502, 1 = 65C02, 2 = 65C816 -0000 = vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only -0001 = chk_a = 1 ; check accumulator -0001 = chk_n = 1 ; check sign (negative) flag -0001 = chk_v = 1 ; check overflow flag -0001 = chk_z = 1 ; check zero flag -0001 = chk_c = 1 ; check carry flag - - end_of_test macro - db $db ;execute 65C02 stop instruction - endm - - bss -0000 = org 0 - ; operands - register Y = carry in -0000 = N1 ds 1 -0001 = N2 ds 1 - ; binary result -0002 = HA ds 1 -0003 = HNVZC ds 1 - ;04 - ; decimal result -0004 = DA ds 1 -0005 = DNVZC ds 1 - ; predicted results -0006 = AR ds 1 -0007 = NF ds 1 - ;08 -0008 = VF ds 1 -0009 = ZF ds 1 -000a = CF ds 1 -000b = ERROR ds 1 - ;0C - ; workspace -000c = N1L ds 1 -000d = N1H ds 1 -000e = N2L ds 1 -000f = N2H ds 2 - - code -0200 = org $200 -0200 : a001 TEST ldy #1 ; initialize Y (used to loop through carry flag values) -0202 : 840b sty ERROR ; store 1 in ERROR until the test passes -0204 : a900 lda #0 ; initialize N1 and N2 -0206 : 8500 sta N1 -0208 : 8501 sta N2 -020a : a501 LOOP1 lda N2 ; N2L = N2 & $0F -020c : 290f and #$0F ; [1] see text - if vld_bcd = 1 - cmp #$0a - bcs NEXT2 - endif -020e : 850e sta N2L -0210 : a501 lda N2 ; N2H = N2 & $F0 -0212 : 29f0 and #$F0 ; [2] see text - if vld_bcd = 1 - cmp #$a0 - bcs NEXT2 - endif -0214 : 850f sta N2H -0216 : 090f ora #$0F ; N2H+1 = (N2 & $F0) + $0F -0218 : 8510 sta N2H+1 -021a : a500 LOOP2 lda N1 ; N1L = N1 & $0F -021c : 290f and #$0F ; [3] see text - if vld_bcd = 1 - cmp #$0a - bcs NEXT1 - endif -021e : 850c sta N1L -0220 : a500 lda N1 ; N1H = N1 & $F0 -0222 : 29f0 and #$F0 ; [4] see text - if vld_bcd = 1 - cmp #$a0 - bcs NEXT1 - endif -0224 : 850d sta N1H -0226 : 204c02 jsr ADD -0229 : 20eb02 jsr A6502 -022c : 20c602 jsr COMPARE -022f : d0fe bne * -0231 : 209002 jsr SUB -0234 : 20f402 jsr S6502 -0237 : 20c602 jsr COMPARE -023a : d0fe bne * -023c : e600 NEXT1 inc N1 ; [5] see text -023e : d0da bne LOOP2 ; loop through all 256 values of N1 -0240 : e601 NEXT2 inc N2 ; [6] see text -0242 : d0c6 bne LOOP1 ; loop through all 256 values of N2 -0244 : 88 dey -0245 : 10c3 bpl LOOP1 ; loop through both values of the carry flag -0247 : a900 lda #0 ; test passed, so store 0 in ERROR -0249 : 850b sta ERROR -024b : DONE - end_of_test -024b : db > db $db ;execute 65C02 stop instruction - - - ; Calculate the actual decimal mode accumulator and flags, the accumulator - ; and flag results when N1 is added to N2 using binary arithmetic, the - ; predicted accumulator result, the predicted carry flag, and the predicted - ; V flag - ; -024c : f8 ADD sed ; decimal mode -024d : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 -024f : a500 lda N1 -0251 : 6501 adc N2 -0253 : 8504 sta DA ; actual accumulator result in decimal mode -0255 : 08 php -0256 : 68 pla -0257 : 8505 sta DNVZC ; actual flags result in decimal mode -0259 : d8 cld ; binary mode -025a : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 -025c : a500 lda N1 -025e : 6501 adc N2 -0260 : 8502 sta HA ; accumulator result of N1+N2 using binary arithmetic - -0262 : 08 php -0263 : 68 pla -0264 : 8503 sta HNVZC ; flags result of N1+N2 using binary arithmetic -0266 : c001 cpy #1 -0268 : a50c lda N1L -026a : 650e adc N2L -026c : c90a cmp #$0A -026e : a200 ldx #0 -0270 : 9006 bcc A1 -0272 : e8 inx -0273 : 6905 adc #5 ; add 6 (carry is set) -0275 : 290f and #$0F -0277 : 38 sec -0278 : 050d A1 ora N1H - ; - ; if N1L + N2L < $0A, then add N2 & $F0 - ; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set) - ; -027a : 750f adc N2H,x -027c : 08 php -027d : b004 bcs A2 -027f : c9a0 cmp #$A0 -0281 : 9003 bcc A3 -0283 : 695f A2 adc #$5F ; add $60 (carry is set) -0285 : 38 sec -0286 : 8506 A3 sta AR ; predicted accumulator result -0288 : 08 php -0289 : 68 pla -028a : 850a sta CF ; predicted carry result -028c : 68 pla - ; - ; note that all 8 bits of the P register are stored in VF - ; -028d : 8508 sta VF ; predicted V flags -028f : 60 rts - - ; Calculate the actual decimal mode accumulator and flags, and the - ; accumulator and flag results when N2 is subtracted from N1 using binary - ; arithmetic - ; -0290 : f8 SUB sed ; decimal mode -0291 : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 -0293 : a500 lda N1 -0295 : e501 sbc N2 -0297 : 8504 sta DA ; actual accumulator result in decimal mode -0299 : 08 php -029a : 68 pla -029b : 8505 sta DNVZC ; actual flags result in decimal mode -029d : d8 cld ; binary mode -029e : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 -02a0 : a500 lda N1 -02a2 : e501 sbc N2 -02a4 : 8502 sta HA ; accumulator result of N1-N2 using binary arithmetic - -02a6 : 08 php -02a7 : 68 pla -02a8 : 8503 sta HNVZC ; flags result of N1-N2 using binary arithmetic -02aa : 60 rts - - if cputype != 1 - ; Calculate the predicted SBC accumulator result for the 6502 and 65816 - ; -02ab : c001 SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 -02ad : a50c lda N1L -02af : e50e sbc N2L -02b1 : a200 ldx #0 -02b3 : b006 bcs S11 -02b5 : e8 inx -02b6 : e905 sbc #5 ; subtract 6 (carry is clear) -02b8 : 290f and #$0F -02ba : 18 clc -02bb : 050d S11 ora N1H - ; - ; if N1L - N2L >= 0, then subtract N2 & $F0 - ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) - ; -02bd : f50f sbc N2H,x -02bf : b002 bcs S12 -02c1 : e95f sbc #$5F ; subtract $60 (carry is clear) -02c3 : 8506 S12 sta AR -02c5 : 60 rts - endif - - if cputype = 1 - ; Calculate the predicted SBC accumulator result for the 6502 and 65C02 - ; - SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 - lda N1L - sbc N2L - ldx #0 - bcs S21 - inx - and #$0F - clc - S21 ora N1H - ; - ; if N1L - N2L >= 0, then subtract N2 & $F0 - ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) - ; - sbc N2H,x - bcs S22 - sbc #$5F ; subtract $60 (carry is clear) - S22 cpx #0 - beq S23 - sbc #6 - S23 sta AR ; predicted accumulator result - rts - endif - - ; Compare accumulator actual results to predicted results - ; - ; Return: - ; Z flag = 1 (BEQ branch) if same - ; Z flag = 0 (BNE branch) if different - ; -02c6 : COMPARE - if chk_a = 1 -02c6 : a504 lda DA -02c8 : c506 cmp AR -02ca : d01e bne C1 - endif - if chk_n = 1 -02cc : a505 lda DNVZC ; [7] see text -02ce : 4507 eor NF -02d0 : 2980 and #$80 ; mask off N flag -02d2 : d016 bne C1 - endif - if chk_v = 1 -02d4 : a505 lda DNVZC ; [8] see text -02d6 : 4508 eor VF -02d8 : 2940 and #$40 ; mask off V flag -02da : d00e bne C1 ; [9] see text - endif - if chk_z = 1 -02dc : a505 lda DNVZC -02de : 4509 eor ZF ; mask off Z flag -02e0 : 2902 and #2 -02e2 : d006 bne C1 ; [10] see text - endif - if chk_c = 1 -02e4 : a505 lda DNVZC -02e6 : 450a eor CF -02e8 : 2901 and #1 ; mask off C flag - endif -02ea : 60 C1 rts - - ; These routines store the predicted values for ADC and SBC for the 6502, - ; 65C02, and 65816 in AR, CF, NF, VF, and ZF - - if cputype = 0 - -02eb : a508 A6502 lda VF ; 6502 - ; - ; since all 8 bits of the P register were stored in VF, bit 7 of VF contains - ; the N flag for NF - ; -02ed : 8507 sta NF -02ef : a503 lda HNVZC -02f1 : 8509 sta ZF -02f3 : 60 rts - -02f4 : 20ab02 S6502 jsr SUB1 -02f7 : a503 lda HNVZC -02f9 : 8507 sta NF -02fb : 8508 sta VF -02fd : 8509 sta ZF -02ff : 850a sta CF -0301 : 60 rts - - endif - if cputype = 1 - - A6502 lda AR ; 65C02 - php - pla - sta NF - sta ZF - rts - - S6502 jsr SUB2 - lda AR - php - pla - sta NF - sta ZF - lda HNVZC - sta VF - sta CF - rts - - endif - if cputype = 2 - - A6502 lda AR ; 65C816 - php - pla - sta NF - sta ZF - rts - - S6502 jsr SUB1 - lda AR - php - pla - sta NF - sta ZF - lda HNVZC - sta VF - sta CF - rts - - endif - -02f4 = end TEST - -No errors in pass 2. -Wrote binary from address $0200 through $0301. -Total size 258 bytes. -Program start address is at $0200 (512). - \ No newline at end of file +000000r 1 ; Verify decimal mode behavior +000000r 1 ; Written by Bruce Clark. This code is public domain. +000000r 1 ; see http://www.6502.org/tutorials/decimal_mode.html +000000r 1 ; +000000r 1 ; Returns: +000000r 1 ; ERROR = 0 if the test passed +000000r 1 ; ERROR = 1 if the test failed +000000r 1 ; modify the code at the DONE label for desired program end +000000r 1 ; +000000r 1 ; This routine requires 17 bytes of RAM -- 1 byte each for: +000000r 1 ; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF +000000r 1 ; and 2 bytes for N2H +000000r 1 ; +000000r 1 ; Variables: +000000r 1 ; N1 and N2 are the two numbers to be added or subtracted +000000r 1 ; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2 +000000r 1 ; DA and DNVZC are the actual accumulator and flag results in decimal mode +000000r 1 ; HA and HNVZC are the accumulator and flag results when N1 and N2 are +000000r 1 ; added or subtracted using binary arithmetic +000000r 1 ; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and +000000r 1 ; flag results, calculated using binary arithmetic +000000r 1 ; +000000r 1 ; This program takes approximately 1 minute at 1 MHz (a few seconds more on +000000r 1 ; a 65C02 than a 6502 or 65816) +000000r 1 ; +000000r 1 +000000r 1 ; Configuration: +000000r 1 cputype = 0 ; 0 = 6502, 1 = 65C02, 2 = 65C816 +000000r 1 vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only +000000r 1 chk_a = 1 ; check accumulator +000000r 1 chk_n = 1 ; check sign (negative) flag +000000r 1 chk_v = 1 ; check overflow flag +000000r 1 chk_z = 1 ; check zero flag +000000r 1 chk_c = 1 ; check carry flag +000000r 1 +000000r 1 .macro end_of_test +000000r 1 BRK +000000r 1 ; .byte $db ;execute 65C02 stop instruction +000000r 1 .endmacro +000000r 1 +000000r 1 .ZEROPAGE +000000r 1 .org 0 +000000 1 ; operands - register Y = carry in +000000 1 00 N1: .res 1,0 +000001 1 00 N2: .res 1,0 +000002 1 ; binary result +000002 1 00 HA: .res 1,0 +000003 1 00 HNVZC: .res 1,0 +000004 1 ;04 +000004 1 ; decimal result +000004 1 00 DA: .res 1,0 +000005 1 00 DNVZC: .res 1,0 +000006 1 ; predicted results +000006 1 00 AR: .res 1,0 +000007 1 00 NF: .res 1,0 +000008 1 ;08 +000008 1 00 VF: .res 1,0 +000009 1 00 ZF: .res 1,0 +00000A 1 00 CF: .res 1,0 +00000B 1 00 ERROR: .res 1,0 +00000C 1 ;0C +00000C 1 ; workspace +00000C 1 00 N1L: .res 1,0 +00000D 1 00 N1H: .res 1,0 +00000E 1 00 N2L: .res 1,0 +00000F 1 00 00 N2H: .res 2,0 +000011 1 +000011 1 .CODE +000011 1 .org $200 +000200 1 A0 01 TEST: ldy #1 ; initialize Y (used to loop through carry flag values) +000202 1 84 0B sty ERROR ; store 1 in ERROR until the test passes +000204 1 A9 00 lda #0 ; initialize N1 and N2 +000206 1 85 00 sta N1 +000208 1 85 01 sta N2 +00020A 1 A5 01 LOOP1: lda N2 ; N2L = N2 & $0F +00020C 1 29 0F and #$0F ; [1] see text +00020E 1 .if vld_bcd = 1 +00020E 1 cmp #$0a +00020E 1 bcs NEXT2 +00020E 1 .endif +00020E 1 85 0E sta N2L +000210 1 A5 01 lda N2 ; N2H = N2 & $F0 +000212 1 29 F0 and #$F0 ; [2] see text +000214 1 .if vld_bcd = 1 +000214 1 cmp #$a0 +000214 1 bcs NEXT2 +000214 1 .endif +000214 1 85 0F sta N2H +000216 1 09 0F ora #$0F ; N2H+1 = (N2 & $F0) + $0F +000218 1 85 10 sta N2H+1 +00021A 1 A5 00 LOOP2: lda N1 ; N1L = N1 & $0F +00021C 1 29 0F and #$0F ; [3] see text +00021E 1 .if vld_bcd = 1 +00021E 1 cmp #$0a +00021E 1 bcs NEXT1 +00021E 1 .endif +00021E 1 85 0C sta N1L +000220 1 A5 00 lda N1 ; N1H = N1 & $F0 +000222 1 29 F0 and #$F0 ; [4] see text +000224 1 .if vld_bcd = 1 +000224 1 cmp #$a0 +000224 1 bcs NEXT1 +000224 1 .endif +000224 1 85 0D sta N1H +000226 1 20 4C 02 jsr ADD +000229 1 20 EB 02 jsr A6502 +00022C 1 20 C6 02 jsr COMPARE +00022F 1 D0 1A bne DONE +000231 1 20 90 02 jsr SUB +000234 1 20 F4 02 jsr S6502 +000237 1 20 C6 02 jsr COMPARE +00023A 1 D0 0F bne DONE +00023C 1 E6 00 NEXT1: inc N1 ; [5] see text +00023E 1 D0 DA bne LOOP2 ; loop through all 256 values of N1 +000240 1 E6 01 NEXT2: inc N2 ; [6] see text +000242 1 D0 C6 bne LOOP1 ; loop through all 256 values of N2 +000244 1 88 dey +000245 1 10 C3 bpl LOOP1 ; loop through both values of the carry flag +000247 1 A9 00 lda #0 ; test passed, so store 0 in ERROR +000249 1 85 0B sta ERROR +00024B 1 DONE: +00024B 1 00 end_of_test +00024C 1 +00024C 1 ; Calculate the actual decimal mode accumulator and flags, the accumulator +00024C 1 ; and flag results when N1 is added to N2 using binary arithmetic, the +00024C 1 ; predicted accumulator result, the predicted carry flag, and the predicted +00024C 1 ; V flag +00024C 1 ; +00024C 1 F8 ADD: sed ; decimal mode +00024D 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +00024F 1 A5 00 lda N1 +000251 1 65 01 adc N2 +000253 1 85 04 sta DA ; actual accumulator result in decimal mode +000255 1 08 php +000256 1 68 pla +000257 1 85 05 sta DNVZC ; actual flags result in decimal mode +000259 1 D8 cld ; binary mode +00025A 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +00025C 1 A5 00 lda N1 +00025E 1 65 01 adc N2 +000260 1 85 02 sta HA ; accumulator result of N1+N2 using binary arithmetic +000262 1 +000262 1 08 php +000263 1 68 pla +000264 1 85 03 sta HNVZC ; flags result of N1+N2 using binary arithmetic +000266 1 C0 01 cpy #1 +000268 1 A5 0C lda N1L +00026A 1 65 0E adc N2L +00026C 1 C9 0A cmp #$0A +00026E 1 A2 00 ldx #0 +000270 1 90 06 bcc A1 +000272 1 E8 inx +000273 1 69 05 adc #5 ; add 6 (carry is set) +000275 1 29 0F and #$0F +000277 1 38 sec +000278 1 05 0D A1: ora N1H +00027A 1 ; +00027A 1 ; if N1L + N2L < $0A, then add N2 & $F0 +00027A 1 ; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set) +00027A 1 ; +00027A 1 75 0F adc N2H,x +00027C 1 08 php +00027D 1 B0 04 bcs A2 +00027F 1 C9 A0 cmp #$A0 +000281 1 90 03 bcc A3 +000283 1 69 5F A2: adc #$5F ; add $60 (carry is set) +000285 1 38 sec +000286 1 85 06 A3: sta AR ; predicted accumulator result +000288 1 08 php +000289 1 68 pla +00028A 1 85 0A sta CF ; predicted carry result +00028C 1 68 pla +00028D 1 ; +00028D 1 ; note that all 8 bits of the P register are stored in VF +00028D 1 ; +00028D 1 85 08 sta VF ; predicted V flags +00028F 1 60 rts +000290 1 +000290 1 ; Calculate the actual decimal mode accumulator and flags, and the +000290 1 ; accumulator and flag results when N2 is subtracted from N1 using binary +000290 1 ; arithmetic +000290 1 ; +000290 1 F8 SUB: sed ; decimal mode +000291 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +000293 1 A5 00 lda N1 +000295 1 E5 01 sbc N2 +000297 1 85 04 sta DA ; actual accumulator result in decimal mode +000299 1 08 php +00029A 1 68 pla +00029B 1 85 05 sta DNVZC ; actual flags result in decimal mode +00029D 1 D8 cld ; binary mode +00029E 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +0002A0 1 A5 00 lda N1 +0002A2 1 E5 01 sbc N2 +0002A4 1 85 02 sta HA ; accumulator result of N1-N2 using binary arithmetic +0002A6 1 +0002A6 1 08 php +0002A7 1 68 pla +0002A8 1 85 03 sta HNVZC ; flags result of N1-N2 using binary arithmetic +0002AA 1 60 rts +0002AB 1 +0002AB 1 .if cputype <> 1 +0002AB 1 ; Calculate the predicted SBC accumulator result for the 6502 and 65816 +0002AB 1 ; +0002AB 1 C0 01 SUB1: cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +0002AD 1 A5 0C lda N1L +0002AF 1 E5 0E sbc N2L +0002B1 1 A2 00 ldx #0 +0002B3 1 B0 06 bcs S11 +0002B5 1 E8 inx +0002B6 1 E9 05 sbc #5 ; subtract 6 (carry is clear) +0002B8 1 29 0F and #$0F +0002BA 1 18 clc +0002BB 1 05 0D S11: ora N1H +0002BD 1 ; +0002BD 1 ; if N1L - N2L >= 0, then subtract N2 & $F0 +0002BD 1 ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) +0002BD 1 ; +0002BD 1 F5 0F sbc N2H,x +0002BF 1 B0 02 bcs S12 +0002C1 1 E9 5F sbc #$5F ; subtract $60 (carry is clear) +0002C3 1 85 06 S12: sta AR +0002C5 1 60 rts +0002C6 1 .endif +0002C6 1 +0002C6 1 .if cputype = 1 +0002C6 1 ; Calculate the predicted SBC accumulator result for the 6502 and 65C02 +0002C6 1 ; +0002C6 1 SUB2: cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +0002C6 1 lda N1L +0002C6 1 sbc N2L +0002C6 1 ldx #0 +0002C6 1 bcs S21 +0002C6 1 inx +0002C6 1 and #$0F +0002C6 1 clc +0002C6 1 S21: ora N1H +0002C6 1 ; +0002C6 1 ; if N1L - N2L >= 0, then subtract N2 & $F0 +0002C6 1 ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) +0002C6 1 ; +0002C6 1 sbc N2H,x +0002C6 1 bcs S22 +0002C6 1 sbc #$5F ; subtract $60 (carry is clear) +0002C6 1 S22: cpx #0 +0002C6 1 beq S23 +0002C6 1 sbc #6 +0002C6 1 S23: sta AR ; predicted accumulator result +0002C6 1 rts +0002C6 1 .endif +0002C6 1 +0002C6 1 ; Compare accumulator actual results to predicted results +0002C6 1 ; +0002C6 1 ; Return: +0002C6 1 ; Z flag = 1 (BEQ branch) if same +0002C6 1 ; Z flag = 0 (BNE branch) if different +0002C6 1 ; +0002C6 1 COMPARE: +0002C6 1 .if chk_a = 1 +0002C6 1 A5 04 lda DA +0002C8 1 C5 06 cmp AR +0002CA 1 D0 1E bne C1 +0002CC 1 .endif +0002CC 1 .if chk_n = 1 +0002CC 1 A5 05 lda DNVZC ; [7] see text +0002CE 1 45 07 eor NF +0002D0 1 29 80 and #$80 ; mask off N flag +0002D2 1 D0 16 bne C1 +0002D4 1 .endif +0002D4 1 .if chk_v = 1 +0002D4 1 A5 05 lda DNVZC ; [8] see text +0002D6 1 45 08 eor VF +0002D8 1 29 40 and #$40 ; mask off V flag +0002DA 1 D0 0E bne C1 ; [9] see text +0002DC 1 .endif +0002DC 1 .if chk_z = 1 +0002DC 1 A5 05 lda DNVZC +0002DE 1 45 09 eor ZF ; mask off Z flag +0002E0 1 29 02 and #2 +0002E2 1 D0 06 bne C1 ; [10] see text +0002E4 1 .endif +0002E4 1 .if chk_c = 1 +0002E4 1 A5 05 lda DNVZC +0002E6 1 45 0A eor CF +0002E8 1 29 01 and #1 ; mask off C flag +0002EA 1 .endif +0002EA 1 60 C1: rts +0002EB 1 +0002EB 1 ; These routines store the predicted values for ADC and SBC for the 6502, +0002EB 1 ; 65C02, and 65816 in AR, CF, NF, VF, and ZF +0002EB 1 +0002EB 1 .if cputype = 0 +0002EB 1 +0002EB 1 A5 08 A6502: lda VF ; 6502 +0002ED 1 ; +0002ED 1 ; since all 8 bits of the P register were stored in VF, bit 7 of VF contains +0002ED 1 ; the N flag for NF +0002ED 1 ; +0002ED 1 85 07 sta NF +0002EF 1 A5 03 lda HNVZC +0002F1 1 85 09 sta ZF +0002F3 1 60 rts +0002F4 1 +0002F4 1 20 AB 02 S6502: jsr SUB1 +0002F7 1 A5 03 lda HNVZC +0002F9 1 85 07 sta NF +0002FB 1 85 08 sta VF +0002FD 1 85 09 sta ZF +0002FF 1 85 0A sta CF +000301 1 60 rts +000302 1 +000302 1 .endif +000302 1 .if cputype = 1 +000302 1 +000302 1 A6502: lda AR ; 65C02 +000302 1 php +000302 1 pla +000302 1 sta NF +000302 1 sta ZF +000302 1 rts +000302 1 +000302 1 S6502: jsr SUB2 +000302 1 lda AR +000302 1 php +000302 1 pla +000302 1 sta NF +000302 1 sta ZF +000302 1 lda HNVZC +000302 1 sta VF +000302 1 sta CF +000302 1 rts +000302 1 +000302 1 .endif +000302 1 .if cputype = 2 +000302 1 +000302 1 A6502: lda AR ; 65C816 +000302 1 php +000302 1 pla +000302 1 sta NF +000302 1 sta ZF +000302 1 rts +000302 1 +000302 1 S6502: jsr SUB1 +000302 1 lda AR +000302 1 php +000302 1 pla +000302 1 sta NF +000302 1 sta ZF +000302 1 lda HNVZC +000302 1 sta VF +000302 1 sta CF +000302 1 rts +000302 1 +000302 1 .endif +000302 1 diff --git a/py65/tests/devices/bcd/65C02_decimal_test.bin b/py65/tests/devices/bcd/65C02_decimal_test.bin index e4b80b1d1d384e971216bbb3651edbfbaec6d6e5..e1ecc33faf1abdcb3900f89986d934cb192c082d 100644 GIT binary patch delta 38 wcmV+>0NMYF0*V5VFdP~nkOCn10wBr)&=2MS(Awq!(8h=m!>Is;3jmQ!z6cTy;s5{u delta 38 wcmV+>0NMYF0*V5VFdY6MkOCn10wBr)(EjEC(Awq!(8h=m!>Is;3)_)QzB$GbkN^Mx diff --git a/py65/tests/devices/bcd/65C02_decimal_test.a65 b/py65/tests/devices/bcd/65C02_decimal_test.c65 similarity index 77% rename from py65/tests/devices/bcd/65C02_decimal_test.a65 rename to py65/tests/devices/bcd/65C02_decimal_test.c65 index 37dfa4b..50e8d49 100644 --- a/py65/tests/devices/bcd/65C02_decimal_test.a65 +++ b/py65/tests/devices/bcd/65C02_decimal_test.c65 @@ -33,99 +33,100 @@ chk_v = 1 ; check overflow flag chk_z = 1 ; check zero flag chk_c = 1 ; check carry flag -end_of_test macro - db $db ;execute 65C02 stop instruction - endm +.macro end_of_test + BRK + ; .byte $db ;execute 65C02 stop instruction +.endmacro - bss - org 0 + .ZEROPAGE + .org 0 ; operands - register Y = carry in -N1 ds 1 -N2 ds 1 +N1: .res 1,0 +N2: .res 1,0 ; binary result -HA ds 1 -HNVZC ds 1 +HA: .res 1,0 +HNVZC: .res 1,0 ;04 ; decimal result -DA ds 1 -DNVZC ds 1 +DA: .res 1,0 +DNVZC: .res 1,0 ; predicted results -AR ds 1 -NF ds 1 +AR: .res 1,0 +NF: .res 1,0 ;08 -VF ds 1 -ZF ds 1 -CF ds 1 -ERROR ds 1 +VF: .res 1,0 +ZF: .res 1,0 +CF: .res 1,0 +ERROR: .res 1,0 ;0C ; workspace -N1L ds 1 -N1H ds 1 -N2L ds 1 -N2H ds 2 +N1L: .res 1,0 +N1H: .res 1,0 +N2L: .res 1,0 +N2H: .res 2,0 - code - org $200 -TEST ldy #1 ; initialize Y (used to loop through carry flag values) + .CODE + .org $200 +TEST: ldy #1 ; initialize Y (used to loop through carry flag values) sty ERROR ; store 1 in ERROR until the test passes lda #0 ; initialize N1 and N2 sta N1 sta N2 -LOOP1 lda N2 ; N2L = N2 & $0F +LOOP1: lda N2 ; N2L = N2 & $0F and #$0F ; [1] see text - if vld_bcd = 1 + .if vld_bcd = 1 cmp #$0a bcs NEXT2 - endif + .endif sta N2L lda N2 ; N2H = N2 & $F0 and #$F0 ; [2] see text - if vld_bcd = 1 + .if vld_bcd = 1 cmp #$a0 bcs NEXT2 - endif + .endif sta N2H ora #$0F ; N2H+1 = (N2 & $F0) + $0F sta N2H+1 -LOOP2 lda N1 ; N1L = N1 & $0F +LOOP2: lda N1 ; N1L = N1 & $0F and #$0F ; [3] see text - if vld_bcd = 1 + .if vld_bcd = 1 cmp #$0a bcs NEXT1 - endif + .endif sta N1L lda N1 ; N1H = N1 & $F0 and #$F0 ; [4] see text - if vld_bcd = 1 + .if vld_bcd = 1 cmp #$a0 bcs NEXT1 - endif + .endif sta N1H jsr ADD jsr A6502 jsr COMPARE - bne * + bne DONE jsr SUB jsr S6502 jsr COMPARE - bne * -NEXT1 inc N1 ; [5] see text + bne DONE +NEXT1: inc N1 ; [5] see text bne LOOP2 ; loop through all 256 values of N1 -NEXT2 inc N2 ; [6] see text +NEXT2: inc N2 ; [6] see text bne LOOP1 ; loop through all 256 values of N2 dey bpl LOOP1 ; loop through both values of the carry flag lda #0 ; test passed, so store 0 in ERROR sta ERROR -DONE +DONE: end_of_test - + ; Calculate the actual decimal mode accumulator and flags, the accumulator ; and flag results when N1 is added to N2 using binary arithmetic, the ; predicted accumulator result, the predicted carry flag, and the predicted -; V flag -; -ADD sed ; decimal mode +; V flag +; +ADD: sed ; decimal mode cpy #1 ; set carry if Y = 1, clear carry if Y = 0 lda N1 adc N2 @@ -138,7 +139,7 @@ ADD sed ; decimal mode lda N1 adc N2 sta HA ; accumulator result of N1+N2 using binary arithmetic - + php pla sta HNVZC ; flags result of N1+N2 using binary arithmetic @@ -152,34 +153,34 @@ ADD sed ; decimal mode adc #5 ; add 6 (carry is set) and #$0F sec -A1 ora N1H -; +A1: ora N1H +; ; if N1L + N2L < $0A, then add N2 & $F0 ; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set) -; +; adc N2H,x php bcs A2 cmp #$A0 bcc A3 -A2 adc #$5F ; add $60 (carry is set) +A2: adc #$5F ; add $60 (carry is set) sec -A3 sta AR ; predicted accumulator result +A3: sta AR ; predicted accumulator result php pla sta CF ; predicted carry result pla -; +; ; note that all 8 bits of the P register are stored in VF -; +; sta VF ; predicted V flags rts - + ; Calculate the actual decimal mode accumulator and flags, and the ; accumulator and flag results when N2 is subtracted from N1 using binary ; arithmetic -; -SUB sed ; decimal mode +; +SUB: sed ; decimal mode cpy #1 ; set carry if Y = 1, clear carry if Y = 0 lda N1 sbc N2 @@ -192,16 +193,16 @@ SUB sed ; decimal mode lda N1 sbc N2 sta HA ; accumulator result of N1-N2 using binary arithmetic - + php pla sta HNVZC ; flags result of N1-N2 using binary arithmetic rts - - if cputype != 1 + + .if cputype <> 1 ; Calculate the predicted SBC accumulator result for the 6502 and 65816 -; -SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +; +SUB1: cpy #1 ; set carry if Y = 1, clear carry if Y = 0 lda N1L sbc N2L ldx #0 @@ -210,22 +211,22 @@ SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 sbc #5 ; subtract 6 (carry is clear) and #$0F clc -S11 ora N1H -; +S11: ora N1H +; ; if N1L - N2L >= 0, then subtract N2 & $F0 ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) -; +; sbc N2H,x bcs S12 sbc #$5F ; subtract $60 (carry is clear) -S12 sta AR +S12: sta AR rts - endif - - if cputype = 1 + .endif + + .if cputype = 1 ; Calculate the predicted SBC accumulator result for the 6502 and 65C02 ; -SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +SUB2: cpy #1 ; set carry if Y = 1, clear carry if Y = 0 lda N1L sbc N2L ldx #0 @@ -233,74 +234,74 @@ SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 inx and #$0F clc -S21 ora N1H -; +S21: ora N1H +; ; if N1L - N2L >= 0, then subtract N2 & $F0 ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) -; +; sbc N2H,x bcs S22 sbc #$5F ; subtract $60 (carry is clear) -S22 cpx #0 +S22: cpx #0 beq S23 sbc #6 -S23 sta AR ; predicted accumulator result +S23: sta AR ; predicted accumulator result rts - endif - + .endif + ; Compare accumulator actual results to predicted results -; -; Return: +; +; Return: ; Z flag = 1 (BEQ branch) if same ; Z flag = 0 (BNE branch) if different -; -COMPARE - if chk_a = 1 +; +COMPARE: + .if chk_a = 1 lda DA cmp AR bne C1 - endif - if chk_n = 1 + .endif + .if chk_n = 1 lda DNVZC ; [7] see text eor NF and #$80 ; mask off N flag bne C1 - endif - if chk_v = 1 + .endif + .if chk_v = 1 lda DNVZC ; [8] see text eor VF and #$40 ; mask off V flag bne C1 ; [9] see text - endif - if chk_z = 1 + .endif + .if chk_z = 1 lda DNVZC eor ZF ; mask off Z flag and #2 bne C1 ; [10] see text - endif - if chk_c = 1 + .endif + .if chk_c = 1 lda DNVZC eor CF and #1 ; mask off C flag - endif -C1 rts - + .endif +C1: rts + ; These routines store the predicted values for ADC and SBC for the 6502, ; 65C02, and 65816 in AR, CF, NF, VF, and ZF - if cputype = 0 + .if cputype = 0 -A6502 lda VF ; 6502 -; +A6502: lda VF ; 6502 +; ; since all 8 bits of the P register were stored in VF, bit 7 of VF contains ; the N flag for NF -; +; sta NF lda HNVZC sta ZF rts - -S6502 jsr SUB1 + +S6502: jsr SUB1 lda HNVZC sta NF sta VF @@ -308,17 +309,17 @@ S6502 jsr SUB1 sta CF rts - endif - if cputype = 1 + .endif + .if cputype = 1 -A6502 lda AR ; 65C02 +A6502: lda AR ; 65C02 php pla sta NF sta ZF rts - -S6502 jsr SUB2 + +S6502: jsr SUB2 lda AR php pla @@ -329,17 +330,17 @@ S6502 jsr SUB2 sta CF rts - endif - if cputype = 2 + .endif + .if cputype = 2 -A6502 lda AR ; 65C816 +A6502: lda AR ; 65C816 php pla sta NF sta ZF rts - -S6502 jsr SUB1 + +S6502: jsr SUB1 lda AR php pla @@ -350,6 +351,4 @@ S6502 jsr SUB1 sta CF rts - endif - - end TEST + .endif \ No newline at end of file diff --git a/py65/tests/devices/bcd/65C02_decimal_test.lst b/py65/tests/devices/bcd/65C02_decimal_test.lst index 352ee3b..4538433 100644 --- a/py65/tests/devices/bcd/65C02_decimal_test.lst +++ b/py65/tests/devices/bcd/65C02_decimal_test.lst @@ -1,367 +1,359 @@ -AS65 Assembler for R6502 [1.42]. Copyright 1994-2007, Frank A. Kingswood Page 1 ------------------------------------------------------ 65C02_decimal_test.a65 ----------------------------------------------------- +ca65 V2.17 - Git N/A +Main file : 65C02_decimal_test.c65 +Current file: 65C02_decimal_test.c65 -355 lines read, no errors in pass 1. - ; Verify decimal mode behavior - ; Written by Bruce Clark. This code is public domain. - ; see http://www.6502.org/tutorials/decimal_mode.html - ; - ; Returns: - ; ERROR = 0 if the test passed - ; ERROR = 1 if the test failed - ; modify the code at the DONE label for desired program end - ; - ; This routine requires 17 bytes of RAM -- 1 byte each for: - ; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF - ; and 2 bytes for N2H - ; - ; Variables: - ; N1 and N2 are the two numbers to be added or subtracted - ; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2 - ; DA and DNVZC are the actual accumulator and flag results in decimal mode - ; HA and HNVZC are the accumulator and flag results when N1 and N2 are - ; added or subtracted using binary arithmetic - ; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and - ; flag results, calculated using binary arithmetic - ; - ; This program takes approximately 1 minute at 1 MHz (a few seconds more on - ; a 65C02 than a 6502 or 65816) - ; - - ; Configuration: -0001 = cputype = 1 ; 0 = 6502, 1 = 65C02, 2 = 65C816 -0000 = vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only -0001 = chk_a = 1 ; check accumulator -0001 = chk_n = 1 ; check sign (negative) flag -0001 = chk_v = 1 ; check overflow flag -0001 = chk_z = 1 ; check zero flag -0001 = chk_c = 1 ; check carry flag - - end_of_test macro - db $db ;execute 65C02 stop instruction - endm - - bss -0000 = org 0 - ; operands - register Y = carry in -0000 = N1 ds 1 -0001 = N2 ds 1 - ; binary result -0002 = HA ds 1 -0003 = HNVZC ds 1 - ;04 - ; decimal result -0004 = DA ds 1 -0005 = DNVZC ds 1 - ; predicted results -0006 = AR ds 1 -0007 = NF ds 1 - ;08 -0008 = VF ds 1 -0009 = ZF ds 1 -000a = CF ds 1 -000b = ERROR ds 1 - ;0C - ; workspace -000c = N1L ds 1 -000d = N1H ds 1 -000e = N2L ds 1 -000f = N2H ds 2 - - code -0200 = org $200 -0200 : a001 TEST ldy #1 ; initialize Y (used to loop through carry flag values) -0202 : 840b sty ERROR ; store 1 in ERROR until the test passes -0204 : a900 lda #0 ; initialize N1 and N2 -0206 : 8500 sta N1 -0208 : 8501 sta N2 -020a : a501 LOOP1 lda N2 ; N2L = N2 & $0F -020c : 290f and #$0F ; [1] see text - if vld_bcd = 1 - cmp #$0a - bcs NEXT2 - endif -020e : 850e sta N2L -0210 : a501 lda N2 ; N2H = N2 & $F0 -0212 : 29f0 and #$F0 ; [2] see text - if vld_bcd = 1 - cmp #$a0 - bcs NEXT2 - endif -0214 : 850f sta N2H -0216 : 090f ora #$0F ; N2H+1 = (N2 & $F0) + $0F -0218 : 8510 sta N2H+1 -021a : a500 LOOP2 lda N1 ; N1L = N1 & $0F -021c : 290f and #$0F ; [3] see text - if vld_bcd = 1 - cmp #$0a - bcs NEXT1 - endif -021e : 850c sta N1L -0220 : a500 lda N1 ; N1H = N1 & $F0 -0222 : 29f0 and #$F0 ; [4] see text - if vld_bcd = 1 - cmp #$a0 - bcs NEXT1 - endif -0224 : 850d sta N1H -0226 : 204c02 jsr ADD -0229 : 20ef02 jsr A6502 -022c : 20ca02 jsr COMPARE -022f : d0fe bne * -0231 : 209002 jsr SUB -0234 : 20f802 jsr S6502 -0237 : 20ca02 jsr COMPARE -023a : d0fe bne * -023c : e600 NEXT1 inc N1 ; [5] see text -023e : d0da bne LOOP2 ; loop through all 256 values of N1 -0240 : e601 NEXT2 inc N2 ; [6] see text -0242 : d0c6 bne LOOP1 ; loop through all 256 values of N2 -0244 : 88 dey -0245 : 10c3 bpl LOOP1 ; loop through both values of the carry flag -0247 : a900 lda #0 ; test passed, so store 0 in ERROR -0249 : 850b sta ERROR -024b : DONE - end_of_test -024b : db > db $db ;execute 65C02 stop instruction - - - ; Calculate the actual decimal mode accumulator and flags, the accumulator - ; and flag results when N1 is added to N2 using binary arithmetic, the - ; predicted accumulator result, the predicted carry flag, and the predicted - ; V flag - ; -024c : f8 ADD sed ; decimal mode -024d : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 -024f : a500 lda N1 -0251 : 6501 adc N2 -0253 : 8504 sta DA ; actual accumulator result in decimal mode -0255 : 08 php -0256 : 68 pla -0257 : 8505 sta DNVZC ; actual flags result in decimal mode -0259 : d8 cld ; binary mode -025a : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 -025c : a500 lda N1 -025e : 6501 adc N2 -0260 : 8502 sta HA ; accumulator result of N1+N2 using binary arithmetic - -0262 : 08 php -0263 : 68 pla -0264 : 8503 sta HNVZC ; flags result of N1+N2 using binary arithmetic -0266 : c001 cpy #1 -0268 : a50c lda N1L -026a : 650e adc N2L -026c : c90a cmp #$0A -026e : a200 ldx #0 -0270 : 9006 bcc A1 -0272 : e8 inx -0273 : 6905 adc #5 ; add 6 (carry is set) -0275 : 290f and #$0F -0277 : 38 sec -0278 : 050d A1 ora N1H - ; - ; if N1L + N2L < $0A, then add N2 & $F0 - ; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set) - ; -027a : 750f adc N2H,x -027c : 08 php -027d : b004 bcs A2 -027f : c9a0 cmp #$A0 -0281 : 9003 bcc A3 -0283 : 695f A2 adc #$5F ; add $60 (carry is set) -0285 : 38 sec -0286 : 8506 A3 sta AR ; predicted accumulator result -0288 : 08 php -0289 : 68 pla -028a : 850a sta CF ; predicted carry result -028c : 68 pla - ; - ; note that all 8 bits of the P register are stored in VF - ; -028d : 8508 sta VF ; predicted V flags -028f : 60 rts - - ; Calculate the actual decimal mode accumulator and flags, and the - ; accumulator and flag results when N2 is subtracted from N1 using binary - ; arithmetic - ; -0290 : f8 SUB sed ; decimal mode -0291 : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 -0293 : a500 lda N1 -0295 : e501 sbc N2 -0297 : 8504 sta DA ; actual accumulator result in decimal mode -0299 : 08 php -029a : 68 pla -029b : 8505 sta DNVZC ; actual flags result in decimal mode -029d : d8 cld ; binary mode -029e : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 -02a0 : a500 lda N1 -02a2 : e501 sbc N2 -02a4 : 8502 sta HA ; accumulator result of N1-N2 using binary arithmetic - -02a6 : 08 php -02a7 : 68 pla -02a8 : 8503 sta HNVZC ; flags result of N1-N2 using binary arithmetic -02aa : 60 rts - - if cputype != 1 - ; Calculate the predicted SBC accumulator result for the 6502 and 65816 - ; - SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 - lda N1L - sbc N2L - ldx #0 - bcs S11 - inx - sbc #5 ; subtract 6 (carry is clear) - and #$0F - clc - S11 ora N1H - ; - ; if N1L - N2L >= 0, then subtract N2 & $F0 - ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) - ; - sbc N2H,x - bcs S12 - sbc #$5F ; subtract $60 (carry is clear) - S12 sta AR - rts - endif - - if cputype = 1 - ; Calculate the predicted SBC accumulator result for the 6502 and 65C02 - ; -02ab : c001 SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 -02ad : a50c lda N1L -02af : e50e sbc N2L -02b1 : a200 ldx #0 -02b3 : b004 bcs S21 -02b5 : e8 inx -02b6 : 290f and #$0F -02b8 : 18 clc -02b9 : 050d S21 ora N1H - ; - ; if N1L - N2L >= 0, then subtract N2 & $F0 - ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) - ; -02bb : f50f sbc N2H,x -02bd : b002 bcs S22 -02bf : e95f sbc #$5F ; subtract $60 (carry is clear) -02c1 : e000 S22 cpx #0 -02c3 : f002 beq S23 -02c5 : e906 sbc #6 -02c7 : 8506 S23 sta AR ; predicted accumulator result -02c9 : 60 rts - endif - - ; Compare accumulator actual results to predicted results - ; - ; Return: - ; Z flag = 1 (BEQ branch) if same - ; Z flag = 0 (BNE branch) if different - ; -02ca : COMPARE - if chk_a = 1 -02ca : a504 lda DA -02cc : c506 cmp AR -02ce : d01e bne C1 - endif - if chk_n = 1 -02d0 : a505 lda DNVZC ; [7] see text -02d2 : 4507 eor NF -02d4 : 2980 and #$80 ; mask off N flag -02d6 : d016 bne C1 - endif - if chk_v = 1 -02d8 : a505 lda DNVZC ; [8] see text -02da : 4508 eor VF -02dc : 2940 and #$40 ; mask off V flag -02de : d00e bne C1 ; [9] see text - endif - if chk_z = 1 -02e0 : a505 lda DNVZC -02e2 : 4509 eor ZF ; mask off Z flag -02e4 : 2902 and #2 -02e6 : d006 bne C1 ; [10] see text - endif - if chk_c = 1 -02e8 : a505 lda DNVZC -02ea : 450a eor CF -02ec : 2901 and #1 ; mask off C flag - endif -02ee : 60 C1 rts - - ; These routines store the predicted values for ADC and SBC for the 6502, - ; 65C02, and 65816 in AR, CF, NF, VF, and ZF - - if cputype = 0 - - A6502 lda VF ; 6502 - ; - ; since all 8 bits of the P register were stored in VF, bit 7 of VF contains - ; the N flag for NF - ; - sta NF - lda HNVZC - sta ZF - rts - - S6502 jsr SUB1 - lda HNVZC - sta NF - sta VF - sta ZF - sta CF - rts - - endif - if cputype = 1 - -02ef : a506 A6502 lda AR ; 65C02 -02f1 : 08 php -02f2 : 68 pla -02f3 : 8507 sta NF -02f5 : 8509 sta ZF -02f7 : 60 rts - -02f8 : 20ab02 S6502 jsr SUB2 -02fb : a506 lda AR -02fd : 08 php -02fe : 68 pla -02ff : 8507 sta NF -0301 : 8509 sta ZF -0303 : a503 lda HNVZC -0305 : 8508 sta VF -0307 : 850a sta CF -0309 : 60 rts - - endif - if cputype = 2 - - A6502 lda AR ; 65C816 - php - pla - sta NF - sta ZF - rts - - S6502 jsr SUB1 - lda AR - php - pla - sta NF - sta ZF - lda HNVZC - sta VF - sta CF - rts - - endif - -02f8 = end TEST - -No errors in pass 2. -Wrote binary from address $0200 through $0309. -Total size 266 bytes. -Program start address is at $0200 (512). - \ No newline at end of file +000000r 1 ; Verify decimal mode behavior +000000r 1 ; Written by Bruce Clark. This code is public domain. +000000r 1 ; see http://www.6502.org/tutorials/decimal_mode.html +000000r 1 ; +000000r 1 ; Returns: +000000r 1 ; ERROR = 0 if the test passed +000000r 1 ; ERROR = 1 if the test failed +000000r 1 ; modify the code at the DONE label for desired program end +000000r 1 ; +000000r 1 ; This routine requires 17 bytes of RAM -- 1 byte each for: +000000r 1 ; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF +000000r 1 ; and 2 bytes for N2H +000000r 1 ; +000000r 1 ; Variables: +000000r 1 ; N1 and N2 are the two numbers to be added or subtracted +000000r 1 ; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2 +000000r 1 ; DA and DNVZC are the actual accumulator and flag results in decimal mode +000000r 1 ; HA and HNVZC are the accumulator and flag results when N1 and N2 are +000000r 1 ; added or subtracted using binary arithmetic +000000r 1 ; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and +000000r 1 ; flag results, calculated using binary arithmetic +000000r 1 ; +000000r 1 ; This program takes approximately 1 minute at 1 MHz (a few seconds more on +000000r 1 ; a 65C02 than a 6502 or 65816) +000000r 1 ; +000000r 1 +000000r 1 ; Configuration: +000000r 1 cputype = 1 ; 0 = 6502, 1 = 65C02, 2 = 65C816 +000000r 1 vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only +000000r 1 chk_a = 1 ; check accumulator +000000r 1 chk_n = 1 ; check sign (negative) flag +000000r 1 chk_v = 1 ; check overflow flag +000000r 1 chk_z = 1 ; check zero flag +000000r 1 chk_c = 1 ; check carry flag +000000r 1 +000000r 1 .macro end_of_test +000000r 1 BRK +000000r 1 ; .byte $db ;execute 65C02 stop instruction +000000r 1 .endmacro +000000r 1 +000000r 1 .ZEROPAGE +000000r 1 .org 0 +000000 1 ; operands - register Y = carry in +000000 1 00 N1: .res 1,0 +000001 1 00 N2: .res 1,0 +000002 1 ; binary result +000002 1 00 HA: .res 1,0 +000003 1 00 HNVZC: .res 1,0 +000004 1 ;04 +000004 1 ; decimal result +000004 1 00 DA: .res 1,0 +000005 1 00 DNVZC: .res 1,0 +000006 1 ; predicted results +000006 1 00 AR: .res 1,0 +000007 1 00 NF: .res 1,0 +000008 1 ;08 +000008 1 00 VF: .res 1,0 +000009 1 00 ZF: .res 1,0 +00000A 1 00 CF: .res 1,0 +00000B 1 00 ERROR: .res 1,0 +00000C 1 ;0C +00000C 1 ; workspace +00000C 1 00 N1L: .res 1,0 +00000D 1 00 N1H: .res 1,0 +00000E 1 00 N2L: .res 1,0 +00000F 1 00 00 N2H: .res 2,0 +000011 1 +000011 1 .CODE +000011 1 .org $200 +000200 1 A0 01 TEST: ldy #1 ; initialize Y (used to loop through carry flag values) +000202 1 84 0B sty ERROR ; store 1 in ERROR until the test passes +000204 1 A9 00 lda #0 ; initialize N1 and N2 +000206 1 85 00 sta N1 +000208 1 85 01 sta N2 +00020A 1 A5 01 LOOP1: lda N2 ; N2L = N2 & $0F +00020C 1 29 0F and #$0F ; [1] see text +00020E 1 .if vld_bcd = 1 +00020E 1 cmp #$0a +00020E 1 bcs NEXT2 +00020E 1 .endif +00020E 1 85 0E sta N2L +000210 1 A5 01 lda N2 ; N2H = N2 & $F0 +000212 1 29 F0 and #$F0 ; [2] see text +000214 1 .if vld_bcd = 1 +000214 1 cmp #$a0 +000214 1 bcs NEXT2 +000214 1 .endif +000214 1 85 0F sta N2H +000216 1 09 0F ora #$0F ; N2H+1 = (N2 & $F0) + $0F +000218 1 85 10 sta N2H+1 +00021A 1 A5 00 LOOP2: lda N1 ; N1L = N1 & $0F +00021C 1 29 0F and #$0F ; [3] see text +00021E 1 .if vld_bcd = 1 +00021E 1 cmp #$0a +00021E 1 bcs NEXT1 +00021E 1 .endif +00021E 1 85 0C sta N1L +000220 1 A5 00 lda N1 ; N1H = N1 & $F0 +000222 1 29 F0 and #$F0 ; [4] see text +000224 1 .if vld_bcd = 1 +000224 1 cmp #$a0 +000224 1 bcs NEXT1 +000224 1 .endif +000224 1 85 0D sta N1H +000226 1 20 4C 02 jsr ADD +000229 1 20 EF 02 jsr A6502 +00022C 1 20 CA 02 jsr COMPARE +00022F 1 D0 1A bne DONE +000231 1 20 90 02 jsr SUB +000234 1 20 F8 02 jsr S6502 +000237 1 20 CA 02 jsr COMPARE +00023A 1 D0 0F bne DONE +00023C 1 E6 00 NEXT1: inc N1 ; [5] see text +00023E 1 D0 DA bne LOOP2 ; loop through all 256 values of N1 +000240 1 E6 01 NEXT2: inc N2 ; [6] see text +000242 1 D0 C6 bne LOOP1 ; loop through all 256 values of N2 +000244 1 88 dey +000245 1 10 C3 bpl LOOP1 ; loop through both values of the carry flag +000247 1 A9 00 lda #0 ; test passed, so store 0 in ERROR +000249 1 85 0B sta ERROR +00024B 1 DONE: +00024B 1 00 end_of_test +00024C 1 +00024C 1 ; Calculate the actual decimal mode accumulator and flags, the accumulator +00024C 1 ; and flag results when N1 is added to N2 using binary arithmetic, the +00024C 1 ; predicted accumulator result, the predicted carry flag, and the predicted +00024C 1 ; V flag +00024C 1 ; +00024C 1 F8 ADD: sed ; decimal mode +00024D 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +00024F 1 A5 00 lda N1 +000251 1 65 01 adc N2 +000253 1 85 04 sta DA ; actual accumulator result in decimal mode +000255 1 08 php +000256 1 68 pla +000257 1 85 05 sta DNVZC ; actual flags result in decimal mode +000259 1 D8 cld ; binary mode +00025A 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +00025C 1 A5 00 lda N1 +00025E 1 65 01 adc N2 +000260 1 85 02 sta HA ; accumulator result of N1+N2 using binary arithmetic +000262 1 +000262 1 08 php +000263 1 68 pla +000264 1 85 03 sta HNVZC ; flags result of N1+N2 using binary arithmetic +000266 1 C0 01 cpy #1 +000268 1 A5 0C lda N1L +00026A 1 65 0E adc N2L +00026C 1 C9 0A cmp #$0A +00026E 1 A2 00 ldx #0 +000270 1 90 06 bcc A1 +000272 1 E8 inx +000273 1 69 05 adc #5 ; add 6 (carry is set) +000275 1 29 0F and #$0F +000277 1 38 sec +000278 1 05 0D A1: ora N1H +00027A 1 ; +00027A 1 ; if N1L + N2L < $0A, then add N2 & $F0 +00027A 1 ; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set) +00027A 1 ; +00027A 1 75 0F adc N2H,x +00027C 1 08 php +00027D 1 B0 04 bcs A2 +00027F 1 C9 A0 cmp #$A0 +000281 1 90 03 bcc A3 +000283 1 69 5F A2: adc #$5F ; add $60 (carry is set) +000285 1 38 sec +000286 1 85 06 A3: sta AR ; predicted accumulator result +000288 1 08 php +000289 1 68 pla +00028A 1 85 0A sta CF ; predicted carry result +00028C 1 68 pla +00028D 1 ; +00028D 1 ; note that all 8 bits of the P register are stored in VF +00028D 1 ; +00028D 1 85 08 sta VF ; predicted V flags +00028F 1 60 rts +000290 1 +000290 1 ; Calculate the actual decimal mode accumulator and flags, and the +000290 1 ; accumulator and flag results when N2 is subtracted from N1 using binary +000290 1 ; arithmetic +000290 1 ; +000290 1 F8 SUB: sed ; decimal mode +000291 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +000293 1 A5 00 lda N1 +000295 1 E5 01 sbc N2 +000297 1 85 04 sta DA ; actual accumulator result in decimal mode +000299 1 08 php +00029A 1 68 pla +00029B 1 85 05 sta DNVZC ; actual flags result in decimal mode +00029D 1 D8 cld ; binary mode +00029E 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +0002A0 1 A5 00 lda N1 +0002A2 1 E5 01 sbc N2 +0002A4 1 85 02 sta HA ; accumulator result of N1-N2 using binary arithmetic +0002A6 1 +0002A6 1 08 php +0002A7 1 68 pla +0002A8 1 85 03 sta HNVZC ; flags result of N1-N2 using binary arithmetic +0002AA 1 60 rts +0002AB 1 +0002AB 1 .if cputype <> 1 +0002AB 1 ; Calculate the predicted SBC accumulator result for the 6502 and 65816 +0002AB 1 ; +0002AB 1 SUB1: cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +0002AB 1 lda N1L +0002AB 1 sbc N2L +0002AB 1 ldx #0 +0002AB 1 bcs S11 +0002AB 1 inx +0002AB 1 sbc #5 ; subtract 6 (carry is clear) +0002AB 1 and #$0F +0002AB 1 clc +0002AB 1 S11: ora N1H +0002AB 1 ; +0002AB 1 ; if N1L - N2L >= 0, then subtract N2 & $F0 +0002AB 1 ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) +0002AB 1 ; +0002AB 1 sbc N2H,x +0002AB 1 bcs S12 +0002AB 1 sbc #$5F ; subtract $60 (carry is clear) +0002AB 1 S12: sta AR +0002AB 1 rts +0002AB 1 .endif +0002AB 1 +0002AB 1 .if cputype = 1 +0002AB 1 ; Calculate the predicted SBC accumulator result for the 6502 and 65C02 +0002AB 1 ; +0002AB 1 C0 01 SUB2: cpy #1 ; set carry if Y = 1, clear carry if Y = 0 +0002AD 1 A5 0C lda N1L +0002AF 1 E5 0E sbc N2L +0002B1 1 A2 00 ldx #0 +0002B3 1 B0 04 bcs S21 +0002B5 1 E8 inx +0002B6 1 29 0F and #$0F +0002B8 1 18 clc +0002B9 1 05 0D S21: ora N1H +0002BB 1 ; +0002BB 1 ; if N1L - N2L >= 0, then subtract N2 & $F0 +0002BB 1 ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) +0002BB 1 ; +0002BB 1 F5 0F sbc N2H,x +0002BD 1 B0 02 bcs S22 +0002BF 1 E9 5F sbc #$5F ; subtract $60 (carry is clear) +0002C1 1 E0 00 S22: cpx #0 +0002C3 1 F0 02 beq S23 +0002C5 1 E9 06 sbc #6 +0002C7 1 85 06 S23: sta AR ; predicted accumulator result +0002C9 1 60 rts +0002CA 1 .endif +0002CA 1 +0002CA 1 ; Compare accumulator actual results to predicted results +0002CA 1 ; +0002CA 1 ; Return: +0002CA 1 ; Z flag = 1 (BEQ branch) if same +0002CA 1 ; Z flag = 0 (BNE branch) if different +0002CA 1 ; +0002CA 1 COMPARE: +0002CA 1 .if chk_a = 1 +0002CA 1 A5 04 lda DA +0002CC 1 C5 06 cmp AR +0002CE 1 D0 1E bne C1 +0002D0 1 .endif +0002D0 1 .if chk_n = 1 +0002D0 1 A5 05 lda DNVZC ; [7] see text +0002D2 1 45 07 eor NF +0002D4 1 29 80 and #$80 ; mask off N flag +0002D6 1 D0 16 bne C1 +0002D8 1 .endif +0002D8 1 .if chk_v = 1 +0002D8 1 A5 05 lda DNVZC ; [8] see text +0002DA 1 45 08 eor VF +0002DC 1 29 40 and #$40 ; mask off V flag +0002DE 1 D0 0E bne C1 ; [9] see text +0002E0 1 .endif +0002E0 1 .if chk_z = 1 +0002E0 1 A5 05 lda DNVZC +0002E2 1 45 09 eor ZF ; mask off Z flag +0002E4 1 29 02 and #2 +0002E6 1 D0 06 bne C1 ; [10] see text +0002E8 1 .endif +0002E8 1 .if chk_c = 1 +0002E8 1 A5 05 lda DNVZC +0002EA 1 45 0A eor CF +0002EC 1 29 01 and #1 ; mask off C flag +0002EE 1 .endif +0002EE 1 60 C1: rts +0002EF 1 +0002EF 1 ; These routines store the predicted values for ADC and SBC for the 6502, +0002EF 1 ; 65C02, and 65816 in AR, CF, NF, VF, and ZF +0002EF 1 +0002EF 1 .if cputype = 0 +0002EF 1 +0002EF 1 A6502: lda VF ; 6502 +0002EF 1 ; +0002EF 1 ; since all 8 bits of the P register were stored in VF, bit 7 of VF contains +0002EF 1 ; the N flag for NF +0002EF 1 ; +0002EF 1 sta NF +0002EF 1 lda HNVZC +0002EF 1 sta ZF +0002EF 1 rts +0002EF 1 +0002EF 1 S6502: jsr SUB1 +0002EF 1 lda HNVZC +0002EF 1 sta NF +0002EF 1 sta VF +0002EF 1 sta ZF +0002EF 1 sta CF +0002EF 1 rts +0002EF 1 +0002EF 1 .endif +0002EF 1 .if cputype = 1 +0002EF 1 +0002EF 1 A5 06 A6502: lda AR ; 65C02 +0002F1 1 08 php +0002F2 1 68 pla +0002F3 1 85 07 sta NF +0002F5 1 85 09 sta ZF +0002F7 1 60 rts +0002F8 1 +0002F8 1 20 AB 02 S6502: jsr SUB2 +0002FB 1 A5 06 lda AR +0002FD 1 08 php +0002FE 1 68 pla +0002FF 1 85 07 sta NF +000301 1 85 09 sta ZF +000303 1 A5 03 lda HNVZC +000305 1 85 08 sta VF +000307 1 85 0A sta CF +000309 1 60 rts +00030A 1 +00030A 1 .endif +00030A 1 .if cputype = 2 +00030A 1 +00030A 1 A6502: lda AR ; 65C816 +00030A 1 php +00030A 1 pla +00030A 1 sta NF +00030A 1 sta ZF +00030A 1 rts +00030A 1 +00030A 1 S6502: jsr SUB1 +00030A 1 lda AR +00030A 1 php +00030A 1 pla +00030A 1 sta NF +00030A 1 sta ZF +00030A 1 lda HNVZC +00030A 1 sta VF +00030A 1 sta CF +00030A 1 rts +00030A 1 +00030A 1 .endif +00030A 1 From 3d30544b95294530b52dc6c706bc83540489afe0 Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 20 Aug 2019 21:17:13 +0100 Subject: [PATCH 12/18] Clean up binary test execution framework --- py65/tests/devices/test_binary_object.py | 61 +++++++++++++----------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/py65/tests/devices/test_binary_object.py b/py65/tests/devices/test_binary_object.py index b9a092d..8b8587c 100644 --- a/py65/tests/devices/test_binary_object.py +++ b/py65/tests/devices/test_binary_object.py @@ -1,11 +1,15 @@ import unittest import sys +import py65.devices.mpu6502 import py65.devices.mpu65c02 class BinaryObjectTests(unittest.TestCase): """Test cases based on executing 65x02 object code.""" + # If test fails, rerun with execution tracing enabled + TRACE_TEST_FAILURE = False + def binaryObjectTestCase(self, filename, load_addr, pc, success_addr, should_trace=None): mpu = self._make_mpu() mpu.pc = pc @@ -31,21 +35,24 @@ class BinaryObjectTests(unittest.TestCase): memory[start_address:start_address + len(bytes)] = bytes def _make_mpu(self, *args, **kargs): - klass = self._get_target_class() - mpu = klass(*args, **kargs) + mpu = self.MPU(*args, **kargs) if 'memory' not in kargs: mpu.memory = 0x10000 * [0xAA] return mpu - # XXX common test case - def decimalTest(self, filename): + def runBinaryTest(self, executor, filename): try: - return self._decimalTest(filename) + return executor(filename) except AssertionError: # Rerun with tracing - return self._decimalTest(filename, trace=True) + if self.TRACE_TEST_FAILURE: + return executor(filename, trace=True) + else: + raise - def _decimalTest(self, filename, trace=False): + +class FunctionalBCDTests(BinaryObjectTests): + def bcd_test_case(self, filename, trace=False): mpu = self._make_mpu() mpu.pc = 0x1000 @@ -55,45 +62,45 @@ class BinaryObjectTests(unittest.TestCase): # $1000: JSR $0200 self._write(mpu.memory, 0x1000, [0x20, 0x00, 0x02]) - should_trace = None - if not should_trace: - should_trace = lambda pc: trace + # Set up BRK vector pointing to $2000 so we can trap PC + self._write(mpu.memory, 0xfffe, [0x00, 0x20]) + + def should_trace(pc): + return trace while True: old_pc = mpu.pc mpu.step(trace=should_trace(mpu.pc)) # If we are looping at the same PC, or we return - # from the JSR, then we are done. - if mpu.pc == old_pc or mpu.pc == 0x1003: + # from the JSR, or we hit the BRK vector, then we are done. + if mpu.pc == old_pc or mpu.pc == 0x1003 or mpu.pc == 0x2000: break - if mpu.memory[0x0b] != 0: + if mpu.memory[0x0b] != 0: # Tests did not complete successfully assert False, ("N1={:02x} N2={:02x} HA={:02x} HNVZC={:08b} DA={" ":02x} DNVZC={:08b} AR={:02x} NF={:08b} VF={:08b} " "ZF={:08b} CF={:08b}".format( mpu.memory[0x00], mpu.memory[0x01], mpu.memory[0x02], - mpu.memory[0x03], mpu.memory[0x04],mpu.memory[0x05], mpu.memory[0x06], - mpu.memory[0x07],mpu.memory[0x08], mpu.memory[0x09], - mpu.memory[0x0a] + mpu.memory[0x03], mpu.memory[0x04], mpu.memory[0x05], + mpu.memory[0x06], mpu.memory[0x07], mpu.memory[0x08], + mpu.memory[0x09], mpu.memory[0x0a] )) -class Functional6502Tests(BinaryObjectTests): +class Functional6502Tests(FunctionalBCDTests): + MPU = py65.devices.mpu6502.MPU - def Xtest6502DecimalTest(self): - self.decimalTest("devices/bcd/6502_decimal_test.bin") - - def _get_target_class(self): - return py65.devices.mpu6502.MPU + def test6502DecimalTest(self): + self.runBinaryTest( + self.bcd_test_case, "devices/bcd/6502_decimal_test.bin") -class Functional65C02Tests(BinaryObjectTests): +class Functional65C02Tests(FunctionalBCDTests): + MPU = py65.devices.mpu65c02.MPU def test65C02DecimalTest(self): - self.decimalTest("devices/bcd/65C02_decimal_test.bin") - - def _get_target_class(self): - return py65.devices.mpu65c02.MPU + self.runBinaryTest( + self.bcd_test_case, "devices/bcd/65C02_decimal_test.bin") def test_suite(): From 3468cdce1ce6b53b2e40c9d4725f71244c0d364d Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 20 Aug 2019 21:17:33 +0100 Subject: [PATCH 13/18] Simplify and clean up --- py65/devices/mpu6502.py | 48 ++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/py65/devices/mpu6502.py b/py65/devices/mpu6502.py index 0358c7b..c5d3a74 100644 --- a/py65/devices/mpu6502.py +++ b/py65/devices/mpu6502.py @@ -446,47 +446,31 @@ class MPU: See e.g. http://6502.org/tutorials/decimal_mode.html#A for details """ data = self.ByteAt(x()) - # - # halfcarry = 1 - decimalcarry = 0 - # adjust0 = 0 - # adjust1 = 0 - # - # nibble0 = (self.a & 0xf) + (~data & 0xf) + (self.p & self.CARRY) - # if nibble0 <= 0xf: - # halfcarry = 0 - # adjust0 = 10 # -0x6 - # nibble1 = ((self.a >> 4) & 0xf) + ((~data >> 4) & 0xf) + halfcarry - # if nibble1 <= 0xf: - # adjust1 = 10 << 4 # -0x60 # the ALU outputs are not yet decimally adjusted aluresult = self.a + (~data & self.byteMask) + (self.p & self.CARRY) - if aluresult > self.byteMask: - decimalcarry = 1 + decimalcarry = aluresult > self.byteMask aluresult &= self.byteMask - AL = (self.a & 0xf) - (data & 0xf) + (self.p & self.CARRY) - 1 + al = (self.a & 0xf) - (data & 0xf) + (self.p & self.CARRY) - 1 - if flags_use_adjusted_result: # Note: 65C02 but not 65816 - A = self.a - data + (self.p & self.CARRY) - 1 - assert (A & self.byteMask) == aluresult + if flags_use_adjusted_result: # 65C02 but not 65816 + a = self.a - data + (self.p & self.CARRY) - 1 + if a < 0: + a -= 0x60 + if al < 0: + a -= 0x6 - if A < 0: - A -= 0x60 + else: # 6502 + # Note: 65816 apparently also uses this logic instead of 65C02 + if al < 0: + al = ((al - 0x6) & 0xf) - 0x10 + a = (self.a & 0xf0) - (data & 0xf0) + al + if a < 0: + a -= 0x60 - if AL < 0: - A -= 0x6 - else: - # Note: 65816 apparently uses this logic instead of 65C02 - if AL < 0: - AL = ((AL - 0x6) & 0xf) - 0x10 - A = (self.a & 0xf0) - (data & 0xf0) + AL - if A < 0: - A -= 0x60 - - adjresult = A & self.byteMask + adjresult = a & self.byteMask if flags_use_adjusted_result: # 65C02 and 65816 # Z and N use adjusted (i.e. decimal) result From 803632c15a8f19ca9a372bcfae1c7cc33d4b727b Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 20 Aug 2019 21:18:15 +0100 Subject: [PATCH 14/18] Add instructions on how to assemble the binary test cases --- py65/tests/devices/bcd/README | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 py65/tests/devices/bcd/README diff --git a/py65/tests/devices/bcd/README b/py65/tests/devices/bcd/README new file mode 100644 index 0000000..697a3c1 --- /dev/null +++ b/py65/tests/devices/bcd/README @@ -0,0 +1,7 @@ +These were compiled with the ca65 assembler available at http://cc65.org + +$ ca65 --cpu 6502 6502_decimal_test.c65 --listing 6502_decimal_test.lst +$ ld65 -t none -o 6502_decimal_test.bin 6502_decimal_test.o + +$ ca65 --cpu 65C02 65C02_decimal_test.c65 --listing 65C02_decimal_test.lst +$ ld65 -t none -o 65C02_decimal_test.bin 65C02_decimal_test.o \ No newline at end of file From 7d5a29cbaace74a9814802847beafc9b29d3428d Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 20 Aug 2019 21:45:37 +0100 Subject: [PATCH 15/18] Oh actually the remaining test case did pass, I just had the wrong completion PC Streamline test harness a bit --- py65/tests/devices/test_klaus.py | 60 ++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/py65/tests/devices/test_klaus.py b/py65/tests/devices/test_klaus.py index 4c7fb82..c6ce44e 100644 --- a/py65/tests/devices/test_klaus.py +++ b/py65/tests/devices/test_klaus.py @@ -2,10 +2,14 @@ import unittest import sys import py65.devices.mpu65c02 + class KlausDormannTests(unittest.TestCase): """Runs Klaus Dormann's 6502-based test suites""" - def klausTestCase(self, filename, load_addr, pc, success_addr, should_trace=None): + MPU = py65.devices.mpu65c02.MPU + + def klausTestCase(self, filename, load_addr, pc, completion_criteria, + should_trace=None): mpu = self._make_mpu() mpu.pc = pc @@ -18,20 +22,50 @@ class KlausDormannTests(unittest.TestCase): while True: old_pc = mpu.pc mpu.step(trace=should_trace(mpu.pc)) - if mpu.pc == old_pc: + if completion_criteria(mpu, old_pc): break - assert mpu.pc == success_addr, "%s 0xb=%02x 0xc=%02x 0xd=%02x 0xf=%02x" % ( - mpu, mpu.memory[0xb], mpu.memory[0xc], mpu.memory[0xd], mpu.memory[0xf]) + return mpu + + def make_completion_criteria(self, success_addr): + def completion_criteria(mpu, old_pc): + return mpu.pc == old_pc or mpu.pc == success_addr + + return completion_criteria def test6502FunctionalTest(self): - self.klausTestCase("devices/6502_functional_test.bin", 0x0, 0x400, 0x3399) + success_addr = 0x3399 + completion_criteria = self.make_completion_criteria(success_addr) - # XXX fails - def Xtest65C02ExtendedOpcodesTest(self): - tracer = lambda pc: (0x1484 <= pc <= 0x16cc) - self.klausTestCase("devices/65C02_extended_opcodes_test_modified.bin" - "", 0xa, 0x400, 0x24a8) #, tracer) + mpu = self.klausTestCase( + "devices/6502_functional_test.bin", 0x0, 0x400, + completion_criteria + ) + + assert mpu.pc == success_addr, ( + "%s 0xb=%02x 0xc=%02x 0xd=%02x 0xf=%02x" % ( + mpu, mpu.memory[0xb], mpu.memory[0xc], mpu.memory[0xd], + mpu.memory[0xf]) + ) + + def test65C02ExtendedOpcodesTest(self): + success_addr = 0x1570 + completion_criteria = self.make_completion_criteria(success_addr) + + # Modified version of 65C02_extended_opcodes_test that defines + # rkwl_wdc_op = 0 (don't test BBR/BBS instructions, which we do not + # implement) and many of the NOP tests for undefined opcodes which are + # not yet implemented here. + mpu = self.klausTestCase( + "devices/65C02_extended_opcodes_test_modified.bin", 0xa, 0x400, + completion_criteria + ) + + assert mpu.pc == success_addr, ( + "%s 0xb=%02x 0xc=%02x 0xd=%02x 0xf=%02x" % ( + mpu, mpu.memory[0xb], mpu.memory[0xc], mpu.memory[0xd], + mpu.memory[0xf]) + ) # Test Helpers @@ -39,17 +73,15 @@ class KlausDormannTests(unittest.TestCase): memory[start_address:start_address + len(bytes)] = bytes def _make_mpu(self, *args, **kargs): - klass = self._get_target_class() - mpu = klass(*args, **kargs) + mpu = self.MPU(*args, **kargs) if 'memory' not in kargs: mpu.memory = 0x10000 * [0xAA] return mpu - def _get_target_class(self): - return py65.devices.mpu65c02.MPU def test_suite(): return unittest.findTestCases(sys.modules[__name__]) + if __name__ == '__main__': unittest.main(defaultTest='test_suite') From 5b28c007d9a4a7eddc096f279b25cbea769c5773 Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 20 Aug 2019 23:02:23 +0100 Subject: [PATCH 16/18] More cleanup to functional tests. Automatically skip the Klaus Dormann ones if not present. --- py65/tests/devices/functional_tests.py | 56 +++++++++ py65/tests/devices/test_bcd_functional.py | 76 ++++++++++++ py65/tests/devices/test_binary_object.py | 111 ------------------ py65/tests/devices/test_klaus.py | 87 -------------- .../devices/test_klaus_dormann_functional.py | 82 +++++++++++++ 5 files changed, 214 insertions(+), 198 deletions(-) create mode 100644 py65/tests/devices/functional_tests.py create mode 100644 py65/tests/devices/test_bcd_functional.py delete mode 100644 py65/tests/devices/test_binary_object.py delete mode 100644 py65/tests/devices/test_klaus.py create mode 100644 py65/tests/devices/test_klaus_dormann_functional.py diff --git a/py65/tests/devices/functional_tests.py b/py65/tests/devices/functional_tests.py new file mode 100644 index 0000000..6adeabb --- /dev/null +++ b/py65/tests/devices/functional_tests.py @@ -0,0 +1,56 @@ +"""Helpers for functional tests based on executing 65x02 object code.""" + + +class FunctionalTestExecutor(object): + def __init__(self, mpu_class, filename, load_addr): + self.mpu_class = mpu_class + self.mpu = self._make_mpu() + + object_code = bytearray(open(filename, "rb").read()) + self.write_memory(load_addr, object_code) + + def _make_mpu(self, *args, **kargs): + mpu = self.mpu_class(*args, **kargs) + if 'memory' not in kargs: + mpu.memory = 0x10000 * [0xAA] + return mpu + + def write_memory(self, start_address, bytes): + self.mpu.memory[start_address:start_address + len(bytes)] = bytes + + @staticmethod + def never_trace_predicate(pc): + return False + + @staticmethod + def always_trace_predicate(pc): + return True + + @staticmethod + def address_completion_predicate(addrs): + """Terminate test when PC loops to itself or enters addrs set""" + def completion_predicate(mpu, old_pc): + return mpu.pc == old_pc or mpu.pc in addrs + + return completion_predicate + + def execute( + self, pc, completion_predicate, + trace_predicate=never_trace_predicate + ): + self.mpu.pc = pc + + while True: + old_pc = self.mpu.pc + self.mpu.step(trace=trace_predicate(self.mpu.pc)) + if completion_predicate(self.mpu, old_pc): + break + + +def trace_on_assertion(executor, *args, **kwargs): + try: + return executor(*args, **kwargs) + except AssertionError: + # Rerun with tracing + return executor(*args, trace=True, **kwargs) + diff --git a/py65/tests/devices/test_bcd_functional.py b/py65/tests/devices/test_bcd_functional.py new file mode 100644 index 0000000..4d7f8fc --- /dev/null +++ b/py65/tests/devices/test_bcd_functional.py @@ -0,0 +1,76 @@ +"""65(c)02-based test suite for BCD implementation correctness. + +See source code in bcd/*.c65 for more details. +""" + +import sys +import unittest + +import py65.devices.mpu6502 +import py65.devices.mpu65c02 +from py65.tests.devices import functional_tests + + +def run_bcd_test_case(mpu_class, filename, trace=False): + executor = functional_tests.FunctionalTestExecutor( + mpu_class, filename, load_addr=0x200) + + # $1000: JSR $0200 + executor.write_memory(0x1000, [0x20, 0x00, 0x02]) + + # Set up BRK vector pointing to $2000 so we can trap PC + executor.write_memory(0xfffe, [0x00, 0x20]) + + if trace: + tracer = executor.always_trace_predicate + else: + tracer = executor.never_trace_predicate + + executor.execute( + 0x1000, + # If we are looping at the same PC, or we return + # from the JSR, or we hit the BRK vector, then we are done. + executor.address_completion_predicate({0x1003, 0x2000}), + tracer + ) + mpu = executor.mpu + + if mpu.memory[0x0b] != 0: # Tests did not complete successfully + # Display internal test state; read the .c65 source to understand + # what these mean about the particular test case that failed. + assert False, ( + "N1={:02x} N2={:02x} HA={:02x} HNVZC={:08b} DA={:02x} " + "DNVZC={:08b} AR={:02x} NF={:08b} VF={:08b} ZF={:08b} " + "CF={:08b}".format( + mpu.memory[0x00], mpu.memory[0x01], mpu.memory[0x02], + mpu.memory[0x03], mpu.memory[0x04], mpu.memory[0x05], + mpu.memory[0x06], mpu.memory[0x07], mpu.memory[0x08], + mpu.memory[0x09], mpu.memory[0x0a] + )) + + +class BCDFunctionalTests(unittest.TestCase): + + @staticmethod + def test6502DecimalTest(): + functional_tests.trace_on_assertion( + run_bcd_test_case, + py65.devices.mpu6502.MPU, + "devices/bcd/6502_decimal_test.bin" + ) + + @staticmethod + def test65c02DecimalTest(): + functional_tests.trace_on_assertion( + run_bcd_test_case, + py65.devices.mpu65c02.MPU, + "devices/bcd/65C02_decimal_test.bin" + ) + + +def test_suite(): + return unittest.findTestCases(sys.modules[__name__]) + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff --git a/py65/tests/devices/test_binary_object.py b/py65/tests/devices/test_binary_object.py deleted file mode 100644 index 8b8587c..0000000 --- a/py65/tests/devices/test_binary_object.py +++ /dev/null @@ -1,111 +0,0 @@ -import unittest -import sys -import py65.devices.mpu6502 -import py65.devices.mpu65c02 - - -class BinaryObjectTests(unittest.TestCase): - """Test cases based on executing 65x02 object code.""" - - # If test fails, rerun with execution tracing enabled - TRACE_TEST_FAILURE = False - - def binaryObjectTestCase(self, filename, load_addr, pc, success_addr, should_trace=None): - mpu = self._make_mpu() - mpu.pc = pc - - object_code = bytearray(open(filename, "r").read()) - self._write(mpu.memory, load_addr, object_code) - - if not should_trace: - should_trace = lambda pc: False - - while True: - old_pc = mpu.pc - mpu.step(trace=should_trace(mpu.pc)) - if mpu.pc == old_pc: - break - - assert mpu.pc == success_addr, "%s 0xb=%02x 0xc=%02x 0xd=%02x 0xf=%02x" % ( - mpu, mpu.memory[0xb], mpu.memory[0xc], mpu.memory[0xd], mpu.memory[0xf]) - - # Test Helpers - - def _write(self, memory, start_address, bytes): - memory[start_address:start_address + len(bytes)] = bytes - - def _make_mpu(self, *args, **kargs): - mpu = self.MPU(*args, **kargs) - if 'memory' not in kargs: - mpu.memory = 0x10000 * [0xAA] - return mpu - - def runBinaryTest(self, executor, filename): - try: - return executor(filename) - except AssertionError: - # Rerun with tracing - if self.TRACE_TEST_FAILURE: - return executor(filename, trace=True) - else: - raise - - -class FunctionalBCDTests(BinaryObjectTests): - def bcd_test_case(self, filename, trace=False): - mpu = self._make_mpu() - mpu.pc = 0x1000 - - object_code = bytearray(open(filename, "r").read()) - self._write(mpu.memory, 0x200, object_code) - - # $1000: JSR $0200 - self._write(mpu.memory, 0x1000, [0x20, 0x00, 0x02]) - - # Set up BRK vector pointing to $2000 so we can trap PC - self._write(mpu.memory, 0xfffe, [0x00, 0x20]) - - def should_trace(pc): - return trace - - while True: - old_pc = mpu.pc - mpu.step(trace=should_trace(mpu.pc)) - # If we are looping at the same PC, or we return - # from the JSR, or we hit the BRK vector, then we are done. - if mpu.pc == old_pc or mpu.pc == 0x1003 or mpu.pc == 0x2000: - break - - if mpu.memory[0x0b] != 0: # Tests did not complete successfully - assert False, ("N1={:02x} N2={:02x} HA={:02x} HNVZC={:08b} DA={" - ":02x} DNVZC={:08b} AR={:02x} NF={:08b} VF={:08b} " - "ZF={:08b} CF={:08b}".format( - mpu.memory[0x00], mpu.memory[0x01], mpu.memory[0x02], - mpu.memory[0x03], mpu.memory[0x04], mpu.memory[0x05], - mpu.memory[0x06], mpu.memory[0x07], mpu.memory[0x08], - mpu.memory[0x09], mpu.memory[0x0a] - )) - - -class Functional6502Tests(FunctionalBCDTests): - MPU = py65.devices.mpu6502.MPU - - def test6502DecimalTest(self): - self.runBinaryTest( - self.bcd_test_case, "devices/bcd/6502_decimal_test.bin") - - -class Functional65C02Tests(FunctionalBCDTests): - MPU = py65.devices.mpu65c02.MPU - - def test65C02DecimalTest(self): - self.runBinaryTest( - self.bcd_test_case, "devices/bcd/65C02_decimal_test.bin") - - -def test_suite(): - return unittest.findTestCases(sys.modules[__name__]) - - -if __name__ == '__main__': - unittest.main(defaultTest='test_suite') diff --git a/py65/tests/devices/test_klaus.py b/py65/tests/devices/test_klaus.py deleted file mode 100644 index c6ce44e..0000000 --- a/py65/tests/devices/test_klaus.py +++ /dev/null @@ -1,87 +0,0 @@ -import unittest -import sys -import py65.devices.mpu65c02 - - -class KlausDormannTests(unittest.TestCase): - """Runs Klaus Dormann's 6502-based test suites""" - - MPU = py65.devices.mpu65c02.MPU - - def klausTestCase(self, filename, load_addr, pc, completion_criteria, - should_trace=None): - mpu = self._make_mpu() - mpu.pc = pc - - object_code = bytearray(open(filename, "r").read()) - self._write(mpu.memory, load_addr, object_code) - - if not should_trace: - should_trace = lambda pc: False - - while True: - old_pc = mpu.pc - mpu.step(trace=should_trace(mpu.pc)) - if completion_criteria(mpu, old_pc): - break - - return mpu - - def make_completion_criteria(self, success_addr): - def completion_criteria(mpu, old_pc): - return mpu.pc == old_pc or mpu.pc == success_addr - - return completion_criteria - - def test6502FunctionalTest(self): - success_addr = 0x3399 - completion_criteria = self.make_completion_criteria(success_addr) - - mpu = self.klausTestCase( - "devices/6502_functional_test.bin", 0x0, 0x400, - completion_criteria - ) - - assert mpu.pc == success_addr, ( - "%s 0xb=%02x 0xc=%02x 0xd=%02x 0xf=%02x" % ( - mpu, mpu.memory[0xb], mpu.memory[0xc], mpu.memory[0xd], - mpu.memory[0xf]) - ) - - def test65C02ExtendedOpcodesTest(self): - success_addr = 0x1570 - completion_criteria = self.make_completion_criteria(success_addr) - - # Modified version of 65C02_extended_opcodes_test that defines - # rkwl_wdc_op = 0 (don't test BBR/BBS instructions, which we do not - # implement) and many of the NOP tests for undefined opcodes which are - # not yet implemented here. - mpu = self.klausTestCase( - "devices/65C02_extended_opcodes_test_modified.bin", 0xa, 0x400, - completion_criteria - ) - - assert mpu.pc == success_addr, ( - "%s 0xb=%02x 0xc=%02x 0xd=%02x 0xf=%02x" % ( - mpu, mpu.memory[0xb], mpu.memory[0xc], mpu.memory[0xd], - mpu.memory[0xf]) - ) - - # Test Helpers - - def _write(self, memory, start_address, bytes): - memory[start_address:start_address + len(bytes)] = bytes - - def _make_mpu(self, *args, **kargs): - mpu = self.MPU(*args, **kargs) - if 'memory' not in kargs: - mpu.memory = 0x10000 * [0xAA] - return mpu - - -def test_suite(): - return unittest.findTestCases(sys.modules[__name__]) - - -if __name__ == '__main__': - unittest.main(defaultTest='test_suite') diff --git a/py65/tests/devices/test_klaus_dormann_functional.py b/py65/tests/devices/test_klaus_dormann_functional.py new file mode 100644 index 0000000..a13e1fd --- /dev/null +++ b/py65/tests/devices/test_klaus_dormann_functional.py @@ -0,0 +1,82 @@ +"""Harness for running Klaus Dormann's 65(c)02 functional test suite + +These are quite comprehensive test suites for 65(c)02 implementation +correctness, but they're licensed under the GPL so we cannot include +the binary object code here (this is just the test harness for executing +them in py65). + +Obtain the object files from +https://github.com/Klaus2m5/6502_65C02_functional_tests instead. +""" + +import os.path +import sys +import unittest + +import py65.devices.mpu6502 +import py65.devices.mpu65c02 +from py65.tests.devices import functional_tests + + +def run_klaus_dormann_test(mpu_class, filename, load_addr, success_addr, + trace=False): + executor = functional_tests.FunctionalTestExecutor( + mpu_class, filename, load_addr) + + if trace: + tracer = executor.always_trace_predicate + else: + tracer = executor.never_trace_predicate + + executor.execute( + 0x400, executor.address_completion_predicate({success_addr}), tracer) + + mpu = executor.mpu + assert mpu.pc == success_addr, ( + "%s 0xb=%02x 0xc=%02x 0xd=%02x 0xf=%02x" % ( + mpu, mpu.memory[0xb], mpu.memory[0xc], mpu.memory[0xd], + mpu.memory[0xf]) + ) + + +class KlausDormannTests(unittest.TestCase): + """Runs Klaus Dormann's 6502-based test suites""" + + def test6502FunctionalTest(self): + filename = "devices/6502_functional_test.bin" + if not os.path.exists(filename): + self.skipTest("%s not available, skipping") + + functional_tests.trace_on_assertion( + run_klaus_dormann_test, + py65.devices.mpu6502.MPU, + filename, + load_addr=0x0, + success_addr=0x3399 + ) + + def test65c02ExtendedOpcodeTest(self): + # Modified version of 65C02_extended_opcodes_test that defines + # rkwl_wdc_op = 0 (don't test BBR/BBS instructions, which we do not + # implement) and many of the NOP tests for undefined opcodes which + # are not yet implemented here. + filename = "devices/65C02_extended_opcodes_test_modified.bin" + + if not os.path.exists(filename): + self.skipTest("%s not available, skipping") + + functional_tests.trace_on_assertion( + run_klaus_dormann_test, + py65.devices.mpu65c02.MPU, + filename, + load_addr=0xa, + success_addr=0x1570 + ) + + +def test_suite(): + return unittest.findTestCases(sys.modules[__name__]) + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') From d6ccff26adba061ee6797ecd4a2a907802214956 Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 20 Aug 2019 23:02:42 +0100 Subject: [PATCH 17/18] Rename to decimal_flags_use_adjusted_results --- py65/devices/mpu6502.py | 6 +++--- py65/devices/mpu65c02.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/py65/devices/mpu6502.py b/py65/devices/mpu6502.py index c5d3a74..23b343c 100644 --- a/py65/devices/mpu6502.py +++ b/py65/devices/mpu6502.py @@ -299,9 +299,9 @@ class MPU: self.FlagsNZ(self.a) def opADC(self, x): - return self._opADC(x, flags_use_adjusted_result=False) + return self._opADC(x, decimal_flags_use_adjusted_result=False) - def _opADC(self, x, flags_use_adjusted_result): + def _opADC(self, x, decimal_flags_use_adjusted_result): data = self.ByteAt(x()) if self.p & self.DECIMAL: @@ -333,7 +333,7 @@ class MPU: self.p &= ~(self.CARRY | self.OVERFLOW | self.NEGATIVE | self.ZERO) - if flags_use_adjusted_result: # 65C02 and 65816 + if decimal_flags_use_adjusted_result: # 65C02 and 65816 # Z and N use adjusted (i.e. decimal) result zerores = adjresult negativeres = adjresult diff --git a/py65/devices/mpu65c02.py b/py65/devices/mpu65c02.py index 5993f47..d0b309d 100644 --- a/py65/devices/mpu65c02.py +++ b/py65/devices/mpu65c02.py @@ -64,7 +64,7 @@ class MPU(mpu6502.MPU): self.memory[address] = m & ~self.a def opADC(self, x): - return self._opADC(x, flags_use_adjusted_result=True) + return self._opADC(x, decimal_flags_use_adjusted_result=True) def opSBC(self, x): return self._opSBC(x, decimal_flags_use_adjusted_result=True) From d4459daa4053e9a683f831b3308fe6625d6cb066 Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 20 Aug 2019 23:04:46 +0100 Subject: [PATCH 18/18] Tweak wording --- py65/tests/devices/bcd/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py65/tests/devices/bcd/README b/py65/tests/devices/bcd/README index 697a3c1..c976107 100644 --- a/py65/tests/devices/bcd/README +++ b/py65/tests/devices/bcd/README @@ -1,4 +1,4 @@ -These were compiled with the ca65 assembler available at http://cc65.org +These .bin files were assembled with ca65 available at http://cc65.org $ ca65 --cpu 6502 6502_decimal_test.c65 --listing 6502_decimal_test.lst $ ld65 -t none -o 6502_decimal_test.bin 6502_decimal_test.o