Refactor CALL implementation, to auto-detect tone routine, and be more readable
authorNick Downing <nick@ndcode.org>
Thu, 26 May 2022 05:28:38 +0000 (15:28 +1000)
committerNick Downing <nick@ndcode.org>
Thu, 26 May 2022 07:37:20 +0000 (17:37 +1000)
apple_io.py

index 70b3d40..ba8cc4f 100644 (file)
@@ -175,21 +175,65 @@ colors = [
   0xf, # white -> intense white
 ]
 
-# see ribbit.bas
+# see /ribbit/ribbit.bas
 # hrcg is loaded at DOS addr - $801 = $9600 - $801 = $8dff
 RBOOT_ENTRY = 0x208
 HRCG_START = 0x8dff # initialization with banner
 HRCG_ENTRY = HRCG_START + 3 # initialization without banner
 
-# see lemonade_tone.lst
+# see /lemonade/lemonade_tone.lst
 LEMONADE_TONE_PERIOD = 0x300
 LEMONADE_TONE_DUR = 0x301
-LEMONADE_TONE_ENTRY = 0x302
+LEMONADE_TONE_START = 0x302
+LEMONADE_TONE_END = 0x317
+LEMONADE_TONE_DATA = [
+  0xAD, 0x30, 0xC0,
+  0x88,
+  0xD0, 0x05,
+  0xCE, 0x01, 0x03,
+  0xF0, 0x09,
+  0xCA,
+  0xD0, 0xF5,
+  0xAE, 0x00, 0x03,
+  0x4C, 0x02, 0x03,
+  0x60,
+]
+
+# see /ribbit/ribbit_tone.lst
+# it is the same as lemonade_tone except for some context save/restore
+LEMONADE_TONE_END2 = 0x31d
+LEMONADE_TONE_DATA2 = [
+  0x20, 0x4A, 0xFF,
+  0xAD, 0x30, 0xC0,
+  0x88,
+  0xD0, 0x05,
+  0xCE, 0x01, 0x03,
+  0xF0, 0x09,
+  0xCA,
+  0xD0, 0xF5,
+  0xAE, 0x00, 0x03,
+  0x4C, 0x05, 0x03,
+  0x20, 0x3F, 0xFF,
+  0x60,
+]
 
-# see little_brick_out_tone.lst
+# see /little_brick_out/little_brick_out_tone.lst
 LITTLE_BRICK_OUT_TONE_PERIOD = 6
 LITTLE_BRICK_OUT_TONE_DUR = 7
-LITTLE_BRICK_OUT_TONE_ENTRY = 0x300
+LITTLE_BRICK_OUT_TONE_START = 0x300
+LITTLE_BRICK_OUT_TONE_END = 0x313
+LITTLE_BRICK_OUT_TONE_DATA = [
+  0xAD, 0x30, 0xC0,
+  0x88,
+  0xD0, 0x04,
+  0xC6, 0x07,
+  0xF0, 0x08,
+  0xCA,
+  0xD0, 0xF6,
+  0xA6, 0x06,
+  0x4C, 0x00, 0x03,
+  0x60,
+]
 
 # see tone.lst
 TONE_REST = 0x300
@@ -200,11 +244,11 @@ TONE_FREQL = 0x30d
 TONE_FREQH = 0x314
 
 # see lemonade_flash_patched.lst
-FLASH_INIT = 0x3600
-FLASH_INIT_PATCHED = 0x95ef
-FLASH_EXECUTE = 0x3603
-FLASH_EXECUTE_PATCHED = 0x9586
-flash_color0 = 0
+LEMONADE_FLASH_INIT = 0x3600
+LEMONADE_FLASH_INIT_PATCHED = 0x95ef
+LEMONADE_FLASH_EXECUTE = 0x3603
+LEMONADE_FLASH_EXECUTE_PATCHED = 0x9586
+lemonade_flash_color0 = 0
 
 # command line
 BEEP_STYLE_ALSA = 0
@@ -560,81 +604,123 @@ def poke(addr, data):
     else:
       mem[addr] = data
 
-def call(addr):
-  global flash_color0
+# often-used tone routines that are installed by POKEing to addresses 768+:
+def lemonade_tone():
+  # for lemonade or ribbit, see /test/lemonade_tone.py
+  period_count = ((low_mem[LEMONADE_TONE_PERIOD] - 1) & 0xff) + 1
+  duration_count = ((low_mem[LEMONADE_TONE_DUR] - 1) & 0xff) + 1
+  cycles = 1.37788799e-02 + duration_count * (
+    -4.21513128e-06 + 1.27999925e+02 / period_count
+  )
+  duration = 1.27361219e-05 + duration_count * (
+    2.50702246e-03 + 2.50310997e-03 / period_count
+  )
+  tone(cycles / duration, duration)
+
+def little_brick_out_tone():
+  # for little brick out, see /test/little_brick_out_tone.py
+  period_count = ((low_mem[LITTLE_BRICK_OUT_TONE_PERIOD] - 1) & 0xff) + 1
+  duration_count = ((low_mem[LITTLE_BRICK_OUT_TONE_DUR] - 1) & 0xff) + 1
+  cycles = 1.37788799e-02 + duration_count * (
+    -4.21513128e-06 + 1.27999925e+02 / period_count
+  )
+  duration = 1.27091766e-05 + duration_count * (
+    2.50897802e-03 + 2.25279897e-03 / period_count
+  )
+  tone(cycles / duration, duration)
+
+# my tone routine
+# - uses 16-bit duration, for longer maximum duration
+# - uses 16-bit frequency, for improved tuning accuracy
+# - allows zero frequency, for a "rest" or accurate delay
+# however, duration and frequency constants in the application must be
+# pre-computed, as code is simplified by not equalizing execution paths
+def tone_tone():
+  frequency_incr = low_mem[TONE_FREQL] + (low_mem[TONE_FREQH] << 8)
+  duration_count = -(low_mem[TONE_DURL] + (low_mem[TONE_DURH] << 8))
+  duration_count = ((duration_count - 1) & 0xffff) + 1
+  duration = 3.58309497e-10 + duration_count * (
+    2.34821712e-05 + frequency_incr * 4.47591203e-11
+  )
+  if frequency_incr:
+    period = 0x1fffe * (2.34821712e-05 / frequency_incr + 4.47591203e-11)
+    tone(1. / period, duration)
+  else:
+    time.sleep(duration)
 
+# the following pair of machine language subroutines for lemonade allow
+# application to quickly replace one colour on the GR screen with another
+def lemonade_flash_init():
+  global lemonade_flash_color0
+  lemonade_flash_color0 = low_mem[ZP_COLOR] & 0xf
+
+def lemonade_flash_execute():
+  global lemonade_flash_color0
+
+  time.sleep(.02)
+  color1 = low_mem[ZP_COLOR] & 0xf
+  for i in range(20):
+    x0 = 40
+    x1 = 0
+    addr = bascalc(i)
+    for j in range(40):
+      if (low_mem[addr + j] & 0xf) == lemonade_flash_color0:
+        low_mem[addr + j] = (low_mem[addr + j] & 0xf0) | color1
+        if j < x0:
+          x0 = j
+        x1 = j + 1
+      if (low_mem[addr + j] >> 4) == lemonade_flash_color0:
+        low_mem[addr + j] = (low_mem[addr + j] & 0xf) | (color1 << 4)
+        if j < x0:
+          x0 = j
+        x1 = j + 1
+    if x1 > x0:
+      gr_update(x0, i, x1, i + 1)
+  lemonade_flash_color0 = color1
+
+def call(addr):
   addr &= 0xffff
   if addr == RBOOT_ENTRY or addr == HRCG_START or addr == HRCG_ENTRY:
+    # these do not do anything locally, as they run in the terminal instead
     pass
-  elif addr == LITTLE_BRICK_OUT_TONE_ENTRY:
-    # for little brick out, see test/little_brick_out_tone.py
-    period_count = ((low_mem[LITTLE_BRICK_OUT_TONE_PERIOD] - 1) & 0xff) + 1
-    duration_count = ((low_mem[LITTLE_BRICK_OUT_TONE_DUR] - 1) & 0xff) + 1
-    cycles = 1.37788799e-02 + duration_count * (
-      -4.21513128e-06 + 1.27999925e+02 / period_count
-    )
-    duration = 1.27091766e-05 + duration_count * (
-      2.50897802e-03 + 2.25279897e-03 / period_count
+  elif (
+    addr == LEMONADE_TONE_START and (
+      low_mem[LEMONADE_TONE_START:LEMONADE_TONE_END] == LEMONADE_TONE_DATA or
+        low_mem[LEMONADE_TONE_START:LEMONADE_TONE_END2] == LEMONADE_TONE_DATA2
     )
-    tone(cycles / duration, duration)
+  ):
+    lemonade_tone()
+  elif (
+    addr == LITTLE_BRICK_OUT_TONE_START and
+      low_mem[LITTLE_BRICK_OUT_TONE_START:LITTLE_BRICK_OUT_TONE_END] ==
+        LITTLE_BRICK_OUT_TONE_DATA
+  ):
+    little_brick_out_tone()
+  elif addr == TONE_REST:
+    low_mem[TONE_FREQL] = 0
+    low_mem[TONE_FREQH] = 0
+    tone_tone()
   elif addr == TONE_REST or addr == TONE_TONE:
-    pass
-    ## for lemonade_patched, see test/tone.py
-    #if addr == TONE_REST:
-    #  low_mem[TONE_FREQL] = 0
-    #  low_mem[TONE_FREQH] = 0
-    #frequency_incr = low_mem[TONE_FREQL] + (low_mem[TONE_FREQH] << 8)
-    #duration_count = -(low_mem[TONE_DURL] + (low_mem[TONE_DURH] << 8))
-    #duration_count = ((duration_count - 1) & 0xffff) + 1
-    #duration = 3.58309497e-10 + duration_count * (
-    #  2.34821712e-05 + frequency_incr * 4.47591203e-11
-    #)
-    #if frequency_incr:
-    #  period = 0x1fffe * (2.34821712e-05 / frequency_incr + 4.47591203e-11)
-    #  tone(1. / period, duration)
-    #else:
-    #  time.sleep(duration)
-  elif addr == LEMONADE_TONE_ENTRY:
-    # for lemonade, see test/lemonade_tone.py
-    period_count = ((low_mem[LEMONADE_TONE_PERIOD] - 1) & 0xff) + 1
-    duration_count = ((low_mem[LEMONADE_TONE_DUR] - 1) & 0xff) + 1
-    cycles = 1.37788799e-02 + duration_count * (
-      -4.21513128e-06 + 1.27999925e+02 / period_count
-    )
-    duration = 1.27361219e-05 + duration_count * (
-      2.50702246e-03 + 2.50310997e-03 / period_count
-    )
-    tone(cycles / duration, duration)
-  elif addr == FLASH_INIT or addr == FLASH_INIT_PATCHED:
-    # for lemonade, lighting (init)
-    flash_color0 = low_mem[ZP_COLOR] & 0xf
-  elif addr == FLASH_EXECUTE or addr == FLASH_EXECUTE_PATCHED:
-    # for lemonade, lightning (execute)
-    time.sleep(.02)
-    flash_color1 = low_mem[ZP_COLOR] & 0xf
-    for i in range(20):
-      x0 = 40
-      x1 = 0
-      addr = bascalc(i)
-      for j in range(40):
-        if (low_mem[addr + j] & 0xf) == flash_color0:
-          low_mem[addr + j] = (low_mem[addr + j] & 0xf0) | flash_color1
-          if j < x0:
-            x0 = j
-          x1 = j + 1
-        if (low_mem[addr + j] >> 4) == flash_color0:
-          low_mem[addr + j] = (low_mem[addr + j] & 0xf) | (flash_color1 << 4)
-          if j < x0:
-            x0 = j
-          x1 = j + 1
-      if x1 > x0:
-        gr_update(x0, i, x1, i + 1)
-    flash_color0 = flash_color1
+    # note: will be loaded with PRINT CHR$(4);"BLOAD TONE.OBJ" which we do not
+    # emulate and therefore it does not appear in low_mem[], it has to go last
+    # (after the above checks for specific tone routines) to avoid a conflict
+    tone_tone()
+  elif (
+    addr == LEMONADE_FLASH_INIT or
+      addr == LEMONADE_FLASH_INIT_PATCHED
+  ):
+    lemonade_flash_init()
+  elif (
+    addr == LEMONADE_FLASH_EXECUTE or
+      addr == LEMONADE_FLASH_EXECUTE_PATCHED
+  ):
+    lemonade_flash_execute()
   elif addr == ROM_CLREOP:
     clreop()
   elif addr == ROM_BELL2:
     tone(1000, 100) # 1 kHz for .1 sec
   elif addr == ROM_HOME:
+    # note: was not in Applesoft 1 (cassette version) so can appear as CALL
     home()
   elif addr == ROM_CLEOL:
     cleol()