Eliminate gr_mem, instead emulate the proper memory layout in ordinary memory
authorNick Downing <nick@ndcode.org>
Mon, 23 May 2022 23:57:57 +0000 (09:57 +1000)
committerNick Downing <nick@ndcode.org>
Tue, 24 May 2022 02:45:30 +0000 (12:45 +1000)
apple_io.py

index 3251540..a8059c3 100644 (file)
@@ -206,31 +206,27 @@ fd_out = sys.stdout.fileno()
 ch_in = ''
 poll_in = select.poll()
 poll_in.register(fd_in, select.POLLIN)
+
+# following values were taken from a 48K Apple after normal boot and NEW
+low_mem = [0] * 0x800
+#low_mem[ZP_WNDLFT] = 0
+low_mem[ZP_WNDWTH] = 40
+#low_mem[ZP_WNDTOP] = 0
+low_mem[ZP_WNDBTM] = 24
+#low_mem[ZP_CH] = 0
+low_mem[ZP_CV] = 23 # likely value (depends on previous terminal activity)
+#low_mem[ZP_COLOR] = 0
+low_mem[ZP_LOMEM] = 4
+low_mem[ZP_LOMEM + 1] = 8
+#low_mem[ZP_HIMEM] = 0
+low_mem[ZP_HIMEM + 1] = 150
+
 mem = {
-  # following values were taken from a 48K Apple after normal boot and NEW
-  ZP_WNDLFT: 0,
-  ZP_WNDWTH: 40,
-  ZP_WNDTOP: 0,
-  ZP_WNDBTM: 24,
-  ZP_CH: 0,
-  ZP_CV: 23, # just a likely value (depends on previous terminal activity)
-  ZP_COLOR: 0,
-  ZP_LOMEM: 4,
-  ZP_LOMEM + 1: 8,
-  ZP_HIMEM: 0,
-  ZP_HIMEM + 1: 150,
-  TONE_PERIOD: 0,
-  TONE_DUR: 0,
-  NICK_DURL: 0,
-  NICK_DURH: 0,
-  NICK_FREQL: 0,
-  NICK_FREQH: 0,
   HW_IOADR: 0,
   HW_PB0: 0,
   HW_PB1: 0,
   HW_PB2: 0,
 }
-gr_mem = [[0 for j in range(40)] for i in range(48)]
 pdl_value = [255 for i in range(4)]
 
 def init():
@@ -287,39 +283,39 @@ def write(data):
 
 def crlf():
   write(
-    '\r\n' + (f'\x1b[{mem[ZP_WNDLFT]:d}C' if mem[ZP_WNDLFT] else '')
+    '\r\n' + (f'\x1b[{low_mem[ZP_WNDLFT]:d}C' if low_mem[ZP_WNDLFT] else '')
   )
-  mem[ZP_CV] += 1
-  if mem[ZP_CV] >= mem[ZP_WNDBTM]:
-    mem[ZP_CV] = mem[ZP_WNDBTM] - 1
-  mem[ZP_CH] = mem[ZP_WNDLFT]
+  low_mem[ZP_CV] += 1
+  if low_mem[ZP_CV] >= low_mem[ZP_WNDBTM]:
+    low_mem[ZP_CV] = low_mem[ZP_WNDBTM] - 1
+  low_mem[ZP_CH] = low_mem[ZP_WNDLFT]
 
 def _print(data):
   for ch in data:
     if ch == '\a':
       tone(1000., .1) # 1 kHz for .1 sec
     elif ch == '\b':
-      if mem[ZP_CH] > mem[ZP_WNDLFT]:
+      if low_mem[ZP_CH] > low_mem[ZP_WNDLFT]:
         write('\b')
-        mem[ZP_CH] -= 1
+        low_mem[ZP_CH] -= 1
     elif ch == '\n':
       write('\n')
-      mem[ZP_CV] += 1
-      if mem[ZP_CV] >= mem[ZP_WNDBTM]:
-        mem[ZP_CV] = mem[ZP_WNDBTM] - 1
+      low_mem[ZP_CV] += 1
+      if low_mem[ZP_CV] >= low_mem[ZP_WNDBTM]:
+        low_mem[ZP_CV] = low_mem[ZP_WNDBTM] - 1
     elif ch == '\r':
       # apple treats \r as \r\n, so if you write e.g. \r\n you'll get \r\n\n
       crlf()
     elif ord(ch) >= 0x20:
       # some applications expect BASL, BASH = base address of current line
-      addr = bascalc(mem[ZP_CV])
-      mem[ZP_BASL] = addr & 0xff
-      mem[ZP_BASH] = addr >> 8
-      mem[addr + mem[ZP_CH]] = (ord(ch) & 0x7f) | 0x80
+      addr = bascalc(low_mem[ZP_CV])
+      low_mem[ZP_BASL] = addr & 0xff
+      low_mem[ZP_BASH] = addr >> 8
+      low_mem[addr + low_mem[ZP_CH]] = (ord(ch) & 0x7f) | 0x80
 
       write(ch)
-      mem[ZP_CH] += 1
-      if mem[ZP_CH] >= mem[ZP_WNDLFT] + mem[ZP_WNDWTH]:
+      low_mem[ZP_CH] += 1
+      if low_mem[ZP_CH] >= low_mem[ZP_WNDLFT] + low_mem[ZP_WNDWTH]:
         crlf()
 
 def get_internal():
@@ -354,7 +350,7 @@ def input():
       if len(line):
         _print('\b')
         line = line[:-1]
-      elif mem[ZP_CH] > mem[ZP_WNDLFT]:
+      elif low_mem[ZP_CH] > low_mem[ZP_WNDLFT]:
         _print('\r')
     else:
       _print(ch)
@@ -372,11 +368,11 @@ def input():
 
 def htab(x):
   write(f'\x1b[{x:d}G')
-  mem[ZP_CH] = x - 1
+  low_mem[ZP_CH] = x - 1
 
 def vtab(y):
   write(f'\x1b[{y:d}d')
-  mem[ZP_CV] = y - 1
+  low_mem[ZP_CV] = y - 1
 
 def cleol():
   write('\x1b[K')
@@ -385,9 +381,9 @@ def clreop():
   write('\x1b[J')
 
 def home():
-  write(f'\x1b[2J\x1b[{mem[ZP_WNDLFT] + 1:d};{mem[ZP_WNDTOP] + 1:d}H')
-  mem[ZP_CH] = mem[ZP_WNDLFT]
-  mem[ZP_CV] = mem[ZP_WNDTOP]
+  write(f'\x1b[2J\x1b[{low_mem[ZP_WNDLFT] + 1:d};{low_mem[ZP_WNDTOP] + 1:d}H')
+  low_mem[ZP_CH] = low_mem[ZP_WNDLFT]
+  low_mem[ZP_CV] = low_mem[ZP_WNDTOP]
 
 def normal():
   write('\x1b[0m')
@@ -428,13 +424,13 @@ def tone(freq, dur): # Hz, s
 
 def himem(addr):
   addr &= 0xffff
-  mem[ZP_HIMEM] = addr & 0xff
-  mem[ZP_HIMEM + 1] = addr >> 8
+  low_mem[ZP_HIMEM] = addr & 0xff
+  low_mem[ZP_HIMEM + 1] = addr >> 8
 
 def lomem(addr):
   addr &= 0xffff
-  mem[ZP_LOMEM] = addr & 0xff
-  mem[ZP_LOMEM + 1] = addr >> 8
+  low_mem[ZP_LOMEM] = addr & 0xff
+  low_mem[ZP_LOMEM + 1] = addr >> 8
 
 def peek(addr):
   addr &= 0xffff
@@ -443,7 +439,7 @@ def peek(addr):
     #if len(ch_in) == 0 and len(poll_in.poll(POLL_TIMEOUT_MS)):
     if len(poll_in.poll(POLL_TIMEOUT_MS)):
       get_internal()
-  return mem.get(addr, 0)
+  return low_mem[addr] if addr < 0x800 else mem.get(addr, 0)
 
 def poke(addr, data):
   global ch_in
@@ -454,10 +450,15 @@ def poke(addr, data):
     mem[HW_IOADR] &= 0x7f
   else:
     data &= 0xff
-    mem[addr] = data
-    if addr == ZP_WNDTOP or addr == ZP_WNDBTM:
-      # save cursor, set scrolling region, restore cursor
-      write(f'\x1b[s\x1b[{mem[ZP_WNDTOP] + 1:d};{mem[ZP_WNDBTM]:d}r\x1b[u')
+    if addr < 0x800:
+      low_mem[addr] = data
+      if addr == ZP_WNDTOP or addr == ZP_WNDBTM:
+        # save cursor, set scrolling region, restore cursor
+        write(
+          f'\x1b[s\x1b[{low_mem[ZP_WNDTOP] + 1:d};{low_mem[ZP_WNDBTM]:d}r\x1b[u'
+        )
+    else:
+      mem[addr] = data
 
 def call(addr):
   global flash_color0
@@ -466,10 +467,10 @@ def call(addr):
   if addr == NICK_REST or addr == NICK_TONE:
     # for lemonade_patched, see test/lemonade_tone_nick.py
     if addr == NICK_REST:
-      mem[NICK_FREQL] = 0
-      mem[NICK_FREQH] = 0
-    frequency_incr = mem[NICK_FREQL] + (mem[NICK_FREQH] << 8)
-    duration_count = -(mem[NICK_DURL] + (mem[NICK_DURH] << 8))
+      low_mem[NICK_FREQL] = 0
+      low_mem[NICK_FREQH] = 0
+    frequency_incr = low_mem[NICK_FREQL] + (low_mem[NICK_FREQH] << 8)
+    duration_count = -(low_mem[NICK_DURL] + (low_mem[NICK_DURH] << 8))
     duration_count = ((duration_count - 1) & 0xffff) + 1
     duration = 3.58309497e-10 + duration_count * (
       2.34821712e-05 + frequency_incr * 4.47591203e-11
@@ -481,8 +482,8 @@ def call(addr):
       time.sleep(duration)
   elif addr == TONE_TONE or addr == TONE_TONE1:
     # for lemonade, see test/lemonade_tone_patched.py
-    period_count = ((mem[TONE_PERIOD] - 1) & 0xff) + 1
-    duration_count = ((mem[TONE_DUR] - 1) & 0xff) + 1
+    period_count = ((low_mem[TONE_PERIOD] - 1) & 0xff) + 1
+    duration_count = ((low_mem[TONE_DUR] - 1) & 0xff) + 1
     cycles = 1.37788799e-02 + duration_count * (
       -4.21513128e-06 + 1.27999925e+02 / period_count
     )
@@ -492,22 +493,23 @@ def call(addr):
     tone(cycles / duration, duration)
   elif addr == FLASH_INIT or addr == FLASH_INIT_PATCHED:
     # for lemonade, lighting (init)
-    flash_color0 = mem[ZP_COLOR]
+    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 = mem[ZP_COLOR]
+    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 gr_mem[i * 2][j] == flash_color0:
-          gr_mem[i * 2][j] = flash_color1
+        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 gr_mem[i * 2 + 1][j] == flash_color0:
-          gr_mem[i * 2 + 1][j] = flash_color1
+        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
@@ -531,22 +533,23 @@ def bascalc(y):
 def text():
   # save cursor, set scrolling region, restore cursor
   write('\x1b[s\x1b1;24r\x1b[u')
-  mem[ZP_WNDLFT] = 0
-  mem[ZP_WNDWTH] = 40
-  mem[ZP_WNDTOP] = 0
-  mem[ZP_WNDBTM] = 24
+  low_mem[ZP_WNDLFT] = 0
+  low_mem[ZP_WNDWTH] = 40
+  low_mem[ZP_WNDTOP] = 0
+  low_mem[ZP_WNDBTM] = 24
 
 def gr():
   # clear screen, set scrolling region (homes cursor)
   write(f'\x1b[2J\x1b[21;24r')
-  mem[ZP_WNDLFT] = 0
-  mem[ZP_WNDWTH] = 40
-  mem[ZP_WNDTOP] = 20
-  mem[ZP_WNDBTM] = 24
-  mem[ZP_CH] = 0
-  mem[ZP_CV] = 20
-  for i in range(40):
-    gr_mem[i][:] = [0 for j in range(40)]
+  low_mem[ZP_WNDLFT] = 0
+  low_mem[ZP_WNDWTH] = 40
+  low_mem[ZP_WNDTOP] = 20
+  low_mem[ZP_WNDBTM] = 24
+  low_mem[ZP_CH] = 0
+  low_mem[ZP_CV] = 20
+  for i in range(20):
+    addr = bascalc(i)
+    low_mem[addr:addr + 40] = [0] * 40
 
 def gr_update(x0, y0, x1, y1):
   write(f'\x1b7') # save all attributes (in case in INVERSE/FLASH mode)
@@ -555,9 +558,10 @@ def gr_update(x0, y0, x1, y1):
   br = -1
   for i in range(y0, y1):
     write(f'\x1b[{i + 1:d};{x0 * gr_width + 1:d}H')
+    addr = bascalc(i)
     for j in range(x0, x1):
-      new_bg = colors[gr_mem[i * 2][j]]
-      new_fg = colors[gr_mem[i * 2 + 1][j]]
+      new_bg = colors[low_mem[addr + j] & 0xf]
+      new_fg = colors[low_mem[addr + j] >> 4]
       # we cannot draw two different intense colours in same block,
       # so just do the best we can (the greater colour gets priority)
       if new_bg < new_fg:
@@ -591,36 +595,59 @@ def gr_update(x0, y0, x1, y1):
   write(f'\x1b8') # restore all attributes (in case in INVERSE/FLASH mode)
 
 def color(n):
-  mem[ZP_COLOR] = n
+  low_mem[ZP_COLOR] = n
 
 def plot(x, y):
-  gr_mem[y][x] = mem[ZP_COLOR]
-  y >>= 1
-  gr_update(x, y, x + 1, y + 1)
+  color = low_mem[ZP_COLOR] & 0xf
+  i = y & 1
+  j = y >> 1
+  addr = bascalc(j)
+  if i == 0:
+    low_mem[addr + x] = (low_mem[addr + x] & 0xf0) | color
+  else:
+    low_mem[addr + x] = (low_mem[addr + x] & 0xf) | (color << 4)
+  gr_update(x, j, x + 1, j + 1)
 
 def hlin(x0, x1, y):
+  color = low_mem[ZP_COLOR] & 0xf
   if x1 < x0:
     x0, x1 = x1, x0
-  for x in range(x0, x1 + 1):
-    gr_mem[y][x] = mem[ZP_COLOR]
-  y >>= 1
-  gr_update(x0, y, x1 + 1, y + 1)
+  i = y & 1
+  j = y >> 1
+  addr = bascalc(j)
+  if i == 0:
+    for x in range(x0, x1 + 1):
+      low_mem[addr + x] = (low_mem[addr + x] & 0xf0) | color
+  else:
+    for x in range(x0, x1 + 1):
+      low_mem[addr + x] = (low_mem[addr + x] & 0xf) | (color << 4)
+  gr_update(x0, j, x1 + 1, j + 1)
 
 def vlin(y0, y1, x):
+  color = low_mem[ZP_COLOR] & 0xf
   if y1 < y0:
     y0, y1 = y1, y0
   for y in range(y0, y1 + 1):
-    gr_mem[y][x] = mem[ZP_COLOR]
+    i = y & 1
+    j = y >> 1
+    addr = bascalc(j)
+    if i == 0:
+      low_mem[addr + x] = (low_mem[addr + x] & 0xf0) | color
+    else:
+      low_mem[addr + x] = (low_mem[addr + x] & 0xf) | (color << 4)
   gr_update(x, y0 >> 1, x + 1, (y1 >> 1) + 1)
 
 def usr(n):
   return 0
 
 def scrn(x, y):
-  return gr_mem[y][x]
+  i = y & 1
+  j = y >> 1
+  addr = bascalc(j)
+  return low_mem[addr + x] & 0xf if i == 0 else low_mem[addr + x] >> 4
 
 def pdl(n):
   return pdl_value[n]
 
 def pos():
-  return mem[ZP_CH]
+  return low_mem[ZP_CH]