Improve the lemonade_tone disassembly slightly, improve the analysis procedure in...
authorNick Downing <nick@ndcode.org>
Fri, 20 May 2022 10:10:50 +0000 (20:10 +1000)
committerNick Downing <nick@ndcode.org>
Fri, 20 May 2022 10:10:50 +0000 (20:10 +1000)
lemonade/Makefile
lemonade/lemonade_flash_disasm.sed
lemonade/lemonade_tone_disasm.sed
test/lemonade_tone_patched.py [new file with mode: 0755]
test/rom_wait.py [new file with mode: 0755]
test/timing.py [deleted file]

index 82f2cd2..1f1ab8e 100644 (file)
@@ -59,7 +59,11 @@ lemonade_tone_patched.asm: lemonade_tone.asm lemonade_tone.asm.patch
        cp lemonade_tone.asm $@
        patch $@ <lemonade_tone.asm.patch
 
-lemonade_tone.asm: bootable.dsk ../orig/Lemonade_Stand_1979_Apple.do
+lemonade_tone.asm: \
+lemonade_tone_disasm.txt \
+lemonade_tone_disasm.sed \
+bootable.dsk \
+../orig/Lemonade_Stand_1979_Apple.do
        rm -f ../linapple-pie/Printer.txt
        tr '\n' '\r' <lemonade_tone_disasm.txt |\
 ( \
@@ -113,7 +117,11 @@ lemonade_flash_patched.asm: lemonade_flash.asm lemonade_flash.asm.patch
        cp lemonade_flash.asm $@
        patch $@ <lemonade_flash.asm.patch
 
-lemonade_flash.asm: bootable.dsk ../orig/Lemonade_Stand_1979_Apple.do
+lemonade_flash.asm: \
+lemonade_flash_disasm.txt \
+lemonade_flash_disasm.sed \
+bootable.dsk \
+../orig/Lemonade_Stand_1979_Apple.do
        rm -f ../linapple-pie/Printer.txt
        tr '\n' '\r' <lemonade_flash_disasm.txt |\
 ( \
index a22e9e6..6fca5b1 100644 (file)
@@ -5,6 +5,6 @@
   s/#l\./#$/
   s/\(,[xy]\)\t/\1/
   s/brk\t/dfb\t0/
-  s/^l.3600/\torg\t$3600\n/
+  s/^l.3600/\torg\t$3600\n&/
   p
 }
index 25386f5..db422a7 100644 (file)
@@ -5,6 +5,6 @@
   s/#l\./#$/
   s/\(,[xy]\)\t/\1/
   s/brk\t/dfb\t0/
-  s/^l.0302/\torg\t$0302\n/
+  s/^l.0302/\torg\t$0302\n&/
   p
 }
diff --git a/test/lemonade_tone_patched.py b/test/lemonade_tone_patched.py
new file mode 100755 (executable)
index 0000000..645b3a0
--- /dev/null
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+
+import numpy
+import py65.devices.mpu6502
+
+CPU_HZ = 3579545 * 4 / 14 # 1.02272714 MHz (from NTSC clock)
+
+mem = [0] * 0x10000
+cpu = py65.devices.mpu6502.MPU(memory = mem, pc = 0x400)
+
+def bload(bin_in):
+  with open(bin_in, 'rb') as fin:
+    bin = list(fin.read())
+    load_addr = bin[0] + (bin[1] << 8)
+    load_size = bin[2] + (bin[3] << 8)
+    assert len(bin) == load_size + 4
+  mem[load_addr:load_addr + load_size] = bin[4:]
+
+bload('../lemonade/lemonade_tone_patched.obj')
+
+# jsr 0x0302
+#mem[0x400:0x403] = [0x20, 0x02, 0x03]
+
+# jsr 0x0310
+# enter here to avoid period error due to entry x = 0
+mem[0x400:0x403] = [0x20, 0x10, 0x03]
+# then run it another 15 times preserving returned x
+mem[0x403:0x430] = [0x20, 0x0d, 0x03] * 15
+
+x = []
+y = []
+for period_count in [1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0xff]:
+  for duration_count in [1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0xff]:
+    mem[0x300] = period_count
+    #mem[0x301] = duration_count
+    cpu.reset()
+    toggles = 0
+    while cpu.pc != 0x430: #0x403:
+      if cpu.pc >= 0x400:
+        mem[0x301] = duration_count
+      if cpu.pc == 0x302:
+       toggles += 1
+      cpu.step()
+    duration = cpu.processorCycles / CPU_HZ / 16
+    cycles = toggles / 2 / 16
+    print(f'period_count {period_count:02x} duration_count {duration_count:02x} cycles {cycles:10.3f} duration {duration:10.6f}')
+    x.append([1, duration_count, duration_count / period_count])
+    y.append([cycles, duration])
+x = numpy.array(x, numpy.double)
+y = numpy.array(y, numpy.double)
+
+p, _, _, _ = numpy.linalg.lstsq(x, y, rcond = None)
+print('p', p)
+
+print(numpy.concatenate([x @ p, y], 1))
+
+# example: generate 440 Hz for 0.125s (55 cycles)
+y = numpy.array([55., .125], numpy.double)
+x = numpy.linalg.solve(p[1:, :].transpose(), y - p[0, :])
+print('x', x)
+duration_count = round(x[0])
+period_count = round(x[0] / x[1])
+
+mem[0x300] = period_count
+#mem[0x301] = duration_count
+cpu.reset()
+toggles = 0
+while cpu.pc != 0x430: #0x403:
+  if cpu.pc >= 0x400:
+    mem[0x301] = duration_count
+  if cpu.pc == 0x302:
+   toggles += 1
+  cpu.step()
+duration = cpu.processorCycles / CPU_HZ / 16
+cycles = toggles / 2 / 16
+print(f'period_count {period_count:02x} duration_count {duration_count:02x} cycles {cycles:10.3f} duration {duration:10.6f}')
diff --git a/test/rom_wait.py b/test/rom_wait.py
new file mode 100755 (executable)
index 0000000..3430682
--- /dev/null
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+
+import math
+import numpy
+import py65.devices.mpu6502
+
+CPU_HZ = 3579545 * 4 / 14 # 1.02272714 MHz (from NTSC clock)
+
+mem = [0] * 0x10000
+cpu = py65.devices.mpu6502.MPU(memory = mem, pc = 0x400)
+
+# monitor ROM
+# fca8: 38           WAIT        sec
+# fca9: 48           WAIT2       pha
+# fcaa: e9 01        WAIT3       sbc     #$01            ;1.0204 usec [wrong]
+# fcac: d0 fc                    bne     WAIT3           ;(13+2712*A+512*A*A) [wrong]
+# fcae: 68                       pla
+# fcaf: e9 01                    sbc     #$01
+# fcb1: d0 f6                    bne     WAIT2
+# fcb3: 60                       rts
+mem[0xfca8:0xfcb4] = [
+  0x38,
+  0x48,
+  0xe9,
+  0x01,
+  0xd0,
+  0xfc,
+  0x68,
+  0xe9,
+  0x01,
+  0xd0,
+  0xf6,
+  0x60
+]
+
+# jsr 0xfca8
+mem[0x400:0x403] = [0x20, 0xa8, 0xfc]
+
+x = []
+y = []
+for duration_count in [1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0xff]:
+  cpu.reset()
+  cpu.a = duration_count
+  while cpu.pc != 0x403:
+    cpu.step()
+  duration = cpu.processorCycles / CPU_HZ
+  print(f'duration_count {duration_count:02x} duration {duration:10.6f}')
+  x.append(duration_count)
+  y.append(duration)
+x = numpy.array(x, numpy.double)
+y = numpy.array(y, numpy.double)
+
+A = (
+  x[:, numpy.newaxis] **
+    numpy.array([0, 1, 2], numpy.int32)[numpy.newaxis, :]
+)
+p, _, _, _ = numpy.linalg.lstsq(A, y, rcond = None)
+print('p', p)
+
+print(numpy.stack([A @ p, y], 1))
+
+# example: delay for 0.125 s
+[c, b, a] = list(p)
+c -= .125
+x0 = (-b - math.sqrt(b * b - 4. * a * c)) / (2. * a)
+print('x0', x0)
+x1 = (-b + math.sqrt(b * b - 4. * a * c)) / (2. * a)
+print('x1', x1)
+duration_count = round(x1)
+
+cpu.reset()
+cpu.a = duration_count
+while cpu.pc != 0x403:
+  cpu.step()
+duration = cpu.processorCycles / CPU_HZ
+print(f'duration_count {duration_count:02x} duration {duration:10.6f}')
diff --git a/test/timing.py b/test/timing.py
deleted file mode 100755 (executable)
index 00350a8..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-#!/usr/bin/env python3
-
-import py65.devices.mpu6502
-import sys
-
-CPU_HZ = 3579545 * 4 / 14 # 1.02272714 MHz (from NTSC clock)
-
-mem = [0] * 0x10000
-
-# lemonade.bas
-
-# 10100POKE770,173:POKE771,48:POKE772,192:POKE773,136:POKE774,208:POKE775,5:POKE776,206:POKE777,1:POKE778,3:POKE779,240:POKE780,9:POKE781,202
-mem[770] = 173
-mem[771] = 48
-mem[772] = 192
-mem[773] = 136
-mem[774] = 208
-mem[775] = 5
-mem[776] = 206
-mem[777] = 1
-mem[778] = 3
-mem[779] = 240
-mem[780] = 9
-mem[781] = 202
-
-#10110POKE782,208:POKE783,245:POKE784,174:POKE785,0:POKE786,3:POKE787,76:POKE788,2:POKE789,3:POKE790,96:POKE791,0:POKE792,0
-mem[782] = 208
-mem[783] = 245
-mem[784] = 174
-mem[785] = 0
-mem[786] = 3
-mem[787] = 76
-mem[788] = 2
-mem[789] = 3
-mem[790] = 96
-#mem[791] = 0
-#mem[792] = 0
-
-cpu = py65.devices.mpu6502.MPU(memory = mem, pc = 0x400)
-
-print('jsr 0x0302')
-mem[0x400] = 0x20
-mem[0x401] = 0x02
-mem[0x402] = 0x03
-total_duration_count = 0
-total_duration_secs = 0.
-total_period_count = 0
-total_period_secs = 0.
-for duration_count in [0x10, 0x20, 0x40, 0x80]:
-  for period_count in [0x10, 0x20, 0x40, 0x80]:
-    mem[0x300] = period_count
-    mem[0x301] = duration_count
-    cpu.reset()
-    toggles = 0
-    while cpu.pc != 0x403:
-      if cpu.pc == 0x302:
-        toggles += 1
-      cpu.step()
-    duration_secs = cpu.processorCycles / CPU_HZ
-    period_secs = duration_secs * 2 / toggles
-    frequency = 1. / period_secs
-    print(f'duration_count {duration_count:02x} duration_secs {duration_secs:.6f} period_count {period_count:02x} period_secs {period_secs:.9f} frequency {frequency:10.3f}')
-    total_duration_count += duration_count
-    total_duration_secs += duration_secs
-    total_period_count += period_count
-    total_period_secs += period_secs
-print(f'duration_secs / duration_count = {total_duration_secs / total_duration_count:.6e}')
-print(f'period_secs / period_count = {total_period_secs / total_period_count:.6e}')
-print(f'period_count / period_secs = {total_period_count / total_period_secs:.6e}')
-print()
-
-# monitor ROM
-
-# fca8: 38           WAIT        sec
-# fca9: 48           WAIT2       pha
-# fcaa: e9 01        WAIT3       sbc     #$01            ;1.0204 usec [wrong]
-# fcac: d0 fc                    bne     WAIT3           ;(13+2712*A+512*A*A) [wrong]
-# fcae: 68                       pla
-# fcaf: e9 01                    sbc     #$01
-# fcb1: d0 f6                    bne     WAIT2
-# fcb3: 60                       rts
-mem[0xfca8] = 0x38
-mem[0xfca9] = 0x48
-mem[0xfcaa] = 0xe9
-mem[0xfcab] = 0x01
-mem[0xfcac] = 0xd0
-mem[0xfcad] = 0xfc
-mem[0xfcae] = 0x68
-mem[0xfcaf] = 0xe9
-mem[0xfcb0] = 0x01
-mem[0xfcb1] = 0xd0
-mem[0xfcb2] = 0xf6
-mem[0xfcb3] = 0x60
-
-print('jsr 0xfca8')
-mem[0x400] = 0x20 # jsr
-mem[0x401] = 0xa8
-mem[0x402] = 0xfc
-total_duration_count = 0
-total_duration_secs = 0.
-for duration_count in [0x10, 0x20, 0x40, 0x80]:
-  cpu.reset()
-  cpu.a = duration_count
-  while cpu.pc != 0x403:
-    cpu.step()
-  duration_secs = cpu.processorCycles / CPU_HZ
-  frequency = 1. / period_secs
-  print(f'duration_count {duration_count:02x} duration_secs {duration_secs:.6f}')
-  total_duration_count += duration_count
-  total_duration_secs += duration_secs
-print(f'duration_secs / duration_count = {total_duration_secs / total_duration_count:.6e}')
-print()