HRCG and joystick improvements, can now play ribbit game (still a bit rough)
authorNick Downing <nick@ndcode.org>
Wed, 25 May 2022 07:35:03 +0000 (17:35 +1000)
committerNick Downing <nick@ndcode.org>
Wed, 25 May 2022 07:35:03 +0000 (17:35 +1000)
apple_io.py
apple_joystick.py
applesoft_basic.py
hrcg/emu_65c02.c
hrcg/ribbit.sh [new file with mode: 0755]
hrcg/terminal.asm
ribbit/ribbit.bas.patch
test_io.bas

index 15bfece..23d4955 100644 (file)
@@ -176,9 +176,10 @@ colors = [
 ]
 
 # see ribbit.bas
+# hrcg is loaded at DOS addr - $801 = $9600 - $801 = $8dff
 RBOOT_ENTRY = 0x208
-RLOAD_START = 0xb000 # for now
-RLOAD_ENTRY = 0xb003 # for now
+HRCG_START = 0x8dff # initialization with banner
+HRCG_ENTRY = HRCG_START + 3 # initialization without banner
 
 # see lemonade_tone.lst
 LEMONADE_TONE_PERIOD = 0x300
@@ -257,7 +258,8 @@ def init():
     atexit.register(deinit)
     tty.setraw(fd_in)
     # auto wrap off, hide cursor, reset attributes
-    write('\x1b[?7l\x1b[?25l\x1b[0m')
+    if not hrcg:
+      write('\x1b[?7l\x1b[?25l\x1b[0m')
 
 def deinit():
   global termios_attr
@@ -266,8 +268,9 @@ def deinit():
     # reset to initial state, auto wrap on, show cursor, reset attributes
     # note: reset to initial state clears the screen which I do not like,
     # but it may be the only way to clear our change to the beep settings
-    #write('\x1bc\x1b[?7h\x1b[?25h\x1b[0m')
-    write('\x1b[?7h\x1b[?25h\x1b[0m')
+    if not hrcg:
+      #write('\x1bc\x1b[?7h\x1b[?25h\x1b[0m')
+      write('\x1b[?7h\x1b[?25h\x1b[0m')
 
     termios.tcsetattr(fd_in, termios.TCSADRAIN, termios_attr)
     atexit.unregister(deinit)
@@ -553,9 +556,7 @@ def call(addr):
   global flash_color0
 
   addr &= 0xffff
-  if addr == RBOOT_ENTRY:
-    pass
-  elif addr == RLOAD_ENTRY:
+  if addr == RBOOT_ENTRY or addr == HRCG_START or addr == HRCG_ENTRY:
     pass
   elif addr == LITTLE_BRICK_OUT_TONE_ENTRY:
     # for little brick out, see test/little_brick_out_tone.py
@@ -784,7 +785,7 @@ def vlin(y0, y1, x):
 
 def usr(n):
   # for ribbit (HRCG)
-  return RLOAD_START
+  return HRCG_START
 
 def scrn(x, y):
   i = y & 1
index 10c9a23..ffc532a 100644 (file)
@@ -14,13 +14,20 @@ thread = None
 stop = False
 poll = select.poll()
 
+flip_x = False
+flip_y = False
+swap_axes = False
+swap_buttons = False
+
 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]
+    # with joystick present it is centred and has buttons not pressed
+    apple_io.pdl_value[:3] = [0x7f] * 3
+    apple_io.mem[apple_io.HW_PB0] = 0
+    apple_io.mem[apple_io.HW_PB1] = 0
+    apple_io.mem[apple_io.HW_PB2] = 0
 
     input_device = evdev.InputDevice(input_path)
 
@@ -43,8 +50,11 @@ def deinit():
     input_device.close()
     input_device = None
 
-    # the timeout value generally indicates no joystick present
-    apple_io.pdl_value[:3] = [255, 255, 255]
+    # with no joystick present it has the timeout value and buttons pressed
+    apple_io.pdl_value[:3] = [0xff] * 3
+    apple_io.mem[apple_io.HW_PB0] = 0x80
+    apple_io.mem[apple_io.HW_PB1] = 0x80
+    apple_io.mem[apple_io.HW_PB2] = 0x80
 
 def run():
   while not stop:
@@ -56,16 +66,20 @@ def run():
         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
+            apple_io.pdl_value[0 + swap_axes] = \
+              (event.value & 0xff) ^ (flip_x * 0xff)
           elif event.code == 1:
-            apple_io.pdl_value[1] = event.value & 0xff
+            apple_io.pdl_value[1 - swap_axes] = \
+              (event.value & 0xff) ^ (flip_y * 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
+            apple_io.mem[apple_io.HW_PB0 + swap_buttons] = \
+              (event.value != 0) * 0x80
+          if event.code == 289:
+            apple_io.mem[apple_io.HW_PB1 - swap_buttons] = \
+              (event.value != 0) * 0x80
           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
+            apple_io.mem[apple_io.HW_PB2] = (event.value != 0) * 0x80
index 75ceb67..3ad6b09 100755 (executable)
@@ -44,12 +44,24 @@ while len(sys.argv) >= 2:
   elif sys.argv[1][:24] == '--inter-statement-delay=':
     t_def.inter_statement_delay = float(sys.argv[1][24:])
   elif sys.argv[1][:11] == '--joystick=':
-    apple_joystick.input_path = sys.argv[1][11:]
+    fields = sys.argv[1][11:].split(',')
+    apple_joystick.input_path = fields[0]
+    for i in fields[1:]:
+      if i == 'flip_x':
+        apple_joystick.flip_x = True
+      elif i == 'flip_y':
+        apple_joystick.flip_y = True
+      elif i == 'swap_axes':
+        apple_joystick.swap_axes = True
+      elif i == 'swap_buttons':
+        apple_joystick.swap_buttons = True
+      else:
+        assert False
   else:
     break
   del sys.argv[1]
 if len(sys.argv) < 2:
-  print(f'usage: {sys.argv[0]:s} [--beep-style=alsa|vt100] [--inter-statement-delay=secs] [--joystick=/dev/input/eventNN] program.tok')
+  print(f'usage: {sys.argv[0]:s} [--beep-style=alsa|vt100] [--inter-statement-delay=secs] [--joystick=/dev/input/eventNN[,flip_x|flip_y|swap_axes|swap_buttons,...]] program.tok')
   sys.exit(EXIT_FAILURE)
 program_bas = sys.argv[1]
 
index 8ee93c9..83b2157 100644 (file)
@@ -12,6 +12,7 @@
 #define APPLE_WIDTH 560
 #define APPLE_HEIGHT 192
 #define APPLE_HIRES0 0x2000
+#define APPLE_HIRES1 0x4000
 
 // includes cool down sequence
 #define PADDED_WIDTH (APPLE_WIDTH + 3)
 #define IO_KBDSTRB 0xc010
 #define IO_SPKR 0xc030
 #define IO_TXTCLR 0xc050
+#define IO_TXTSET 0xc051
 #define IO_MIXCLR 0xc052
+#define IO_MIXSET 0xc053
 #define IO_LOWSCR 0xc054
+#define IO_HISCR 0xc055
 #define IO_HIRES 0xc057
 #define IO_PB0 0xc061
 #define IO_PB1 0xc062
@@ -47,6 +51,8 @@
 #define USLEEP_LO 0xc0f6
 #define USLEEP_HI 0xc0f7
 #define SYS_EXIT 0xc0f8
+#define DOS_LO 0xc0f9
+#define DOS_HI 0xc0fa
 
 #define RESET_VECTOR 0xfffc
 
@@ -109,6 +115,8 @@ struct trace {
 #endif
 uint8_t key_waiting;
 uint8_t usleep_lo;
+uint8_t dos_lo;
+int hires = APPLE_HIRES0;
 int exit_flag;
 
 int bload(char *name) {
@@ -124,8 +132,15 @@ int bload(char *name) {
       ;
     c = *q;
     *q++ = 0;
-    if (*p == 'A' || *p == 'a')
-      load_address = atoi(p + 1);
+    if (*p == 'a') {
+      ++p;
+      int base = 10;
+      if (*p == '$') {
+        ++p;
+        base = 16;
+      }
+      load_address = (int)strtol(p, NULL, base);
+    }
     else {
       fprintf(stderr, "unknown BLOAD option: %s\n", p);
       exit(EXIT_FAILURE);
@@ -154,6 +169,27 @@ int bload(char *name) {
   return load_address;
 }
 
+void dos(char *line) {
+  //fprintf(stderr, "dos: %s\n", line);
+  if (memcmp(line, "BLOAD", 5) == 0) {
+    char *p;
+    for (p = line + 5; *p == ' '; ++p)
+      ;
+    if (strcmp(p, "RBOOT") != 0) {
+      for (char *q = p; *q; ++q)
+        if (*q == ' ')
+          *q = '_';
+        else if (*q >= 'A' && *q <= 'Z')
+          *q += 'a' - 'A';
+      bload(p);
+    }
+  }
+  else {
+    fprintf(stderr, "unrecognized DOS command: %s\n", line);
+    exit(EXIT_FAILURE);
+  }
+}
+
 uint8_t mem_read(uint16_t addr, bool isDbg) {
   if ((addr & 0xff00) != IO_PAGE) {
 #if 0 // breakpoint
@@ -178,6 +214,11 @@ uint8_t mem_read(uint16_t addr, bool isDbg) {
   switch (addr) {
   case IO_KBD:
     return key_waiting;
+  case IO_LOWSCR:
+    hires = APPLE_HIRES0;
+    break;
+  case IO_HISCR:
+    hires = APPLE_HIRES1;
     break;
   case STDIN_DATA:
     {
@@ -222,6 +263,8 @@ uint8_t mem_read(uint16_t addr, bool isDbg) {
     }
   case USLEEP_LO:
     return usleep_lo;
+  case DOS_LO:
+    return dos_lo;
   }
   return 0xff;
 }
@@ -236,6 +279,12 @@ void mem_write(uint16_t addr, uint8_t val) {
   case IO_KBDSTRB:
     key_waiting &= 0x7f;
     break;
+  case IO_LOWSCR:
+    hires = APPLE_HIRES0;
+    break;
+  case IO_HISCR:
+    hires = APPLE_HIRES1;
+    break;
   case STDOUT_DATA:
     if (write(STDOUT_FILENO, &val, 1) == -1) {
       perror("write()");
@@ -258,6 +307,12 @@ void mem_write(uint16_t addr, uint8_t val) {
     exit_flag = val | 0x100;
     vrEmu6502Jam(cpu);
     break;
+  case DOS_LO:
+    dos_lo = val;
+    break;
+  case DOS_HI:
+    dos((char *)(mem + (dos_lo | (val << 8))));
+    break;
   }
 }
 
@@ -413,7 +468,7 @@ int main(int argc, char **argv) {
     memset(frame, 0, WINDOW_HEIGHT * WINDOW_WIDTH * sizeof(uint32_t));
     for (int i = 0; i < APPLE_HEIGHT; ++i) {
       int line =
-        APPLE_HIRES0 |
+        hires |
         (i >> 6) * 40 |
         ((i & 0x38) << 4) |
         ((i & 7) << 10);
diff --git a/hrcg/ribbit.sh b/hrcg/ribbit.sh
new file mode 100755 (executable)
index 0000000..5cabf07
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+../applesoft_basic.py --hrcg --joystick=/dev/input/event19,flip_y,swap_buttons ../ribbit/ribbit_patched.tok |\
+./emu_65c02 monitor_rom.obj hrcg.obj terminal.obj
index 8a202f8..2037eeb 100644 (file)
@@ -4,8 +4,11 @@ wndtop equ     $22
 wndbtm equ     $23
 ch     equ     $24
 cv     equ     $25
-state  equ     $fe
-number equ     $ff
+prevch equ     $fc
+state  equ     $fd
+prevnum        equ     $fe
+num    equ     $ff
+linebuf        equ     $200
 chario equ     $3ea
 hrcg   equ     $8dff
 extfont        equ     hrcg+7
@@ -21,6 +24,8 @@ serr.s        equ     $c0f5
 slp.lo equ     $c0f6
 slp.hi equ     $c0f7
 s.exit equ     $c0f8
+dos.lo equ     $c0f9
+dos.hi equ     $c0fa
 cout   equ     $fded
        org     $800
        lda     #0
@@ -39,6 +44,8 @@ cout  equ     $fded
        lda     #<font
        sta     extfont+1
        jsr     hrcg
+       lda     #$d                     cr
+       sta     prevch
        ldx     #0
 newst  stx     state
 loop   lda     sout.s
@@ -50,58 +57,98 @@ loop        lda     sout.s
        sta     sout.d
        jmp     loop
 nosout lda     sin.s
-       bpl     nosin
-       lda     sin.d
+       bmi     issin
+       lda     >1000
+       sta     slp.lo
+       lda     <1000
+       sta     slp.hi
+       jmp     loop
+issin  lda     sin.d
        ldx     state
        beq     state0
        dex
        beq     state1
        dex
        beq     state2
-       lda     #1
+       dex
+       bne     notst3
+       jmp     state3
+notst3 lda     #1
        sta     s.exit
-state0 cmp     #$1b                    esc
+state0 cmp     #4                      ctrl-d
+       bne     notctld
+       ldy     prevch
+       cpy     #$d                     but only after cr
+       bne     notctld
+       stx     num                     use num as linebuf index
+       ldx     #3
+       jmp     newst
+notctld        cmp     #$1b                    esc
        bne     notesc
        inx
        jmp     newst
+notesc sta     prevch
+       ora     #$80
+       jsr     cout
+       jmp     loop
 state1 cmp     #$5b                    [
-       bne     aborte
-       stx     number
+       bne     newst                   invalid escape sequence
+       stx     prevnum
+       stx     num
        ldx     #2
        jmp     newst
 state2 cmp     #$30                    0
-       bcc     aborte
+       bcc     newst                   invalid escape sequence
        cmp     #$3a
        bcs     notdig
        sec
        sbc     #$30                    digit value
-       asl     number
+       asl     num
        clc
-       adc     number                  + number * 2
-       asl     number
-       asl     number
+       adc     num                     + num * 2
+       asl     num
+       asl     num
        clc
-       adc     number                  + number * 8
-       sta     number                  to number
+       adc     num                     + num * 8
+       sta     num                     to num
+       jmp     loop
+notdig cmp     #$3b                    ;
+       bne     notscln
+       lda     num
+       sta     prevnum
+       stx     num
        jmp     loop
-notdig cmp     #$47                    G
+notscln        cmp     #$47                    G
        bne     notch
-       dec     number
-       lda     number
+       dec     num
+       lda     num
        sta     ch
        jmp     newst
 notch  cmp     #$64                    d
-       bne     aborte
-       dec     number
-       lda     number
+       bne     notcv
+       dec     num
+       lda     num
        sta     cv
        jmp     newst
-aborte stx     state
-notesc ora     #$80
-       jsr     cout
-       jmp     loop
-nosin  lda     >1000
-       sta     slp.lo
-       lda     <1000
-       sta     slp.hi
+notcv  cmp     #$72                    r
+       bne     notwnd
+       dec     prevnum
+       lda     prevnum
+       sta     wndtop
+       lda     num
+       sta     wndbtm
+notwnd jmp     newst                   invalid escape sequence
+state3 ldy     num
+       cmp     #$d
+       bne     notcr
+       txa
+       sta     linebuf,y
+       lda     #>linebuf
+       sta     dos.lo
+       lda     #<linebuf
+       sta     dos.hi
+       jmp     newst
+notcr  sta     linebuf,y
+       iny
+       sty     num
        jmp     loop
index e69de29..c636332 100644 (file)
@@ -0,0 +1,30 @@
+--- ribbit.bas 2022-05-25 17:23:30.448202796 +1000
++++ ribbit_patched.bas 2022-05-25 17:29:46.852217967 +1000
+@@ -1,6 +1,7 @@
+ 10GOTO10000:REM INITIALIZE
+ 20GOSUB900:GOTO1000
+ 100REM FLUTTER BY
++105FORJ=1TO75:NEXT
+ 120BC=NOTBC:IFBCTHENVTABBY:HTABBX:PRINTBUG$((BD<0),2):RETURN
+ 130IFBX>1ANDBX<38THEN150
+ 140VTABBY:HTABBX:PRINTBUG$(0,0):BY=INT(RND(1)*8)*2+3:BX=(RND(1)<.5)*37+1:BD=1-(BX>20)*2:BS=BS+1:GOSUB2000
+@@ -37,7 +38,7 @@
+ 850PRINT" RIB*BIT ";:RETURN
+ 900REM TURN AROUND
+ 910VTABFY:HTABFX:PRINTFROG$(FD,1);:GOSUB100:RETURN
+-1000FORI=1TO100:NEXT:REM MAIN LOOP
++1000REM MAIN LOOP
+ 1010FORI=1TORND(1)*400+100:GOSUB100
+ 1020P0=PDL(0):IFP0<80ANDFDTHENFD=0:GOSUB900
+ 1030IFP0>160ANDNOTFDTHENFD=1:GOSUB900
+@@ -139,8 +140,8 @@
+ 20110VTAB10:PRINT"\13DO YOU KNOW HOW TO PLAY THIS GAME?\v"
+ 20120PRINTIA$;IT$;:VTAB1:HTAB1:PRINT"   ";
+ 20200FY=1:FORFX=2TO32STEP2
+-20210VTABFY:HTABFX:PRINTFROG$(1,3):FORI=1TO20:NEXT
+-20220VTABFY:HTABFX+1:PRINTFROG$(1,4):FORI=1TO20:NEXT
++20210VTABFY:HTABFX:PRINTFROG$(1,3):FORI=1TO100:NEXT
++20220VTABFY:HTABFX+1:PRINTFROG$(1,4):FORI=1TO100:NEXT
+ 20230VTABFY:HTABFX+2:PRINTFROG$(1,1)
+ 20240FORI=1TO100:NEXT:NEXT
+ 20250FORI=1TO1000:NEXT:VTABFY:HTABFX:PRINTIP$;FROG$(0,1)
index 635ed99..d12fc30 100644 (file)
@@ -1,6 +1,8 @@
+5 SPEED=10
 10 HOME
 20 HTAB 10
 30 INVERSE:PRINT "MY PROGRAM":NORMAL
+32 SPEED=255
 35 FOR I=0 TO 31:POKE 1280+I,I*8:NEXT
 40 VTAB 5
 50 PRINT CHR$(7)"TYPE SOMETHING: ";