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 |\
( \
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 |\
( \
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
}
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
}
--- /dev/null
+#!/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}')
--- /dev/null
+#!/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}')
+++ /dev/null
-#!/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()