[Oberon] RISC emulator

Paul Reed paulreed at paddedcell.com
Thu Mar 20 09:28:57 CET 2014


Hi Peter,

> I've made an emulator for the Oberon RISC machine. It can boot Oberon
> and everything seems to work, but it hasn't received much testing yet.
> It's available at:
>
> https://github.com/pdewacht/oberon-risc-emu

This is awesome!  Thank you (and much respect!) for doing the heavy-lifting.

Below I have some mods for your approval and/or comment:

1. add a "-fullscreen" option for screens which are only 768 pixels high
(e.g. laptops)
2. add a crude third-button emulation with the left Alt key (e.g. for
trackpads)
3. add the host side of the PCLink file transfer protocol

With your permission, I'd like to link to your code, and provide binaries,
on projectoberon.com.

With best regards,
Paul

Index: Makefile
===================================================================
--- Makefile	(revision 15)
+++ Makefile	(working copy)
@@ -7,7 +7,8 @@
 	sdl-main.c \
 	sdl-ps2.c sdl-ps2.h \
 	risc.c risc.h risc-boot.inc \
-	risc-sd.c risc-sd.h
+	risc-sd.c risc-sd.h \
+	pclink.c pclink.h

 risc: $(RISC_SOURCE)
 	$(CC) -o $@ $(filter %.c, $^) $(RISC_CFLAGS)
Index: README.md
===================================================================
--- README.md	(revision 15)
+++ README.md	(working copy)
@@ -28,8 +28,10 @@
   * Very inaccurate, but good enough for Oberon. If you're going to
     hack the SD card routines, you'll need to use real hardware.

-* RS-232
-  * Not implemented.
+* RS-232 (PR)
+  * implements PCLink protocol to send/receive single files at a time
+    e.g. to receive Test.Mod into Oberon, run PCLink1.Start,
+    then in host risc current directory, echo Test.Mod > PCLink.REC

 * Network
   * Not implemented.
Index: risc.c
===================================================================
--- risc.c	(revision 15)
+++ risc.c	(working copy)
@@ -5,6 +5,7 @@
 #include <stdio.h>
 #include "risc.h"
 #include "risc-sd.h"
+#include "pclink.h"

 #define MemSize      0x100000
 #define MemWords     (MemSize / 4)
@@ -311,11 +312,12 @@

 static void risc_store_byte(struct RISC *risc, uint32_t address, uint8_t
value) {
   if (address < IOStart) {
-    uint32_t w = risc_load_word(risc, address);
+    uint32_t w = risc->RAM[address/4];
     uint32_t shift = (address & 3) * 8;
     w &= ~(0xFFu << shift);
-    w |= (uint32_t)value << shift;
-    risc_store_word(risc, address, w);
+    risc->RAM[address/4] = w | (uint32_t)value << shift;
+  } else {
+    risc_store_io(risc, address, value);
   }
 }

@@ -330,6 +332,14 @@
       // Switches
       return 0;
     }
+    case 8: {
+      // RS232 data
+      return PCLink_RData();
+    }
+    case 12: {
+      // RS232 status
+      return PCLink_RStat();
+    }
     case 16: {
       // SPI data
       if (risc->spi_selected == 1 && risc->sd_card) {
@@ -385,6 +395,10 @@
       printf("\n");
       break;
     }
+    case 8: {
+      // RS232 data
+      PCLink_TData(value);
+    }
     case 16: {
       // SPI write
       if (risc->spi_selected == 1 && risc->sd_card) {
Index: sdl-main.c
===================================================================
--- sdl-main.c	(revision 15)
+++ sdl-main.c	(working copy)
@@ -22,11 +22,12 @@
 void update_texture(uint32_t *framebuffer, SDL_Texture *texture);

 int main (int argc, char *argv[]) {
-  if (argc != 2) {
-    fprintf(stderr, "Usage: risc disk-file-name\n");
+  bool fullscreen = (strcmp(argv[1], "-fullscreen") == 0);
+  if (argc < 2 || (fullscreen && argc < 3)) {
+    fprintf(stderr, "Usage: risc [-fullscreen] disk-file-name\n");
     return 1;
   }
-  struct RISC *risc = risc_new(argv[1]);
+  struct RISC *risc = risc_new(argv[1+fullscreen]);

   if (SDL_Init(SDL_INIT_VIDEO) != 0) {
     fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
@@ -65,6 +66,9 @@
   init_texture(texture);
   SDL_RenderCopy(renderer, texture, NULL, NULL);
   SDL_RenderPresent(renderer);
+  if (fullscreen) {
+    SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
+  }
   SDL_ShowWindow(window);

   bool done = false;
@@ -91,6 +95,8 @@
             if (event.key.state == SDL_PRESSED) {
               risc_reset(risc);
             }
+          } else if (event.key.keysym.sym == SDLK_LALT) {  //emulate
middle button
+            risc_mouse_button(risc, 2, event.key.state == SDL_PRESSED);
           } else {
             uint8_t scancode[MAX_PS2_CODE_LEN];
             int len = ps2_encode(event.key.keysym.scancode,

----pclink.h-----------------------
#ifndef PCLINK_H
#define PCLINK_H

#include <stdbool.h>
#include <stdint.h>

uint32_t PCLink_RData();
uint32_t PCLink_RStat();
void PCLink_TData(uint32_t value);

#endif  // PCLINK_H

----pclink.c-----------------------
//pclink.c for Peter De Wachter's RISC emulator PDR 20.3.14
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include "pclink.h"

#define ACK 0x10
#define REC 0x21
#define SND 0x22

static const char * RecName = "PCLink.REC"; //e.g. echo Test.Mod > PCLink.REC
static const char * SndName = "PCLink.SND";
static int mode = 0, fd = -1;
static int txcount, rxcount, fnlen, flen;
static char szFilename[32];
static char buf[257];

static bool GetJob(const char * JobName) {
  bool res = false;
  struct stat st;
  FILE * f;

  if (stat(JobName, &st) == 0) {
    if (st.st_size > 0 && st.st_size <= 33) {
      f = fopen(JobName, "r");
      if (f) {
        fscanf(f, "%s", szFilename);
        fclose(f);
        res = true; txcount = 0; rxcount = 0; fnlen = strlen(szFilename)+1;
      }
    }
    if (!res) {
      unlink(JobName); //clean up
    }
  }
  return res;
}

uint32_t PCLink_RStat() {
  struct stat st;

  if (!mode) {
    if (GetJob(RecName)) {
      if (stat(szFilename, &st) == 0 && st.st_size >= 0 && st.st_size <
0x1000000) {
        fd = open(szFilename, O_RDONLY);
        if (fd != -1) {
          flen = st.st_size; mode = REC;
          printf("PCLink REC Filename: %s size %d\n", szFilename, flen);
        }
      }
      if (!mode) {
        unlink(RecName); //clean up
      }
    } else if (GetJob(SndName)) {
      fd = open(szFilename, O_CREAT|O_TRUNC|O_RDWR,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
      if (fd != -1) {
        flen = -1; mode = SND;
        printf("PCLink SND Filename: %s\n", szFilename);
      }
      if (!mode) {
        unlink(SndName); //clean up
      }
    }
  }
  return 2 + (mode != 0); //xmit always ready
}

uint32_t PCLink_RData() {
  char ch = 0;

  if (mode) {
    if (rxcount == 0) {
      ch = mode;
    } else if (rxcount < fnlen+1) {
      ch = szFilename[rxcount-1];
    } else if (mode == SND) {
      ch = ACK;
      if (flen == 0) {
        mode = 0; unlink(SndName);
      }
    } else {
      int pos = (rxcount - fnlen - 1) % 256;
      if (pos == 0) {
        if (flen > 255) {
          ch = 255;
        } else {
          ch = flen;
        }
      } else {
        read(fd, &ch, 1);
        flen--;
        if (flen == 0) {
          mode = 0; unlink(RecName);
        }
      }
    }
  }

  rxcount++;
  return ch;
}

void PCLink_TData(uint32_t value) {
  if (mode) {
    if (txcount == 0) {
      if (value != ACK) {
        close(fd); fd = -1;
        if (mode == SND) {
          unlink(szFilename);  //file not found, delete file created
          unlink(SndName); //clean up
        } else {
          unlink(RecName); //clean up
        }
        mode = 0;
      }
    } else if (mode == SND) {
      int lim;

      int pos = (txcount-1) % 256;
      buf[pos] = value;
      lim = (unsigned char)buf[0];
      if (pos == lim) {
        write(fd, buf+1, lim);
        if (lim < 255) {
          flen = 0; close(fd);
        }
      }
    }
  }
  txcount++;
}





More information about the Oberon mailing list