Implement text mode and make soft switches more generic (no split screen yet)
authorNick Downing <nick@ndcode.org>
Fri, 3 Jun 2022 13:01:54 +0000 (23:01 +1000)
committerNick Downing <nick@ndcode.org>
Fri, 3 Jun 2022 13:02:03 +0000 (23:02 +1000)
terminal/cg_rom_view.py [new file with mode: 0755]
terminal/emu_65c02.c

diff --git a/terminal/cg_rom_view.py b/terminal/cg_rom_view.py
new file mode 100755 (executable)
index 0000000..132ca57
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+
+import numpy
+import sys
+import PIL.Image
+
+EXIT_SUCCESS = 0
+EXIT_FAILURE = 1
+
+lsb_first = False # True for IIe, False for II+
+show_hi_bit = False
+while len(sys.argv) >= 2:
+  if sys.argv[1] == '--lsb_first':
+    lsb_first = True
+  elif sys.argv[1] == '--show_hi_bit':
+    show_hi_bit = True
+  else:
+    break
+  del sys.argv[1]
+if len(sys.argv) < 3:
+  print(f'usage: {sys.argv[0]} [--lsb_first] [--show_hi_bit] cg_rom.bin cg_rom.png')
+cg_rom_bin = sys.argv[1]
+cg_rom_png = sys.argv[2]
+
+with open(cg_rom_bin, 'rb') as fin:
+  data = list(fin.read())
+
+n_cols = 16
+assert len(data) % (n_cols * 8) == 0
+n_rows = len(data) // (n_cols * 8)
+
+data = (
+  (
+    numpy.array(data, numpy.uint8)[:, numpy.newaxis] >>
+      (
+        (
+          numpy.arange(8, dtype = numpy.int32)
+        if lsb_first else
+          numpy.arange(7, -1, -1, dtype = numpy.int32)
+        )
+      if show_hi_bit else
+        (
+          numpy.arange(7, dtype = numpy.int32)
+        if lsb_first else
+          numpy.arange(6, -1, -1, dtype = numpy.int32)
+        )
+      )[numpy.newaxis, :]
+  ) & 1
+).astype(bool)
+
+image = numpy.concatenate(
+  [
+    numpy.concatenate(
+      [
+        data[(i * n_cols + j) * 8:(i * n_cols + j + 1) * 8, :]
+        for j in range(n_cols)
+      ],
+      1
+    )
+    for i in range(n_rows)
+  ],
+  0
+)
+
+image_pil = PIL.Image.new('P', (image.shape[1], image.shape[0]))
+image_pil.frombytes(image.tobytes())
+image_pil.putpalette([0, 0, 0, 0xff, 0xff, 0xff])
+image_pil.save(cg_rom_png)
index 7a53c1e..9faeac5 100644 (file)
@@ -16,6 +16,8 @@
 
 #define APPLE_WIDTH 560
 #define APPLE_HEIGHT 192
+#define APPLE_TEXT0 0x400
+#define APPLE_TEXT1 0x800
 #define APPLE_HIRES0 0x2000
 #define APPLE_HIRES1 0x4000
 
 #define IO_KBD 0xc000
 #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
 #define IO_PADDL0 0xc064
@@ -96,6 +91,9 @@ uint32_t palette[0x10] = {
   0xffffffff,
 };
 
+// can make this green or amber to be more realistic
+uint32_t mono_palette[2] = {0xff000000, 0xffffffff};
+
 VrEmu6502 *cpu;
 
 #define ROM_SIZE 0x800
@@ -131,9 +129,14 @@ struct trace {
 uint8_t key_waiting;
 uint8_t usleep_lo;
 uint8_t dos_lo;
-int hires = APPLE_HIRES0;
 int usleep_count, exit_flag;
 
+#define SOFT_SWITCH_TEXT 1
+#define SOFT_SWITCH_MIXED 2
+#define SOFT_SWITCH_PAGE 4
+#define SOFT_SWITCH_HIRES 8
+uint8_t soft_switches = SOFT_SWITCH_TEXT;
+
 void rom_load(char *name, uint8_t *data, int size) {
   int fd = open(name, O_RDONLY);
   if (fd == -1) {
@@ -242,11 +245,28 @@ uint8_t mem_read(uint16_t addr, bool isDbg) {
   switch (addr) {
   case IO_KBD:
     return key_waiting;
-  case IO_LOWSCR:
-    hires = APPLE_HIRES0;
+  case IO_KBDSTRB:
+    key_waiting &= 0x7f;
+    break;
+  case 0xc050:
+  case 0xc052:
+  case 0xc054:
+  case 0xc056:
+  case 0xc058:
+  case 0xc05a:
+  case 0xc05c:
+  case 0xc05e:
+    soft_switches &= ~(1 << ((addr >> 1) & 7));
     break;
-  case IO_HISCR:
-    hires = APPLE_HIRES1;
+  case 0xc051:
+  case 0xc053:
+  case 0xc055:
+  case 0xc057:
+  case 0xc059:
+  case 0xc05b:
+  case 0xc05d:
+  case 0xc05f:
+    soft_switches |= 1 << ((addr >> 1) & 7);
     break;
   case STDIN_DATA:
     {
@@ -307,11 +327,25 @@ void mem_write(uint16_t addr, uint8_t val) {
   case IO_KBDSTRB:
     key_waiting &= 0x7f;
     break;
-  case IO_LOWSCR:
-    hires = APPLE_HIRES0;
+  case 0xc050:
+  case 0xc052:
+  case 0xc054:
+  case 0xc056:
+  case 0xc058:
+  case 0xc05a:
+  case 0xc05c:
+  case 0xc05e:
+    soft_switches &= ~(1 << ((addr >> 1) & 7));
     break;
-  case IO_HISCR:
-    hires = APPLE_HIRES1;
+  case 0xc051:
+  case 0xc053:
+  case 0xc055:
+  case 0xc057:
+  case 0xc059:
+  case 0xc05b:
+  case 0xc05d:
+  case 0xc05f:
+    soft_switches |= 1 << ((addr >> 1) & 7);
     break;
   case STDOUT_DATA:
     if (write(fd_out, &val, 1) == -1) {
@@ -399,7 +433,7 @@ int main(int argc, char **argv) {
   rom_load(cg_rom_file, cg_rom, CG_ROM_SIZE);
   rom_load(f8_rom_file, mem + 0xf800, ROM_SIZE);
 
-  int load_address = 0;
+  int load_address = -1;
   while (argn < argc) {
     char *p = argv[argn++];
     if (strcmp(p, "--") == 0)
@@ -407,6 +441,12 @@ int main(int argc, char **argv) {
     load_address = obj_load(p);
   }
 
+  // do this before creating the CPU
+  if (load_address != -1) {
+    mem[RESET_VECTOR] = (uint8_t)(load_address & 0xff);
+    mem[RESET_VECTOR + 1] = (uint8_t)(load_address >> 8);
+  }
+
   // open pty and child process if requested
   if (argn < argc) {
     int fd_master = posix_openpt(O_RDWR);
@@ -425,7 +465,7 @@ int main(int argc, char **argv) {
       exit(EXIT_FAILURE);
     }
 
-    // Open the slave side ot the PTY
+    // open the slave side of the pty
     char *slave_name = ptsname(fd_master);
     int fd_slave = open(slave_name, O_RDWR);
     if (fd_slave == -1) {
@@ -530,10 +570,6 @@ int main(int argc, char **argv) {
     fd_out = fd_master;
   }
 
-  // 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(fd_in)) {
     if (tcgetattr(fd_in, &termios_attr) == -1) {
       perror("tcgetattr()");
@@ -668,35 +704,73 @@ int main(int argc, char **argv) {
     // draw
     memset(frame, 0, WINDOW_HEIGHT * WINDOW_WIDTH * sizeof(uint32_t));
     for (int i = 0; i < APPLE_HEIGHT; ++i) {
-      int line =
-        hires |
-        (i >> 6) * 40 |
-        ((i & 0x38) << 4) |
-        ((i & 7) << 10);
+      int base = (i >> 6) * 40 | ((i & 0x38) << 4);
+      int row = i & 7;
       uint32_t buf[PADDED_WORDS];
       memset(buf, 0, PADDED_WORDS * sizeof(uint32_t));
-      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 l = ((j * 7 + k) << 1) + hibit;
-            buf[l >> 5] |= 1 << (l & 0x1f);
-            ++l;
-            buf[l >> 5] |= 1 << (l & 0x1f);
+
+      if (soft_switches & SOFT_SWITCH_HIRES) {
+        // hires
+        int line = (
+          soft_switches & SOFT_SWITCH_PAGE ? APPLE_HIRES1 : APPLE_HIRES0
+        ) | base | (row << 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 l = ((j * 7 + k) << 1) + hibit;
+              buf[l >> 5] |= 1 << (l & 0x1f);
+              ++l;
+              buf[l >> 5] |= 1 << (l & 0x1f);
+            }
+            data >>= 1;
+          }
+        }
+      }
+      else {
+        // text or gr (only text for now; MSB first)
+        int line = (
+          soft_switches & SOFT_SWITCH_PAGE ? APPLE_TEXT1 : APPLE_TEXT0
+        ) | base;
+        for (int j = 0; j < 40; ++j) {
+          int data = cg_rom[(mem[line + j] << 3) | row];
+          for (int k = 0; k < 7; ++k) {
+            if (data & 0x40) {
+              int l = (j * 7 + k) << 1;
+              buf[l >> 5] |= 1 << (l & 0x1f);
+              ++l;
+              buf[l >> 5] |= 1 << (l & 0x1f);
+            }
+            data <<= 1;
           }
-          data >>= 1;
         }
       }
-      int k = 0, x = 0, y = i * WINDOW_Y_SCALE;
-      for (int j = 0; j < PADDED_WIDTH; ++j) {
-        int mask = 1 << (j & 3);
-        k = (k & ~mask) | ((buf[j >> 5] >> (j & 0x1c)) & mask);
-        uint32_t v = palette[k];
-        for (int l = 0; l < WINDOW_X_SCALE; ++l) {
-          for (int m = 0; m < WINDOW_Y_SCALE; ++m)
-            frame[y + m][x] = v;
-          ++x;
+
+      if (soft_switches & SOFT_SWITCH_TEXT) {
+        // text (no colour burst)
+        int x = 3 * WINDOW_X_SCALE / 2, y = i * WINDOW_Y_SCALE;
+        for (int j = 0; j < APPLE_WIDTH; ++j) {
+          uint32_t v = mono_palette[(buf[j >> 5] >> (j & 0x1f)) & 1];
+          for (int l = 0; l < WINDOW_X_SCALE; ++l) {
+            for (int m = 0; m < WINDOW_Y_SCALE; ++m)
+              frame[y + m][x] = v;
+            ++x;
+          }
+        }
+      }
+      else {
+        // hires or gr (colour burst)
+        int k = 0, x = 0, y = i * WINDOW_Y_SCALE;
+        for (int j = 0; j < PADDED_WIDTH; ++j) {
+          int mask = 1 << (j & 3);
+          k = (k & ~mask) | ((buf[j >> 5] >> (j & 0x1c)) & mask);
+          uint32_t v = palette[k];
+          for (int l = 0; l < WINDOW_X_SCALE; ++l) {
+            for (int m = 0; m < WINDOW_Y_SCALE; ++m)
+              frame[y + m][x] = v;
+            ++x;
+          }
         }
       }
     }