Add basic HRCG terminal program (cut-down emulated apple running HRCG for now)
authorNick Downing <nick@ndcode.org>
Wed, 25 May 2022 00:56:51 +0000 (10:56 +1000)
committerNick Downing <nick@ndcode.org>
Wed, 25 May 2022 02:58:51 +0000 (12:58 +1000)
13 files changed:
.gitignore
.gitmodules
apple_io.py
applesoft_basic.py
hrcg/Makefile [new file with mode: 0644]
hrcg/bootable.txt [new file with mode: 0644]
hrcg/emu_65c02.c [new file with mode: 0644]
hrcg/hrcg_obj_bsave.txt [new file with mode: 0644]
hrcg/monitor_rom_obj_header.txt [new file with mode: 0644]
hrcg/terminal.asm [new file with mode: 0644]
hrcg/terminal_asm.txt [new file with mode: 0644]
hrcg/vrEmu6502 [new submodule]
orig/Makefile

index 4c8a9c3..15100b8 100644 (file)
@@ -1,11 +1,15 @@
 *.bin
-*.obj
 *.lst
+*.o
+*.obj
+*.pic
+*.set
 *.tok
 __pycache__
 /bas_to_tok.py
 /element.py
 /hrcg/bootable.dsk
+/hrcg/emu_65c02
 /lemonade/bootable.dsk
 /lemonade/lemonade.bas
 /lemonade/lemonade_flash.asm
@@ -19,6 +23,8 @@ __pycache__
 /little_brick_out/little_brick_out_tone.asm
 /little_brick_out/little_brick_out_patched.bas
 /little_brick_out/little_brick_out_patched.dsk
+/orig/apple2.rom
+/orig/apple_ii+_rom.zip
 /orig/Apple_DOS_3.3_Master.dsk
 /orig/Apple_DOS_v3.3_1980_Apple.do
 /orig/DOS_Tool_Kit_v1.0_1980_Apple.do
index c1699c3..99aff21 100644 (file)
@@ -4,3 +4,6 @@
 [submodule "linapple-pie"]
        path = linapple-pie
        url = https://github.com/nickd4/linapple-pie.git
+[submodule "hrcg/vrEmu6502"]
+       path = hrcg/vrEmu6502
+       url = https://github.com/nickd4/vrEmu6502
index 53660bb..703d983 100644 (file)
@@ -243,6 +243,7 @@ mem = {
 pdl_value = [255 for i in range(4)]
 current_gr = False
 current_speed = 255
+hrcg = False
 
 def init():
   global termios_attr, pcm
@@ -297,9 +298,10 @@ def write(data):
   os.write(fd_out, bytes(data, 'utf-8'))
 
 def crlf():
-  write(
-    '\r\n' + (f'\x1b[{low_mem[ZP_WNDLFT]:d}C' if low_mem[ZP_WNDLFT] else '')
-  )
+  if not hrcg:
+    write(
+      '\r\n' + (f'\x1b[{low_mem[ZP_WNDLFT]:d}C' if low_mem[ZP_WNDLFT] else '')
+    )
   low_mem[ZP_CV] += 1
   if low_mem[ZP_CV] >= low_mem[ZP_WNDBTM]:
     low_mem[ZP_CV] = low_mem[ZP_WNDBTM] - 1
@@ -320,6 +322,8 @@ def _print(data):
         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
+      if hrcg:
+        write('\r')
       crlf()
     elif ord(ch) >= 0x20:
       # some applications expect BASL, BASH = base address of current line
@@ -346,6 +350,8 @@ def _print(data):
 
       if current_speed < 255:
         time.sleep((255 - current_speed) / 2550.) # up to .1s
+    elif hrcg:
+      write(ch)
 
 def get_internal():
   global ch_in
index b4fdb1f..75ceb67 100755 (executable)
@@ -39,6 +39,8 @@ beep_styles = {
 while len(sys.argv) >= 2:
   if sys.argv[1][:7] == '--beep-style=':
     apple_io.beep_style = beep_styles[sys.arg[1][7:]]
+  elif sys.argv[1] == '--hrcg':
+    apple_io.hrcg = True
   elif sys.argv[1][:24] == '--inter-statement-delay=':
     t_def.inter_statement_delay = float(sys.argv[1][24:])
   elif sys.argv[1][:11] == '--joystick=':
diff --git a/hrcg/Makefile b/hrcg/Makefile
new file mode 100644 (file)
index 0000000..b3ac99f
--- /dev/null
@@ -0,0 +1,93 @@
+CFLAGS=-g -Wall -O3 -DVR_6502_EMU_STATIC=1
+LDFLAGS=-g
+
+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: \
+emu_65c02 \
+hrcg.obj \
+monitor_rom.obj \
+terminal.obj \
+frogs1.set \
+frogs2.set \
+lily_pad.pic
+
+emu_65c02: emu_65c02.o vrEmu6502/src/vrEmu6502.o
+       ${CC} ${LDFLAGS} -o $@ $^ -lSDL2
+
+hrcg.obj: \
+bootable.dsk \
+../orig/DOS_Tool_Kit_v1.0_1980_Apple.do
+       cp bootable.dsk __temp__.dsk
+       tr '\n' '\r' <hrcg_obj_bsave.txt |\
+( \
+  cd ../linapple-pie && \
+  ./linapple -b -1 ../hrcg/__temp__.dsk -2 ../orig/DOS_Tool_Kit_v1.0_1980_Apple.do \
+)
+       ${DOS33} __temp__.dsk LOAD HRCG.OBJ $@
+       rm __temp__.dsk
+
+monitor_rom.obj: ../orig/apple2.rom monitor_rom_obj_header.txt
+       ( \
+  xxd -r <monitor_rom_obj_header.txt; \
+  dd if=$< bs=1024 skip=18 count=2 \
+) >$@
+
+terminal.obj: \
+terminal.asm \
+terminal_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' <terminal.asm |\
+LC_ALL=C tr '\000-\177' '\200-\377' >__temp__.asm
+       ${MKDOS33FS} __temp__.dsk
+       ${DOS33} __temp__.dsk SAVE T __temp__.asm TERMINAL.ASM
+       rm -f ../linapple-pie/Printer.txt
+       tr '\n' '\r' <terminal_asm.txt |\
+( \
+  cd ../linapple-pie && \
+  ./linapple -b -1 ../hrcg/DOS_Tool_Kit_v1.0_1980_Apple.do -2 ../hrcg/__temp__.dsk \
+)
+       tr -d '\r' <../linapple-pie/Printer.txt >terminal.lst
+       ${DOS33} __temp__.dsk LOAD TERMINAL.OBJ $@
+       rm DOS_Tool_Kit_v1.0_1980_Apple.do __temp__.asm __temp__.dsk
+
+frogs1.set: \
+../orig/DOS_Tool_Kit_v1.0_1980_Apple.do
+       ${DOS33} $< LOAD FROGS1.SET $@
+
+frogs2.set: \
+../orig/DOS_Tool_Kit_v1.0_1980_Apple.do
+       ${DOS33} $< LOAD FROGS2.SET $@
+
+lily_pad.pic: \
+../orig/DOS_Tool_Kit_v1.0_1980_Apple.do
+       ${DOS33} $< LOAD "LILY PAD.PIC" $@
+       
+bootable.dsk: ../orig/Apple_DOS_v3.3_1980_Apple.do bootable.txt
+       dd if=/dev/zero of=$@ count=35 bs=4096
+       tr '\n' '\r' <../hrcg/bootable.txt |\
+( \
+  cd ../linapple-pie && \
+  ./linapple -b -1 ../orig/Apple_DOS_v3.3_1980_Apple.do -2 ../hrcg/$@ \
+)
+
+clean:
+       rm -f \
+*.o \
+vrEmu6502/src/*.o \
+emu_65c02 \
+hrcg.obj \
+monitor_rom.obj \
+bootable.dsk \
+__temp__.asm \
+__temp__.dsk \
+__defs__.txt \
+__refs__.txt
diff --git a/hrcg/bootable.txt b/hrcg/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/hrcg/emu_65c02.c b/hrcg/emu_65c02.c
new file mode 100644 (file)
index 0000000..4464c84
--- /dev/null
@@ -0,0 +1,579 @@
+#include <fcntl.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <SDL2/SDL.h>
+#include "vrEmu6502/src/vrEmu6502.h"
+
+#define APPLE_WIDTH 560
+#define APPLE_HEIGHT 192
+#define APPLE_HIRES0 0x2000
+
+#define WINDOW_X_SCALE 2
+#define WINDOW_Y_SCALE 4
+#define WINDOW_WIDTH (APPLE_WIDTH * WINDOW_X_SCALE)
+#define WINDOW_HEIGHT (APPLE_HEIGHT * WINDOW_Y_SCALE)
+
+#define INSTRS_PER_UPDATE 1500
+
+#define IO_PAGE 0xc000
+#define IO_KBD 0xc000
+#define IO_KBDSTRB 0xc010
+#define IO_SPKR 0xc030
+#define IO_TXTCLR 0xc050
+#define IO_MIXCLR 0xc052
+#define IO_LOWSCR 0xc054
+#define IO_HIRES 0xc057
+#define IO_PB0 0xc061
+#define IO_PB1 0xc062
+#define IO_PADDL0 0xc064
+#define IO_PADDL1 0xc065
+#define IO_PTRIG 0xc070
+#define STDIN_DATA 0xc0f0
+#define STDOUT_DATA 0xc0f1
+#define STDERR_DATA 0xc0f2
+#define STDIN_STATUS 0xc0f3
+#define STDOUT_STATUS 0xc0f4
+#define STDERR_STATUS 0xc0f5
+#define USLEEP_LO 0xc0f6
+#define USLEEP_HI 0xc0f7
+#define SYS_EXIT 0xc0f8
+
+#define RESET_VECTOR 0xfffc
+
+#define TRACE 0
+
+struct termios termios_attr;
+
+SDL_Window *window;
+SDL_Renderer *renderer;
+SDL_Texture *texture;
+SDL_Surface *surface;
+uint32_t frame[WINDOW_HEIGHT][WINDOW_WIDTH];
+
+// linear 0.250000 -> gamma encoded 0.537099 -> 0x89
+// linear 0.500000 -> gamma encoded 0.735357 -> 0xbc
+// linear 0.750000 -> gamma encoded 0.880825 -> 0xe1
+uint32_t argb[WINDOW_X_SCALE * 4] = {
+               // hue     R    G    B
+  0xffff8900,  //  15  1.00 0.25 0.00
+  //0xffff0000,        //   0  1.00 0.00 0.00
+  //0xffff0089,        // 345  1.00 0.00 0.25
+  0xffff00bc,  // 330  1.00 0.00 0.50
+  //0xffff00e1,        // 315  1.00 0.00 0.75
+  //0xffff00ff,        // 300  1.00 0.00 1.00
+  0xffe100ff,  // 285  0.75 0.00 1.00
+  //0xffbc00ff,        // 270  0.50 0.00 1.00
+  //0xff8900ff,        // 255  0.25 0.00 1.00
+  0xff0000ff,  // 289  0.00 0.00 1.00
+  //0xff0089ff,        // 225  0.00 0.25 1.00
+  //0xff00bcff,        // 210  0.00 0.50 1.00
+  0xff00e1ff,  // 195  0.00 0.75 1.00
+  //0xff00ffff,        // 1bc  0.00 1.00 1.00
+  //0xff00ffe1,        // 165  0.00 1.00 0.75
+  0xff00ffbc,  // 150  0.00 1.00 0.50
+  //0xff00ff89,        // 135  0.00 1.00 0.25
+  //0xff00ff00,        // 120  0.00 1.00 0.00
+  0xff89ff00,  // 105  0.25 1.00 0.00
+  //0xffbcff00,        //  90  0.50 1.00 0.00
+  //0xffe1ff00,        //  75  0.75 1.00 0.00
+  0xffffff00,  //  60  1.00 1.00 0.00
+  //0xffffe100,        //  45  1.00 0.75 0.00
+  //0xffffbc00,        //  30  1.00 0.50 0.00
+};
+
+VrEmu6502 *cpu;
+
+#define MEM_SIZE 0x10000
+uint8_t mem[MEM_SIZE];
+
+#if TRACE
+#define N_PC_PAIRS 0x10001
+struct pc_pair {
+  uint16_t pc0;
+  uint16_t pc1;
+} pc_pairs[N_PC_PAIRS];
+
+#define TRACE_REG_A 0
+#define TRACE_REG_X 1
+#define TRACE_REG_Y 2
+#define TRACE_REG_S 3
+#define TRACE_REG_P 4
+#define N_TRACE_REGS 5
+struct trace {
+  uint8_t min_unsigned;
+  int8_t min_signed;
+  uint8_t min_bitwise;
+  uint8_t max_unsigned;
+  int8_t max_signed;
+  uint8_t max_bitwise;
+} trace[MEM_SIZE][N_TRACE_REGS];
+#endif
+uint8_t key_waiting;
+uint8_t usleep_lo;
+int exit_flag;
+
+int bload(char *name) {
+  int load_address = -1;
+  char *p;
+  for (p = name; *p && *p != ','; ++p)
+    ;
+  int c = *p;
+  *p++ = 0;
+  while (c) {
+    char *q;
+    for (q = p; *q && *q != ','; ++q)
+      ;
+    c = *q;
+    *q++ = 0;
+    if (*p == 'A' || *p == 'a')
+      load_address = atoi(p + 1);
+    else {
+      fprintf(stderr, "unknown BLOAD option: %s\n", p);
+      exit(EXIT_FAILURE);
+    }
+    p = q;
+  }
+
+  int fd = open(name, O_RDONLY);
+  if (fd == -1) {
+    perror(name);
+    exit(EXIT_FAILURE);
+  }
+  uint8_t header[4];
+  if (read(fd, header, 4) != 4) {
+    perror("read()");
+    exit(EXIT_FAILURE);
+  }
+  if (load_address == -1)
+    load_address = header[0] + (header[1] << 8);
+  int load_size = header[2] + (header[3] << 8);
+  if (read(fd, mem + load_address, load_size) != load_size) {
+    perror("read()");
+    exit(EXIT_FAILURE);
+  }
+  close(fd);
+  return load_address;
+}
+
+uint8_t mem_read(uint16_t addr, bool isDbg) {
+  if ((addr & 0xff00) != IO_PAGE) {
+#if 0 // breakpoint
+    if (addr == 0x17d1) { // opcode fetch
+      int fd = open("core.bin", O_WRONLY | O_CREAT, 0666);
+      if (fd == -1) {
+        perror("core.bin");
+        exit(EXIT_FAILURE);
+      }
+      if (write(fd, mem, MEM_SIZE) != MEM_SIZE) {
+        perror("write()");
+        exit(EXIT_FAILURE);
+      }
+      close(fd);
+      exit(EXIT_FAILURE);
+    }
+#endif
+
+    return mem[addr];
+  }
+
+  switch (addr) {
+  case IO_KBD:
+    return key_waiting;
+    break;
+  case STDIN_DATA:
+    {
+      uint8_t data = 'X' - 0x40;
+      ssize_t count = read(STDIN_FILENO, &data, 1);
+      if (count == -1) {
+        perror("read()");
+        exit(EXIT_FAILURE);
+      }
+      if (data == 'X' - 0x40) { // count == 0 or ctrl-x (unassigned by hrcg)
+        exit_flag = 0x101;
+        vrEmu6502Jam(cpu);
+      }
+      return data;
+    }
+  case STDIN_STATUS:
+    {
+      struct pollfd fd = {STDIN_FILENO, POLLIN, 0};
+      if (poll(&fd, 1, 0) == -1) {
+        perror("poll()");
+        exit(EXIT_FAILURE);
+      }
+      return ((fd.revents & POLLIN) != 0) << 7;
+    }
+  case STDOUT_STATUS:
+    {
+      struct pollfd fd = {STDOUT_FILENO, POLLOUT, 0};
+      if (poll(&fd, 1, 0) == -1) {
+        perror("poll()");
+        exit(EXIT_FAILURE);
+      }
+      return ((fd.revents & POLLOUT) != 0) << 7;
+    }
+  case STDERR_STATUS:
+    {
+      struct pollfd fd = {STDERR_FILENO, POLLOUT, 0};
+      if (poll(&fd, 1, 0) == -1) {
+        perror("poll()");
+        exit(EXIT_FAILURE);
+      }
+      return ((fd.revents & POLLOUT) != 0) << 7;
+    }
+  case USLEEP_LO:
+    return usleep_lo;
+  }
+  return 0xff;
+}
+
+void mem_write(uint16_t addr, uint8_t val) {
+  if ((addr & 0xff00) != IO_PAGE) {
+    mem[addr] = val;
+    return;
+  }
+
+  switch (addr) {
+  case IO_KBDSTRB:
+    key_waiting &= 0x7f;
+    break;
+  case STDOUT_DATA:
+    if (write(STDOUT_FILENO, &val, 1) == -1) {
+      perror("write()");
+      exit(EXIT_FAILURE);
+    }
+    break;
+  case STDERR_DATA:
+    if (write(STDERR_FILENO, &val, 1) == -1) {
+      perror("write()");
+      exit(EXIT_FAILURE);
+    }
+    break;
+  case USLEEP_LO:
+    usleep_lo = val;
+    break;
+  case USLEEP_HI:
+    usleep(usleep_lo | (val << 8));
+    break;
+  case SYS_EXIT:
+    exit_flag = val | 0x100;
+    vrEmu6502Jam(cpu);
+    break;
+  }
+}
+
+void termios_atexit(void) {
+  if (tcsetattr(STDIN_FILENO, TCSADRAIN, &termios_attr) == -1)
+    perror("tcsetattr()");
+}
+
+int main(int argc, char **argv) {
+  int argn = 1;
+  bool timing = false;
+  if (argn < argc && strcmp(argv[argn], "-t") == 0) {
+    timing = true;
+    ++argn;
+  }
+
+  if (argn >= argc) {
+    printf("usage: %s [-t] program.bin\n", argv[0]);
+    exit(EXIT_FAILURE);
+  }
+
+  int load_address = 0;
+  while (argn < argc)
+    load_address = bload(argv[argn++]);
+
+  // do this before creating the CPU
+  mem[RESET_VECTOR] = (uint8_t)(load_address & 0xff);
+  mem[RESET_VECTOR + 1] = (uint8_t)(load_address >> 8);
+
+  if (isatty(STDIN_FILENO)) {
+    if (tcgetattr(STDIN_FILENO, &termios_attr) == -1) {
+      perror("tcgetattr()");
+      exit(EXIT_FAILURE);
+    }
+    atexit(termios_atexit);
+
+    // see https://github.com/python/cpython/blob/3.10/Lib/tty.py
+    struct termios attr = termios_attr;
+    attr.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+    attr.c_oflag &= ~OPOST;
+    attr.c_cflag &= ~(CSIZE | PARENB);
+    attr.c_cflag |= CS8;
+    attr.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+    attr.c_cc[VMIN] = 1;
+    attr.c_cc[VTIME] = 0;
+    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr) == -1) {
+      perror("tcsetattr()");
+      exit(EXIT_FAILURE);
+    }
+  }
+
+  if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+    fprintf(stderr, "SDL_Init(): %s\n\n", SDL_GetError());
+    exit(EXIT_FAILURE);
+  }
+
+  if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) {
+    fprintf(stderr, "SDL_SetHint(): %s\n", SDL_GetError());
+    exit(EXIT_FAILURE);
+  }
+
+  window = SDL_CreateWindow(
+    "render",
+    SDL_WINDOWPOS_UNDEFINED,
+    SDL_WINDOWPOS_UNDEFINED,
+    WINDOW_WIDTH,
+    WINDOW_HEIGHT,
+    SDL_WINDOW_SHOWN
+  );
+  if (window == NULL) {
+    fprintf(stderr, "SDL_CreateWindow(): %s\n", SDL_GetError());
+    exit(EXIT_FAILURE);
+  }
+
+  renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
+  if (renderer == NULL) {
+    fprintf(stderr, "SDL_CreateRenderer(): %s\n", SDL_GetError());
+    exit(EXIT_FAILURE);
+  }
+
+  texture = SDL_CreateTexture(
+    renderer,
+    SDL_PIXELFORMAT_ARGB8888,
+    SDL_TEXTUREACCESS_STREAMING,
+    WINDOW_WIDTH,
+    WINDOW_HEIGHT
+  );
+  if (texture == NULL) {
+    fprintf(stderr, "SDL_CreateTexture(): %s\n", SDL_GetError());
+    exit(EXIT_FAILURE);
+  }
+
+  cpu = vrEmu6502New(CPU_65C02, mem_read, mem_write);
+  if (cpu == NULL) {
+    perror("malloc()");
+    exit(EXIT_FAILURE);
+  }
+
+#if TRACE
+  memset(pc_pairs, 0xff, N_PC_PAIRS * sizeof(struct pc_pair));
+
+  for (int i = 0; i < MEM_SIZE; ++i)
+    for (int j = 0; j < N_TRACE_REGS; ++j) {
+      trace[i][j].min_unsigned = 0xff;
+      trace[i][j].min_signed = 0x7f;
+      trace[i][j].min_bitwise = 0xff;
+      trace[i][j].max_unsigned = 0x00;
+      trace[i][j].max_signed = -0x80;
+      trace[i][j].max_bitwise = 0x00;
+    }
+#endif
+
+  // main loop
+  long nb_instructions = 0, nb_cycles = 0;
+  while (true) {
+    SDL_Event event;
+    while (SDL_PollEvent(&event))
+      switch (event.type) {
+      case SDL_QUIT:
+        goto quit;
+      case SDL_KEYDOWN:
+        {
+          SDL_KeyboardEvent *e = (SDL_KeyboardEvent *)&event;
+          int i = e->keysym.sym;
+          if (i < 0x80) {
+            //switch (i) {
+            //default:
+              if (i >= 'a' && i <= 'z')
+                i -= 0x20;
+              if (i >= 0x40 && i < 0x60 && (e->keysym.mod & KMOD_CTRL))
+                i -= 0x40;
+              key_waiting = i | 0x80;
+            //  break;
+            //}
+          }
+          else
+            switch (i) {
+            case SDLK_LEFT:
+              key_waiting = 0x88;
+              break;
+            case SDLK_RIGHT:
+              key_waiting = 0x95;
+              break;
+            }
+        }
+        break;
+      }
+
+    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xff);
+    SDL_RenderClear(renderer);
+
+    // draw
+    memset(frame, 0, WINDOW_HEIGHT * WINDOW_WIDTH * sizeof(uint32_t));
+    for (int i = 0; i < APPLE_HEIGHT; ++i) {
+      int y = i * WINDOW_Y_SCALE;
+      int line =
+        APPLE_HIRES0 |
+        (i >> 6) * 40 |
+        ((i & 0x38) << 4) |
+        ((i & 7) << 10);
+      for (int j = 0; j < 40; ++j) {
+        int data = mem[line + j];
+        int hibit = data >> 7;
+        for (int k = 0; k < 7; ++k) {
+          if (data & 1) {
+            int x = (((j * 7 + k) << 1) + hibit) * WINDOW_X_SCALE;
+            for (int l = 0; l < WINDOW_X_SCALE * 2; ++l) {
+              uint32_t v = argb[x & (WINDOW_X_SCALE * 4 - 1)];
+              for (int m = 0; m < WINDOW_Y_SCALE; ++m)
+                frame[y + m][x] = v;
+              ++x;
+            }
+          }
+          data >>= 1;
+        }
+      }
+    }
+    SDL_UpdateTexture(texture, NULL, frame, WINDOW_WIDTH * sizeof(uint32_t));
+    SDL_RenderCopy(renderer, texture, NULL, NULL);
+    SDL_RenderPresent(renderer);
+    SDL_Delay(1);
+
+    int i, j;
+#if TRACE
+    struct pc_pair pc_pair = {vrEmu6502GetPC(cpu), 0};
+    for (int k = 0; k < INSTRS_PER_UPDATE; ++k) {
+      i = vrEmu6502Run(cpu, 1, &j);
+      nb_instructions += i;
+      nb_cycles += j;
+      if (i == 0)
+        break;
+
+      pc_pair.pc1 = vrEmu6502GetPC(cpu);
+      bool show_trace = false;
+
+      int hash = (19 * pc_pair.pc0 + 37 * pc_pair.pc1) % N_PC_PAIRS;
+      for (int i = 0; i < N_PC_PAIRS; ++i) {
+        if (
+          pc_pairs[hash].pc0 == pc_pair.pc0 &&
+            pc_pairs[hash].pc1 == pc_pair.pc1
+        )
+          break;
+        if (
+          pc_pairs[hash].pc0 == 0xffff &&
+            pc_pairs[hash].pc1 == 0xffff
+        ) {
+          pc_pairs[hash] = pc_pair;
+          show_trace = true;
+          break;
+        }
+        ++hash;
+        if (hash >= N_PC_PAIRS)
+          hash = 0;
+      }
+
+      uint8_t regs[N_TRACE_REGS] = {
+        vrEmu6502GetAcc(cpu),
+        vrEmu6502GetX(cpu),
+        vrEmu6502GetY(cpu),
+        vrEmu6502GetStackPointer(cpu),
+        vrEmu6502GetStatus(cpu)
+      };
+      for (int i = 0; i < N_TRACE_REGS; ++i) {
+        if (regs[i] < trace[pc_pair.pc1][i].min_unsigned) {
+          trace[pc_pair.pc1][i].min_unsigned = regs[i];
+          show_trace = true;
+        }
+        if ((int8_t)regs[i] < trace[pc_pair.pc1][i].min_signed) {
+          trace[pc_pair.pc1][i].min_signed = (int8_t)regs[i];
+          show_trace = true;
+        }
+        if (~regs[i] & trace[pc_pair.pc1][i].min_bitwise) {
+          trace[pc_pair.pc1][i].min_bitwise &= regs[i];
+          show_trace = true;
+        }
+        if (regs[i] > trace[pc_pair.pc1][i].max_unsigned) {
+          trace[pc_pair.pc1][i].max_unsigned = regs[i];
+          show_trace = true;
+        }
+        if ((int8_t)regs[i] > trace[pc_pair.pc1][i].max_signed) {
+          trace[pc_pair.pc1][i].max_signed = (int8_t)regs[i];
+          show_trace = true;
+        }
+        if (regs[i] & ~trace[pc_pair.pc1][i].max_bitwise) {
+          trace[pc_pair.pc1][i].max_bitwise |= regs[i];
+          show_trace = true;
+        }
+      }
+
+      if (show_trace)
+        fprintf(
+          stderr,
+          "pc=%04x,%04x a=%02x%02x%02x,%02x%02x%02x x=%02x%02x%02x,%02x%02x%02x y=%02x%02x%02x,%02x%02x%02x s=%02x%02x%02x,%02x%02x%02x p=%02x%02x%02x,%02x%02x%02x\n",
+          pc_pair.pc0,
+          pc_pair.pc1,
+          trace[pc_pair.pc1][TRACE_REG_A].min_unsigned,
+          (uint8_t)trace[pc_pair.pc1][TRACE_REG_A].min_signed,
+          trace[pc_pair.pc1][TRACE_REG_A].min_bitwise,
+          trace[pc_pair.pc1][TRACE_REG_A].max_unsigned,
+          (uint8_t)trace[pc_pair.pc1][TRACE_REG_A].max_signed,
+          trace[pc_pair.pc1][TRACE_REG_A].max_bitwise,
+          trace[pc_pair.pc1][TRACE_REG_X].min_unsigned,
+          (uint8_t)trace[pc_pair.pc1][TRACE_REG_X].min_signed,
+          trace[pc_pair.pc1][TRACE_REG_X].min_bitwise,
+          trace[pc_pair.pc1][TRACE_REG_X].max_unsigned,
+          (uint8_t)trace[pc_pair.pc1][TRACE_REG_X].max_signed,
+          trace[pc_pair.pc1][TRACE_REG_X].max_bitwise,
+          trace[pc_pair.pc1][TRACE_REG_Y].min_unsigned,
+          (uint8_t)trace[pc_pair.pc1][TRACE_REG_Y].min_signed,
+          trace[pc_pair.pc1][TRACE_REG_Y].min_bitwise,
+          trace[pc_pair.pc1][TRACE_REG_Y].max_unsigned,
+          (uint8_t)trace[pc_pair.pc1][TRACE_REG_Y].max_signed,
+          trace[pc_pair.pc1][TRACE_REG_Y].max_bitwise,
+          trace[pc_pair.pc1][TRACE_REG_S].min_unsigned,
+          (uint8_t)trace[pc_pair.pc1][TRACE_REG_S].min_signed,
+          trace[pc_pair.pc1][TRACE_REG_S].min_bitwise,
+          trace[pc_pair.pc1][TRACE_REG_S].max_unsigned,
+          (uint8_t)trace[pc_pair.pc1][TRACE_REG_S].max_signed,
+          trace[pc_pair.pc1][TRACE_REG_S].max_bitwise,
+          trace[pc_pair.pc1][TRACE_REG_P].min_unsigned,
+          (uint8_t)trace[pc_pair.pc1][TRACE_REG_P].min_signed,
+          trace[pc_pair.pc1][TRACE_REG_P].min_bitwise,
+          trace[pc_pair.pc1][TRACE_REG_P].max_unsigned,
+          (uint8_t)trace[pc_pair.pc1][TRACE_REG_P].max_signed,
+          trace[pc_pair.pc1][TRACE_REG_P].max_bitwise
+        );
+      pc_pair.pc0 = pc_pair.pc1;
+    }
+#else
+    i = vrEmu6502Run(cpu, INSTRS_PER_UPDATE, &j);
+    nb_instructions += i;
+    nb_cycles += j;
+    if (i < INSTRS_PER_UPDATE)
+      break;
+#endif
+  }
+
+quit:
+  vrEmu6502Destroy(cpu);
+
+  SDL_DestroyRenderer(renderer);
+  SDL_DestroyWindow(window);
+  SDL_Quit();
+
+  if (timing)
+    fprintf(
+      stderr,
+      "%lu instructions executed on %lu cycles\n",
+      nb_instructions,
+      nb_cycles
+    );
+  exit(exit_flag & 0xff);
+}
diff --git a/hrcg/hrcg_obj_bsave.txt b/hrcg/hrcg_obj_bsave.txt
new file mode 100644 (file)
index 0000000..1a493db
--- /dev/null
@@ -0,0 +1,7 @@
+NEW
+10 PRINT CHR$(4);"BLOAD RBOOT,D2":CALL 520
+20 EN=PEEK(115)+256*PEEK(116):PRINT EN
+30 PRINT USR(0),"HRCG"
+40 ST=PEEK(115)+256*PEEK(116):PRINT ST
+50 PRINT CHR$(4);"BSAVE HRCG.OBJ,A";ST;",L";EN-ST;",D1"
+RUN
diff --git a/hrcg/monitor_rom_obj_header.txt b/hrcg/monitor_rom_obj_header.txt
new file mode 100644 (file)
index 0000000..705f3d1
--- /dev/null
@@ -0,0 +1 @@
+00000000: 00f8 0008
diff --git a/hrcg/terminal.asm b/hrcg/terminal.asm
new file mode 100644 (file)
index 0000000..be6b7bd
--- /dev/null
@@ -0,0 +1,58 @@
+wndleft        equ     $20
+wndwdth        equ     $21
+wndtop equ     $22
+wndbtm equ     $23
+ch     equ     $24
+cv     equ     $25
+chario equ     $3ea
+hrcg   equ     $8dff
+extfont        equ     hrcg+6
+font   equ     hrcg-$600               hard coded 2 sets
+kbd    equ     $c000
+kbdstrb        equ     $c010
+sin.d  equ     $c0f0
+sout.d equ     $c0f1
+serr.d equ     $c0f2
+sin.s  equ     $c0f3
+sout.s equ     $c0f4
+serr.s equ     $c0f5
+slp.lo equ     $c0f6
+slp.hi equ     $c0f7
+s.exit equ     $c0f8
+cout   equ     $fded
+       org     $800
+       lda     #0
+       sta     wndleft
+       sta     wndtop
+       sta     ch
+       sta     cv
+       lda     #40
+       sta     wndwdth
+       lda     #24
+       sta     wndbtm
+       lda     #$60                    rts
+       sta     chario
+       lda     #>font
+       sta     extfont
+       lda     #<font
+       sta     extfont+1
+       jsr     hrcg
+loop   lda     sout.s
+       bpl     nosout
+       lda     kbd
+       bpl     nosout
+       sta     kbdstrb
+       and     #$7f
+       sta     sout.d
+       jmp     loop
+nosout lda     sin.s
+       bpl     nosin
+       lda     sin.d
+       ora     #$80
+       jsr     cout
+       jmp     loop
+nosin  lda     >1000
+       sta     slp.lo
+       lda     <1000
+       sta     slp.hi
+       jmp     loop
diff --git a/hrcg/terminal_asm.txt b/hrcg/terminal_asm.txt
new file mode 100644 (file)
index 0000000..0ac1480
--- /dev/null
@@ -0,0 +1,5 @@
+BRUN EDASM.OBJ
+PR#1
+DR 2
+ASM TERMINAL.ASM,TERMINAL.OBJ
+
diff --git a/hrcg/vrEmu6502 b/hrcg/vrEmu6502
new file mode 160000 (submodule)
index 0000000..c62bf11
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit c62bf1102d16e5cba3af033475d10799ecfffb2d
index 51275ce..c245815 100644 (file)
@@ -3,7 +3,8 @@ all: \
 Apple_DOS_3.3_Master.dsk \
 Apple_DOS_v3.3_1980_Apple.do \
 DOS_Tool_Kit_v1.0_1980_Apple.do \
-Lemonade_Stand_1979_Apple.do
+Lemonade_Stand_1979_Apple.do \
+apple2.rom
 
 # little brick out
 Apple_DOS_3.3_Master.dsk:
@@ -23,11 +24,20 @@ Lemonade_Stand_1979_Apple.do:
        rm -f $@
        wget https://archive.org/download/Lemonade_Stand_1979_Apple/$@
 
+apple2.rom: apple_ii+_rom.zip
+       unzip -L $< $@
+
+apple_ii+_rom.zip:
+       rm -f $@
+       wget -O $@ https://mirrors.apple2.org.za/ftp.apple.asimov.net/emulators/rom_images/apple_ii%2B_rom.zip
+
 clean:
        # avoid hitting archive.org every time
        # they can be deleted manually if needed
-       #rm -f \
+       rm -f \
+apple2.rom \
 #Apple_DOS_3.3_Master.dsk \
 #Apple_DOS_v3.3_1980_Apple.do \
 #DOS_Tool_Kit_v1.0_1980_Apple.do \
-#Lemonade_Stand_1979_Apple.do
+#Lemonade_Stand_1979_Apple.do \
+#apple_ii+_rom.zip