Add joystick support, and keyboard ready-polling support via HW_IOADR
authorNick Downing <nick@ndcode.org>
Mon, 23 May 2022 11:46:32 +0000 (21:46 +1000)
committerNick Downing <nick@ndcode.org>
Tue, 24 May 2022 02:29:05 +0000 (12:29 +1000)
apple_io.py
apple_joystick.py [new file with mode: 0644]
applesoft_basic.py
applesoft_basic.t
test_pdl.bas [new file with mode: 0644]

index 9bfa047..22e8089 100644 (file)
@@ -11,6 +11,9 @@ import tty
 PCM_RATE = 44100
 PCM_PERIODSIZE = 441
 
+# target will yield this much time whenever it polls HW_IOADR
+POLL_TIMEOUT_MS = 1
+
 # see https://www.tinaja.com/ebooks/tearing_rework.pdf
 ZP_WNDLFT = 0x20 # Left side of scroll window
 ZP_WNDWTH = 0x21 # Width of scroll window
@@ -221,8 +224,13 @@ mem = {
   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(40)]
+pdl_value = [255 for i in range(4)]
 
 def init():
   global termios_attr, pcm
@@ -254,9 +262,6 @@ def pr_hash(n):
 def in_hash(n):
   pass
 
-def read_ready():
-  return len(poll_in.poll(1)) != 0
-
 def read(n):
   return str(os.read(fd_in, 1), 'utf-8')
 
@@ -295,6 +300,10 @@ def _print(data):
         crlf()
 
 def get():
+  # if there is a lookahead from PEEK of HW_IOADR, cancel and return it
+  if mem[HW_IOADR] >= 0x80:
+    mem[HW_IOADR] &= 0x7f
+    return chr(HW_IOADR)
   ch = read(1) 
   if len(ch) == 0:
     raise Exception('end of input') # due to piping or input redirection
@@ -394,6 +403,9 @@ def lomem(addr):
 
 def peek(addr):
   addr &= 0xffff
+  if addr == HW_IOADR:
+    if mem[HW_IOADR] < 0x80 and len(poll_in.poll(POLL_TIMEOUT_MS)):
+      mem[HW_IOADR] = ord(read(1)) | 0x80
   return mem.get(addr, 0)
 
 def poke(addr, data):
@@ -558,7 +570,7 @@ def scrn(x, y):
   return gr_mem[y][x]
 
 def pdl(n):
-  return 127
+  return pdl_value[n]
 
 def pos():
   return mem[ZP_CH]
diff --git a/apple_joystick.py b/apple_joystick.py
new file mode 100644 (file)
index 0000000..10c9a23
--- /dev/null
@@ -0,0 +1,71 @@
+import apple_io
+import evdev
+import select
+import sys
+import threading
+
+POLL_TIMEOUT_MS = 1 # program will take this long to exit
+
+input_path = None
+input_device = None
+input_thread = None
+
+thread = None
+stop = False
+poll = select.poll()
+
+def init():
+  global input_device, poll, stop, thread
+  assert input_device is None
+  if input_path is not None:
+    # we will not update these until we get some movement,
+    # so start with a reasonable value (not timeout value)
+    apple_io.pdl_value[:3] = [128, 128, 128]
+
+    input_device = evdev.InputDevice(input_path)
+
+    poll = select.poll()
+    poll.register(input_device.fd, select.POLLIN)
+
+    stop = False
+    thread = threading.Thread(target = run, daemon = True)
+    thread.start()
+
+def deinit():
+  global input_device, poll, stop, thread
+  if thread is not None:
+    stop = True
+    thread.join()
+    thread = None
+
+    poll = None
+
+    input_device.close()
+    input_device = None
+
+    # the timeout value generally indicates no joystick present
+    apple_io.pdl_value[:3] = [255, 255, 255]
+
+def run():
+  while not stop:
+    if len(poll.poll(POLL_TIMEOUT_MS)):
+      for event in input_device.read():
+        # the below event codes were determined by experiment using my
+        # Mad Catz, Inc. Mad Catz V.1 Stick (USB ID 0738:2237), I don't
+        # know whether other joysticks use consistent axis/button codes
+        if event.type == evdev.ecodes.EV_ABS:
+          #sys.stderr.write(f'abs code {event.code:d} value {event.value:d}\r\n')
+          if event.code == 0:
+            apple_io.pdl_value[0] = event.value & 0xff
+          elif event.code == 1:
+            apple_io.pdl_value[1] = event.value & 0xff
+          elif event.code == 5:
+            apple_io.pdl_value[2] = event.value & 0xff
+        elif event.type == evdev.ecodes.EV_KEY:
+          #sys.stderr.write(f'key code {event.code:d} value {event.value:d}\r\n')
+          if event.code == 288:
+            apple_io.mem[apple_io.HW_PB0] = 0x80 if event.value else 0
+          if event.code == 290:
+            apple_io.mem[apple_io.HW_PB1] = 0x80 if event.value else 0
+          if event.code == 291:
+            apple_io.mem[apple_io.HW_PB2] = 0x80 if event.value else 0
index 1c693b8..92d7dfd 100755 (executable)
@@ -19,6 +19,7 @@
 import apple_io
 import element
 import lex_yy
+import apple_joystick
 import sys
 import t_def
 import y_tab
@@ -40,34 +41,31 @@ while len(sys.argv) >= 2:
     apple_io.beep_style = beep_styles[sys.arg[1][7:]]
   elif sys.argv[1][:11] == '--gr-width=':
     apple_io.gr_width = int(sys.argv[1][11:])
+  elif sys.argv[1][:11] == '--joystick=':
+    apple_joystick.input_path = sys.argv[1][11:]
   else:
     break
   del sys.argv[1]
 if len(sys.argv) < 2:
-  print(f'usage: {sys.argv[0]:s} [--beep-style=alsa|vt100] [--gr-width=n] program.tok')
+  print(f'usage: {sys.argv[0]:s} [--beep-style=alsa|vt100] [--gr-width=n] [--joystick=/dev/input/eventNN] program.tok')
   sys.exit(EXIT_FAILURE)
 program_bas = sys.argv[1]
 
 with open(program_bas) as fin:
-  #print('y_tab.yyparse()')
   lex_yy.yyin = fin
   program = y_tab.yyparse(t_def.Program)
-
-#print('element.serialize()')
 #element.serialize(program, sys.stdout)
 
-#print('program.post_process()')
 program.post_process(program)
-
-#print('element.serialize()')
 #element.serialize(program, sys.stdout)
 
-#print('apple_io.init()')
 apple_io.init()
 try:
+  apple_joystick.init()
   #print('context.run()')
   context = t_def.Context(program)
   context.run()
 finally:
   #print('apple_io.deinit()')
   apple_io.deinit()
+  apple_joystick.deinit()
index aa3dcc8..2e35683 100644 (file)
@@ -865,6 +865,10 @@ def get(self, context):
 @method(RValuePdl)
 def get(self, context):
   value = self.children[0].get_int(context)
+  if value < 0 or value >= 4:
+    raise Exception(
+      f'?ILLEGAL QUANTITY ERROR IN {context.line_number():d}'
+    )
   return float(apple_io.pdl(value))
 @method(RValuePos)
 def get(self, context):
diff --git a/test_pdl.bas b/test_pdl.bas
new file mode 100644 (file)
index 0000000..261ae30
--- /dev/null
@@ -0,0 +1,3 @@
+10PRINTPDL(0);" ";PDL(1);" ";PDL(2);" ";PEEK(-16287)>=128;" ";PEEK(-16286)>=128;" ";PEEK(-16285)>=128
+20IFPEEK(-16384)<128THEN10
+30GETI$