Add ribbit, and various language features needed to make it parse and execute
authorNick Downing <nick@ndcode.org>
Tue, 24 May 2022 11:44:39 +0000 (21:44 +1000)
committerNick Downing <nick@ndcode.org>
Tue, 24 May 2022 11:44:39 +0000 (21:44 +1000)
12 files changed:
.gitignore
apple_io.py
applesoft_basic.t
applesoft_basic.y
ribbit/Makefile [new file with mode: 0644]
ribbit/bootable.txt [new file with mode: 0644]
ribbit/ribbit.bas.patch [new file with mode: 0644]
ribbit/ribbit_tone_asm.txt [new file with mode: 0644]
ribbit/ribbit_tone_disasm.sed [new file with mode: 0644]
ribbit/ribbit_tone_disasm.txt [new file with mode: 0644]
ribbit/tone.asm [new file with mode: 0644]
ribbit/tone_asm.txt [new file with mode: 0644]

index 2922576..f2ee7b5 100644 (file)
@@ -22,6 +22,11 @@ __pycache__
 /orig/Apple_DOS_v3.3_1980_Apple.do
 /orig/DOS_Tool_Kit_v1.0_1980_Apple.do
 /orig/Lemonade_Stand_1979_Apple.do
+/ribbit/bootable.dsk
+/ribbit/ribbit.bas
+/ribbit/ribbit_tone.asm
+/ribbit/ribbit_patched.bas
+/ribbit/ribbit_patched.dsk
 /test/bootable.dsk
 /test/lemonade_music.bas
 /tok_to_bas.py
index 8311318..53660bb 100644 (file)
@@ -175,15 +175,20 @@ colors = [
   0xf, # white -> intense white
 ]
 
+# see ribbit.bas
+RBOOT_ENTRY = 0x208
+RLOAD_START = 0xb000 # for now
+RLOAD_ENTRY = 0xb003 # for now
+
 # see lemonade_tone.lst
 LEMONADE_TONE_PERIOD = 0x300
 LEMONADE_TONE_DUR = 0x301
-LEMONADE_TONE_TONE = 0x302
+LEMONADE_TONE_ENTRY = 0x302
 
 # see little_brick_out_tone.lst
 LITTLE_BRICK_OUT_TONE_PERIOD = 6
 LITTLE_BRICK_OUT_TONE_DUR = 7
-LITTLE_BRICK_OUT_TONE_TONE = 0x300
+LITTLE_BRICK_OUT_TONE_ENTRY = 0x300
 
 # see tone.lst
 TONE_REST = 0x300
@@ -236,7 +241,8 @@ mem = {
   HW_PB2: 0,
 }
 pdl_value = [255 for i in range(4)]
-gr_on = False
+current_gr = False
+current_speed = 255
 
 def init():
   global termios_attr, pcm
@@ -330,7 +336,7 @@ def _print(data):
       )
       low_mem[addr + low_mem[ZP_CH]] = data
 
-      if gr_on and low_mem[ZP_CV] < 20:
+      if current_gr and low_mem[ZP_CV] < 20:
         write(gr_encode([data])[0] + invflg())
       else:
         write(ch)
@@ -338,6 +344,9 @@ def _print(data):
       if low_mem[ZP_CH] >= low_mem[ZP_WNDLFT] + low_mem[ZP_WNDWTH]:
         crlf()
 
+      if current_speed < 255:
+        time.sleep((255 - current_speed) / 2550.) # up to .1s
+
 def get_internal():
   global ch_in
 
@@ -523,7 +532,11 @@ def call(addr):
   global flash_color0
 
   addr &= 0xffff
-  if addr == LITTLE_BRICK_OUT_TONE_TONE:
+  if addr == RBOOT_ENTRY:
+    pass
+  elif addr == RLOAD_ENTRY:
+    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
@@ -551,7 +564,7 @@ def call(addr):
     #  tone(1. / period, duration)
     #else:
     #  time.sleep(duration)
-  elif addr == LEMONADE_TONE_TONE:
+  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
@@ -602,7 +615,7 @@ def bascalc(y):
   return 0x400 | ((y & 7) << 7) | ((y >> 3) * 40)
 
 def text():
-  global gr_on
+  global current_gr
 
   # save cursor, set scrolling region, restore cursor
   write('\x1b[s\x1b[1;24r\x1b[u')
@@ -610,10 +623,10 @@ def text():
   low_mem[ZP_WNDWTH] = 40
   low_mem[ZP_WNDTOP] = 0
   low_mem[ZP_WNDBTM] = 24
-  gr_on = False
+  current_gr = False
 
 def gr():
-  global gr_on
+  global current_gr
 
   # clear screen, set scrolling region (homes cursor)
   #write(f'\x1b[2J\x1b[21;24r\x1b[1;21H')
@@ -627,7 +640,7 @@ def gr():
   for i in range(20):
     addr = bascalc(i)
     low_mem[addr:addr + 40] = [0] * 40
-  gr_on = True
+  current_gr = True
 
 # takes memory encoded with upper pixel in bits 0-3, lower in bits 4-7
 # returns a stream of unicode characters with attribute-setting escapes
@@ -699,7 +712,7 @@ def gr_update(x0, y0, x1, y1):
     addr = bascalc(i)
     data, attr = (
       gr_encode(low_mem[addr + x0:addr + x1], attr)
-    if gr_on and i < 20 else
+    if current_gr and i < 20 else
       text_encode(low_mem[addr + x0:addr + x1], attr)
     )
     write(f'\x1b[{i + 1:d};{x0 + 1:d}H{data:s}')
@@ -749,7 +762,8 @@ def vlin(y0, y1, x):
   gr_update(x, y0 >> 1, x + 1, (y1 >> 1) + 1)
 
 def usr(n):
-  return 0
+  # for ribbit (HRCG)
+  return RLOAD_START
 
 def scrn(x, y):
   i = y & 1
@@ -762,3 +776,7 @@ def pdl(n):
 
 def pos():
   return low_mem[ZP_CH]
+
+def speed(n):
+  global current_speed
+  current_speed = n & 0xff
index 4488900..75ec80a 100644 (file)
@@ -227,7 +227,8 @@ class Context:
     k = 0,
     l = 1,
     m = 0,
-    clicks = 0
+    clicks = 0,
+    onerr = -1
   ):
     self.program = program if program is not None else Program()
     self.variables = variables if variables is not None else {}
@@ -245,6 +246,9 @@ class Context:
     # count peek(-16336) within a statement, convert to a tone
     self.clicks = clicks
 
+    # program index for ONERR GOTO (not used for anything yet)
+    self.onerr = onerr
+
   def run(self):
     try:
       self.execute()
@@ -300,12 +304,18 @@ class Context:
       self.variables[name] = variable
     return variable
 
-  def find_array(self, name):
-    # array is created and initialized to 11 0s if not existent
+  def find_array(self, name, n_dim):
+    # array is created with 11^n_dim zeroed entries if non-existent
     array = self.arrays.get(name)
     if array is None:
       null_value = '' if name[-1] == '$' else 0 if name[-1] == '%' else 0.
-      array = [Variable(null_value) for i in range(11)]
+      def create_array(i):
+        return (
+          Variable(null_value)
+        if i >= n_dim else
+          [create_array(i + 1) for j in range(11)]
+        )
+      array = create_array(0)
       self.arrays[name] = array
     return array
 
@@ -560,7 +570,8 @@ def execute(self, context):
   assert False
 @method(StatementHGR)
 def execute(self, context):
-  assert False
+  # for ribbit (HRCG)
+  pass
 @method(StatementHColorEqual)
 def execute(self, context):
   assert False
@@ -625,7 +636,7 @@ def execute(self, context):
   apple_io.lomem(value)
 @method(StatementOnErr)
 def execute(self, context):
-  assert False
+  context.onerr = context.find_line(self.children[0].int_value)
 @method(StatementResume)
 def execute(self, context):
   assert False
@@ -637,7 +648,12 @@ def execute(self, context):
   assert False
 @method(StatementSpeedEqual)
 def execute(self, context):
-  assert False
+  value = self.children[0].get_int(context)
+  if value < 0:
+    raise Exception(
+      f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
+    )
+  apple_io.speed(value)
 @method(StatementLet)
 def execute(self, context):
   value = self.children[1].get(context)
@@ -1087,7 +1103,7 @@ def find_variable(self, context):
 @method(LValueArray)
 def find_variable(self, context):
   name = self.children[0].str_value
-  variable = context.find_array(name)
+  variable = context.find_array(name, len(self.children) - 1)
   for i in self.children[1:]:
     if not isinstance(variable, list):
       # too many subscripts
index 94e87bd..3a21bc0 100644 (file)
@@ -187,14 +187,14 @@ statement_opt
   | %space (?E{t_def.StatementVLin}TOKEN_VLIN rvalue ',' rvalue TOKEN_AT rvalue)
   | %space (?E{t_def.StatementHGR2}TOKEN_HGR2)
   | %space (?E{t_def.StatementHGR}TOKEN_HGR)
-  | %space (?E{t_def.StatementHColorEqual}TOKEN_HCOLOR_EQUAL)
+  | %space (?E{t_def.StatementHColorEqual}TOKEN_HCOLOR_EQUAL rvalue)
   | %space (?E{t_def.StatementHPlot}TOKEN_HPLOT)
   | %space (?E{t_def.StatementDraw}TOKEN_DRAW)
   | %space (?E{t_def.StatementXDraw}TOKEN_XDRAW)
   | %space (?E{t_def.StatementHTab}TOKEN_HTAB rvalue)
   | %space (?E{t_def.StatementHome}TOKEN_HOME)
-  | %space (?E{t_def.StatementRotEqual}TOKEN_ROT_EQUAL)
-  | %space (?E{t_def.StatementScaleEqual}TOKEN_SCALE_EQUAL)
+  | %space (?E{t_def.StatementRotEqual}TOKEN_ROT_EQUAL rvalue)
+  | %space (?E{t_def.StatementScaleEqual}TOKEN_SCALE_EQUAL rvalue)
   | %space (?E{t_def.StatementShLoad}TOKEN_SHLOAD)
   | %space (?E{t_def.StatementTrace}TOKEN_TRACE)
   | %space (?E{t_def.StatementNoTrace}TOKEN_NOTRACE)
@@ -206,11 +206,11 @@ statement_opt
   | %space (?E{t_def.StatementVTab}TOKEN_VTAB rvalue)
   | %space (?E{t_def.StatementHimemColon}TOKEN_HIMEM_COLON rvalue)
   | %space (?E{t_def.StatementLomemColon}TOKEN_LOMEM_COLON rvalue)
-  | %space (?E{t_def.StatementOnErr}TOKEN_ONERR)
+  | %space (?E{t_def.StatementOnErr}TOKEN_ONERR TOKEN_GOTO INT_LITERAL)
   | %space (?E{t_def.StatementResume}TOKEN_RESUME)
   | %space (?E{t_def.StatementRecall}TOKEN_RECALL)
   | %space (?E{t_def.StatementStore}TOKEN_STORE)
-  | %space (?E{t_def.StatementSpeedEqual}TOKEN_SPEED_EQUAL)
+  | %space (?E{t_def.StatementSpeedEqual}TOKEN_SPEED_EQUAL rvalue)
   | %space (?E{t_def.StatementLet}lvalue TOKEN_EQUAL rvalue)
   | %space (?E{t_def.StatementLet}TOKEN_LET lvalue TOKEN_EQUAL rvalue)
   | %space (?E{t_def.StatementGoto}TOKEN_GOTO INT_LITERAL)
@@ -321,7 +321,7 @@ rvalue
   | %space (?E{t_def.RValueSgn}TOKEN_SGN '(' rvalue ')')
   | %space (?E{t_def.RValueInt}TOKEN_INT '(' rvalue ')')
   | %space (?E{t_def.RValueAbs}TOKEN_ABS '(' rvalue ')')
-  | %space (?E{t_def.RValueUsr}TOKEN_USR '(' rvalue ')')
+  | %space (?E{t_def.RValueUsr}TOKEN_USR '(' rvalue ')' ',' STR_LITERAL)
   | %space (?E{t_def.RValueFre}TOKEN_FRE '(' rvalue ')')
   | %space (?E{t_def.RValueScrnLParen}TOKEN_SCRN_LPAREN rvalue ',' rvalue ')')
   | %space (?E{t_def.RValuePdl}TOKEN_PDL '(' rvalue ')')
diff --git a/ribbit/Makefile b/ribbit/Makefile
new file mode 100644 (file)
index 0000000..484bbc9
--- /dev/null
@@ -0,0 +1,132 @@
+DOS33=../dos33fsprogs/utils/dos33fs-utils/dos33
+MKDOS33FS=../dos33fsprogs/utils/dos33fs-utils/mkdos33fs
+TOK_TO_BIN=../tok_to_bin.py
+BIN_TO_TOK=../bin_to_tok.py
+TOK_TO_BAS=../tok_to_bas.py
+BAS_TO_TOK=../bas_to_tok.py
+
+.PHONY: all
+all: ribbit_patched.dsk ribbit_tone.obj
+
+ribbit_patched.dsk: ribbit_patched.bin tone.obj bootable.dsk
+       cp bootable.dsk $@
+       ${DOS33} $@ DELETE HELLO
+       ${DOS33} $@ SAVE A ribbit_patched.bin "RIBBIT PATCHED"
+       ${DOS33} $@ SAVE B tone.obj TONE.OBJ
+       ${DOS33} $@ HELLO "RIBBIT PATCHED"
+
+ribbit_patched.bin: ribbit_patched.tok
+       ${TOK_TO_BIN} <$< >$@
+
+ribbit_patched.tok: ribbit_patched.bas
+       ${BAS_TO_TOK} <$< >$@
+
+ribbit_patched.bas: ribbit.bas ribbit.bas.patch
+       cp ribbit.bas $@
+       patch $@ <ribbit.bas.patch
+
+ribbit.bas: ribbit.tok
+       ${TOK_TO_BAS} <$< >$@
+
+ribbit.tok: ribbit.bin
+       ${BIN_TO_TOK} <$< >$@
+
+ribbit.bin: ../orig/DOS_Tool_Kit_v1.0_1980_Apple.do
+       ${DOS33} $< LOAD RIBBIT $@
+
+tone.obj: \
+tone.asm \
+tone_asm.txt \
+bootable.dsk \
+../orig/DOS_Tool_Kit_v1.0_1980_Apple.do
+       cp ../orig/DOS_Tool_Kit_v1.0_1980_Apple.do .
+       tr '\t\na-z' ' \rA-Z' <tone.asm |\
+LC_ALL=C tr '\000-\177' '\200-\377' >__temp__.asm
+       ${MKDOS33FS} __temp__.dsk
+       ${DOS33} __temp__.dsk SAVE T __temp__.asm TONE.ASM
+       rm -f ../linapple-pie/Printer.txt
+       tr '\n' '\r' <tone_asm.txt |\
+( \
+  cd ../linapple-pie && \
+  ./linapple -b -1 ../ribbit/DOS_Tool_Kit_v1.0_1980_Apple.do -2 ../ribbit/__temp__.dsk \
+)
+       tr -d '\r' <../linapple-pie/Printer.txt >tone.lst
+       ${DOS33} __temp__.dsk LOAD TONE.OBJ $@
+       rm DOS_Tool_Kit_v1.0_1980_Apple.do __temp__.asm __temp__.dsk
+
+ribbit_tone.obj: \
+ribbit_tone.asm \
+ribbit_tone_asm.txt \
+bootable.dsk \
+../orig/DOS_Tool_Kit_v1.0_1980_Apple.do
+       cp ../orig/DOS_Tool_Kit_v1.0_1980_Apple.do .
+       tr '\t\na-z' ' \rA-Z' <ribbit_tone.asm |\
+LC_ALL=C tr '\000-\177' '\200-\377' >__temp__.asm
+       ${MKDOS33FS} __temp__.dsk
+       ${DOS33} __temp__.dsk SAVE T __temp__.asm "RIBBIT TONE.ASM"
+       rm -f ../linapple-pie/Printer.txt
+       tr '\n' '\r' <ribbit_tone_asm.txt |\
+( \
+  cd ../linapple-pie && \
+  ./linapple -b -1 ../ribbit/DOS_Tool_Kit_v1.0_1980_Apple.do -2 ../ribbit/__temp__.dsk \
+)
+       tr -d '\r' <../linapple-pie/Printer.txt >ribbit_tone.lst
+       ${DOS33} __temp__.dsk LOAD "RIBBIT TONE.OBJ" $@
+       rm DOS_Tool_Kit_v1.0_1980_Apple.do __temp__.asm __temp__.dsk
+
+ribbit_tone.asm: \
+ribbit_tone_disasm.txt \
+ribbit_tone_disasm.sed \
+bootable.dsk \
+../orig/DOS_Tool_Kit_v1.0_1980_Apple.do
+       rm -f ../linapple-pie/Printer.txt
+       tr '\n' '\r' <ribbit_tone_disasm.txt |\
+( \
+  cd ../linapple-pie && \
+  ./linapple -b -1 ../ribbit/bootable.dsk -2 ../orig/DOS_Tool_Kit_v1.0_1980_Apple.do \
+)
+       tr -d '\r' <../linapple-pie/Printer.txt |\
+sed -nf ribbit_tone_disasm.sed >__temp__.asm
+       sed -ne 's/^\(l\.[0-9a-f]\+\).*/\1/p' <__temp__.asm >__defs__.txt
+       sed -ne 's/^.\+\(l\.[0-9a-f]\+\).*/\1/p' <__temp__.asm >__refs__.txt
+       for i in `cat __defs__.txt`; \
+do \
+  if ! grep -q $$i __refs__.txt; \
+  then \
+    sed -e "s/$$i//" -i __temp__.asm; \
+  fi; \
+done
+       ( \
+  for i in `LC_ALL=C sort <__refs__.txt |uniq`; \
+  do \
+    if ! grep -q $$i __defs__.txt; \
+    then \
+      echo $$i; \
+    fi; \
+  done |sed -e 's/^l.\(.*\)/&\tequ\t$$\1/'; \
+  cat __temp__.asm; \
+) >$@
+       rm __temp__.asm __defs__.txt __refs__.txt
+
+bootable.dsk: ../orig/Apple_DOS_v3.3_1980_Apple.do bootable.txt
+       dd if=/dev/zero of=$@ count=35 bs=4096
+       tr '\n' '\r' <../ribbit/bootable.txt |\
+( \
+  cd ../linapple-pie && \
+  ./linapple -b -1 ../orig/Apple_DOS_v3.3_1980_Apple.do -2 ../ribbit/$@ \
+)
+
+clean:
+       rm -f \
+ribbit_patched.dsk \
+ribbit_patched.bin \
+ribbit_patched.tok \
+ribbit_patched.bas \
+ribbit.bas \
+ribbit.tok \
+ribbit.bin \
+bootable.dsk \
+__temp__.asm \
+__temp__.dsk \
+__defs__.txt \
+__refs__.txt
diff --git a/ribbit/bootable.txt b/ribbit/bootable.txt
new file mode 100644 (file)
index 0000000..b027cda
--- /dev/null
@@ -0,0 +1,4 @@
+NEW
+10 HOME
+20 PRINT "HELLO"
+INIT HELLO,D2
diff --git a/ribbit/ribbit.bas.patch b/ribbit/ribbit.bas.patch
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ribbit/ribbit_tone_asm.txt b/ribbit/ribbit_tone_asm.txt
new file mode 100644 (file)
index 0000000..522f5d2
--- /dev/null
@@ -0,0 +1,5 @@
+BRUN EDASM.OBJ
+PR#1
+DR 2
+ASM RIBBIT TONE.ASM,RIBBIT TONE.OBJ
+
diff --git a/ribbit/ribbit_tone_disasm.sed b/ribbit/ribbit_tone_disasm.sed
new file mode 100644 (file)
index 0000000..ed0e06d
--- /dev/null
@@ -0,0 +1,9 @@
+/^0302-/,/^031C-/{
+  y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
+  s/^\([^ ]\{4\}\)-   \([^ ]\{2\}\( [^ ]\{2\}\)*\) \+\([^ ]\{3\}\)   \(.*\)/l.\1\t\4\t\5\t\t\t\2/
+  s/\$/l./
+  s/#l\./#$/
+  s/\(,[xy]\)\t/\1/
+  s/^l.0302/\torg\t$0302\n&/
+  p
+}
diff --git a/ribbit/ribbit_tone_disasm.txt b/ribbit/ribbit_tone_disasm.txt
new file mode 100644 (file)
index 0000000..1e72497
--- /dev/null
@@ -0,0 +1,6 @@
+LOAD RIBBIT,D2
+10950 RETURN
+GOSUB 10900
+PR#1
+CALL -151
+302L
diff --git a/ribbit/tone.asm b/ribbit/tone.asm
new file mode 100644 (file)
index 0000000..ee672e8
--- /dev/null
@@ -0,0 +1,26 @@
+spkr   equ     $c030
+       org     $300
+rest   lda     #0
+       sta     freql
+       sta     freqh
+tone   ldx     #0
+durl   equ     *-1
+       ldy     #0
+durh   equ     *-1
+loop   lda     #0
+freql  equ     *-1
+       adc     #0
+countl equ     *-1
+       sta     countl
+       lda     #0
+freqh  equ     *-1
+       adc     #0
+counth equ     *-1
+       sta     counth
+       bcc     nospkr
+       lda     spkr
+nospkr inx
+       bne     loop
+       iny
+       bne     loop
+       rts
diff --git a/ribbit/tone_asm.txt b/ribbit/tone_asm.txt
new file mode 100644 (file)
index 0000000..f51e61c
--- /dev/null
@@ -0,0 +1,5 @@
+BRUN EDASM.OBJ
+PR#1
+DR 2
+ASM TONE.ASM,TONE.OBJ
+