├── .gitignore ├── DiskImage ├── Oberon-2016-04-18.dsk ├── Oberon-2016-08-02.dsk ├── Oberon-2019-01-21.dsk └── Oberon-2020-08-18.dsk ├── Libretro ├── Makefile.common ├── jni │ ├── Android.mk │ └── Application.mk ├── libretro.c ├── libretro.h └── link.T ├── Makefile ├── Makefile.libretro ├── Mods ├── Clipboard.Mod ├── Display.Mod ├── Input.Mod └── Input.Mod.diff ├── README.md ├── pcreceive.sh ├── pcsend.sh ├── po2013.png ├── src ├── disk.c ├── disk.h ├── fp-test │ ├── .gitignore │ ├── Makefile │ ├── add.c │ ├── div.c │ ├── flr.c │ ├── flt.c │ ├── idiv.c │ ├── mul.c │ ├── numbers.py │ └── v2c.py ├── pclink.c ├── pclink.h ├── raw-serial.c ├── raw-serial.h ├── risc-boot.inc ├── risc-fp.c ├── risc-fp.h ├── risc-io.h ├── risc.c ├── risc.h ├── sdl-clipboard.c ├── sdl-clipboard.h ├── sdl-main.c ├── sdl-ps2.c └── sdl-ps2.h └── tools ├── Makefile ├── asciidecoder.rs └── ob2unix.rs /.gitignore: -------------------------------------------------------------------------------- 1 | risc 2 | *.img 3 | *.so 4 | /src/disk.o 5 | /src/pclink.o 6 | /src/raw-serial.o 7 | /src/risc-fp.o 8 | /src/risc.o 9 | /Libretro/libretro.o 10 | -------------------------------------------------------------------------------- /DiskImage/Oberon-2016-04-18.dsk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pdewacht/oberon-risc-emu/26c8ac5737c71811803c87ad51f1f0d6e62e71fe/DiskImage/Oberon-2016-04-18.dsk -------------------------------------------------------------------------------- /DiskImage/Oberon-2016-08-02.dsk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pdewacht/oberon-risc-emu/26c8ac5737c71811803c87ad51f1f0d6e62e71fe/DiskImage/Oberon-2016-08-02.dsk -------------------------------------------------------------------------------- /DiskImage/Oberon-2019-01-21.dsk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pdewacht/oberon-risc-emu/26c8ac5737c71811803c87ad51f1f0d6e62e71fe/DiskImage/Oberon-2019-01-21.dsk -------------------------------------------------------------------------------- /DiskImage/Oberon-2020-08-18.dsk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pdewacht/oberon-risc-emu/26c8ac5737c71811803c87ad51f1f0d6e62e71fe/DiskImage/Oberon-2020-08-18.dsk -------------------------------------------------------------------------------- /Libretro/Makefile.common: -------------------------------------------------------------------------------- 1 | INCFLAGS := -I$(CORE_DIR)/src 2 | 3 | SOURCES_C := \ 4 | $(CORE_DIR)/Libretro/libretro.c \ 5 | $(CORE_DIR)/src/risc.c \ 6 | $(CORE_DIR)/src/risc-fp.c \ 7 | $(CORE_DIR)/src/disk.c \ 8 | $(CORE_DIR)/src/pclink.c \ 9 | $(CORE_DIR)/src/raw-serial.c \ 10 | -------------------------------------------------------------------------------- /Libretro/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | GIT_VERSION := "Git ($(shell git describe --abbrev=4 --dirty --always --tags))" 3 | 4 | CORE_DIR := $(LOCAL_PATH)/../.. 5 | LIBRETRO_DIR := $(LOCAL_PATH)/.. 6 | 7 | include $(LOCAL_PATH)/../Makefile.common 8 | 9 | COREFLAGS := -DINLINE=inline -DHAVE_STDINT_H -DHAVE_INTTYPES_H -D__LIBRETRO__ -DLIBRETRO -DGIT_VERSION=\"$(GIT_VERSION)\" 10 | 11 | include $(CLEAR_VARS) 12 | LOCAL_MODULE := retro 13 | LOCAL_SRC_FILES := $(SOURCES_CXX) $(SOURCES_C) 14 | LOCAL_CXXFLAGS := $(COREFLAGS) $(INCFLAGS) 15 | LOCAL_CFLAGS := $(COREFLAGS) $(INCFLAGS) 16 | LOCAL_LDFLAGS := -Wl,-version-script=$(LIBRETRO_DIR)/link.T 17 | 18 | LOCAL_C_INCLUDES = $(CORE_DIR) 19 | 20 | include $(BUILD_SHARED_LIBRARY) 21 | -------------------------------------------------------------------------------- /Libretro/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := all 2 | APP_STL := c++_static 3 | APP_PLATFORM := android-21 4 | -------------------------------------------------------------------------------- /Libretro/libretro.c: -------------------------------------------------------------------------------- 1 | #include "libretro.h" 2 | #include "risc.h" 3 | #include "disk.h" 4 | #include "pclink.h" 5 | #include "raw-serial.h" 6 | #include "sdl-ps2.h" 7 | 8 | #include 9 | #include 10 | 11 | static int clamp(int x, int min, int max) { 12 | if (x < min) return min; 13 | if (x > max) return max; 14 | return x; 15 | } 16 | 17 | #define CPU_HZ 25000000 18 | #define FPS 30 19 | 20 | static uint16_t FOR = 0x0000, AFT = 0xFFFF; 21 | 22 | unsigned retro_api_version(void) { 23 | return RETRO_API_VERSION; } 24 | 25 | void retro_get_system_info(struct retro_system_info *info) 26 | { 27 | info->library_name = "Oberon"; 28 | info->library_version = GIT_VERSION; 29 | info->valid_extensions = "dsk"; 30 | info->need_fullpath = true; 31 | info->block_extract = false; 32 | } 33 | 34 | 35 | void dummy_log(enum retro_log_level level, const char *fmt, ...) { } 36 | 37 | static retro_log_printf_t _log_cb; 38 | 39 | static retro_environment_t _environ_cb; 40 | static retro_video_refresh_t _video_cb; 41 | static retro_input_poll_t _input_poll_cb; 42 | static retro_input_state_t _input_state_cb; 43 | 44 | static struct RISC *_risc = NULL; 45 | static struct RISC_SPI *_spi_disk = NULL; 46 | 47 | static uint32_t _ms_counter; 48 | 49 | static int _mouse_x, _mouse_y; 50 | 51 | enum k_type { 52 | K_UNKNOWN = 0, 53 | K_NORMAL, 54 | K_EXTENDED, 55 | K_NUMLOCK_HACK, 56 | K_SHIFT_HACK, 57 | }; 58 | 59 | struct k_info { 60 | uint8_t code; 61 | uint8_t type; 62 | }; 63 | 64 | static struct k_info _keymap[RETROK_LAST]; 65 | 66 | static struct retro_framebuffer _framebuffer; 67 | 68 | void _keyboard_cb(bool down, unsigned keycode, 69 | uint32_t character, uint16_t key_modifiers) 70 | { 71 | uint8_t buf[MAX_PS2_CODE_LEN]; 72 | 73 | int i = 0; 74 | struct k_info info = _keymap[keycode]; 75 | 76 | switch (info.type) { 77 | case K_UNKNOWN: 78 | printf("unknown Libretro keycode %d\n", keycode); 79 | break; 80 | 81 | case K_NORMAL: 82 | if (!down) 83 | buf[i++] = 0xf0; 84 | buf[i++] = info.code; 85 | break; 86 | 87 | case K_EXTENDED: 88 | buf[i++] = 0xe0; 89 | if (!down) 90 | buf[i++] = 0xf0; 91 | buf[i++] = info.code; 92 | break; 93 | 94 | case K_NUMLOCK_HACK: 95 | // This assumes Num Lock is always active 96 | if (down) { 97 | // fake shift press 98 | buf[i++] = 0xe0; 99 | buf[i++] = 0x12; 100 | buf[i++] = 0xe0; 101 | buf[i++] = info.code; 102 | } else { 103 | buf[i++] = 0xe0; 104 | buf[i++] = 0xf0; 105 | buf[i++] = info.code; 106 | // fake shift release 107 | buf[i++] = 0xe0; 108 | buf[i++] = 0xf0; 109 | buf[i++] = 0x12; 110 | } 111 | break; 112 | 113 | case K_SHIFT_HACK: 114 | if (down) { 115 | // fake shift release 116 | if (key_modifiers & RETROKMOD_SHIFT) { 117 | buf[i++] = 0xe0; 118 | buf[i++] = 0xf0; 119 | buf[i++] = 0x12; 120 | } 121 | buf[i++] = 0xe0; 122 | buf[i++] = info.code; 123 | } else { 124 | buf[i++] = 0xe0; 125 | buf[i++] = 0xf0; 126 | buf[i++] = info.code; 127 | // fake shift press 128 | if (key_modifiers & RETROKMOD_SHIFT) { 129 | buf[i++] = 0xE0; 130 | buf[i++] = 0x59; 131 | } 132 | } 133 | break; 134 | } 135 | 136 | risc_keyboard_input(_risc, buf, i); 137 | } 138 | 139 | static const struct retro_controller_description ports_keyboard[] = 140 | { 141 | { "Keyboard + Mouse", RETRO_DEVICE_KEYBOARD }, 142 | { 0 }, 143 | }; 144 | static const struct retro_controller_info ports[] = { 145 | { ports_keyboard, 1 }, 146 | { 0 }, 147 | }; 148 | 149 | void retro_set_environment(retro_environment_t cb) { 150 | _environ_cb = cb; 151 | _environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports); 152 | } 153 | 154 | void retro_set_video_refresh(retro_video_refresh_t cb) { 155 | _video_cb = cb; } 156 | 157 | void retro_set_input_poll(retro_input_poll_t cb) { 158 | _input_poll_cb = cb; } 159 | 160 | void retro_set_input_state(retro_input_state_t cb) { 161 | _input_state_cb = cb; } 162 | 163 | void retro_set_audio_sample(retro_audio_sample_t cb) { } 164 | void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { } 165 | 166 | void retro_init(void) 167 | { 168 | _risc = risc_new(); 169 | risc_set_serial(_risc, &pclink); 170 | 171 | struct retro_log_callback log_callback; 172 | _log_cb = _environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log_callback) 173 | ? log_callback.log 174 | : dummy_log; 175 | } 176 | 177 | void retro_deinit(void) 178 | { 179 | if (_risc) { 180 | free(_risc); 181 | _risc = NULL; 182 | } 183 | } 184 | 185 | void retro_set_controller_port_device(unsigned port, unsigned device) { } 186 | 187 | void retro_reset(void) { 188 | _ms_counter = 1; 189 | risc_reset(_risc); 190 | } 191 | 192 | size_t retro_serialize_size(void) { return 0; } 193 | 194 | bool retro_serialize( void *data, size_t size) { return false; } 195 | bool retro_unserialize(const void *data, size_t size) { return false; } 196 | 197 | void retro_cheat_reset(void) { } 198 | void retro_cheat_set(unsigned index, bool enabled, const char *code) { } 199 | 200 | /* Loads a game. */ 201 | bool retro_load_game(const struct retro_game_info *game) 202 | { 203 | struct retro_keyboard_callback callback = { _keyboard_cb }; 204 | if (!_environ_cb(RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK, &callback)) { 205 | _log_cb(RETRO_LOG_ERROR, "core requires keyboard callback support\n"); 206 | return false; 207 | } 208 | 209 | if (game->path) 210 | _spi_disk = disk_new(game->path); 211 | if (!_spi_disk) { 212 | _log_cb(RETRO_LOG_ERROR, "failed to load disk image\n"); 213 | return false; 214 | } 215 | 216 | risc_set_spi(_risc, 1, _spi_disk); 217 | risc_set_serial(_risc, raw_serial_new("/dev/null", "/dev/null")); 218 | 219 | enum retro_pixel_format pf = RETRO_PIXEL_FORMAT_RGB565; 220 | _environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &pf); 221 | /* if this fails then its still 0RGB1555 */ 222 | 223 | if (!_environ_cb(RETRO_ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER, &_framebuffer)) { 224 | _framebuffer.width = RISC_FRAMEBUFFER_WIDTH; 225 | _framebuffer.height = RISC_FRAMEBUFFER_HEIGHT; 226 | } 227 | 228 | _framebuffer.width = _framebuffer.width & ~31U; 229 | 230 | if (_framebuffer.width < 32) 231 | _framebuffer.width = 32; 232 | 233 | if (_framebuffer.height < 32) 234 | _framebuffer.height = 32; 235 | 236 | _framebuffer.data = malloc( 237 | _framebuffer.width * 238 | _framebuffer.height * 239 | sizeof(uint16_t)); 240 | 241 | risc_configure_memory(_risc, 1, _framebuffer.width, _framebuffer.height); 242 | 243 | _ms_counter = 1; 244 | _mouse_x = 0; 245 | _mouse_y = _framebuffer.height; 246 | 247 | return true; 248 | } 249 | 250 | void retro_get_system_av_info(struct retro_system_av_info *info) 251 | { 252 | info->geometry.base_width = 253 | info->geometry.max_width = _framebuffer.width; 254 | info->geometry.base_height = 255 | info->geometry.max_height = _framebuffer.height; 256 | info->geometry.aspect_ratio = 0.0; 257 | info->timing.fps = FPS; 258 | info->timing.sample_rate = 0; 259 | } 260 | 261 | bool retro_load_game_special( 262 | unsigned game_type, 263 | const struct retro_game_info *info, size_t num_info 264 | ) { return false; } 265 | 266 | /* Unloads a currently loaded game. */ 267 | void retro_unload_game(void) 268 | { 269 | if (!_risc && _spi_disk) { 270 | free(_spi_disk); 271 | _spi_disk = NULL; 272 | } 273 | } 274 | 275 | unsigned retro_get_region(void) { return ~0U; } 276 | 277 | void *retro_get_memory_data(unsigned id) { return NULL; } 278 | size_t retro_get_memory_size(unsigned id) { return 0; } 279 | 280 | void retro_run(void) 281 | { 282 | _input_poll_cb(); 283 | 284 | int mx = _mouse_x + _input_state_cb( 285 | 0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X); 286 | int my = _mouse_y - _input_state_cb( 287 | 0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y); 288 | 289 | _mouse_x = clamp(mx, 0, _framebuffer.width); 290 | _mouse_y = clamp(my, 0, _framebuffer.height); 291 | risc_mouse_moved(_risc, _mouse_x, _mouse_y); 292 | 293 | risc_mouse_button(_risc, 1, 294 | _input_state_cb(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)); 295 | risc_mouse_button(_risc, 2, 296 | _input_state_cb(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)); 297 | risc_mouse_button(_risc, 3, 298 | _input_state_cb(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT)); 299 | 300 | risc_set_time(_risc, _ms_counter); 301 | _ms_counter += 1000 / FPS; 302 | risc_run(_risc, CPU_HZ / FPS); 303 | 304 | struct Damage damage = risc_get_framebuffer_damage(_risc); 305 | if (damage.y1 <= damage.y2) { 306 | 307 | uint32_t *in = risc_get_framebuffer_ptr(_risc); 308 | uint16_t *out = _framebuffer.data; 309 | uint16_t colors[] = { AFT, FOR }; 310 | 311 | for (int line = damage.y2; line >= damage.y1; line--) { 312 | int in_line = line * (_framebuffer.width / 32); 313 | 314 | int out_idx = ((_framebuffer.height-line-1) * _framebuffer.width) 315 | + damage.x1 * 32; 316 | 317 | for (int col = damage.x1; col <= damage.x2; col++) { 318 | uint32_t chunk = in[in_line + col]; 319 | for (int b = 0; b < 32; b++) { 320 | out[out_idx] = colors[chunk&1]; 321 | chunk >>= 1; 322 | out_idx++; 323 | } 324 | } 325 | } 326 | } 327 | 328 | _video_cb( 329 | _framebuffer.data, 330 | _framebuffer.width, 331 | _framebuffer.height, 332 | _framebuffer.width << 1); 333 | } 334 | 335 | static struct k_info _keymap[RETROK_LAST] = { 336 | [ RETROK_BACKSPACE ] = { 0x66, K_NORMAL }, 337 | [ RETROK_TAB ] = { 0x0d, K_NORMAL }, 338 | [ RETROK_RETURN ] = { 0x5a, K_NORMAL }, 339 | [ RETROK_QUOTE ] = { 0x52, K_NORMAL }, 340 | [ RETROK_ESCAPE ] = { 0x76, K_NORMAL }, 341 | [ RETROK_SPACE ] = { 0x29, K_NORMAL }, 342 | [ RETROK_COMMA ] = { 0x41, K_NORMAL }, 343 | [ RETROK_MINUS ] = { 0x4e, K_NORMAL }, 344 | [ RETROK_PERIOD ] = { 0x49, K_NORMAL }, 345 | [ RETROK_SLASH ] = { 0x4a, K_NORMAL }, 346 | [ RETROK_0 ] = { 0x45, K_NORMAL }, 347 | [ RETROK_1 ] = { 0x16, K_NORMAL }, 348 | [ RETROK_2 ] = { 0x1e, K_NORMAL }, 349 | [ RETROK_3 ] = { 0x26, K_NORMAL }, 350 | [ RETROK_4 ] = { 0x25, K_NORMAL }, 351 | [ RETROK_5 ] = { 0x2e, K_NORMAL }, 352 | [ RETROK_6 ] = { 0x36, K_NORMAL }, 353 | [ RETROK_7 ] = { 0x3d, K_NORMAL }, 354 | [ RETROK_8 ] = { 0x3e, K_NORMAL }, 355 | [ RETROK_9 ] = { 0x46, K_NORMAL }, 356 | [ RETROK_SEMICOLON ] = { 0x4c, K_NORMAL }, 357 | [ RETROK_EQUALS ] = { 0x55, K_NORMAL }, 358 | [ RETROK_LEFTBRACKET ] = { 0x54, K_NORMAL }, 359 | [ RETROK_BACKSLASH ] = { 0x5d, K_NORMAL }, 360 | [ RETROK_RIGHTBRACKET ] = { 0x5b, K_NORMAL }, 361 | [ RETROK_BACKQUOTE ] = { 0x0e, K_NORMAL }, 362 | [ RETROK_a ] = { 0x1c, K_NORMAL }, 363 | [ RETROK_b ] = { 0x32, K_NORMAL }, 364 | [ RETROK_c ] = { 0x21, K_NORMAL }, 365 | [ RETROK_d ] = { 0x23, K_NORMAL }, 366 | [ RETROK_e ] = { 0x24, K_NORMAL }, 367 | [ RETROK_f ] = { 0x2b, K_NORMAL }, 368 | [ RETROK_g ] = { 0x34, K_NORMAL }, 369 | [ RETROK_h ] = { 0x33, K_NORMAL }, 370 | [ RETROK_i ] = { 0x43, K_NORMAL }, 371 | [ RETROK_j ] = { 0x3b, K_NORMAL }, 372 | [ RETROK_k ] = { 0x42, K_NORMAL }, 373 | [ RETROK_l ] = { 0x4b, K_NORMAL }, 374 | [ RETROK_m ] = { 0x3a, K_NORMAL }, 375 | [ RETROK_n ] = { 0x31, K_NORMAL }, 376 | [ RETROK_o ] = { 0x44, K_NORMAL }, 377 | [ RETROK_p ] = { 0x4d, K_NORMAL }, 378 | [ RETROK_q ] = { 0x15, K_NORMAL }, 379 | [ RETROK_r ] = { 0x2d, K_NORMAL }, 380 | [ RETROK_s ] = { 0x1b, K_NORMAL }, 381 | [ RETROK_t ] = { 0x2c, K_NORMAL }, 382 | [ RETROK_u ] = { 0x3c, K_NORMAL }, 383 | [ RETROK_v ] = { 0x2a, K_NORMAL }, 384 | [ RETROK_w ] = { 0x1d, K_NORMAL }, 385 | [ RETROK_x ] = { 0x22, K_NORMAL }, 386 | [ RETROK_y ] = { 0x35, K_NORMAL }, 387 | [ RETROK_z ] = { 0x1a, K_NORMAL }, 388 | [ RETROK_LEFTBRACE ] = { 123, K_NORMAL }, 389 | [ RETROK_BAR ] = { 124, K_NORMAL }, 390 | [ RETROK_RIGHTBRACE ] = { 125, K_NORMAL }, 391 | [ RETROK_TILDE ] = { 126, K_NORMAL }, 392 | [ RETROK_DELETE ] = { 127, K_NORMAL }, 393 | 394 | [ RETROK_KP0 ] = { 0x70, K_NORMAL }, 395 | [ RETROK_KP1 ] = { 0x69, K_NORMAL }, 396 | [ RETROK_KP2 ] = { 0x72, K_NORMAL }, 397 | [ RETROK_KP3 ] = { 0x7a, K_NORMAL }, 398 | [ RETROK_KP4 ] = { 0x6b, K_NORMAL }, 399 | [ RETROK_KP5 ] = { 0x73, K_NORMAL }, 400 | [ RETROK_KP6 ] = { 0x74, K_NORMAL }, 401 | [ RETROK_KP7 ] = { 0x6c, K_NORMAL }, 402 | [ RETROK_KP8 ] = { 0x75, K_NORMAL }, 403 | [ RETROK_KP9 ] = { 0x7d, K_NORMAL }, 404 | [ RETROK_KP_PERIOD ] = { 0x71, K_NORMAL }, 405 | [ RETROK_KP_DIVIDE ] = { 0x4a, K_SHIFT_HACK }, 406 | [ RETROK_KP_MULTIPLY ] = { 0x7c, K_NORMAL }, 407 | [ RETROK_KP_MINUS ] = { 0x7b, K_NORMAL }, 408 | [ RETROK_KP_PLUS ] = { 0x79, K_NORMAL }, 409 | [ RETROK_KP_ENTER ] = { 0x5A, K_EXTENDED }, 410 | 411 | [ RETROK_UP ] = { 0x75, K_NUMLOCK_HACK }, 412 | [ RETROK_DOWN ] = { 0x72, K_NUMLOCK_HACK }, 413 | [ RETROK_RIGHT ] = { 0x74, K_NUMLOCK_HACK }, 414 | [ RETROK_LEFT ] = { 0x6b, K_NUMLOCK_HACK }, 415 | [ RETROK_INSERT ] = { 0x70, K_NUMLOCK_HACK }, 416 | [ RETROK_HOME ] = { 0x6c, K_NUMLOCK_HACK }, 417 | [ RETROK_END ] = { 0x69, K_NUMLOCK_HACK }, 418 | [ RETROK_PAGEUP ] = { 0x7d, K_NUMLOCK_HACK }, 419 | [ RETROK_PAGEDOWN ] = { 0x7a, K_NUMLOCK_HACK }, 420 | 421 | [ RETROK_F1 ] = { 0x05, K_NORMAL }, 422 | [ RETROK_F2 ] = { 0x06, K_NORMAL }, 423 | [ RETROK_F3 ] = { 0x04, K_NORMAL }, 424 | [ RETROK_F4 ] = { 0x0c, K_NORMAL }, 425 | [ RETROK_F5 ] = { 0x03, K_NORMAL }, 426 | [ RETROK_F6 ] = { 0x0b, K_NORMAL }, 427 | [ RETROK_F7 ] = { 0x83, K_NORMAL }, 428 | [ RETROK_F8 ] = { 0x0a, K_NORMAL }, 429 | [ RETROK_F9 ] = { 0x01, K_NORMAL }, 430 | [ RETROK_F10 ] = { 0x09, K_NORMAL }, 431 | [ RETROK_F11 ] = { 0x78, K_NORMAL }, 432 | [ RETROK_F12 ] = { 0x07, K_NORMAL }, 433 | 434 | 435 | [ RETROK_RSHIFT ] = { 0x59, K_EXTENDED }, 436 | [ RETROK_LSHIFT ] = { 0x12, K_NORMAL }, 437 | [ RETROK_RCTRL ] = { 0x14, K_EXTENDED }, 438 | [ RETROK_LCTRL ] = { 0x14, K_NORMAL }, 439 | [ RETROK_RALT ] = { 0x11, K_EXTENDED }, 440 | [ RETROK_LALT ] = { 0x11, K_NORMAL }, 441 | 442 | [ RETROK_MENU ] = { 0x27, K_EXTENDED }, 443 | }; 444 | -------------------------------------------------------------------------------- /Libretro/link.T: -------------------------------------------------------------------------------- 1 | { 2 | global: retro_*; 3 | local: *; 4 | }; 5 | 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -g -Os -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unused-parameter 2 | SDL2_CONFIG = sdl2-config 3 | 4 | RISC_CFLAGS = $(CFLAGS) -std=c99 `$(SDL2_CONFIG) --cflags --libs` -lm 5 | 6 | RISC_SOURCE = \ 7 | src/sdl-main.c \ 8 | src/sdl-ps2.c src/sdl-ps2.h \ 9 | src/risc.c src/risc.h src/risc-boot.inc \ 10 | src/risc-fp.c src/risc-fp.h \ 11 | src/disk.c src/disk.h \ 12 | src/pclink.c src/pclink.h \ 13 | src/raw-serial.c src/raw-serial.h \ 14 | src/sdl-clipboard.c src/sdl-clipboard.h 15 | 16 | risc: $(RISC_SOURCE) 17 | $(CC) -o $@ $(filter %.c, $^) $(RISC_CFLAGS) 18 | 19 | # Assumes SDL2 framework download, following README instructions for install. 20 | osx: $(RISC_SOURCE) 21 | gcc -framework SDL2 -F /Library/Frameworks -o risc $(filter %.c, $^) \ 22 | -I /Library/Frameworks/SDL2.framework/Headers/ 23 | 24 | clean: 25 | rm -f risc 26 | -------------------------------------------------------------------------------- /Makefile.libretro: -------------------------------------------------------------------------------- 1 | DEBUG = 0 2 | HAVE_EXCEPTIONS = 0 3 | LAGFIX=1 4 | HAVE_STRINGS_H = 1 5 | 6 | SPACE := 7 | SPACE := $(SPACE) $(SPACE) 8 | BACKSLASH := 9 | BACKSLASH := \$(BACKSLASH) 10 | filter_out1 = $(filter-out $(firstword $1),$1) 11 | filter_out2 = $(call filter_out1,$(call filter_out1,$1)) 12 | 13 | ifeq ($(platform),) 14 | platform = unix 15 | ifeq ($(shell uname -s),) 16 | platform = win 17 | else ifneq ($(findstring MINGW,$(shell uname -s)),) 18 | platform = win 19 | else ifneq ($(findstring Darwin,$(shell uname -s)),) 20 | platform = osx 21 | arch = intel 22 | ifeq ($(shell uname -p),powerpc) 23 | arch = ppc 24 | endif 25 | else ifneq ($(findstring win,$(shell uname -s)),) 26 | platform = win 27 | endif 28 | else ifneq (,$(findstring armv,$(platform))) 29 | override platform += unix 30 | else ifneq (,$(findstring rpi,$(platform))) 31 | override platform += unix 32 | endif 33 | 34 | TARGET_NAME = oberon 35 | 36 | ifneq ($(platform), genode) 37 | ifeq (,$(findstring msvc,$(platform))) 38 | LIBS += -lm 39 | endif 40 | endif 41 | 42 | CORE_DIR := . 43 | 44 | GIT_VERSION := " $(shell cd $(CORE_DIR); git log -1 --pretty='%cd %h' --date=short || echo unknown)" 45 | CFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\" 46 | 47 | ifeq ($(LAGFIX),1) 48 | CFLAGS += -DLAGFIX 49 | endif 50 | 51 | LINK_SCRIPT := $(CORE_DIR)/Libretro/link.T 52 | 53 | # Genode 54 | ifeq ($(platform), genode) 55 | TARGET := $(TARGET_NAME)_libretro.lib.so 56 | PKG_CONFIG_LIBS := genode-lib genode-libc 57 | CFLAGS += -D__GENODE__ 58 | CFLAGS += $(shell pkg-config --cflags $(PKG_CONFIG_LIBS)) 59 | 60 | LDFLAGS += -shared --version-script=$(CORE_DIR)/Libretro/link.T 61 | LDFLAGS += $(shell pkg-config --libs $(PKG_CONFIG_LIBS)) 62 | 63 | CC = /usr/local/genode-gcc/bin/genode-x86-gcc 64 | LD = /usr/local/genode-gcc/bin/genode-x86-ld 65 | 66 | AR = /usr/local/genode-gcc/bin/genode-x86-ar -rcs 67 | RANLIB = /usr/local/genode-gcc/bin/genode-x86-ranlib 68 | 69 | # Unix 70 | else ifneq (,$(findstring unix,$(platform))) 71 | TARGET := $(TARGET_NAME)_libretro.so 72 | fpic := -fPIC 73 | ifneq ($(findstring SunOS,$(shell uname -a)),) 74 | CC = gcc 75 | SHARED := -shared -z defs 76 | else 77 | SHARED := -shared --version-script=$(LINK_SCRIPT) 78 | endif 79 | ifneq ($(findstring Haiku,$(shell uname -a)),) 80 | LIBS := 81 | endif 82 | 83 | # ARM 84 | ifneq (,$(findstring armv,$(platform))) 85 | CFLAGS += -DARM 86 | # Raspberry Pi 87 | else ifneq (,$(findstring rpi,$(platform))) 88 | CFLAGS += -DARM 89 | endif 90 | 91 | # OS X 92 | else ifeq ($(platform), osx) 93 | TARGET := $(TARGET_NAME)_libretro.dylib 94 | fpic := -fPIC 95 | SHARED := -dynamiclib 96 | arch = intel 97 | ifeq ($(shell uname -p),powerpc) 98 | arch = ppc 99 | endif 100 | ifeq ($(arch),ppc) 101 | CFLAGS += -DBLARGG_BIG_ENDIAN=1 -D__ppc__ 102 | endif 103 | OSXVER = `sw_vers -productVersion | cut -d. -f 2` 104 | OSX_LT_MAVERICKS = `(( $(OSXVER) <= 9)) && echo "YES"` 105 | fpic += -mmacosx-version-min=10.1 106 | 107 | # iOS 108 | else ifneq (,$(findstring ios,$(platform))) 109 | TARGET := $(TARGET_NAME)_libretro_ios.dylib 110 | SHARED := -dynamiclib 111 | fpic := -fPIC 112 | ifeq ($(IOSSDK),) 113 | IOSSDK := $(shell xcodebuild -version -sdk iphoneos Path) 114 | endif 115 | ifeq ($(platform),ios-arm64) 116 | CC = clang -arch arm64 -isysroot $(IOSSDK) 117 | else 118 | CC = clang -arch armv7 -isysroot $(IOSSDK) 119 | endif 120 | LD = $(CC) 121 | CFLAGS += -DIOS 122 | CFLAGS += -DARM 123 | ifeq ($(platform),$(filter $(platform),ios9 ios-arm64)) 124 | CC += -miphoneos-version-min=8.0 125 | CFLAGS += -miphoneos-version-min=8.0 126 | else 127 | CC += -miphoneos-version-min=5.0 128 | CFLAGS += -miphoneos-version-min=5.0 129 | endif 130 | 131 | # Theos 132 | else ifeq ($(platform), theos_ios) 133 | DEPLOYMENT_IOSVERSION = 5.0 134 | TARGET = iphone:latest:$(DEPLOYMENT_IOSVERSION) 135 | ARCHS = armv7 armv7s 136 | TARGET_IPHONEOS_DEPLOYMENT_VERSION=$(DEPLOYMENT_IOSVERSION) 137 | THEOS_BUILD_DIR := objs 138 | include $(THEOS)/makefiles/common.mk 139 | LIBRARY_NAME = $(TARGET_NAME)_libretro_ios 140 | 141 | # QNX 142 | else ifeq ($(platform), qnx) 143 | TARGET := $(TARGET_NAME)_libretro_$(platform).so 144 | fpic := -fPIC 145 | SHARED := -shared --version-script=$(LINK_SCRIPT) 146 | CC = qcc -Vgcc_notarmv7le 147 | AR = QCC -Vgcc_ntoarmv7le 148 | CFLAGS += -D__BLACKBERRY_QNX__ 149 | CFLAGS += -DARM 150 | 151 | # Vita 152 | else ifeq ($(platform), vita) 153 | TARGET := $(TARGET_NAME)_libretro_$(platform).so 154 | fpic := -fPIC 155 | CC = arm-vita-eabi-gcc$(EXE_EXT) 156 | AR = arm-vita-eabi-ar$(EXE_EXT) 157 | CFLAGS += -DVITA 158 | 159 | # PS3 160 | else ifneq (,$(filter $(platform), ps3 sncps3 psl1ght)) 161 | TARGET := $(TARGET_NAME)_libretro_$(platform).a 162 | CFLAGS += -DBLARGG_BIG_ENDIAN=1 -D__ppc__ 163 | STATIC_LINKING = 1 164 | HAVE_STRINGS_H = 0 165 | 166 | # sncps3 167 | ifneq (,$(findstring sncps3,$(platform))) 168 | TARGET := $(TARGET_NAME)_libretro_ps3.a 169 | CC = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe 170 | AR = $(CELL_SDK)/host-win32/sn/bin/ps3snarl.exe 171 | 172 | # PS3 173 | else ifneq (,$(findstring ps3,$(platform))) 174 | CC = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-gcc.exe 175 | AR = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-ar.exe 176 | 177 | # Lightweight PS3 Homebrew SDK 178 | else ifneq (,$(findstring psl1ght,$(platform))) 179 | CC = $(PS3DEV)/ppu/bin/ppu-gcc$(EXE_EXT) 180 | AR = $(PS3DEV)/ppu/bin/ppu-ar$(EXE_EXT) 181 | endif 182 | 183 | # Xbox 360 184 | else ifeq ($(platform), xenon) 185 | TARGET := $(TARGET_NAME)_libretro_xenon360.a 186 | CC = xenon-gcc$(EXE_EXT) 187 | AR = xenon-ar$(EXE_EXT) 188 | CFLAGS += -D__LIBXENON__ -m32 -D__ppc__ 189 | STATIC_LINKING = 1 190 | 191 | # Nintendo Game Cube / Wii / WiiU 192 | else ifneq (,$(filter $(platform), ngc wii wiiu)) 193 | TARGET := $(TARGET_NAME)_libretro_$(platform).a 194 | CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT) 195 | AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT) 196 | CFLAGS += -mcpu=750 -meabi -mhard-float -DBLARGG_BIG_ENDIAN=1 -D__ppc__ 197 | CFLAGS += -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int 198 | STATIC_LINKING = 1 199 | 200 | # Nintendo WiiU 201 | ifneq (,$(findstring wiiu,$(platform))) 202 | CFLAGS += -mwup 203 | 204 | # Nintendo Wii 205 | else ifneq (,$(findstring wii,$(platform))) 206 | CFLAGS += -DGEKKO -mrvl 207 | 208 | # Nintendo Game Cube 209 | else ifneq (,$(findstring ngc,$(platform))) 210 | CFLAGS += -DGEKKO -mrvl 211 | endif 212 | 213 | # Emscripten 214 | else ifeq ($(platform), emscripten) 215 | TARGET := $(TARGET_NAME)_libretro_$(platform).bc 216 | STATIC_LINKING = 1 217 | 218 | # Windows MSVC 2003 Xbox 1 219 | else ifeq ($(platform), xbox1_msvc2003) 220 | CFLAGS += -D__WIN32__ 221 | TARGET := $(TARGET_NAME)_libretro_xdk1.lib 222 | MSVCBINDIRPREFIX = $(XDK)/xbox/bin/vc71 223 | CC = "$(MSVCBINDIRPREFIX)/CL.exe" 224 | LD = "$(MSVCBINDIRPREFIX)/lib.exe" 225 | 226 | export INCLUDE := $(XDK)/xbox/include 227 | export LIB := $(XDK)/xbox/lib 228 | PSS_STYLE :=2 229 | CFLAGS += -D_XBOX -D_XBOX1 230 | STATIC_LINKING=1 231 | HAS_GCC := 0 232 | # Windows MSVC 2010 Xbox 360 233 | else ifeq ($(platform), xbox360_msvc2010) 234 | CFLAGS += -D__WIN32__ 235 | TARGET := $(TARGET_NAME)_libretro_xdk360.lib 236 | MSVCBINDIRPREFIX = $(XEDK)/bin/win32 237 | CC = "$(MSVCBINDIRPREFIX)/cl.exe" 238 | LD = "$(MSVCBINDIRPREFIX)/lib.exe" 239 | 240 | export INCLUDE := $(XEDK)/include/xbox 241 | export LIB := $(XEDK)/lib/xbox 242 | PSS_STYLE :=2 243 | CFLAGS += -D_XBOX -D_XBOX360 244 | STATIC_LINKING=1 245 | HAS_GCC := 0 246 | 247 | # Windows MSVC 2017 all architectures 248 | else ifneq (,$(findstring windows_msvc2017,$(platform))) 249 | CFLAGS += -D__WIN32__ 250 | 251 | PlatformSuffix = $(subst windows_msvc2017_,,$(platform)) 252 | ifneq (,$(findstring desktop,$(PlatformSuffix))) 253 | WinPartition = desktop 254 | MSVC2017CompileFlags = -DWINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP 255 | LDFLAGS += -MANIFEST -LTCG:incremental -NXCOMPAT -DYNAMICBASE -DEBUG -OPT:REF -INCREMENTAL:NO -SUBSYSTEM:WINDOWS -MANIFESTUAC:"level='asInvoker' uiAccess='false'" -OPT:ICF -ERRORREPORT:PROMPT -NOLOGO -TLBID:1 256 | LIBS += kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib 257 | else ifneq (,$(findstring uwp,$(PlatformSuffix))) 258 | WinPartition = uwp 259 | MSVC2017CompileFlags = -DWINAPI_FAMILY=WINAPI_FAMILY_APP -D_WINDLL -D_UNICODE -DUNICODE -D__WRL_NO_DEFAULT_LIB__ -ZW:nostdlib -EHsc 260 | LDFLAGS += -APPCONTAINER -NXCOMPAT -DYNAMICBASE -MANIFEST:NO -LTCG -OPT:REF -SUBSYSTEM:CONSOLE -MANIFESTUAC:NO -OPT:ICF -ERRORREPORT:PROMPT -NOLOGO -TLBID:1 -DEBUG:FULL -WINMD:NO 261 | LIBS += WindowsApp.lib 262 | endif 263 | 264 | # Specific to this core 265 | MSVC2017CompileFlags += -D__WIN32__ 266 | 267 | CFLAGS += $(MSVC2017CompileFlags) 268 | 269 | TargetArchMoniker = $(subst $(WinPartition)_,,$(PlatformSuffix)) 270 | 271 | CC = cl.exe 272 | 273 | reg_query = $(call filter_out2,$(subst $2,,$(shell reg query "$2" -v "$1" 2>nul))) 274 | fix_path = $(subst $(SPACE),\ ,$(subst \,/,$1)) 275 | 276 | ProgramFiles86w := $(shell cmd /c "echo %PROGRAMFILES(x86)%") 277 | ProgramFiles86 := $(shell cygpath "$(ProgramFiles86w)") 278 | 279 | WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0) 280 | WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_CURRENT_USER\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0) 281 | WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0) 282 | WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_CURRENT_USER\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0) 283 | WindowsSdkDir := $(WindowsSdkDir) 284 | 285 | WindowsSDKVersion ?= $(firstword $(foreach folder,$(subst $(subst \,/,$(WindowsSdkDir)Include/),,$(wildcard $(call fix_path,$(WindowsSdkDir)Include\*))),$(if $(wildcard $(call fix_path,$(WindowsSdkDir)Include/$(folder)/um/Windows.h)),$(folder),)))$(BACKSLASH) 286 | WindowsSDKVersion := $(WindowsSDKVersion) 287 | 288 | VsInstallBuildTools = $(ProgramFiles86)/Microsoft Visual Studio/2017/BuildTools 289 | VsInstallEnterprise = $(ProgramFiles86)/Microsoft Visual Studio/2017/Enterprise 290 | VsInstallProfessional = $(ProgramFiles86)/Microsoft Visual Studio/2017/Professional 291 | VsInstallCommunity = $(ProgramFiles86)/Microsoft Visual Studio/2017/Community 292 | 293 | VsInstallRoot ?= $(shell if [ -d "$(VsInstallBuildTools)" ]; then echo "$(VsInstallBuildTools)"; fi) 294 | ifeq ($(VsInstallRoot), ) 295 | VsInstallRoot = $(shell if [ -d "$(VsInstallEnterprise)" ]; then echo "$(VsInstallEnterprise)"; fi) 296 | endif 297 | ifeq ($(VsInstallRoot), ) 298 | VsInstallRoot = $(shell if [ -d "$(VsInstallProfessional)" ]; then echo "$(VsInstallProfessional)"; fi) 299 | endif 300 | ifeq ($(VsInstallRoot), ) 301 | VsInstallRoot = $(shell if [ -d "$(VsInstallCommunity)" ]; then echo "$(VsInstallCommunity)"; fi) 302 | endif 303 | VsInstallRoot := $(VsInstallRoot) 304 | 305 | VcCompilerToolsVer := $(shell cat "$(VsInstallRoot)/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt" | grep -o '[0-9\.]*') 306 | VcCompilerToolsDir := $(VsInstallRoot)/VC/Tools/MSVC/$(VcCompilerToolsVer) 307 | 308 | WindowsSDKSharedIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\shared") 309 | WindowsSDKUCRTIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\ucrt") 310 | WindowsSDKUMIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\um") 311 | WindowsSDKUCRTLibDir := $(shell cygpath -w "$(WindowsSdkDir)\Lib\$(WindowsSDKVersion)\ucrt\$(TargetArchMoniker)") 312 | WindowsSDKUMLibDir := $(shell cygpath -w "$(WindowsSdkDir)\Lib\$(WindowsSDKVersion)\um\$(TargetArchMoniker)") 313 | 314 | # For some reason the HostX86 compiler doesn't like compiling for x64 315 | # ("no such file" opening a shared library), and vice-versa. 316 | # Work around it for now by using the strictly x86 compiler for x86, and x64 for x64. 317 | # NOTE: What about ARM? 318 | ifneq (,$(findstring x64,$(TargetArchMoniker))) 319 | VCCompilerToolsBinDir := $(VcCompilerToolsDir)\bin\HostX64 320 | else 321 | VCCompilerToolsBinDir := $(VcCompilerToolsDir)\bin\HostX86 322 | endif 323 | 324 | PATH := $(shell IFS=$$'\n'; cygpath "$(VCCompilerToolsBinDir)/$(TargetArchMoniker)"):$(PATH) 325 | PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VsInstallRoot)/Common7/IDE") 326 | INCLUDE := $(shell IFS=$$'\n'; cygpath -w "$(VcCompilerToolsDir)/include") 327 | LIB := $(shell IFS=$$'\n'; cygpath -w "$(VcCompilerToolsDir)/lib/$(TargetArchMoniker)") 328 | ifneq (,$(findstring uwp,$(PlatformSuffix))) 329 | LIB := $(shell IFS=$$'\n'; cygpath -w "$(LIB)/store") 330 | endif 331 | 332 | export INCLUDE := $(INCLUDE);$(WindowsSDKSharedIncludeDir);$(WindowsSDKUCRTIncludeDir);$(WindowsSDKUMIncludeDir) 333 | export LIB := $(LIB);$(WindowsSDKUCRTLibDir);$(WindowsSDKUMLibDir) 334 | TARGET := $(TARGET_NAME)_libretro.dll 335 | PSS_STYLE :=2 336 | LDFLAGS += -DLL 337 | 338 | # Windows MSVC 2010 x64 339 | else ifeq ($(platform), windows_msvc2010_x64) 340 | CFLAGS += -D__WIN32__ 341 | CC = cl.exe 342 | 343 | PATH := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/bin/amd64"):$(PATH) 344 | PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../IDE") 345 | LIB := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/lib/amd64") 346 | INCLUDE := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/include") 347 | 348 | WindowsSdkDir := $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')lib/x64 349 | WindowsSdkDir ?= $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')lib/x64 350 | 351 | WindowsSdkDirInc := $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')Include 352 | WindowsSdkDirInc ?= $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')Include 353 | 354 | 355 | INCFLAGS_PLATFORM = -I"$(WindowsSdkDirInc)" 356 | export INCLUDE := $(INCLUDE) 357 | export LIB := $(LIB);$(WindowsSdkDir) 358 | TARGET := $(TARGET_NAME)_libretro.dll 359 | PSS_STYLE :=2 360 | LDFLAGS += -DLL 361 | LIBS := 362 | # Windows MSVC 2010 x86 363 | else ifeq ($(platform), windows_msvc2010_x86) 364 | CFLAGS += -D__WIN32__ 365 | CC = cl.exe 366 | 367 | PATH := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/bin"):$(PATH) 368 | PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../IDE") 369 | LIB := $(shell IFS=$$'\n'; cygpath -w "$(VS100COMNTOOLS)../../VC/lib") 370 | INCLUDE := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/include") 371 | 372 | WindowsSdkDir := $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')lib 373 | WindowsSdkDir ?= $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')lib 374 | 375 | WindowsSdkDirInc := $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')Include 376 | WindowsSdkDirInc ?= $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')Include 377 | 378 | 379 | INCFLAGS_PLATFORM = -I"$(WindowsSdkDirInc)" 380 | export INCLUDE := $(INCLUDE) 381 | export LIB := $(LIB);$(WindowsSdkDir) 382 | TARGET := $(TARGET_NAME)_libretro.dll 383 | PSS_STYLE :=2 384 | LDFLAGS += -DLL 385 | LIBS := 386 | 387 | # Windows MSVC 2003 x86 388 | else ifeq ($(platform), windows_msvc2003_x86) 389 | CFLAGS += -D__WIN32__ 390 | CC = cl.exe 391 | 392 | PATH := $(shell IFS=$$'\n'; cygpath "$(VS71COMNTOOLS)../../Vc7/bin"):$(PATH) 393 | PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VS71COMNTOOLS)../IDE") 394 | INCLUDE := $(shell IFS=$$'\n'; cygpath "$(VS71COMNTOOLS)../../Vc7/include") 395 | LIB := $(shell IFS=$$'\n'; cygpath -w "$(VS71COMNTOOLS)../../Vc7/lib") 396 | BIN := $(shell IFS=$$'\n'; cygpath "$(VS71COMNTOOLS)../../Vc7/bin") 397 | 398 | WindowsSdkDir := $(INETSDK) 399 | 400 | export INCLUDE := $(INCLUDE);$(INETSDK)/Include;src/drivers/libretro/msvc/msvc-2005 401 | export LIB := $(LIB);$(WindowsSdkDir);$(INETSDK)/Lib 402 | TARGET := $(TARGET_NAME)_libretro.dll 403 | PSS_STYLE :=2 404 | LDFLAGS += -DLL 405 | CFLAGS += -D_CRT_SECURE_NO_DEPRECATE 406 | 407 | # Windows MSVC 2005 x86 408 | else ifeq ($(platform), windows_msvc2005_x86) 409 | CFLAGS += -D__WIN32__ 410 | CC = cl.exe 411 | 412 | PATH := $(shell IFS=$$'\n'; cygpath "$(VS80COMNTOOLS)../../VC/bin"):$(PATH) 413 | PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VS80COMNTOOLS)../IDE") 414 | INCLUDE := $(shell IFS=$$'\n'; cygpath "$(VS80COMNTOOLS)../../VC/include") 415 | LIB := $(shell IFS=$$'\n'; cygpath -w "$(VS80COMNTOOLS)../../VC/lib") 416 | BIN := $(shell IFS=$$'\n'; cygpath "$(VS80COMNTOOLS)../../VC/bin") 417 | 418 | WindowsSdkDir := $(INETSDK) 419 | 420 | export INCLUDE := $(INCLUDE);$(INETSDK)/Include;libretro-common/include/compat/msvc 421 | export LIB := $(LIB);$(WindowsSdkDir);$(INETSDK)/Lib 422 | TARGET := $(TARGET_NAME)_libretro.dll 423 | PSS_STYLE :=2 424 | LDFLAGS += -DLL 425 | CFLAGS += -D_CRT_SECURE_NO_DEPRECATE 426 | # Windows 427 | else 428 | TARGET := $(TARGET_NAME)_libretro.dll 429 | CC =? gcc 430 | SHARED := -shared -static-libgcc -s --version-script=$(LINK_SCRIPT) 431 | endif 432 | 433 | ifeq ($(DEBUG), 1) 434 | ifneq (,$(findstring msvc,$(platform))) 435 | CFLAGS += -Od -Zi -DDEBUG -D_DEBUG 436 | 437 | ifeq ($(STATIC_LINKING),1) 438 | CFLAGS += -MTd 439 | else 440 | CFLAGS += -MDd 441 | endif 442 | else 443 | CFLAGS += -O0 -g -DDEBUG 444 | endif 445 | else 446 | CFLAGS += -O2 -DNDEBUG 447 | 448 | ifneq (,$(findstring msvc,$(platform))) 449 | ifeq ($(STATIC_LINKING),1) 450 | CFLAGS += -MT 451 | else 452 | CFLAGS += -MD 453 | endif 454 | endif 455 | endif 456 | 457 | include $(CORE_DIR)/Libretro/Makefile.common 458 | 459 | OBJECTS := $(SOURCES_C:.c=.o) 460 | 461 | ifeq ($(platform), sncps3) 462 | WARNINGS_DEFINES = 463 | CODE_DEFINES = 464 | else ifneq (,$(findstring msvc,$(platform))) 465 | WARNINGS_DEFINES = 466 | CODE_DEFINES = 467 | else 468 | WARNINGS_DEFINES = -Wall -W -Wno-unused-parameter 469 | CODE_DEFINES = -fomit-frame-pointer 470 | endif 471 | 472 | CFLAGS += $(CODE_DEFINES) $(WARNINGS_DEFINES) $(fpic) 473 | CFLAGS += -DRIGHTSHIFT_IS_SAR -D__LIBRETRO__ -DALLOW_CPU_OVERCLOCK 474 | CFLAGS += -DHAVE_STDINT_H 475 | ifeq (,$(findstring msvc,$(platform))) 476 | ifeq ($(HAVE_STRINGS_H), 1) 477 | CFLAGS += -DHAVE_STRINGS_H 478 | endif 479 | CFLAGS += -pedantic 480 | endif 481 | 482 | OBJOUT = -o 483 | LINKOUT = -o 484 | 485 | ifneq (,$(findstring msvc,$(platform))) 486 | OBJOUT = -Fo 487 | LINKOUT = -out: 488 | ifeq ($(STATIC_LINKING),1) 489 | LD ?= lib.exe 490 | STATIC_LINKING=0 491 | else 492 | LD = link.exe 493 | endif 494 | endif 495 | 496 | INCFLAGS += $(INCFLAGS_PLATFORM) 497 | 498 | ifeq ($(platform), theos_ios) 499 | COMMON_FLAGS := -DIOS -DARM $(COMMON_DEFINES) $(INCFLAGS) -I$(THEOS_INCLUDE_PATH) -Wno-error 500 | $(LIBRARY_NAME)_CFLAGS += $(CFLAGS) $(COMMON_FLAGS) 501 | ${LIBRARY_NAME}_FILES = $(SOURCES_CXX) $(SOURCES_C) 502 | include $(THEOS_MAKE_PATH)/library.mk 503 | else 504 | all: $(TARGET) 505 | 506 | $(TARGET): $(OBJECTS) 507 | ifeq ($(STATIC_LINKING), 1) 508 | $(AR) rcs $@ $(OBJECTS) 509 | else 510 | $(LD) $(fpic) $(SHARED) $(LINKOUT)$@ $(OBJECTS) $(LDFLAGS) $(LIBS) 511 | endif 512 | 513 | %.o: %.c 514 | $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $(OBJOUT)$@ $< 515 | 516 | clean: 517 | rm -f $(OBJECTS) $(TARGET) 518 | endif 519 | 520 | .PHONY: test 521 | 522 | test: $(TARGET) 523 | retroarch -L $< $(CORE_DIR)/DiskImage/Oberon-2019-01-21.dsk 524 | -------------------------------------------------------------------------------- /Mods/Clipboard.Mod: -------------------------------------------------------------------------------- 1 | MODULE Clipboard; 2 | IMPORT SYSTEM, Texts, Viewers, TextFrames, Oberon; 3 | 4 | CONST control = -24; data = -20; 5 | 6 | PROCEDURE Copy(T: Texts.Text; beg, end: INTEGER); 7 | VAR R: Texts.Reader; 8 | ch: CHAR; 9 | BEGIN 10 | Texts.OpenReader(R, T, beg); 11 | SYSTEM.PUT(control, end - beg); 12 | WHILE beg < end DO 13 | Texts.Read(R, ch); 14 | SYSTEM.PUT(data, ch); 15 | beg := beg + 1 16 | END 17 | END Copy; 18 | 19 | PROCEDURE CopySelection*; 20 | VAR T: Texts.Text; 21 | beg, end, time: INTEGER; 22 | BEGIN 23 | Oberon.GetSelection(T, beg, end, time); 24 | IF time >= 0 THEN Copy(T, beg, end) END 25 | END CopySelection; 26 | 27 | PROCEDURE CopyViewer*; 28 | VAR V: Viewers.Viewer; 29 | F: TextFrames.Frame; 30 | BEGIN 31 | V := Oberon.MarkedViewer(); 32 | IF (V # NIL) & (V.dsc # NIL) & (V.dsc.next IS TextFrames.Frame) THEN 33 | F := V.dsc.next(TextFrames.Frame); 34 | Copy(F.text, 0, F.text.len) 35 | END 36 | END CopyViewer; 37 | 38 | PROCEDURE Paste*; 39 | VAR W: Texts.Writer; 40 | V: Viewers.Viewer; 41 | F: TextFrames.Frame; 42 | len, i: INTEGER; 43 | ch: CHAR; 44 | BEGIN 45 | V := Oberon.FocusViewer; 46 | IF (V # NIL) & (V.dsc # NIL) & (V.dsc.next IS TextFrames.Frame) THEN 47 | SYSTEM.GET(control, len); 48 | IF len > 0 THEN 49 | Texts.OpenWriter(W); 50 | FOR i := 1 TO len DO 51 | SYSTEM.GET(data, ch); 52 | Texts.Write(W, ch) 53 | END; 54 | F := V.dsc.next(TextFrames.Frame); 55 | Texts.Insert(F.text, F.carloc.pos, W.buf); 56 | TextFrames.SetCaret(F, F.carloc.pos + len) 57 | END 58 | END 59 | END Paste; 60 | 61 | END Clipboard. 62 | -------------------------------------------------------------------------------- /Mods/Display.Mod: -------------------------------------------------------------------------------- 1 | MODULE Display; (*NW 5.11.2013 / 17.1.2019*) 2 | (* Adapted for a customizable framebuffer size -- see git history *) 3 | 4 | IMPORT SYSTEM; 5 | 6 | CONST black* = 0; white* = 1; (*black = background*) 7 | replace* = 0; paint* = 1; invert* = 2; (*modes*) 8 | (* base = 0E7F00H; *) (*adr of 1024 x 768 pixel, monocolor display frame*) 9 | (* In the emulator, the frame buffer address might be moved depending on memory configuration *) 10 | 11 | TYPE Frame* = POINTER TO FrameDesc; 12 | FrameMsg* = RECORD END ; 13 | Handler* = PROCEDURE (F: Frame; VAR M: FrameMsg); 14 | FrameDesc* = RECORD next*, dsc*: Frame; 15 | X*, Y*, W*, H*: INTEGER; 16 | handle*: Handler 17 | END ; 18 | 19 | VAR Base*, Width*, Height*, Span: INTEGER; 20 | arrow*, star*, hook*, updown*, block*, cross*, grey*: INTEGER; 21 | (*a pattern is an array of bytes; the first is its width (< 32), the second its height, the rest the raster*) 22 | 23 | PROCEDURE Handle*(F: Frame; VAR M: FrameMsg); 24 | BEGIN 25 | IF (F # NIL) & (F.handle # NIL) THEN F.handle(F, M) END 26 | END Handle; 27 | 28 | (* raster ops *) 29 | 30 | PROCEDURE Dot*(col, x, y, mode: INTEGER); 31 | VAR a: INTEGER; u, s: SET; 32 | BEGIN a := Base + (x DIV 32)*4 + y*Span; 33 | s := {x MOD 32}; SYSTEM.GET(a, u); 34 | IF mode = paint THEN SYSTEM.PUT(a, u + s) 35 | ELSIF mode = invert THEN SYSTEM.PUT(a, u / s) 36 | ELSE (*mode = replace*) 37 | IF col # black THEN SYSTEM.PUT(a, u + s) ELSE SYSTEM.PUT(a, u - s) END 38 | END 39 | END Dot; 40 | 41 | PROCEDURE ReplConst*(col, x, y, w, h, mode: INTEGER); 42 | VAR al, ar, a0, a1, i: INTEGER; left, right, mid, pix, pixl, pixr: SET; 43 | BEGIN al := Base + y*Span; 44 | ar := ((x+w-1) DIV 32)*4 + al; al := (x DIV 32)*4 + al; 45 | IF ar = al THEN 46 | mid := {(x MOD 32) .. ((x+w-1) MOD 32)}; 47 | a1 := al; 48 | FOR i := 1 TO h DO 49 | SYSTEM.GET(a1, pix); 50 | IF mode = invert THEN SYSTEM.PUT(a1, pix / mid) 51 | ELSIF (mode = replace) & (col = black) THEN (*erase*) SYSTEM.PUT(a1, pix - mid) 52 | ELSE (* (mode = paint) OR (mode = replace) & (col # black) *) SYSTEM.PUT(a1, pix + mid) 53 | END; 54 | INC(a1, Span) 55 | END 56 | ELSIF ar > al THEN 57 | left := {(x MOD 32) .. 31}; right := {0 .. ((x+w-1) MOD 32)}; 58 | a0 := al; 59 | FOR i := 1 TO h DO 60 | SYSTEM.GET(a0, pixl); SYSTEM.GET(ar, pixr); 61 | IF mode = invert THEN 62 | SYSTEM.PUT(a0, pixl / left); 63 | FOR a1 := a0+4 TO ar-4 BY 4 DO SYSTEM.GET(a1, pix); SYSTEM.PUT(a1, -pix) END ; 64 | SYSTEM.PUT(ar, pixr / right) 65 | ELSIF (mode = replace) & (col = black) THEN (*erase*) 66 | SYSTEM.PUT(a0, pixl - left); 67 | FOR a1 := a0+4 TO ar-4 BY 4 DO SYSTEM.PUT(a1, {}) END ; 68 | SYSTEM.PUT(ar, pixr - right) 69 | ELSE (* (mode = paint) OR (mode = replace) & (col # black) *) 70 | SYSTEM.PUT(a0, pixl + left); 71 | FOR a1 := a0+4 TO ar-4 BY 4 DO SYSTEM.PUT(a1, {0 .. 31}) END ; 72 | SYSTEM.PUT(ar, pixr + right) 73 | END ; 74 | INC(ar, Span); INC(a0, Span) 75 | END 76 | END 77 | END ReplConst; 78 | 79 | PROCEDURE CopyPattern*(col, patadr, x, y, mode: INTEGER); (*only for modes = paint, invert*) 80 | VAR a0, pwd, i: INTEGER; 81 | w, h, pbt: BYTE; pix, mask: SET; 82 | BEGIN SYSTEM.GET(patadr, w); SYSTEM.GET(patadr+1, h); INC(patadr, 2); 83 | a0 := Base + (x DIV 32)*4 + y*Span; x := x MOD 32; mask := SYSTEM.VAL(SET, ASR(7FFFFFFFH, 31-x)); 84 | FOR i := 1 TO h DO 85 | (*build pattern line; w <= 32*) 86 | SYSTEM.GET(patadr, pbt); INC(patadr); pwd := pbt; 87 | IF w > 8 THEN SYSTEM.GET(patadr, pbt); INC(patadr); pwd := pbt*100H + pwd; 88 | IF w > 16 THEN SYSTEM.GET(patadr, pbt); INC(patadr); pwd := pbt*10000H + pwd; 89 | IF w > 24 THEN SYSTEM.GET(patadr, pbt); INC(patadr); pwd := pbt*1000000H + pwd END 90 | END 91 | END ; 92 | SYSTEM.GET(a0, pix); 93 | IF mode = invert THEN SYSTEM.PUT(a0, SYSTEM.VAL(SET, LSL(pwd, x)) / pix) 94 | ELSE SYSTEM.PUT(a0, SYSTEM.VAL(SET, LSL(pwd, x)) + pix) 95 | END ; 96 | IF x + w > 32 THEN (*spill over*) 97 | SYSTEM.GET(a0+4, pix); 98 | IF mode = invert THEN SYSTEM.PUT(a0+4, SYSTEM.VAL(SET, ASR(pwd, -x)) * mask/ pix) 99 | ELSE SYSTEM.PUT(a0+4, SYSTEM.VAL(SET, ASR(pwd, -x)) * mask+ pix) 100 | END 101 | END; 102 | INC(a0, Span) 103 | END 104 | END CopyPattern; 105 | 106 | PROCEDURE CopyBlock*(sx, sy, w, h, dx, dy, mode: INTEGER); (*only for mode = replace*) 107 | VAR sa, da, sa0, sa1, d, len: INTEGER; 108 | u0, u1, u2, u3, v0, v1, v2, v3, n: INTEGER; 109 | end, step: INTEGER; 110 | src, dst, spill: SET; 111 | m0, m1, m2, m3: SET; 112 | BEGIN 113 | u0 := sx DIV 32; u1 := sx MOD 32; u2 := (sx+w) DIV 32; u3 := (sx+w) MOD 32; 114 | v0 := dx DIV 32; v1 := dx MOD 32; v2 := (dx+w) DIV 32; v3 := (dx+w) MOD 32; 115 | sa := Base + u0*4 + sy*Span; da := Base + v0*4 + dy*Span; 116 | d := da - sa; n := u1 - v1; (*displacement in words and bits*) 117 | len := (u2 - u0) * 4; 118 | m0 := {v1 .. 31}; m2 := {v3 .. 31}; m3 := m0 / m2; 119 | IF d >= 0 THEN (*copy up, scan down*) sa0 := sa + (h-1)*Span; end := sa-Span; step := -Span 120 | ELSE (*copy down, scan up*) sa0 := sa; end := sa + h*Span; step := Span 121 | END ; 122 | WHILE sa0 # end DO 123 | IF n >= 0 THEN (*shift right*) m1 := {n .. 31}; 124 | IF v1 + w >= 32 THEN 125 | SYSTEM.GET(sa0+len, src); src := ROR(src, n); 126 | SYSTEM.GET(sa0+len+d, dst); 127 | SYSTEM.PUT(sa0+len+d, (dst * m2) + (src - m2)); 128 | spill := src - m1; 129 | FOR sa1 := sa0 + len-4 TO sa0+4 BY -4 DO 130 | SYSTEM.GET(sa1, src); src := ROR(src, n); 131 | SYSTEM.PUT(sa1+d, spill + (src * m1)); 132 | spill := src - m1 133 | END ; 134 | SYSTEM.GET(sa0, src); src := ROR(src, n); 135 | SYSTEM.GET(sa0+d, dst); 136 | SYSTEM.PUT(sa0+d, (src * m0) + (dst - m0)) 137 | ELSE SYSTEM.GET(sa0, src); src := ROR(src, n); 138 | SYSTEM.GET(sa0+d, dst); 139 | SYSTEM.PUT(sa0+d, (src * m3) + (dst - m3)) 140 | END 141 | ELSE (*shift left*) m1 := {-n .. 31}; 142 | SYSTEM.GET(sa0, src); src := ROR(src, n); 143 | SYSTEM.GET(sa0+d, dst); 144 | IF v1 + w < 32 THEN 145 | SYSTEM.PUT(sa0+d, (dst - m3) + (src * m3)) 146 | ELSE SYSTEM.PUT(sa0+d, (dst - m0) + (src * m0)); 147 | spill := src - m1; 148 | FOR sa1 := sa0+4 TO sa0 + len-4 BY 4 DO 149 | SYSTEM.GET(sa1, src); src := ROR(src, n); 150 | SYSTEM.PUT(sa1+d, spill + (src * m1)); 151 | spill := src - m1 152 | END ; 153 | SYSTEM.GET(sa0+len, src); src := ROR(src, n); 154 | SYSTEM.GET(sa0+len+d, dst); 155 | SYSTEM.PUT(sa0+len+d, (src - m2) + (dst * m2)) 156 | END 157 | END ; 158 | INC(sa0, step) 159 | END 160 | END CopyBlock; 161 | 162 | PROCEDURE ReplPattern*(col, patadr, x, y, w, h, mode: INTEGER); 163 | (* pattern width = 32, fixed; pattern starts at patadr+4, for mode = invert only *) 164 | VAR al, ar, a0, a1, i: INTEGER; 165 | pta0, pta1: INTEGER; (*pattern addresses*) 166 | ph: BYTE; 167 | left, right, mid, pix, pixl, pixr, ptw: SET; 168 | BEGIN al := Base + y*Span; SYSTEM.GET(patadr+1, ph); 169 | pta0 := patadr+4; pta1 := ph*4 + pta0; 170 | ar := ((x+w-1) DIV 32)*4 + al; al := (x DIV 32)*4 + al; 171 | IF ar = al THEN 172 | mid := {(x MOD 32) .. ((x+w-1) MOD 32)}; 173 | a1 := al; 174 | FOR i := 1 TO h DO 175 | SYSTEM.GET(a1, pix); SYSTEM.GET(pta0, ptw); SYSTEM.PUT(a1, (pix - mid) + (pix/ptw * mid)); INC(pta0, 4); 176 | IF pta0 = pta1 THEN pta0 := patadr+4 END; 177 | INC(a1, Span); 178 | END 179 | ELSIF ar > al THEN 180 | left := {(x MOD 32) .. 31}; right := {0 .. ((x+w-1) MOD 32)}; 181 | a0 := al; 182 | FOR i := 1 TO h DO 183 | SYSTEM.GET(a0, pixl); SYSTEM.GET(pta0, ptw); SYSTEM.PUT(a0, (pixl - left) + (pixl/ptw * left)); 184 | FOR a1 := a0+4 TO ar-4 BY 4 DO SYSTEM.GET(a1, pix); SYSTEM.PUT(a1, pix/ptw) END ; 185 | SYSTEM.GET(ar, pixr); SYSTEM.PUT(ar, (pixr - right) + (pixr/ptw * right)); 186 | INC(pta0, 4); INC(ar, Span); 187 | IF pta0 = pta1 THEN pta0 := patadr+4 END; 188 | INC(a0, Span) 189 | END 190 | END 191 | END ReplPattern; 192 | 193 | PROCEDURE InitResolution; 194 | VAR magic: INTEGER; 195 | BEGIN 196 | Base := 0E7F00H; 197 | SYSTEM.GET(Base, magic); 198 | IF magic = 53697A65H THEN 199 | SYSTEM.GET(Base + 4, Width); 200 | SYSTEM.GET(Base + 8, Height); 201 | Span := 128; 202 | ELSIF magic = 53697A66H THEN 203 | SYSTEM.GET(Base + 4, Width); 204 | SYSTEM.GET(Base + 8, Height); 205 | Span := Width DIV 8 206 | ELSIF magic = 53697A67H THEN 207 | SYSTEM.GET(Base + 4, Width); 208 | SYSTEM.GET(Base + 8, Height); 209 | SYSTEM.GET(Base + 12, Base); 210 | Span := Width DIV 8 211 | ELSE 212 | Width := 1024; Height := 768; Span := 128 213 | END; 214 | END InitResolution; 215 | 216 | BEGIN InitResolution; 217 | arrow := SYSTEM.ADR($0F0F 0060 0070 0038 001C 000E 0007 8003 C101 E300 7700 3F00 1F00 3F00 7F00 FF00$); 218 | star := SYSTEM.ADR($0F0F 8000 8220 8410 8808 9004 A002 C001 7F7F C001 A002 9004 8808 8410 8220 8000$); 219 | hook := SYSTEM.ADR($0C0C 070F 8707 C703 E701 F700 7F00 3F00 1F00 0F00 0700 0300 01$); 220 | updown := SYSTEM.ADR($080E 183C 7EFF 1818 1818 1818 FF7E3C18$); 221 | block := SYSTEM.ADR($0808 FFFF C3C3 C3C3 FFFF$); 222 | cross := SYSTEM.ADR($0F0F 0140 0220 0410 0808 1004 2002 4001 0000 4001 2002 1004 0808 0410 0220 0140$); 223 | grey := SYSTEM.ADR($2002 0000 5555 5555 AAAA AAAA$) 224 | END Display. 225 | -------------------------------------------------------------------------------- /Mods/Input.Mod: -------------------------------------------------------------------------------- 1 | MODULE Input; (*NW 5.10.86 / 15.11.90 Ceres-2; PDR 21.4.12 / NW 15.5.2013 Ceres-4*) 2 | (* Adapted for a customizable framebuffer size -- see git history *) 3 | IMPORT SYSTEM; 4 | 5 | CONST msAdr = -40; kbdAdr = -36; 6 | VAR kbdCode: BYTE; (*last keyboard code read*) 7 | Recd, Up, Shift, Ctrl, Ext: BOOLEAN; 8 | KTabAdr: INTEGER; (*keyboard code translation table*) 9 | MW, MH, MX, MY: INTEGER; (*mouse limits and coords*) 10 | MK: SET; (*mouse keys*) 11 | 12 | (*FIFO implemented in hardware, because every read must be handled, 13 | including tracking the state of the Shift and Ctrl keys*) 14 | 15 | PROCEDURE Peek(); 16 | BEGIN 17 | IF SYSTEM.BIT(msAdr, 28) THEN 18 | SYSTEM.GET(kbdAdr, kbdCode); 19 | IF kbdCode = 0F0H THEN Up := TRUE 20 | ELSIF kbdCode = 0E0H THEN Ext := TRUE 21 | ELSE 22 | IF (kbdCode = 12H) OR (kbdCode = 59H) THEN (*shift*) Shift := ~Up 23 | ELSIF kbdCode = 14H THEN (*ctrl*) Ctrl := ~Up 24 | ELSIF ~Up THEN Recd := TRUE (*real key going down*) 25 | END ; 26 | Up := FALSE; Ext := FALSE 27 | END 28 | END; 29 | END Peek; 30 | 31 | PROCEDURE Available*(): INTEGER; 32 | BEGIN Peek(); 33 | RETURN ORD(Recd) 34 | END Available; 35 | 36 | PROCEDURE Read*(VAR ch: CHAR); 37 | BEGIN 38 | WHILE ~Recd DO Peek() END ; 39 | IF Shift OR Ctrl THEN INC(kbdCode, 80H) END; (*ctrl implies shift*) 40 | (* ch := kbdTab[kbdCode]; *) 41 | SYSTEM.GET(KTabAdr + kbdCode, ch); 42 | IF Ctrl THEN ch := CHR(ORD(ch) MOD 20H) END; 43 | Recd := FALSE 44 | END Read; 45 | 46 | PROCEDURE Mouse*(VAR keys: SET; VAR x, y: INTEGER); 47 | VAR w: INTEGER; 48 | BEGIN SYSTEM.GET(msAdr, w); 49 | keys := SYSTEM.VAL(SET, w DIV 1000000H MOD 8); 50 | x := w MOD 1000H; y := (w DIV 1000H) MOD 1000H; 51 | IF x >= MW THEN x := MW-1 END; 52 | IF y >= MH THEN y := MH-1 END 53 | END Mouse; 54 | 55 | PROCEDURE SetMouseLimits*(w, h: INTEGER); 56 | BEGIN MW := w; MH := h 57 | END SetMouseLimits; 58 | 59 | PROCEDURE Init*; 60 | BEGIN Up := FALSE; Shift := FALSE; Ctrl := FALSE; Recd := FALSE; 61 | KTabAdr := SYSTEM.ADR($ 62 | 00 00 00 00 00 1A 00 00 00 00 00 00 00 09 60 00 63 | 00 00 00 00 00 71 31 00 00 00 7A 73 61 77 32 00 64 | 00 63 78 64 65 34 33 00 00 20 76 66 74 72 35 00 65 | 00 6E 62 68 67 79 36 00 00 00 6D 6A 75 37 38 00 66 | 00 2C 6B 69 6F 30 39 00 00 2E 2F 6C 3B 70 2D 00 67 | 00 00 27 00 5B 3D 00 00 00 00 0D 5D 00 5C 00 00 68 | 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 69 | 00 7F 00 00 00 00 1B 00 00 00 00 00 00 00 00 00 70 | 00 00 00 00 00 00 00 00 00 00 00 00 00 09 7E 00 71 | 00 00 00 00 00 51 21 00 00 00 5A 53 41 57 40 00 72 | 00 43 58 44 45 24 23 00 00 20 56 46 54 52 25 00 73 | 00 4E 42 48 47 59 5E 00 00 00 4D 4A 55 26 2A 00 74 | 00 3C 4B 49 4F 29 28 00 00 3E 3F 4C 3A 50 5F 00 75 | 00 00 22 00 7B 2B 00 00 00 00 0D 7D 00 7C 00 00 76 | 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 77 | 00 7F 00 00 00 00 1B 00 00 00 00 00 00 00 00 00$) 78 | END Init; 79 | 80 | BEGIN Init 81 | END Input. 82 | -------------------------------------------------------------------------------- /Mods/Input.Mod.diff: -------------------------------------------------------------------------------- 1 | --- a/Input.Mod 2 | +++ b/Input.Mod 3 | @@ -46,7 +46,8 @@ 4 | VAR w: INTEGER; 5 | BEGIN SYSTEM.GET(msAdr, w); 6 | keys := SYSTEM.VAL(SET, w DIV 1000000H MOD 8); 7 | - x := w MOD 400H; y := (w DIV 1000H) MOD 400H; 8 | + x := w MOD 1000H; y := (w DIV 1000H) MOD 1000H; 9 | + IF x >= MW THEN x := MW-1 END; 10 | IF y >= MH THEN y := MH-1 END 11 | END Mouse; 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Oberon RISC Emulator 2 | 3 | This is an emulator for the Oberon RISC machine. For more information, 4 | [see Niklaus Wirth's site](https://www.inf.ethz.ch/personal/wirth/). For 5 | newcomers to the Oberon family of operating systems, the document 6 | [Using Oberon] in the [Project Oberon section] is a must-read. 7 | 8 | [Using Oberon]: https://www.inf.ethz.ch/personal/wirth/ProjectOberon/UsingOberon.pdf 9 | [Project Oberon section]: https://www.inf.ethz.ch/personal/wirth/ProjectOberon/index.html 10 | 11 | ![Screenshot](po2013.png) 12 | 13 | ## Building 14 | 15 | To build the emulator, you need the SDL2 library and a C compiler that 16 | understands C99 (GCC and clang are fine). 17 | 18 | [SDL2]: http://libsdl.org/ 19 | 20 | ### Linux 21 | 22 | To install the needed packages on Debian, Ubuntu and derived 23 | distributions, use this command: 24 | 25 | sudo apt-get install build-essential libsdl2-dev 26 | 27 | See your distribution's documentation if you're using something else. 28 | 29 | After that, build the emulator using the command `make`. 30 | 31 | ### OS X 32 | 33 | I can't give much support for OS X, but I've had many reports saying 34 | it works fine. The main stumbling block seems to be that there are two 35 | ways to install the SDL development files: Unix style and Xcode style, 36 | as explained in the [SDL Mac OS X FAQ]. 37 | 38 | For Unix style, build using the command `make`. 39 | For Xcode style, use `make osx`. 40 | 41 | [SDL Mac OS X FAQ]: https://wiki.libsdl.org/FAQMacOSX 42 | 43 | ### Windows 44 | 45 | There's a pre-compiled version in Github's Releases section. 46 | 47 | See the [SDL site][SDL2] for how to set up a compiler 48 | for Windows. It's fiddly. 49 | 50 | Alternatively, you can set up a cross compiler from Linux, which is 51 | also rather fiddle, and build with a command such as: (This is mostly 52 | for my own future reference.) 53 | 54 | make CC=i686-w64-mingw32-gcc-win32 \ 55 | SDL2_CONFIG=/usr/local/cross-tools/i686-w64-mingw32/bin/sdl2-config 56 | 57 | 58 | ## Disk image 59 | 60 | You can find an up-to-date disk image in the [DiskImage/](DiskImage/) 61 | directory. These images contain a full Project Oberon 2013 system, 62 | with all source code, symbol files, etc. 63 | 64 | They also have a few tweaks for better integration with the emulator 65 | (though it should still work fine on real hardware): 66 | 67 | * The Display module supports variable display resolution. (The 68 | original module was hardcoded for 1024x768.) 69 | 70 | * There's a Clipboard module for basic clipboard integration, 71 | documented below. 72 | 73 | The source code for these modifications can be found in the 74 | [Mods/](Mods/) directory. The tools to generate the disk image exist 75 | in the [Project Norebo] repository. 76 | 77 | [Project Norebo]: https://github.com/pdewacht/project-norebo 78 | 79 | 80 | ## Command line options 81 | 82 | Usage: `risc [options] disk-image.dsk` 83 | 84 | * `--fullscreen` Start the emulator in fullscreen mode. 85 | * `--mem ` Give the system more than 1 megabyte of RAM. 86 | * `--size x` Use a non-standard window size. 87 | * `--leds` Print the LED changes to stdout. Useful if you're working on the kernel, 88 | noisy otherwise. 89 | 90 | ## Keyboard and mouse 91 | 92 | The Oberon system assumes you use a US keyboard layout and a three button mouse. 93 | You can use the left alt key to emulate a middle click. 94 | 95 | The following keys are available: 96 | * `Alt-F4` Quit the emulator. 97 | * `F11` or `Shift-Command-F` Toggle fullscreen mode. 98 | * `F12` Soft-reset the Oberon machine. 99 | 100 | 101 | ## Transferring files 102 | 103 | First start the PCLink1 task by middle-clicking on the PCLink1.Run command. 104 | Transfer files using the pcreceive.sh and pcsend.sh scripts. 105 | 106 | Alternatively, use the clipboard integration to exchange text. 107 | 108 | 109 | ## Clipboard integration 110 | 111 | The Clipboard module provides access to the host operating system's 112 | clipboard using these commands: 113 | 114 | * `Clipboard.Paste` 115 | * `Clipboard.CopySelection` 116 | * `Clipboard.CopyViewer` 117 | 118 | 119 | ## Known issues 120 | 121 | * The wireless network interface is not emulated. 122 | * Proper documentation is needed. 123 | 124 | 125 | ## Copyright 126 | 127 | Copyright © 2014 Peter De Wachter 128 | 129 | Permission to use, copy, modify, and/or distribute this software for 130 | any purpose with or without fee is hereby granted, provided that the 131 | above copyright notice and this permission notice appear in all 132 | copies. 133 | 134 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 135 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 136 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 137 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 138 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 139 | PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 140 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 141 | PERFORMANCE OF THIS SOFTWARE. 142 | -------------------------------------------------------------------------------- /pcreceive.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ $# -ne 1 ]; then 3 | echo "Usage: $0 filename" 4 | echo "Triggers receive of a file into the Oberon system from its host." 5 | echo "(Start PCLink first in Oberon, by middle-click or Alt on PCLink1.Run)" 6 | exit 1 7 | fi 8 | echo $1 > PCLink.REC 9 | -------------------------------------------------------------------------------- /pcsend.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ $# -ne 1 ]; then 3 | echo "Usage: $0 filename" 4 | echo "Triggers send of a file out of the Oberon system to its host." 5 | echo "(Start PCLink first in Oberon, by middle-click or Alt on PCLink1.Run)" 6 | exit 1 7 | fi 8 | echo $1 > PCLink.SND 9 | -------------------------------------------------------------------------------- /po2013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pdewacht/oberon-risc-emu/26c8ac5737c71811803c87ad51f1f0d6e62e71fe/po2013.png -------------------------------------------------------------------------------- /src/disk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "disk.h" 8 | 9 | enum DiskState { 10 | diskCommand, 11 | diskRead, 12 | diskWrite, 13 | diskWriting, 14 | }; 15 | 16 | struct Disk { 17 | struct RISC_SPI spi; 18 | 19 | enum DiskState state; 20 | FILE *file; 21 | uint32_t offset; 22 | 23 | uint32_t rx_buf[128]; 24 | int rx_idx; 25 | 26 | uint32_t tx_buf[128+2]; 27 | int tx_cnt; 28 | int tx_idx; 29 | }; 30 | 31 | 32 | static uint32_t disk_read(const struct RISC_SPI *spi); 33 | static void disk_write(const struct RISC_SPI *spi, uint32_t value); 34 | static void disk_run_command(struct Disk *disk); 35 | static void seek_sector(FILE *f, uint32_t secnum); 36 | static void read_sector(FILE *f, uint32_t buf[static 128]); 37 | static void write_sector(FILE *f, uint32_t buf[static 128]); 38 | 39 | 40 | struct RISC_SPI *disk_new(const char *filename) { 41 | struct Disk *disk = calloc(1, sizeof(*disk)); 42 | disk->spi = (struct RISC_SPI) { 43 | .read_data = disk_read, 44 | .write_data = disk_write 45 | }; 46 | 47 | disk->state = diskCommand; 48 | 49 | if (filename) { 50 | disk->file = fopen(filename, "rb+"); 51 | if (disk->file == 0) { 52 | fprintf(stderr, "Can't open file \"%s\": %s\n", filename, strerror(errno)); 53 | exit(1); 54 | } 55 | 56 | // Check for filesystem-only image, starting directly at sector 1 (DiskAdr 29) 57 | read_sector(disk->file, &disk->tx_buf[0]); 58 | disk->offset = (disk->tx_buf[0] == 0x9B1EA38D) ? 0x80002 : 0; 59 | } 60 | 61 | return &disk->spi; 62 | } 63 | 64 | static void disk_write(const struct RISC_SPI *spi, uint32_t value) { 65 | struct Disk *disk = (struct Disk *)spi; 66 | disk->tx_idx++; 67 | switch (disk->state) { 68 | case diskCommand: { 69 | if ((uint8_t)value != 0xFF || disk->rx_idx != 0) { 70 | disk->rx_buf[disk->rx_idx] = value; 71 | disk->rx_idx++; 72 | if (disk->rx_idx == 6) { 73 | disk_run_command(disk); 74 | disk->rx_idx = 0; 75 | } 76 | } 77 | break; 78 | } 79 | case diskRead: { 80 | if (disk->tx_idx == disk->tx_cnt) { 81 | disk->state = diskCommand; 82 | disk->tx_cnt = 0; 83 | disk->tx_idx = 0; 84 | } 85 | break; 86 | } 87 | case diskWrite: { 88 | if (value == 254) { 89 | disk->state = diskWriting; 90 | } 91 | break; 92 | } 93 | case diskWriting: { 94 | if (disk->rx_idx < 128) { 95 | disk->rx_buf[disk->rx_idx] = value; 96 | } 97 | disk->rx_idx++; 98 | if (disk->rx_idx == 128) { 99 | write_sector(disk->file, &disk->rx_buf[0]); 100 | } 101 | if (disk->rx_idx == 130) { 102 | disk->tx_buf[0] = 5; 103 | disk->tx_cnt = 1; 104 | disk->tx_idx = -1; 105 | disk->rx_idx = 0; 106 | disk->state = diskCommand; 107 | } 108 | break; 109 | } 110 | } 111 | } 112 | 113 | static uint32_t disk_read(const struct RISC_SPI *spi) { 114 | struct Disk *disk = (struct Disk *)spi; 115 | uint32_t result; 116 | if (disk->tx_idx >= 0 && disk->tx_idx < disk->tx_cnt) { 117 | result = disk->tx_buf[disk->tx_idx]; 118 | } else { 119 | result = 255; 120 | } 121 | return result; 122 | } 123 | 124 | static void disk_run_command(struct Disk *disk) { 125 | uint32_t cmd = disk->rx_buf[0]; 126 | uint32_t arg = (disk->rx_buf[1] << 24) 127 | | (disk->rx_buf[2] << 16) 128 | | (disk->rx_buf[3] << 8) 129 | | disk->rx_buf[4]; 130 | 131 | switch (cmd) { 132 | case 81: { 133 | disk->state = diskRead; 134 | disk->tx_buf[0] = 0; 135 | disk->tx_buf[1] = 254; 136 | seek_sector(disk->file, arg - disk->offset); 137 | read_sector(disk->file, &disk->tx_buf[2]); 138 | disk->tx_cnt = 2 + 128; 139 | break; 140 | } 141 | case 88: { 142 | disk->state = diskWrite; 143 | seek_sector(disk->file, arg - disk->offset); 144 | disk->tx_buf[0] = 0; 145 | disk->tx_cnt = 1; 146 | break; 147 | } 148 | default: { 149 | disk->tx_buf[0] = 0; 150 | disk->tx_cnt = 1; 151 | break; 152 | } 153 | } 154 | disk->tx_idx = -1; 155 | } 156 | 157 | static void seek_sector(FILE *f, uint32_t secnum) { 158 | if (f) { 159 | fseek(f, secnum * 512, SEEK_SET); 160 | } 161 | } 162 | 163 | static void read_sector(FILE *f, uint32_t buf[static 128]) { 164 | uint8_t bytes[512] = { 0 }; 165 | if (f) { 166 | fread(bytes, 512, 1, f); 167 | } 168 | for (int i = 0; i < 128; i++) { 169 | buf[i] = (uint32_t)bytes[i*4+0] 170 | | ((uint32_t)bytes[i*4+1] << 8) 171 | | ((uint32_t)bytes[i*4+2] << 16) 172 | | ((uint32_t)bytes[i*4+3] << 24); 173 | } 174 | } 175 | 176 | static void write_sector(FILE *f, uint32_t buf[static 128]) { 177 | if (f) { 178 | uint8_t bytes[512]; 179 | for (int i = 0; i < 128; i++) { 180 | bytes[i*4+0] = (uint8_t)(buf[i] ); 181 | bytes[i*4+1] = (uint8_t)(buf[i] >> 8); 182 | bytes[i*4+2] = (uint8_t)(buf[i] >> 16); 183 | bytes[i*4+3] = (uint8_t)(buf[i] >> 24); 184 | } 185 | fwrite(bytes, 512, 1, f); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/disk.h: -------------------------------------------------------------------------------- 1 | #ifndef DISK_H 2 | #define DISK_H 3 | 4 | #include "risc-io.h" 5 | 6 | struct RISC_SPI *disk_new(const char *filename); 7 | 8 | #endif // DISK_H 9 | -------------------------------------------------------------------------------- /src/fp-test/.gitignore: -------------------------------------------------------------------------------- 1 | *.inc 2 | add 3 | div 4 | flr 5 | flt 6 | mul 7 | idiv 8 | -------------------------------------------------------------------------------- /src/fp-test/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall -Wextra -O3 -march=native 2 | 3 | TESTS = add flr flt mul div idiv 4 | 5 | VPATH = $(VERILOG) 6 | 7 | compile: $(TESTS) 8 | test: $(addprefix test-, $(TESTS)) 9 | 10 | test-%: % 11 | ./$< 12 | 13 | %: %.c 14 | gcc -std=c99 -o $@ $(filter %.c, $^) $(CFLAGS) 15 | 16 | RISC_FP = ../risc-fp.c ../risc-fp.h 17 | add: add.c $(RISC_FP) numbers.inc FPAdder.inc 18 | flr: flr.c $(RISC_FP) numbers.inc FPAdder.inc 19 | flt: flt.c $(RISC_FP) numbers.inc FPAdder.inc 20 | mul: mul.c $(RISC_FP) numbers.inc FPMultiplier.inc 21 | div: div.c $(RISC_FP) numbers.inc FPDivider.inc 22 | idiv: idiv.c $(RISC_FP) numbers.inc Divider.inc 23 | 24 | numbers.inc: numbers.py 25 | python3 $< > $@ 26 | 27 | %.inc: %.v v2c.py 28 | python3 v2c.py < $< > $@ 29 | 30 | %.inc: 31 | # If the previous rule failed... 32 | $(error Set the VERILOG environment variable to the directory with the Verilog sources) 33 | 34 | clean: 35 | rm -f $(TESTS) *.inc 36 | -------------------------------------------------------------------------------- /src/fp-test/add.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../risc-fp.h" 6 | #include "numbers.inc" 7 | #include "FPAdder.inc" 8 | 9 | static bool clk __attribute__((unused)); 10 | 11 | static uint32_t v_add(uint32_t in_x, uint32_t in_y) { 12 | x = in_x; 13 | y = in_y; 14 | run = 0; 15 | cycle(); 16 | run = 1; 17 | do { 18 | cycle(); 19 | } while (stall()); 20 | return z(); 21 | } 22 | 23 | 24 | int main() { 25 | int count = 0; 26 | int errors = 0; 27 | for (int i = 0; i < numbers_cnt; i++) { 28 | for (int j = 0; j < numbers_cnt; j++) { 29 | uint32_t v = v_add(numbers[i], numbers[j]); 30 | uint32_t fp = fp_add(numbers[i], numbers[j], 0, 0); 31 | if (v != fp && errors < 10) { 32 | printf("add: %08x %08x => v %08x | fp %08x\n", numbers[i], numbers[j], v, fp); 33 | } 34 | errors += fp != v; 35 | count += 1; 36 | } 37 | if ((i % 500) == 0) { 38 | int p = count * 100 / numbers_cnt / numbers_cnt; 39 | printf("add: %d%% (%d errors)\n", p, errors); 40 | } 41 | } 42 | printf("add: errors: %d tests: %d\n", errors, count); 43 | return errors != 0; 44 | } 45 | -------------------------------------------------------------------------------- /src/fp-test/div.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../risc-fp.h" 6 | #include "numbers.inc" 7 | #include "FPDivider.inc" 8 | 9 | static bool clk __attribute__((unused)); 10 | 11 | static uint32_t v_div(uint32_t in_x, uint32_t in_y) { 12 | x = in_x; 13 | y = in_y; 14 | run = 0; 15 | cycle(); 16 | run = 1; 17 | do { 18 | cycle(); 19 | } while (stall()); 20 | return z(); 21 | } 22 | 23 | 24 | 25 | int main() { 26 | int count = 0; 27 | int errors = 0; 28 | for (int i = 0; i < numbers_cnt; i++) { 29 | for (int j = 0; j < numbers_cnt; j++) { 30 | uint32_t v = v_div(numbers[i], numbers[j]); 31 | uint32_t fp = fp_div(numbers[i], numbers[j]); 32 | if (v != fp && errors < 10) { 33 | printf("div: %08x %08x => v %08x | fp %08x\n", numbers[i], numbers[j], v, fp); 34 | } 35 | errors += fp != v; 36 | count += 1; 37 | } 38 | if ((i % 500) == 0) { 39 | int p = count * 100 / numbers_cnt / numbers_cnt; 40 | printf("div: %d%% (%d errors)\n", p, errors); 41 | } 42 | } 43 | printf("div: errors: %d tests: %d\n", errors, count); 44 | return errors != 0; 45 | } 46 | -------------------------------------------------------------------------------- /src/fp-test/flr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../risc-fp.h" 6 | #include "numbers.inc" 7 | #include "FPAdder.inc" 8 | 9 | static bool clk __attribute__((unused)); 10 | static bool v = 1; 11 | 12 | static uint32_t v_add(uint32_t in_x, uint32_t in_y) { 13 | x = in_x; 14 | y = in_y; 15 | run = 0; 16 | cycle(); 17 | run = 1; 18 | do { 19 | cycle(); 20 | } while (stall()); 21 | return z(); 22 | } 23 | 24 | int main() { 25 | int count = 0; 26 | int errors = 0; 27 | for (int i = 0; i < numbers_cnt; i++) { 28 | uint32_t v = v_add(numbers[i], 0x4B00U<<16); 29 | uint32_t fp = fp_add(numbers[i], 0x4B00U<<16, 0, 1); 30 | if (v != fp && errors < 10) { 31 | printf("flr: %08x => v %08x | fp %08x\n", numbers[i], v, fp); 32 | } 33 | errors += fp != v; 34 | count += 1; 35 | } 36 | printf("flr: errors: %d tests: %d\n", errors, count); 37 | return errors != 0; 38 | } 39 | -------------------------------------------------------------------------------- /src/fp-test/flt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../risc-fp.h" 6 | #include "numbers.inc" 7 | #include "FPAdder.inc" 8 | 9 | static bool clk __attribute__((unused)); 10 | static bool u = 1; 11 | 12 | static uint32_t v_add(uint32_t in_x, uint32_t in_y) { 13 | x = in_x; 14 | y = in_y; 15 | run = 0; 16 | cycle(); 17 | run = 1; 18 | do { 19 | cycle(); 20 | } while (stall()); 21 | return z(); 22 | } 23 | 24 | 25 | int main() { 26 | int count = 0; 27 | int errors = 0; 28 | for (int i = 0; i < numbers_cnt; i++) { 29 | uint32_t v = v_add(numbers[i], 0x4B00U<<16); 30 | uint32_t fp = fp_add(numbers[i], 0x4B00U<<16, 1, 0); 31 | if (v != fp && errors < 10) { 32 | printf("flt: %08x => v %08x | fp %08x\n", numbers[i], v, fp); 33 | } 34 | errors += fp != v; 35 | count += 1; 36 | } 37 | printf("flt: errors: %d tests: %d\n", errors, count); 38 | return errors != 0; 39 | } 40 | -------------------------------------------------------------------------------- /src/fp-test/idiv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../risc-fp.h" 6 | #include "numbers.inc" 7 | #include "Divider.inc" 8 | 9 | static bool clk __attribute__((unused)); 10 | 11 | static struct idiv v_idiv(uint32_t in_x, uint32_t in_y, bool signed_div) { 12 | x = in_x; 13 | y = in_y; 14 | u = signed_div; 15 | run = 0; 16 | cycle(); 17 | run = 1; 18 | do { 19 | cycle(); 20 | } while (stall()); 21 | return (struct idiv){ quot(), rem() }; 22 | } 23 | 24 | 25 | int main() { 26 | int count = 0; 27 | int errors = 0; 28 | for (int s = 0; s < 2; s++) { 29 | for (int i = 0; i < numbers_cnt; i++) { 30 | for (int j = 0; j < numbers_cnt; j++) { 31 | struct idiv v = v_idiv(numbers[i], numbers[j], s); 32 | struct idiv e = idiv(numbers[i], numbers[j], s); 33 | bool error = v.quot != e.quot || v.rem != e.rem; 34 | if (error && errors < 20) { 35 | printf("idiv (signed=%d): 0x%x %d => v (%d,%d) | emu (%d,%d)\n", 36 | s, numbers[i], numbers[j], 37 | v.quot, v.rem, e.quot, e.rem); 38 | } 39 | errors += error; 40 | count += 1; 41 | } 42 | if ((i % 500) == 0) { 43 | int p = count * 100LL / numbers_cnt / numbers_cnt / 2; 44 | printf("idiv: %d%% (%d errors)\n", p, errors); 45 | } 46 | } 47 | } 48 | printf("idiv: errors: %d tests: %d\n", errors, count); 49 | return errors != 0; 50 | } 51 | -------------------------------------------------------------------------------- /src/fp-test/mul.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../risc-fp.h" 6 | #include "numbers.inc" 7 | #include "FPMultiplier.inc" 8 | 9 | static bool clk __attribute__((unused)); 10 | 11 | static uint32_t v_mul(uint32_t in_x, uint32_t in_y) { 12 | x = in_x; 13 | y = in_y; 14 | run = 0; 15 | cycle(); 16 | run = 1; 17 | do { 18 | cycle(); 19 | } while (stall()); 20 | return z(); 21 | } 22 | 23 | 24 | int main() { 25 | int count = 0; 26 | int errors = 0; 27 | for (int i = 0; i < numbers_cnt; i++) { 28 | for (int j = 0; j < numbers_cnt; j++) { 29 | uint32_t v = v_mul(numbers[i], numbers[j]); 30 | uint32_t fp = fp_mul(numbers[i], numbers[j]); 31 | if (v != fp && errors < 10) { 32 | printf("mul: %08x %08x => v %08x | fp %08x\n", numbers[i], numbers[j], v, fp); 33 | } 34 | errors += fp != v; 35 | count += 1; 36 | } 37 | if ((i % 500) == 0) { 38 | int p = count * 100 / numbers_cnt / numbers_cnt; 39 | printf("mul: %d%% (%d errors)\n", p, errors); 40 | } 41 | } 42 | printf("mul: errors: %d tests: %d\n", errors, count); 43 | return errors != 0; 44 | } 45 | -------------------------------------------------------------------------------- /src/fp-test/numbers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import random 4 | import sys 5 | 6 | count = int(sys.argv[1]) if len(sys.argv) > 1 else 25000 7 | 8 | numbers = [] 9 | for e in range(256): 10 | numbers.append((e << 23) | 0) 11 | numbers.append((e << 23) | 1) 12 | numbers.append((e << 23) | 0x7fffff) 13 | numbers.append((e << 23) | 0x7ffffe) 14 | numbers.extend([x | 0x80000000 for x in numbers]) 15 | numbers.extend(random.getrandbits(32) for x in range(count - len(numbers))) 16 | 17 | print("const int numbers_cnt = %d;" % count) 18 | print("const uint32_t numbers[%d] = {" % count) 19 | print(",\n".join('0x%08X' % x for x in numbers)) 20 | print("};") 21 | -------------------------------------------------------------------------------- /src/fp-test/v2c.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This is a big hack to translate the algorithm from a Verilog module to C. 4 | # Don't expect this to work for any modules other than the ones it was 5 | # intended for... 6 | 7 | import re 8 | 9 | class ScanError(BaseException): 10 | def __init__(self, scanner): 11 | self.where = scanner.where() 12 | 13 | 14 | class Scanner(): 15 | KEYWORDS = { 16 | 'module', 'input', 'output', 'reg', 'wire', 'assign', 17 | 'always', 'posedge', 'begin', 'end', 'endmodule', 18 | } 19 | 20 | SCAN_RE = re.compile(r''' 21 | (?P [_a-zA-Z][a-zA-Z0-9]* ) | 22 | (?P (?P[0-9]+) 'b (?P[0-1]+) ) | 23 | (?P (?P[0-9]+) 'h (?P[0-9A-Fa-f]+) ) | 24 | (?P [0-9]+ ) | 25 | (?P == | <= | [();\[:\]=^+-?{}~&|@] ) | 26 | \Z 27 | ''', re.X) 28 | 29 | SKIP_RE = re.compile(r''' 30 | (?: //.* | `.* | \s+ )* 31 | ''', re.X) 32 | 33 | def __init__(self, string): 34 | self.string = string 35 | self.pos = 0 36 | self.next_pos = self.SKIP_RE.match(self.string, pos=self.pos).end() 37 | 38 | def __iter__(self): 39 | return self 40 | 41 | def __next__(self): 42 | self.value = None 43 | while True: 44 | self.pos = self.next_pos 45 | match = self.SCAN_RE.match(self.string, pos=self.pos) 46 | if match is None: 47 | raise ScanError(self) 48 | self.next_pos = self.SKIP_RE.match(self.string, pos=match.end()).end() 49 | 50 | if match.group('word'): 51 | w = match.group('word') 52 | return (w, None) if w in self.KEYWORDS else ('', w) 53 | elif match.group('bits'): 54 | bitlen = int(match.group('bitlen')) 55 | bitdata = int(match.group('bitdata'), 2) 56 | return ('', bits(size=bitlen, value=bitdata)) 57 | elif match.group('hex'): 58 | hexlen = int(match.group('hexlen')) 59 | hexdata = int(match.group('hexdata'), 16) 60 | return ('', bits(size=hexlen, value=hexdata)) 61 | elif match.group('int'): 62 | return ('', int(match.group('int'))) 63 | elif match.group('sym'): 64 | return (match.group('sym'), None) 65 | else: 66 | raise StopIteration 67 | 68 | def where(self): 69 | line = self.string.count('\n', 0, self.pos) + 1 70 | col = self.pos - self.string.rfind('\n', 0, self.pos) 71 | return (line, col) 72 | 73 | 74 | class bits: 75 | def __init__(self, *, size, value): 76 | self.size = size 77 | self.value = value 78 | 79 | def __str__(self): 80 | return '<%d bits: %s>' % (self.size, self.value) 81 | 82 | 83 | class ParseError(BaseException): 84 | pass 85 | 86 | 87 | class Parser(): 88 | def __init__(self, text): 89 | self.names = {} 90 | self.registers = {} 91 | self.bit_by_bit = {} 92 | self.scanner = Scanner(text) 93 | self.token, self.value = None, None 94 | self.next_token() 95 | 96 | def next_token(self): 97 | self.token, self.value = next(self.scanner) 98 | 99 | def expected(self, *tokens): 100 | raise ParseError("at %s: expected %s, found %s" % 101 | (self.scanner.where(), tokens, self.token)) 102 | 103 | def skip_over(self, token): 104 | if self.token != token: 105 | self.expected(token) 106 | self.next_token() 107 | 108 | def skipping_over(self, *tokens): 109 | if self.token in tokens: 110 | self.next_token() 111 | return True 112 | 113 | def name(self): 114 | n = self.value 115 | self.skip_over('') 116 | return n 117 | 118 | def int(self): 119 | n = self.value 120 | self.skip_over('') 121 | return n 122 | 123 | def skip_over_name(self, name): 124 | n = self.name() 125 | if n != name: 126 | self.expected(name) 127 | 128 | def parse_program(self): 129 | self.parse_module() 130 | while self.token != 'endmodule': 131 | if self.skipping_over('reg'): 132 | self.parse_reg() 133 | elif self.skipping_over('wire'): 134 | self.parse_wire() 135 | elif self.skipping_over('assign'): 136 | self.parse_assign() 137 | elif self.skipping_over('always'): 138 | self.parse_always() 139 | else: 140 | raise ParseError('wtf %s' % self.token) 141 | print("static void cycle() {") 142 | for n, expr in self.registers.items(): 143 | size = self.names[n].size 144 | print(" %s %s_tmp = (%s) & %s;" % ( 145 | self.get_type(size), n, expr.value, self.mask(size))) 146 | for n, expr in self.registers.items(): 147 | print(" %s = %s_tmp;" % (n, n)) 148 | print("}") 149 | 150 | def get_type(self, size): 151 | return 'bool' if size == 1 else 'uint64_t' 152 | 153 | def declare_input(self, name, size): 154 | type = self.get_type(size) 155 | self.names[name] = bits(size=size, value=name) 156 | print('static %s %s;' % (type, name)) 157 | 158 | def declare_wire(self, name, size): 159 | type = self.get_type(size) 160 | self.names[name] = bits(size=size, value=name+'()') 161 | print('static %s %s(void);' % (type, name)) 162 | 163 | def declare_reg(self, name, size): 164 | type = self.get_type(size) 165 | self.names[name] = bits(size=size, value=name) 166 | print('static %s %s;' % (type, name)) 167 | 168 | def parse_module(self): 169 | self.skip_over('module') 170 | self.skip_over('') 171 | self.skip_over('(') 172 | while not self.skipping_over('endmodule'): 173 | if self.skipping_over(')'): 174 | break 175 | elif self.skipping_over('input'): 176 | size = self.parse_decl_bitlen() 177 | while self.token == '': 178 | name = self.value 179 | self.declare_input(name, size) 180 | self.next_token() 181 | if not self.skipping_over(','): 182 | break 183 | elif self.skipping_over('output'): 184 | size = self.parse_decl_bitlen() 185 | while self.token == '': 186 | name = self.value 187 | self.declare_wire(name, size) 188 | self.next_token() 189 | if not self.skipping_over(','): 190 | break 191 | else: 192 | raise ParseError("don't understand module statement: %s" % self.token) 193 | self.skip_over(';') 194 | 195 | def parse_decl_bitlen(self): 196 | if self.skipping_over('['): 197 | n1 = self.int(); 198 | self.skip_over(':') 199 | n2 = self.int(); 200 | self.skip_over(']') 201 | if n2 != 0: 202 | raise ParseError("end of range is not 0: %s,%s" % (n1,n2)) 203 | return n1 + 1 204 | else: 205 | return 1 206 | 207 | def parse_wire(self): 208 | size = self.parse_decl_bitlen() 209 | while True: 210 | name = self.name() 211 | self.declare_wire(name, size) 212 | if self.skipping_over(';'): 213 | break 214 | self.skip_over(',') 215 | 216 | def parse_reg(self): 217 | size = self.parse_decl_bitlen() 218 | while True: 219 | name = self.name() 220 | self.declare_reg(name, size) 221 | if self.skipping_over(';'): 222 | break 223 | self.skip_over(',') 224 | 225 | def parse_assign(self): 226 | name = self.name() 227 | size = self.names[name].size 228 | if self.skipping_over('['): 229 | self.parse_assign_bit_by_bit(name) 230 | else: 231 | self.skip_over('=') 232 | a = self.parse_expression() 233 | self.skip_over(';') 234 | print('static %s %s(void) { return (%s) & %s; }' % 235 | (self.get_type(size), name, a.value, self.mask(size))) 236 | 237 | def parse_assign_bit_by_bit(self, name): 238 | size = self.names[name].size 239 | if name not in self.bit_by_bit: 240 | self.bit_by_bit[name] = [None] * size 241 | idx = self.int() 242 | self.skip_over(']') 243 | self.skip_over('=') 244 | a = self.parse_expression() 245 | if a.size != 1: 246 | raise ParseError("Expected a 1-bit expression") 247 | self.skip_over(';') 248 | self.bit_by_bit[name][idx] = a.value 249 | if all(b is not None for b in self.bit_by_bit[name]): 250 | parts = ['((%s & 1) << %s)' % (self.bit_by_bit[name][i], i) for i in range(size)] 251 | expr = '(%s)' % ' | '.join(reversed(parts)) 252 | print('static %s %s(void) { return %s; }' % (self.get_type(size), name, expr)) 253 | 254 | def parse_always(self): 255 | self.skip_over('@') 256 | self.skip_over('(') 257 | self.skip_over('posedge') 258 | self.skip_over('(') 259 | self.skip_over('') 260 | self.skip_over(')') 261 | self.skip_over(')') 262 | if self.skipping_over('begin'): 263 | while not self.skipping_over('end'): 264 | self.parse_reg_assign() 265 | else: 266 | self.parse_reg_assign() 267 | 268 | def parse_reg_assign(self): 269 | n = self.name() 270 | self.skip_over('<=') 271 | e = self.parse_expression() 272 | self.skip_over(';') 273 | self.registers[n] = e; 274 | 275 | def parse_expression(self): 276 | return self.parse_expr_trinary() 277 | 278 | def parse_expr_trinary(self): 279 | a = self.parse_expr_or() 280 | if not self.skipping_over('?'): 281 | return a 282 | b = self.parse_expr_trinary() 283 | self.skip_over(':') 284 | c = self.parse_expr_trinary() 285 | size = self.size(b, c) 286 | value="(%s ? %s : %s)" % (a.value, b.value, c.value) 287 | return bits(size=size, value=value) 288 | 289 | def parse_expr_or(self): 290 | a = self.parse_expr_xor() 291 | while self.skipping_over('|'): 292 | b = self.parse_expr_xor() 293 | size = self.size(a, b) 294 | value = "(%s | %s)" % (a.value, b.value) 295 | a = bits(size=size, value=value) 296 | return a 297 | 298 | def parse_expr_xor(self): 299 | a = self.parse_expr_and() 300 | while self.skipping_over('^'): 301 | b = self.parse_expr_and() 302 | size = self.size(a, b) 303 | value = "(%s ^ %s)" % (a.value, b.value) 304 | a = bits(size=size, value=value) 305 | return a 306 | 307 | def parse_expr_and(self): 308 | a = self.parse_expr_eq() 309 | while self.skipping_over('&'): 310 | b = self.parse_expr_eq() 311 | size = self.size(a, b) 312 | value = "(%s & %s)" % (a.value, b.value) 313 | a = bits(size=size, value=value) 314 | return a 315 | 316 | def parse_expr_eq(self): 317 | a = self.parse_expr_plus() 318 | while self.skipping_over('=='): 319 | b = self.parse_expr_plus() 320 | value = "(%s == %s)" % (a.value, b.value) 321 | a = bits(size=1, value=value) 322 | return a 323 | 324 | def parse_expr_plus(self): 325 | a = self.parse_expr_concat() 326 | while True: 327 | op = self.token 328 | if not self.skipping_over('+', '-'): 329 | return a 330 | b = self.parse_expr_concat() 331 | size = self.size(a, b) 332 | value = "(%s %s %s)" % (a.value, op, b.value) 333 | a = bits(size=size, value=value) 334 | 335 | def parse_expr_concat(self): 336 | if not self.skipping_over('{'): 337 | return self.parse_expr_unary_plus() 338 | exprs = [] 339 | while True: 340 | if self.token == '': 341 | exprs.append(self.parse_expr_repeat()) 342 | else: 343 | exprs.append(self.parse_expression()) 344 | if not self.skipping_over(','): 345 | break 346 | self.skip_over('}') 347 | size = 0 348 | parts = [] 349 | for b in reversed(exprs): 350 | parts.append('((%s & %s) << %s)' % (b.value, self.mask(b.size), size)) 351 | size += b.size 352 | return bits(size=size, value='(%s)' % ' | '.join(reversed(parts))) 353 | 354 | def parse_expr_repeat(self): 355 | count = self.int() 356 | self.skip_over('{') 357 | expr = self.parse_expr_number() 358 | self.skip_over('}') 359 | if expr.size != 1: 360 | raise ParseError("Can only repeat 1-bit values") 361 | parts = [] 362 | for i in range(count): 363 | parts.append('((%s & 1) << %s)' % (expr.value, i)) 364 | return bits(size=count, value='(%s)' % ' | '.join(parts)) 365 | 366 | def parse_expr_unary_plus(self): 367 | if self.skipping_over('+') or not self.skipping_over('-'): 368 | return self.parse_expr_negation() 369 | a = self.parse_expr_number() 370 | value = "(-%s)" % (a.value,) 371 | return bits(size=a.size, value=value) 372 | 373 | def parse_expr_negation(self): 374 | if not self.skipping_over('~'): 375 | return self.parse_expr_number() 376 | a = self.parse_expr_number() 377 | value = "((~%s) & %s)" % (a.value, self.mask(a.size)) 378 | return bits(size=a.size, value=value) 379 | 380 | def parse_expr_number(self): 381 | if self.skipping_over('('): 382 | a = self.parse_expression() 383 | self.skip_over(')') 384 | elif self.token == '': 385 | a = bits(value=self.value, size=None) 386 | self.next_token() 387 | elif self.token == '': 388 | a = self.value 389 | self.next_token() 390 | elif self.token == '': 391 | a = self.names[self.value] 392 | self.next_token() 393 | else: 394 | raise ParseError("Don't now what to do with %s" % self.token) 395 | 396 | if self.skipping_over('['): 397 | n1 = self.int() 398 | if self.skipping_over(':'): 399 | n2 = self.int() 400 | else: 401 | n2 = n1 402 | self.skip_over(']') 403 | if n1 < n2: 404 | raise ParseError("bogus range %s" % ((n1,n2),)) 405 | if n1 >= a.size: 406 | raise ParseError("range %s out of bound in %s" % ((n1,n2), a)) 407 | rsize = n1 - n2 + 1 408 | rvalue = '((%s >> %d) & %s)' % (a.value, n2, self.mask(n1-n2+1)) 409 | a = bits(size=rsize, value=rvalue) 410 | return a 411 | 412 | def mask(self, n): 413 | if n is None: n = 32 414 | return '0x%XULL' % ((1< 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "pclink.h" 9 | 10 | #define ACK 0x10 11 | #define REC 0x21 12 | #define SND 0x22 13 | 14 | #ifndef S_IRGRP 15 | #define S_IRGRP 0 16 | #endif 17 | 18 | #ifndef S_IWGRP 19 | #define S_IWGRP 0 20 | #endif 21 | 22 | #ifndef S_IROTH 23 | #define S_IROTH 0 24 | #endif 25 | 26 | #ifndef S_IWOTH 27 | #define S_IWOTH 0 28 | #endif 29 | 30 | 31 | static const char * RecName = "PCLink.REC"; // e.g. echo Test.Mod > PCLink.REC 32 | static const char * SndName = "PCLink.SND"; 33 | static uint8_t mode = 0; 34 | static int fd = -1; 35 | static int txcount, rxcount, fnlen, flen; 36 | static char szFilename[32]; 37 | static char buf[257]; 38 | 39 | static bool GetJob(const char *JobName) { 40 | bool res = false; 41 | struct stat st; 42 | FILE * f; 43 | 44 | if (stat(JobName, &st) == 0) { 45 | if (st.st_size > 0 && st.st_size <= 33) { 46 | f = fopen(JobName, "r"); 47 | if (f) { 48 | fscanf(f, "%31s", szFilename); 49 | fclose(f); 50 | res = true; txcount = 0; rxcount = 0; fnlen = (int)strlen(szFilename)+1; 51 | } 52 | } 53 | if (!res) { 54 | unlink(JobName); // clean up 55 | } 56 | } 57 | return res; 58 | } 59 | 60 | static uint32_t PCLink_RStat(const struct RISC_Serial *serial) { 61 | struct stat st; 62 | 63 | if (!mode) { 64 | if (GetJob(RecName)) { 65 | if (stat(szFilename, &st) == 0 && st.st_size >= 0 && st.st_size < 0x1000000) { 66 | fd = open(szFilename, O_RDONLY); 67 | if (fd != -1) { 68 | flen = (int)st.st_size; mode = REC; 69 | printf("PCLink REC Filename: %s size %d\n", szFilename, flen); 70 | } 71 | } 72 | if (!mode) { 73 | unlink(RecName); // clean up 74 | } 75 | } else if (GetJob(SndName)) { 76 | fd = open(szFilename, O_CREAT|O_TRUNC|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); 77 | if (fd != -1) { 78 | flen = -1; mode = SND; 79 | printf("PCLink SND Filename: %s\n", szFilename); 80 | } 81 | if (!mode) { 82 | unlink(SndName); // clean up 83 | } 84 | } 85 | } 86 | return 2 + (mode != 0); // xmit always ready 87 | } 88 | 89 | static uint32_t PCLink_RData(const struct RISC_Serial *serial) { 90 | uint8_t ch = 0; 91 | 92 | if (mode) { 93 | if (rxcount == 0) { 94 | ch = mode; 95 | } else if (rxcount < fnlen+1) { 96 | ch = szFilename[rxcount-1]; 97 | } else if (mode == SND) { 98 | ch = ACK; 99 | if (flen == 0) { 100 | mode = 0; unlink(SndName); 101 | } 102 | } else { 103 | int pos = (rxcount - fnlen - 1) % 256; 104 | if (pos == 0 || flen == 0) { 105 | if (flen > 255) { 106 | ch = 255; 107 | } else { 108 | ch = (uint8_t)flen; 109 | if (flen == 0) { 110 | mode = 0; unlink(RecName); 111 | } 112 | } 113 | } else { 114 | read(fd, &ch, 1); 115 | flen--; 116 | } 117 | } 118 | } 119 | 120 | rxcount++; 121 | return ch; 122 | } 123 | 124 | static void PCLink_TData(const struct RISC_Serial *serial, uint32_t value) { 125 | if (mode) { 126 | if (txcount == 0) { 127 | if (value != ACK) { 128 | close(fd); fd = -1; 129 | if (mode == SND) { 130 | unlink(szFilename); // file not found, delete file created 131 | unlink(SndName); // clean up 132 | } else { 133 | unlink(RecName); // clean up 134 | } 135 | mode = 0; 136 | } 137 | } else if (mode == SND) { 138 | int lim; 139 | 140 | int pos = (txcount-1) % 256; 141 | buf[pos] = (uint8_t)value; 142 | lim = (unsigned char)buf[0]; 143 | if (pos == lim) { 144 | write(fd, buf+1, lim); 145 | if (lim < 255) { 146 | flen = 0; close(fd); 147 | } 148 | } 149 | } 150 | } 151 | txcount++; 152 | } 153 | 154 | 155 | const struct RISC_Serial pclink = { 156 | .read_status = PCLink_RStat, 157 | .read_data = PCLink_RData, 158 | .write_data = PCLink_TData 159 | }; 160 | -------------------------------------------------------------------------------- /src/pclink.h: -------------------------------------------------------------------------------- 1 | #ifndef PCLINK_H 2 | #define PCLINK_H 3 | 4 | #include "risc-io.h" 5 | 6 | extern const struct RISC_Serial pclink; 7 | 8 | #endif // PCLINK_H 9 | -------------------------------------------------------------------------------- /src/raw-serial.c: -------------------------------------------------------------------------------- 1 | #ifdef _WIN32 2 | 3 | #include 4 | #include 5 | #include "raw-serial.h" 6 | 7 | struct RawSerial { 8 | struct RISC_Serial serial; 9 | HANDLE handle; 10 | }; 11 | 12 | static uint32_t read_status(const struct RISC_Serial *serial) { 13 | struct RawSerial *s = (struct RawSerial *)serial; 14 | DWORD available_bytes; 15 | if (!PeekNamedPipe(s->handle, 0, 0, 0, &available_bytes, 0)) 16 | available_bytes = 0; 17 | return 2 + (available_bytes > 0); 18 | } 19 | 20 | static uint32_t read_data(const struct RISC_Serial *serial) { 21 | if (read_status(serial) != 3) return 0; 22 | struct RawSerial *s = (struct RawSerial *)serial; 23 | uint8_t byte = 0; 24 | DWORD numBytesRead = 0; 25 | while (numBytesRead == 0) { 26 | if (!ReadFile(s->handle, &byte, 1, &numBytesRead, NULL)) 27 | return 0; 28 | } 29 | return byte; 30 | } 31 | 32 | static void write_data(const struct RISC_Serial *serial, uint32_t data) { 33 | struct RawSerial *s = (struct RawSerial *)serial; 34 | uint8_t byte = (uint8_t)data; 35 | DWORD numBytesWritten = 0; 36 | while (numBytesWritten == 0) { 37 | if (!WriteFile(s->handle, &byte, 1, &numBytesWritten, NULL)) 38 | return; 39 | } 40 | } 41 | 42 | struct RISC_Serial *raw_serial_new(const char *filename_in, const char *filename_out) { 43 | char pipe_name[257]; 44 | HANDLE file_handle; 45 | 46 | if (strcmp(filename_out, "/dev/null") == 0) { 47 | if (filename_in[0] == '\\' && strlen(filename_in) <= 256) { 48 | strcpy(pipe_name, filename_in); 49 | } else if (strlen(filename_in) <= 256 - 9) { 50 | strcpy(pipe_name, "\\\\.\\pipe\\"); 51 | strcat(pipe_name, filename_in); 52 | } else { 53 | fprintf(stderr, "Invalid --serial-in pipe name.\n"); 54 | return NULL; 55 | } 56 | file_handle = CreateNamedPipe(pipe_name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS, 1, 256, 256, 0, NULL); 57 | if (file_handle == NULL || file_handle == INVALID_HANDLE_VALUE) { 58 | fprintf(stderr, "Failed to create outbound pipe instance."); 59 | return NULL; 60 | } 61 | fprintf(stderr, "Waiting for client to connect.\n"); 62 | if (!ConnectNamedPipe(file_handle, NULL)) { 63 | fprintf(stderr, "Failed to accept connection to named pipe.\n"); 64 | CloseHandle(file_handle); 65 | return NULL; 66 | } 67 | } else if (strcmp(filename_in, "/dev/null") == 0) { 68 | if (filename_out[0] == '\\' && strlen(filename_out) <= 256) { 69 | strcpy(pipe_name, filename_out); 70 | } else if (strlen(filename_out) <= 256 - 9) { 71 | strcpy(pipe_name, "\\\\.\\pipe\\"); 72 | strcat(pipe_name, filename_out); 73 | } else { 74 | fprintf(stderr, "Invalid --serial-out pipe/device name.\n"); 75 | return NULL; 76 | } 77 | file_handle = CreateFile(pipe_name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 78 | NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS, NULL); 79 | if (file_handle == NULL || file_handle == INVALID_HANDLE_VALUE) { 80 | fprintf(stderr, "Failed to connect to pipe/device.\n"); 81 | return NULL; 82 | } 83 | } else { 84 | fprintf(stderr, "On Windows, either specify --serial-out to connect to a named pipe or --serial-in to create one.\n"); 85 | return NULL; 86 | } 87 | 88 | struct RawSerial *s = malloc(sizeof(*s)); 89 | if (!s) { 90 | fprintf(stderr, "Allocate structure failed.\n"); 91 | CloseHandle(file_handle); 92 | return NULL; 93 | } 94 | 95 | *s = (struct RawSerial) { 96 | .serial = { 97 | .read_status = &read_status, 98 | .read_data = &read_data, 99 | .write_data = &write_data 100 | }, 101 | .handle = file_handle 102 | }; 103 | 104 | return &s->serial; 105 | } 106 | 107 | #else // _WIN32 108 | 109 | #include 110 | #include 111 | #include 112 | #include 113 | #include 114 | #include 115 | #include "raw-serial.h" 116 | 117 | struct RawSerial { 118 | struct RISC_Serial serial; 119 | int fd_in; 120 | int fd_out; 121 | }; 122 | 123 | static int max(int a, int b) { 124 | return a > b ? a : b; 125 | } 126 | 127 | static uint32_t read_status(const struct RISC_Serial *serial) { 128 | struct RawSerial *s = (struct RawSerial *)serial; 129 | struct timeval tv = { 0, 0 }; 130 | fd_set read_fds; 131 | fd_set write_fds; 132 | FD_ZERO(&read_fds); 133 | FD_SET(s->fd_in, &read_fds); 134 | FD_ZERO(&write_fds); 135 | FD_SET(s->fd_out, &write_fds); 136 | int nfds = max(s->fd_in, s->fd_out) + 1; 137 | int r = select(nfds, &read_fds, &write_fds, NULL, &tv); 138 | int status = 0; 139 | if (r > 0) { 140 | if (FD_ISSET(s->fd_in, &read_fds)) { 141 | status |= 1; 142 | } 143 | if (FD_ISSET(s->fd_out, &write_fds)) { 144 | status |= 2; 145 | } 146 | } 147 | return status; 148 | } 149 | 150 | static uint32_t read_data(const struct RISC_Serial *serial) { 151 | struct RawSerial *s = (struct RawSerial *)serial; 152 | uint8_t byte = 0; 153 | read(s->fd_in, &byte, 1); 154 | return byte; 155 | } 156 | 157 | static void write_data(const struct RISC_Serial *serial, uint32_t data) { 158 | struct RawSerial *s = (struct RawSerial *)serial; 159 | uint8_t byte = (uint8_t)data; 160 | write(s->fd_out, &byte, 1); 161 | } 162 | 163 | struct RISC_Serial *raw_serial_new(const char *filename_in, const char *filename_out) { 164 | int fd_in, fd_out; 165 | 166 | fd_in = open(filename_in, O_RDONLY | O_NONBLOCK); 167 | if (fd_in < 0) { 168 | perror("Failed to open serial input file"); 169 | goto fail1; 170 | } 171 | 172 | fd_out = open(filename_out, O_RDWR | O_NONBLOCK); 173 | if (fd_out < 0) { 174 | perror("Failed to open serial output file"); 175 | goto fail2; 176 | } 177 | 178 | struct RawSerial *s = malloc(sizeof(*s)); 179 | if (!s) { 180 | goto fail3; 181 | } 182 | 183 | *s = (struct RawSerial){ 184 | .serial = { 185 | .read_status = &read_status, 186 | .read_data = &read_data, 187 | .write_data = &write_data 188 | }, 189 | .fd_in = fd_in, 190 | .fd_out = fd_out 191 | }; 192 | return &s->serial; 193 | 194 | fail3: 195 | close(fd_out); 196 | fail2: 197 | close(fd_in); 198 | fail1: 199 | return NULL; 200 | } 201 | 202 | #endif // _WIN32 203 | -------------------------------------------------------------------------------- /src/raw-serial.h: -------------------------------------------------------------------------------- 1 | #ifndef RAW_SERIAL_H 2 | #define RAW_SERIAL_H 3 | 4 | #include "risc-io.h" 5 | 6 | struct RISC_Serial *raw_serial_new(const char *filename_in, const char *filename_out); 7 | 8 | #endif // SERIAL_H 9 | -------------------------------------------------------------------------------- /src/risc-boot.inc: -------------------------------------------------------------------------------- 1 | 0xE7000151, 0x00000000, 0x00000000, 0x00000000, 2 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, 3 | 0x4EE90014, 0xAFE00000, 0xA0E00004, 0x40000000, 4 | 0xA0E00008, 0x40000004, 0xA0E00010, 0x80E00010, 5 | 0x40090001, 0xA0E00010, 0x5000FFCC, 0x80000000, 6 | 0x40030001, 0xE8FFFFFC, 0x5000FFC8, 0x80000000, 7 | 0xA0E0000C, 0x80E00008, 0x81E0000C, 0x00080001, 8 | 0x40030008, 0xA0E00008, 0x80E00010, 0xE9FFFFEF, 9 | 0x80E00008, 0x81E00004, 0xA0100000, 0x8FE00000, 10 | 0x4EE80014, 0xC700000F, 0x4EE90010, 0xAFE00000, 11 | 0x40E80004, 0xF7FFFFDE, 0x80E00004, 0x40090000, 12 | 0xE6000012, 0x40E80008, 0xF7FFFFD9, 0x40E8000C, 13 | 0xF7FFFFD7, 0x80E00008, 0x81E0000C, 0xA1000000, 14 | 0x80E00008, 0x40080004, 0xA0E00008, 0x80E00004, 15 | 0x40090004, 0xA0E00004, 0x80E00004, 0xE9FFFFF3, 16 | 0x40E80004, 0xF7FFFFCA, 0xE7FFFFEB, 0x8FE00000, 17 | 0x4EE80010, 0xC700000F, 0x4EE90008, 0xAFE00000, 18 | 0xA0E00004, 0x5000FFD4, 0x41000000, 0xA1000000, 19 | 0x80E00004, 0x40090000, 0xE600000B, 0x80E00004, 20 | 0x40090001, 0xA0E00004, 0x5000FFD0, 0x5100FFFF, 21 | 0xA1000000, 0x5000FFD4, 0x80000000, 0x40030001, 22 | 0xE8FFFFFC, 0xE7FFFFF2, 0x8FE00000, 0x4EE80008, 23 | 0xC700000F, 0x4EE90008, 0xAFE00000, 0xA0E00004, 24 | 0x5000FFD4, 0x41000001, 0xA1000000, 0x5000FFD0, 25 | 0x81E00004, 0xA1000000, 0x5000FFD4, 0x80000000, 26 | 0x40030001, 0xE8FFFFFC, 0x8FE00000, 0x4EE80008, 27 | 0xC700000F, 0x4EE90018, 0xAFE00000, 0xA0E00004, 28 | 0xA1E00008, 0x40000001, 0xF7FFFFD3, 0x5000FFD0, 29 | 0x80000000, 0xA0E00010, 0x80E00010, 0x400900FF, 30 | 0xE9FFFFF8, 0x400000FF, 0xF7FFFFE2, 0x5000FFD0, 31 | 0x80000000, 0xA0E00010, 0x80E00010, 0x400900FF, 32 | 0xE9FFFFF8, 0x80E00004, 0x40090008, 0xE9000003, 33 | 0x40000087, 0xA0E00014, 0xE7000007, 0x80E00004, 34 | 0xE9000003, 0x40000095, 0xA0E00014, 0xE7000002, 35 | 0x400000FF, 0xA0E00014, 0x80E00004, 0x4004003F, 36 | 0x40080040, 0xF7FFFFCB, 0x40000018, 0x41090000, 37 | 0xE5000008, 0xA0E0000C, 0x80E00008, 0x81E0000C, 38 | 0x00030001, 0xF7FFFFC3, 0x80E0000C, 0x5008FFF8, 39 | 0xE7FFFFF6, 0x80E00014, 0xF7FFFFBE, 0x40000020, 40 | 0xA0E0000C, 0x400000FF, 0xF7FFFFBA, 0x5000FFD0, 41 | 0x80000000, 0xA0E00010, 0x80E0000C, 0x40090001, 42 | 0xA0E0000C, 0x80E00010, 0x40090080, 0xE5000002, 43 | 0x80E0000C, 0xE9FFFFF3, 0x8FE00000, 0x4EE80018, 44 | 0xC700000F, 0x4EE9000C, 0xAFE00000, 0x40000009, 45 | 0xF7FFFF91, 0x40000000, 0x41000000, 0xF7FFFFB5, 46 | 0x40000008, 0x410001AA, 0xF7FFFFB2, 0x5000FFFF, 47 | 0xF7FFFFA0, 0x5000FFFF, 0xF7FFFF9E, 0x5000FFFF, 48 | 0xF7FFFF9C, 0x40000037, 0x41000000, 0xF7FFFFA9, 49 | 0x40000029, 0x41000001, 0x4111001E, 0xF7FFFFA5, 50 | 0x5000FFD0, 0x80000000, 0xA0E00004, 0x5000FFFF, 51 | 0xF7FFFF90, 0x5000FFFF, 0xF7FFFF8E, 0x5000FFFF, 52 | 0xF7FFFF8C, 0x40002710, 0xF7FFFF73, 0x80E00004, 53 | 0xE9FFFFEC, 0x40000010, 0x41000200, 0xF7FFFF95, 54 | 0x40000001, 0xF7FFFF6C, 0x8FE00000, 0x4EE8000C, 55 | 0xC700000F, 0x4EE9000C, 0xAFE00000, 0xA0E00004, 56 | 0x4000003A, 0x41000000, 0xF7FFFF8A, 0x5000FFD0, 57 | 0x80000000, 0xA0E00008, 0x5000FFFF, 0xF7FFFF75, 58 | 0x80E00008, 0xE9000004, 0x5000FFD0, 0x80000000, 59 | 0x40030007, 0xE0000005, 0x80E00004, 0x80000000, 60 | 0x40010009, 0x81E00004, 0xA0100000, 0x5000FFFF, 61 | 0xF7FFFF68, 0x5000FFFF, 0xF7FFFF66, 0x40000001, 62 | 0xF7FFFF4D, 0x8FE00000, 0x4EE8000C, 0xC700000F, 63 | 0x4EE90014, 0xAFE00000, 0xA0E00004, 0xA1E00008, 64 | 0x40E80004, 0xF7FFFFDB, 0x40000011, 0x81E00004, 65 | 0xF7FFFF68, 0x40000000, 0xA0E0000C, 0x5000FFFF, 66 | 0xF7FFFF54, 0x5000FFD0, 0x80000000, 0xA0E00010, 67 | 0x80E0000C, 0x40080001, 0xA0E0000C, 0x80E00010, 68 | 0x400900FE, 0xE9FFFFF5, 0x5000FFD4, 0x41000005, 69 | 0xA1000000, 0x40000000, 0x410901FC, 0xEE000014, 70 | 0xA0E0000C, 0x5000FFD0, 0x5100FFFF, 0xA1000000, 71 | 0x5000FFD4, 0x80000000, 0x40030001, 0xE8FFFFFC, 72 | 0x5000FFD0, 0x80000000, 0xA0E00010, 0x80E00008, 73 | 0x81E00010, 0xA1000000, 0x80E00008, 0x40080004, 74 | 0xA0E00008, 0x80E0000C, 0x40080004, 0xE7FFFFEA, 75 | 0x400000FF, 0xF7FFFF2F, 0x400000FF, 0xF7FFFF2D, 76 | 0x40000001, 0xF7FFFF14, 0x8FE00000, 0x4EE80014, 77 | 0xC700000F, 0x4EE90014, 0xAFE00000, 0x60000008, 78 | 0x40060004, 0xA0E00004, 0x80E00004, 0x41000000, 79 | 0xF7FFFFBF, 0x40000010, 0x80000000, 0xA0E00010, 80 | 0x80E00004, 0x40080001, 0xA0E00004, 0x40000200, 81 | 0xA0E00008, 0x80E00008, 0x81E00010, 0x00090001, 82 | 0xED00000A, 0x80E00004, 0x81E00008, 0xF7FFFFB0, 83 | 0x80E00004, 0x40080001, 0xA0E00004, 0x80E00008, 84 | 0x40080200, 0xA0E00008, 0xE7FFFFF2, 0x8FE00000, 85 | 0x4EE80014, 0xC700000F, 0x4D000000, 0x5E00FFC0, 86 | 0x6E000008, 0x4C000020, 0x0000000F, 0x40090000, 87 | 0xE9000012, 0x40000080, 0x5100FFC4, 0xA0100000, 88 | 0xF7FFFF50, 0x5000FFC4, 0x80000000, 0x40030001, 89 | 0xE8000005, 0x40000081, 0x5100FFC4, 0xA0100000, 90 | 0xF7FFFEC1, 0xE7000004, 0x40000082, 0x5100FFC4, 91 | 0xA0100000, 0xF7FFFFC7, 0xE7000008, 0x5000FFC4, 92 | 0x80000000, 0x40030001, 0xE8000004, 0x40000081, 93 | 0x5100FFC4, 0xA0100000, 0xF7FFFEB3, 0x4000000C, 94 | 0x6100000E, 0x41167EF0, 0xA1000000, 0x40000018, 95 | 0x61000008, 0xA1000000, 0x40000084, 0x5100FFC4, 96 | 0xA0100000, 0x40000000, 0xC7000000, 97 | -------------------------------------------------------------------------------- /src/risc-fp.c: -------------------------------------------------------------------------------- 1 | #include "risc-fp.h" 2 | 3 | uint32_t fp_add(uint32_t x, uint32_t y, bool u, bool v) { 4 | bool xs = (x & 0x80000000) != 0; 5 | uint32_t xe; 6 | int32_t x0; 7 | if (!u) { 8 | xe = (x >> 23) & 0xFF; 9 | uint32_t xm = ((x & 0x7FFFFF) << 1) | 0x1000000; 10 | x0 = (int32_t)(xs ? -xm : xm); 11 | } else { 12 | xe = 150; 13 | x0 = (int32_t)(x & 0x00FFFFFF) << 8 >> 7; 14 | } 15 | 16 | bool ys = (y & 0x80000000) != 0; 17 | uint32_t ye = (y >> 23) & 0xFF; 18 | uint32_t ym = ((y & 0x7FFFFF) << 1); 19 | if (!u && !v) ym |= 0x1000000; 20 | int32_t y0 = (int32_t)(ys ? -ym : ym); 21 | 22 | uint32_t e0; 23 | int32_t x3, y3; 24 | if (ye > xe) { 25 | uint32_t shift = ye - xe; 26 | e0 = ye; 27 | x3 = shift > 31 ? x0 >> 31 : x0 >> shift; 28 | y3 = y0; 29 | } else { 30 | uint32_t shift = xe - ye; 31 | e0 = xe; 32 | x3 = x0; 33 | y3 = shift > 31 ? y0 >> 31 : y0 >> shift; 34 | } 35 | 36 | uint32_t sum = ((xs << 26) | (xs << 25) | (x3 & 0x01FFFFFF)) 37 | + ((ys << 26) | (ys << 25) | (y3 & 0x01FFFFFF)); 38 | 39 | uint32_t s = (((sum & (1 << 26)) ? -sum : sum) + 1) & 0x07FFFFFF; 40 | 41 | uint32_t e1 = e0 + 1; 42 | uint32_t t3 = s >> 1; 43 | if ((s & 0x3FFFFFC) != 0) { 44 | while ((t3 & (1<<24)) == 0) { 45 | t3 <<= 1; 46 | e1--; 47 | } 48 | } else { 49 | t3 <<= 24; 50 | e1 -= 24; 51 | } 52 | 53 | bool xn = (x & 0x7FFFFFFF) == 0; 54 | bool yn = (y & 0x7FFFFFFF) == 0; 55 | 56 | if (v) { 57 | return (int32_t)(sum << 5) >> 6; 58 | } else if (xn) { 59 | return (u | yn) ? 0 : y; 60 | } else if (yn) { 61 | return x; 62 | } else if ((t3 & 0x01FFFFFF) == 0 || (e1 & 0x100) != 0) { 63 | return 0; 64 | } else { 65 | return ((sum & 0x04000000) << 5) | (e1 << 23) | ((t3 >> 1) & 0x7FFFFF); 66 | } 67 | } 68 | 69 | uint32_t fp_mul(uint32_t x, uint32_t y) { 70 | uint32_t sign = (x ^ y) & 0x80000000; 71 | uint32_t xe = (x >> 23) & 0xFF; 72 | uint32_t ye = (y >> 23) & 0xFF; 73 | 74 | uint32_t xm = (x & 0x7FFFFF) | 0x800000; 75 | uint32_t ym = (y & 0x7FFFFF) | 0x800000; 76 | uint64_t m = (uint64_t)xm * ym; 77 | 78 | uint32_t e1 = (xe + ye) - 127; 79 | uint32_t z0; 80 | if ((m & (1ULL << 47)) != 0) { 81 | e1++; 82 | z0 = ((m >> 23) + 1) & 0xFFFFFF; 83 | } else { 84 | z0 = ((m >> 22) + 1) & 0xFFFFFF; 85 | } 86 | 87 | if (xe == 0 || ye == 0) { 88 | return 0; 89 | } else if ((e1 & 0x100) == 0) { 90 | return sign | ((e1 & 0xFF) << 23) | (z0 >> 1); 91 | } else if ((e1 & 0x80) == 0) { 92 | return sign | (0xFF << 23) | (z0 >> 1); 93 | } else { 94 | return 0; 95 | } 96 | } 97 | 98 | uint32_t fp_div(uint32_t x, uint32_t y) { 99 | uint32_t sign = (x ^ y) & 0x80000000; 100 | uint32_t xe = (x >> 23) & 0xFF; 101 | uint32_t ye = (y >> 23) & 0xFF; 102 | 103 | uint32_t xm = (x & 0x7FFFFF) | 0x800000; 104 | uint32_t ym = (y & 0x7FFFFF) | 0x800000; 105 | uint32_t q1 = (uint32_t)(xm * (1ULL << 25) / ym); 106 | 107 | uint32_t e1 = (xe - ye) + 126; 108 | uint32_t q2; 109 | if ((q1 & (1 << 25)) != 0) { 110 | e1++; 111 | q2 = (q1 >> 1) & 0xFFFFFF; 112 | } else { 113 | q2 = q1 & 0xFFFFFF; 114 | } 115 | uint32_t q3 = q2 + 1; 116 | 117 | if (xe == 0) { 118 | return 0; 119 | } else if (ye == 0) { 120 | return sign | (0xFF << 23); 121 | } else if ((e1 & 0x100) == 0) { 122 | return sign | ((e1 & 0xFF) << 23) | (q3 >> 1); 123 | } else if ((e1 & 0x80) == 0) { 124 | return sign | (0xFF << 23) | (q2 >> 1); 125 | } else { 126 | return 0; 127 | } 128 | } 129 | 130 | struct idiv idiv(uint32_t x, uint32_t y, bool signed_div) { 131 | bool sign = ((int32_t)x < 0) & signed_div; 132 | uint32_t x0 = sign ? -x : x; 133 | 134 | uint64_t RQ = x0; 135 | for (int S = 0; S < 32; ++S) { 136 | uint32_t w0 = (uint32_t)(RQ >> 31); 137 | uint32_t w1 = w0 - y; 138 | if ((int32_t)w1 < 0) { 139 | RQ = ((uint64_t)w0 << 32) | ((RQ & 0x7FFFFFFFU) << 1); 140 | } else { 141 | RQ = ((uint64_t)w1 << 32) | ((RQ & 0x7FFFFFFFU) << 1) | 1; 142 | } 143 | } 144 | 145 | struct idiv d = { (uint32_t)RQ, (uint32_t)(RQ >> 32) }; 146 | if (sign) { 147 | d.quot = -d.quot; 148 | if (d.rem) { 149 | d.quot -= 1; 150 | d.rem = y - d.rem; 151 | } 152 | } 153 | return d; 154 | } 155 | -------------------------------------------------------------------------------- /src/risc-fp.h: -------------------------------------------------------------------------------- 1 | #ifndef RISC_FP_H 2 | #define RISC_FP_H 3 | 4 | #include 5 | #include 6 | 7 | uint32_t fp_add(uint32_t x, uint32_t y, bool u, bool v); 8 | uint32_t fp_mul(uint32_t x, uint32_t y); 9 | uint32_t fp_div(uint32_t x, uint32_t y); 10 | 11 | struct idiv { uint32_t quot, rem; }; 12 | struct idiv idiv(uint32_t x, uint32_t y, bool signed_div); 13 | 14 | #endif // RISC_FP_H 15 | -------------------------------------------------------------------------------- /src/risc-io.h: -------------------------------------------------------------------------------- 1 | #ifndef RISC_IO_H 2 | #define RISC_IO_H 3 | 4 | #include 5 | 6 | struct RISC_Serial { 7 | uint32_t (*read_status)(const struct RISC_Serial *); 8 | uint32_t (*read_data)(const struct RISC_Serial *); 9 | void (*write_data)(const struct RISC_Serial *, uint32_t); 10 | }; 11 | 12 | struct RISC_SPI { 13 | uint32_t (*read_data)(const struct RISC_SPI *); 14 | void (*write_data)(const struct RISC_SPI *, uint32_t); 15 | }; 16 | 17 | struct RISC_Clipboard { 18 | void (*write_control)(const struct RISC_Clipboard *, uint32_t); 19 | uint32_t (*read_control)(const struct RISC_Clipboard *); 20 | void (*write_data)(const struct RISC_Clipboard *, uint32_t); 21 | uint32_t (*read_data)(const struct RISC_Clipboard *); 22 | }; 23 | 24 | struct RISC_LED { 25 | void (*write)(const struct RISC_LED *, uint32_t); 26 | }; 27 | 28 | #endif // RISC_IO_H 29 | -------------------------------------------------------------------------------- /src/risc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "risc.h" 7 | #include "risc-fp.h" 8 | 9 | 10 | // Our memory layout is slightly different from the FPGA implementation: 11 | // The FPGA uses a 20-bit address bus and thus ignores the top 12 bits, 12 | // while we use all 32 bits. This allows us to have more than 1 megabyte 13 | // of RAM. 14 | // 15 | // In the default configuration, the emulator is compatible with the 16 | // FPGA system. But If the user requests more memory, we move the 17 | // framebuffer to make room for a larger Oberon heap. This requires a 18 | // custom Display.Mod. 19 | 20 | 21 | #define DefaultMemSize 0x00100000 22 | #define DefaultDisplayStart 0x000E7F00 23 | 24 | #define ROMStart 0xFFFFF800 25 | #define ROMWords 512 26 | #define IOStart 0xFFFFFFC0 27 | 28 | 29 | struct RISC { 30 | uint32_t PC; 31 | uint32_t R[16]; 32 | uint32_t H; 33 | bool Z, N, C, V; 34 | 35 | uint32_t mem_size; 36 | uint32_t display_start; 37 | 38 | uint32_t progress; 39 | uint32_t current_tick; 40 | uint32_t mouse; 41 | uint8_t key_buf[16]; 42 | uint32_t key_cnt; 43 | uint32_t switches; 44 | 45 | const struct RISC_LED *leds; 46 | const struct RISC_Serial *serial; 47 | uint32_t spi_selected; 48 | const struct RISC_SPI *spi[4]; 49 | const struct RISC_Clipboard *clipboard; 50 | 51 | int fb_width; // words 52 | int fb_height; // lines 53 | struct Damage damage; 54 | 55 | uint32_t *RAM; 56 | uint32_t ROM[ROMWords]; 57 | }; 58 | 59 | enum { 60 | MOV, LSL, ASR, ROR, 61 | AND, ANN, IOR, XOR, 62 | ADD, SUB, MUL, DIV, 63 | FAD, FSB, FML, FDV, 64 | }; 65 | 66 | static void risc_single_step(struct RISC *risc); 67 | static void risc_set_register(struct RISC *risc, int reg, uint32_t value); 68 | static uint32_t risc_load_word(struct RISC *risc, uint32_t address); 69 | static uint8_t risc_load_byte(struct RISC *risc, uint32_t address); 70 | static void risc_store_word(struct RISC *risc, uint32_t address, uint32_t value); 71 | static void risc_store_byte(struct RISC *risc, uint32_t address, uint8_t value); 72 | static uint32_t risc_load_io(struct RISC *risc, uint32_t address); 73 | static void risc_store_io(struct RISC *risc, uint32_t address, uint32_t value); 74 | 75 | static const uint32_t bootloader[ROMWords] = { 76 | #include "risc-boot.inc" 77 | }; 78 | 79 | 80 | struct RISC *risc_new() { 81 | struct RISC *risc = calloc(1, sizeof(*risc)); 82 | risc->mem_size = DefaultMemSize; 83 | risc->display_start = DefaultDisplayStart; 84 | risc->fb_width = RISC_FRAMEBUFFER_WIDTH / 32; 85 | risc->fb_height = RISC_FRAMEBUFFER_HEIGHT; 86 | risc->damage = (struct Damage){ 87 | .x1 = 0, 88 | .y1 = 0, 89 | .x2 = risc->fb_width - 1, 90 | .y2 = risc->fb_height - 1 91 | }; 92 | risc->RAM = calloc(1, risc->mem_size); 93 | memcpy(risc->ROM, bootloader, sizeof(risc->ROM)); 94 | risc_reset(risc); 95 | return risc; 96 | } 97 | 98 | void risc_configure_memory(struct RISC *risc, int megabytes_ram, int screen_width, int screen_height) { 99 | if (megabytes_ram < 1) { 100 | megabytes_ram = 1; 101 | } 102 | if (megabytes_ram > 32) { 103 | megabytes_ram = 32; 104 | } 105 | 106 | risc->display_start = megabytes_ram << 20; 107 | risc->mem_size = risc->display_start + (screen_width * screen_height) / 8; 108 | risc->fb_width = screen_width / 32; 109 | risc->fb_height = screen_height; 110 | risc->damage = (struct Damage){ 111 | .x1 = 0, 112 | .y1 = 0, 113 | .x2 = risc->fb_width - 1, 114 | .y2 = risc->fb_height - 1 115 | }; 116 | 117 | free(risc->RAM); 118 | risc->RAM = calloc(1, risc->mem_size); 119 | 120 | // Patch the new constants in the bootloader. 121 | uint32_t mem_lim = risc->display_start - 16; 122 | risc->ROM[372] = 0x61000000 + (mem_lim >> 16); 123 | risc->ROM[373] = 0x41160000 + (mem_lim & 0x0000FFFF); 124 | uint32_t stack_org = risc->display_start / 2; 125 | risc->ROM[376] = 0x61000000 + (stack_org >> 16); 126 | 127 | // Inform the display driver of the framebuffer layout. 128 | // This isn't a very pretty mechanism, but this way our disk images 129 | // should still boot on the standard FPGA system. 130 | risc->RAM[DefaultDisplayStart/4] = 0x53697A67; 131 | risc->RAM[DefaultDisplayStart/4+1] = screen_width; 132 | risc->RAM[DefaultDisplayStart/4+2] = screen_height; 133 | risc->RAM[DefaultDisplayStart/4+3] = risc->display_start; 134 | 135 | risc_reset(risc); 136 | } 137 | 138 | void risc_set_leds(struct RISC *risc, const struct RISC_LED *leds) { 139 | risc->leds = leds; 140 | } 141 | 142 | void risc_set_serial(struct RISC *risc, const struct RISC_Serial *serial) { 143 | risc->serial = serial; 144 | } 145 | 146 | void risc_set_spi(struct RISC *risc, int index, const struct RISC_SPI *spi) { 147 | if (index == 1 || index == 2) { 148 | risc->spi[index] = spi; 149 | } 150 | } 151 | 152 | void risc_set_clipboard(struct RISC *risc, const struct RISC_Clipboard *clipboard) { 153 | risc->clipboard = clipboard; 154 | } 155 | 156 | void risc_set_switches(struct RISC *risc, int switches) { 157 | risc->switches = switches; 158 | } 159 | 160 | void risc_reset(struct RISC *risc) { 161 | risc->PC = ROMStart/4; 162 | } 163 | 164 | void risc_run(struct RISC *risc, int cycles) { 165 | risc->progress = 20; 166 | // The progress value is used to detect that the RISC cpu is busy 167 | // waiting on the millisecond counter or on the keyboard ready 168 | // bit. In that case it's better to just pause emulation until the 169 | // next frame. 170 | for (int i = 0; i < cycles && risc->progress; i++) { 171 | risc_single_step(risc); 172 | } 173 | } 174 | 175 | static void risc_single_step(struct RISC *risc) { 176 | uint32_t ir; 177 | if (risc->PC < risc->mem_size / 4) { 178 | ir = risc->RAM[risc->PC]; 179 | } else if (risc->PC >= ROMStart/4 && risc->PC < ROMStart/4 + ROMWords) { 180 | ir = risc->ROM[risc->PC - ROMStart/4]; 181 | } else { 182 | fprintf(stderr, "Branched into the void (PC=0x%08X), resetting...\n", risc->PC); 183 | risc_reset(risc); 184 | return; 185 | } 186 | risc->PC++; 187 | 188 | const uint32_t pbit = 0x80000000; 189 | const uint32_t qbit = 0x40000000; 190 | const uint32_t ubit = 0x20000000; 191 | const uint32_t vbit = 0x10000000; 192 | 193 | if ((ir & pbit) == 0) { 194 | // Register instructions 195 | uint32_t a = (ir & 0x0F000000) >> 24; 196 | uint32_t b = (ir & 0x00F00000) >> 20; 197 | uint32_t op = (ir & 0x000F0000) >> 16; 198 | uint32_t im = ir & 0x0000FFFF; 199 | uint32_t c = ir & 0x0000000F; 200 | 201 | uint32_t a_val, b_val, c_val; 202 | b_val = risc->R[b]; 203 | if ((ir & qbit) == 0) { 204 | c_val = risc->R[c]; 205 | } else if ((ir & vbit) == 0) { 206 | c_val = im; 207 | } else { 208 | c_val = 0xFFFF0000 | im; 209 | } 210 | 211 | switch (op) { 212 | case MOV: { 213 | if ((ir & ubit) == 0) { 214 | a_val = c_val; 215 | } else if ((ir & qbit) != 0) { 216 | a_val = c_val << 16; 217 | } else if ((ir & vbit) != 0) { 218 | a_val = 0xD0 | // ??? 219 | (risc->N * 0x80000000U) | 220 | (risc->Z * 0x40000000U) | 221 | (risc->C * 0x20000000U) | 222 | (risc->V * 0x10000000U); 223 | } else { 224 | a_val = risc->H; 225 | } 226 | break; 227 | } 228 | case LSL: { 229 | a_val = b_val << (c_val & 31); 230 | break; 231 | } 232 | case ASR: { 233 | a_val = ((int32_t)b_val) >> (c_val & 31); 234 | break; 235 | } 236 | case ROR: { 237 | a_val = (b_val >> (c_val & 31)) | (b_val << (-c_val & 31)); 238 | break; 239 | } 240 | case AND: { 241 | a_val = b_val & c_val; 242 | break; 243 | } 244 | case ANN: { 245 | a_val = b_val & ~c_val; 246 | break; 247 | } 248 | case IOR: { 249 | a_val = b_val | c_val; 250 | break; 251 | } 252 | case XOR: { 253 | a_val = b_val ^ c_val; 254 | break; 255 | } 256 | case ADD: { 257 | a_val = b_val + c_val; 258 | if ((ir & ubit) != 0) { 259 | a_val += risc->C; 260 | } 261 | risc->C = a_val < b_val; 262 | risc->V = ((a_val ^ c_val) & (a_val ^ b_val)) >> 31; 263 | break; 264 | } 265 | case SUB: { 266 | a_val = b_val - c_val; 267 | if ((ir & ubit) != 0) { 268 | a_val -= risc->C; 269 | } 270 | risc->C = a_val > b_val; 271 | risc->V = ((b_val ^ c_val) & (a_val ^ b_val)) >> 31; 272 | break; 273 | } 274 | case MUL: { 275 | uint64_t tmp; 276 | if ((ir & ubit) == 0) { 277 | tmp = (int64_t)(int32_t)b_val * (int64_t)(int32_t)c_val; 278 | } else { 279 | tmp = (uint64_t)b_val * (uint64_t)c_val; 280 | } 281 | a_val = (uint32_t)tmp; 282 | risc->H = (uint32_t)(tmp >> 32); 283 | break; 284 | } 285 | case DIV: { 286 | if ((int32_t)c_val > 0) { 287 | if ((ir & ubit) == 0) { 288 | a_val = (int32_t)b_val / (int32_t)c_val; 289 | risc->H = (int32_t)b_val % (int32_t)c_val; 290 | if ((int32_t)risc->H < 0) { 291 | a_val--; 292 | risc->H += c_val; 293 | } 294 | } else { 295 | a_val = b_val / c_val; 296 | risc->H = b_val % c_val; 297 | } 298 | } else { 299 | struct idiv q = idiv(b_val, c_val, ir & ubit); 300 | a_val = q.quot; 301 | risc->H = q.rem; 302 | } 303 | break; 304 | } 305 | case FAD: { 306 | a_val = fp_add(b_val, c_val, ir & ubit, ir & vbit); 307 | break; 308 | } 309 | case FSB: { 310 | a_val = fp_add(b_val, c_val ^ 0x80000000, ir & ubit, ir & vbit); 311 | break; 312 | } 313 | case FML: { 314 | a_val = fp_mul(b_val, c_val); 315 | break; 316 | } 317 | case FDV: { 318 | a_val = fp_div(b_val, c_val); 319 | break; 320 | } 321 | default: { 322 | abort(); // unreachable 323 | } 324 | } 325 | risc_set_register(risc, a, a_val); 326 | } 327 | else if ((ir & qbit) == 0) { 328 | // Memory instructions 329 | uint32_t a = (ir & 0x0F000000) >> 24; 330 | uint32_t b = (ir & 0x00F00000) >> 20; 331 | int32_t off = ir & 0x000FFFFF; 332 | off = (off ^ 0x00080000) - 0x00080000; // sign-extend 333 | 334 | uint32_t address = risc->R[b] + off; 335 | if ((ir & ubit) == 0) { 336 | uint32_t a_val; 337 | if ((ir & vbit) == 0) { 338 | a_val = risc_load_word(risc, address); 339 | } else { 340 | a_val = risc_load_byte(risc, address); 341 | } 342 | risc_set_register(risc, a, a_val); 343 | } else { 344 | if ((ir & vbit) == 0) { 345 | risc_store_word(risc, address, risc->R[a]); 346 | } else { 347 | risc_store_byte(risc, address, (uint8_t)risc->R[a]); 348 | } 349 | } 350 | } 351 | else { 352 | // Branch instructions 353 | bool t = (ir >> 27) & 1; 354 | switch ((ir >> 24) & 7) { 355 | case 0: t ^= risc->N; break; 356 | case 1: t ^= risc->Z; break; 357 | case 2: t ^= risc->C; break; 358 | case 3: t ^= risc->V; break; 359 | case 4: t ^= risc->C | risc->Z; break; 360 | case 5: t ^= risc->N ^ risc->V; break; 361 | case 6: t ^= (risc->N ^ risc->V) | risc->Z; break; 362 | case 7: t ^= true; break; 363 | default: abort(); // unreachable 364 | } 365 | if (t) { 366 | if ((ir & vbit) != 0) { 367 | risc_set_register(risc, 15, risc->PC * 4); 368 | } 369 | if ((ir & ubit) == 0) { 370 | uint32_t c = ir & 0x0000000F; 371 | risc->PC = risc->R[c] / 4; 372 | } else { 373 | int32_t off = ir & 0x00FFFFFF; 374 | off = (off ^ 0x00800000) - 0x00800000; // sign-extend 375 | risc->PC = risc->PC + off; 376 | } 377 | } 378 | } 379 | } 380 | 381 | static void risc_set_register(struct RISC *risc, int reg, uint32_t value) { 382 | risc->R[reg] = value; 383 | risc->Z = value == 0; 384 | risc->N = (int32_t)value < 0; 385 | } 386 | 387 | static uint32_t risc_load_word(struct RISC *risc, uint32_t address) { 388 | if (address < risc->mem_size) { 389 | return risc->RAM[address/4]; 390 | } else { 391 | return risc_load_io(risc, address); 392 | } 393 | } 394 | 395 | static uint8_t risc_load_byte(struct RISC *risc, uint32_t address) { 396 | uint32_t w = risc_load_word(risc, address); 397 | return (uint8_t)(w >> (address % 4 * 8)); 398 | } 399 | 400 | static void risc_update_damage(struct RISC *risc, int w) { 401 | int row = w / risc->fb_width; 402 | int col = w % risc->fb_width; 403 | if (row < risc->fb_height) { 404 | if (col < risc->damage.x1) { 405 | risc->damage.x1 = col; 406 | } 407 | if (col > risc->damage.x2) { 408 | risc->damage.x2 = col; 409 | } 410 | if (row < risc->damage.y1) { 411 | risc->damage.y1 = row; 412 | } 413 | if (row > risc->damage.y2) { 414 | risc->damage.y2 = row; 415 | } 416 | } 417 | } 418 | 419 | static void risc_store_word(struct RISC *risc, uint32_t address, uint32_t value) { 420 | if (address < risc->display_start) { 421 | risc->RAM[address/4] = value; 422 | } else if (address < risc->mem_size) { 423 | risc->RAM[address/4] = value; 424 | risc_update_damage(risc, address/4 - risc->display_start/4); 425 | } else { 426 | risc_store_io(risc, address, value); 427 | } 428 | } 429 | 430 | static void risc_store_byte(struct RISC *risc, uint32_t address, uint8_t value) { 431 | if (address < risc->mem_size) { 432 | uint32_t w = risc_load_word(risc, address); 433 | uint32_t shift = (address & 3) * 8; 434 | w &= ~(0xFFu << shift); 435 | w |= (uint32_t)value << shift; 436 | risc_store_word(risc, address, w); 437 | } else { 438 | risc_store_io(risc, address, (uint32_t)value); 439 | } 440 | } 441 | 442 | static uint32_t risc_load_io(struct RISC *risc, uint32_t address) { 443 | switch (address - IOStart) { 444 | case 0: { 445 | // Millisecond counter 446 | risc->progress--; 447 | return risc->current_tick; 448 | } 449 | case 4: { 450 | // Switches 451 | return risc->switches; 452 | } 453 | case 8: { 454 | // RS232 data 455 | if (risc->serial) { 456 | return risc->serial->read_data(risc->serial); 457 | } 458 | return 0; 459 | } 460 | case 12: { 461 | // RS232 status 462 | if (risc->serial) { 463 | return risc->serial->read_status(risc->serial); 464 | } 465 | return 0; 466 | } 467 | case 16: { 468 | // SPI data 469 | const struct RISC_SPI *spi = risc->spi[risc->spi_selected]; 470 | if (spi != NULL) { 471 | return spi->read_data(spi); 472 | } 473 | return 255; 474 | } 475 | case 20: { 476 | // SPI status 477 | // Bit 0: rx ready 478 | // Other bits unused 479 | return 1; 480 | } 481 | case 24: { 482 | // Mouse input / keyboard status 483 | uint32_t mouse = risc->mouse; 484 | if (risc->key_cnt > 0) { 485 | mouse |= 0x10000000; 486 | } else { 487 | risc->progress--; 488 | } 489 | return mouse; 490 | } 491 | case 28: { 492 | // Keyboard input 493 | if (risc->key_cnt > 0) { 494 | uint8_t scancode = risc->key_buf[0]; 495 | risc->key_cnt--; 496 | memmove(&risc->key_buf[0], &risc->key_buf[1], risc->key_cnt); 497 | return scancode; 498 | } 499 | return 0; 500 | } 501 | case 40: { 502 | // Clipboard control 503 | if (risc->clipboard) { 504 | return risc->clipboard->read_control(risc->clipboard); 505 | } 506 | return 0; 507 | } 508 | case 44: { 509 | // Clipboard data 510 | if (risc->clipboard) { 511 | return risc->clipboard->read_data(risc->clipboard); 512 | } 513 | return 0; 514 | } 515 | default: { 516 | return 0; 517 | } 518 | } 519 | } 520 | 521 | static void risc_store_io(struct RISC *risc, uint32_t address, uint32_t value) { 522 | switch (address - IOStart) { 523 | case 4: { 524 | // LED control 525 | if (risc->leds) { 526 | risc->leds->write(risc->leds, value); 527 | } 528 | break; 529 | } 530 | case 8: { 531 | // RS232 data 532 | if (risc->serial) { 533 | risc->serial->write_data(risc->serial, value); 534 | } 535 | break; 536 | } 537 | case 16: { 538 | // SPI write 539 | const struct RISC_SPI *spi = risc->spi[risc->spi_selected]; 540 | if (spi != NULL) { 541 | spi->write_data(spi, value); 542 | } 543 | break; 544 | } 545 | case 20: { 546 | // SPI control 547 | // Bit 0-1: slave select 548 | // Bit 2: fast mode 549 | // Bit 3: netwerk enable 550 | // Other bits unused 551 | risc->spi_selected = value & 3; 552 | break; 553 | } 554 | case 40: { 555 | // Clipboard control 556 | if (risc->clipboard) { 557 | risc->clipboard->write_control(risc->clipboard, value); 558 | } 559 | break; 560 | } 561 | case 44: { 562 | // Clipboard data 563 | if (risc->clipboard) { 564 | risc->clipboard->write_data(risc->clipboard, value); 565 | } 566 | break; 567 | } 568 | } 569 | } 570 | 571 | 572 | void risc_set_time(struct RISC *risc, uint32_t tick) { 573 | risc->current_tick = tick; 574 | } 575 | 576 | void risc_mouse_moved(struct RISC *risc, int mouse_x, int mouse_y) { 577 | if (mouse_x >= 0 && mouse_x < 4096) { 578 | risc->mouse = (risc->mouse & ~0x00000FFF) | mouse_x; 579 | } 580 | if (mouse_y >= 0 && mouse_y < 4096) { 581 | risc->mouse = (risc->mouse & ~0x00FFF000) | (mouse_y << 12); 582 | } 583 | } 584 | 585 | void risc_mouse_button(struct RISC *risc, int button, bool down) { 586 | if (button >= 1 && button < 4) { 587 | uint32_t bit = 1 << (27 - button); 588 | if (down) { 589 | risc->mouse |= bit; 590 | } else { 591 | risc->mouse &= ~bit; 592 | } 593 | } 594 | } 595 | 596 | void risc_keyboard_input(struct RISC *risc, uint8_t *scancodes, uint32_t len) { 597 | if (sizeof(risc->key_buf) - risc->key_cnt >= len) { 598 | memmove(&risc->key_buf[risc->key_cnt], scancodes, len); 599 | risc->key_cnt += len; 600 | } 601 | } 602 | 603 | uint32_t *risc_get_framebuffer_ptr(struct RISC *risc) { 604 | return &risc->RAM[risc->display_start/4]; 605 | } 606 | 607 | struct Damage risc_get_framebuffer_damage(struct RISC *risc) { 608 | struct Damage dmg = risc->damage; 609 | risc->damage = (struct Damage){ 610 | .x1 = risc->fb_width, 611 | .x2 = 0, 612 | .y1 = risc->fb_height, 613 | .y2 = 0 614 | }; 615 | return dmg; 616 | } 617 | -------------------------------------------------------------------------------- /src/risc.h: -------------------------------------------------------------------------------- 1 | #ifndef RISC_H 2 | #define RISC_H 3 | 4 | #include 5 | #include 6 | #include "risc-io.h" 7 | 8 | // This is the standard size of the framebuffer, can be overridden. 9 | #define RISC_FRAMEBUFFER_WIDTH 1024 10 | #define RISC_FRAMEBUFFER_HEIGHT 768 11 | 12 | struct RISC; 13 | 14 | struct Damage { 15 | int x1, x2, y1, y2; 16 | }; 17 | 18 | struct RISC *risc_new(void); 19 | void risc_configure_memory(struct RISC *risc, int megabytes_ram, int screen_width, int screen_height); 20 | void risc_set_leds(struct RISC *risc, const struct RISC_LED *leds); 21 | void risc_set_serial(struct RISC *risc, const struct RISC_Serial *serial); 22 | void risc_set_spi(struct RISC *risc, int index, const struct RISC_SPI *spi); 23 | void risc_set_clipboard(struct RISC *risc, const struct RISC_Clipboard *clipboard); 24 | void risc_set_switches(struct RISC *risc, int switches); 25 | 26 | void risc_reset(struct RISC *risc); 27 | void risc_run(struct RISC *risc, int cycles); 28 | void risc_set_time(struct RISC *risc, uint32_t tick); 29 | void risc_mouse_moved(struct RISC *risc, int mouse_x, int mouse_y); 30 | void risc_mouse_button(struct RISC *risc, int button, bool down); 31 | void risc_keyboard_input(struct RISC *risc, uint8_t *scancodes, uint32_t len); 32 | 33 | uint32_t *risc_get_framebuffer_ptr(struct RISC *risc); 34 | struct Damage risc_get_framebuffer_damage(struct RISC *risc); 35 | 36 | #endif // RISC_H 37 | -------------------------------------------------------------------------------- /src/sdl-clipboard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "sdl-clipboard.h" 6 | 7 | enum State { IDLE, GET, PUT }; 8 | 9 | static enum State state = IDLE; 10 | static char *data = NULL; 11 | static size_t data_ptr = 0; 12 | static size_t data_len = 0; 13 | 14 | static void reset() { 15 | state = IDLE; 16 | free(data); 17 | data = NULL; 18 | data_len = 0; 19 | data_ptr = 0; 20 | } 21 | 22 | static uint32_t clipboard_control_read(const struct RISC_Clipboard *clip) { 23 | uint32_t r = 0; 24 | reset(); 25 | data = SDL_GetClipboardText(); 26 | if (data) { 27 | data_len = strlen(data); 28 | if (data_len > UINT32_MAX) { 29 | reset(); 30 | } 31 | else if (data_len > 0) { 32 | state = GET; 33 | r = (uint32_t)data_len; 34 | // Decrease length if data contains CR/LF line endings 35 | const char *p = data; 36 | while ((p = strchr(p, '\r')) != NULL) { 37 | if (*++p == '\n') { 38 | r--; 39 | } 40 | } 41 | } 42 | } 43 | return r; 44 | } 45 | 46 | static void clipboard_control_write(const struct RISC_Clipboard *clip, uint32_t len) { 47 | reset(); 48 | if (len < UINT32_MAX) { 49 | char *buf = malloc(len + 1); 50 | if (buf != 0) { 51 | data = buf; 52 | data_len = len; 53 | state = PUT; 54 | } 55 | } 56 | } 57 | 58 | static uint32_t clipboard_data_read(const struct RISC_Clipboard *clip) { 59 | uint32_t result = 0; 60 | if (state == GET) { 61 | assert(data && data_ptr < data_len); 62 | result = (uint8_t)data[data_ptr]; 63 | data_ptr++; 64 | if (result == '\r' && data[data_ptr] == '\n') { 65 | data_ptr++; 66 | } else if (result == '\n') { 67 | result = '\r'; 68 | } 69 | if (data_ptr == data_len) { 70 | reset(); 71 | } 72 | } 73 | return result; 74 | } 75 | 76 | static void clipboard_data_write(const struct RISC_Clipboard *clip, uint32_t c) { 77 | if (state == PUT) { 78 | assert(data && data_ptr < data_len); 79 | if ((char)c == '\r') { 80 | c = '\n'; 81 | } 82 | data[data_ptr] = (char)c; 83 | ++data_ptr; 84 | if (data_ptr == data_len) { 85 | data[data_ptr] = 0; 86 | SDL_SetClipboardText(data); 87 | reset(); 88 | } 89 | } 90 | } 91 | 92 | const struct RISC_Clipboard sdl_clipboard = { 93 | .write_control = clipboard_control_write, 94 | .read_control = clipboard_control_read, 95 | .write_data = clipboard_data_write, 96 | .read_data = clipboard_data_read 97 | }; 98 | -------------------------------------------------------------------------------- /src/sdl-clipboard.h: -------------------------------------------------------------------------------- 1 | #ifndef SDL_CLIPBOARD_H 2 | #define SDL_CLIPBOARD_H 3 | 4 | #include "risc-io.h" 5 | 6 | extern const struct RISC_Clipboard sdl_clipboard; 7 | 8 | #endif // SDL_CLIPBOARD_H 9 | -------------------------------------------------------------------------------- /src/sdl-main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "risc.h" 11 | #include "risc-io.h" 12 | #include "disk.h" 13 | #include "pclink.h" 14 | #include "raw-serial.h" 15 | #include "sdl-ps2.h" 16 | #include "sdl-clipboard.h" 17 | 18 | #define CPU_HZ 25000000 19 | #define FPS 60 20 | 21 | static uint32_t BLACK = 0x657b83, WHITE = 0xfdf6e3; 22 | //static uint32_t BLACK = 0x000000, WHITE = 0xFFFFFF; 23 | //static uint32_t BLACK = 0x0000FF, WHITE = 0xFFFF00; 24 | //static uint32_t BLACK = 0x000000, WHITE = 0x00FF00; 25 | 26 | #define MAX_HEIGHT 2048 27 | #define MAX_WIDTH 2048 28 | 29 | static int best_display(const SDL_Rect *rect); 30 | static int clamp(int x, int min, int max); 31 | static enum Action map_keyboard_event(SDL_KeyboardEvent *event); 32 | static void show_leds(const struct RISC_LED *leds, uint32_t value); 33 | static double scale_display(SDL_Window *window, const SDL_Rect *risc_rect, SDL_Rect *display_rect); 34 | static void update_texture(struct RISC *risc, SDL_Texture *texture, const SDL_Rect *risc_rect); 35 | 36 | enum Action { 37 | ACTION_OBERON_INPUT, 38 | ACTION_QUIT, 39 | ACTION_RESET, 40 | ACTION_TOGGLE_FULLSCREEN, 41 | ACTION_FAKE_MOUSE1, 42 | ACTION_FAKE_MOUSE2, 43 | ACTION_FAKE_MOUSE3 44 | }; 45 | 46 | struct KeyMapping { 47 | int state; 48 | SDL_Keycode sym; 49 | SDL_Keymod mod1, mod2; 50 | enum Action action; 51 | }; 52 | 53 | struct KeyMapping key_map[] = { 54 | { SDL_PRESSED, SDLK_F4, KMOD_ALT, 0, ACTION_QUIT }, 55 | { SDL_PRESSED, SDLK_F12, 0, 0, ACTION_RESET }, 56 | { SDL_PRESSED, SDLK_DELETE, KMOD_CTRL, KMOD_SHIFT, ACTION_RESET }, 57 | { SDL_PRESSED, SDLK_F11, 0, 0, ACTION_TOGGLE_FULLSCREEN }, 58 | { SDL_PRESSED, SDLK_RETURN, KMOD_ALT, 0, ACTION_TOGGLE_FULLSCREEN }, 59 | { SDL_PRESSED, SDLK_f, KMOD_GUI, KMOD_SHIFT, ACTION_TOGGLE_FULLSCREEN }, // Mac? 60 | { SDL_PRESSED, SDLK_LALT, 0, 0, ACTION_FAKE_MOUSE2 }, 61 | { SDL_RELEASED, SDLK_LALT, 0, 0, ACTION_FAKE_MOUSE2 }, 62 | }; 63 | 64 | static struct option long_options[] = { 65 | { "zoom", required_argument, NULL, 'z' }, 66 | { "fullscreen", no_argument, NULL, 'f' }, 67 | { "leds", no_argument, NULL, 'L' }, 68 | { "mem", required_argument, NULL, 'm' }, 69 | { "size", required_argument, NULL, 's' }, 70 | { "serial-in", required_argument, NULL, 'I' }, 71 | { "serial-out", required_argument, NULL, 'O' }, 72 | { "boot-from-serial", no_argument, NULL, 'S' }, 73 | { NULL, no_argument, NULL, 0 } 74 | }; 75 | 76 | static void fail(int code, const char *fmt, ...) { 77 | va_list ap; 78 | va_start(ap, fmt); 79 | vfprintf(stderr, fmt, ap); 80 | va_end(ap); 81 | fputc('\n', stderr); 82 | exit(code); 83 | } 84 | 85 | static void usage() { 86 | puts("Usage: risc [OPTIONS...] DISK-IMAGE\n" 87 | "\n" 88 | "Options:\n" 89 | " --fullscreen Start the emulator in full screen mode\n" 90 | " --zoom REAL Scale the display in windowed mode\n" 91 | " --leds Log LED state on stdout\n" 92 | " --mem MEGS Set memory size\n" 93 | " --size WIDTHxHEIGHT Set framebuffer size\n" 94 | " --boot-from-serial Boot from serial line (disk image not required)\n" 95 | " --serial-in FILE Read serial input from FILE\n" 96 | " --serial-out FILE Write serial output to FILE\n" 97 | ); 98 | exit(1); 99 | } 100 | 101 | int main (int argc, char *argv[]) { 102 | struct RISC *risc = risc_new(); 103 | risc_set_serial(risc, &pclink); 104 | risc_set_clipboard(risc, &sdl_clipboard); 105 | 106 | struct RISC_LED leds = { 107 | .write = show_leds 108 | }; 109 | 110 | bool fullscreen = false; 111 | double zoom = 0; 112 | SDL_Rect risc_rect = { 113 | .w = RISC_FRAMEBUFFER_WIDTH, 114 | .h = RISC_FRAMEBUFFER_HEIGHT 115 | }; 116 | bool size_option = false; 117 | int mem_option = 0; 118 | const char *serial_in = NULL; 119 | const char *serial_out = NULL; 120 | bool boot_from_serial = false; 121 | 122 | int opt; 123 | while ((opt = getopt_long(argc, argv, "z:fLm:s:I:O:S", long_options, NULL)) != -1) { 124 | switch (opt) { 125 | case 'z': { 126 | double x = strtod(optarg, 0); 127 | if (x > 0) { 128 | zoom = x; 129 | } 130 | break; 131 | } 132 | case 'f': { 133 | fullscreen = true; 134 | break; 135 | } 136 | case 'L': { 137 | risc_set_leds(risc, &leds); 138 | break; 139 | } 140 | case 'm': { 141 | if (sscanf(optarg, "%d", &mem_option) != 1) { 142 | usage(); 143 | } 144 | break; 145 | } 146 | case 's': { 147 | int w, h; 148 | if (sscanf(optarg, "%dx%d", &w, &h) != 2) { 149 | usage(); 150 | } 151 | risc_rect.w = clamp(w, 32, MAX_WIDTH) & ~31; 152 | risc_rect.h = clamp(h, 32, MAX_HEIGHT); 153 | size_option = true; 154 | break; 155 | } 156 | case 'I': { 157 | serial_in = optarg; 158 | break; 159 | } 160 | case 'O': { 161 | serial_out = optarg; 162 | break; 163 | } 164 | case 'S': { 165 | boot_from_serial = true; 166 | risc_set_switches(risc, 1); 167 | break; 168 | } 169 | default: { 170 | usage(); 171 | } 172 | } 173 | } 174 | 175 | if (mem_option || size_option) { 176 | risc_configure_memory(risc, mem_option, risc_rect.w, risc_rect.h); 177 | } 178 | 179 | if (optind == argc - 1) { 180 | risc_set_spi(risc, 1, disk_new(argv[optind])); 181 | } else if (optind == argc && boot_from_serial) { 182 | /* Allow diskless boot */ 183 | risc_set_spi(risc, 1, disk_new(NULL)); 184 | } else { 185 | usage(); 186 | } 187 | 188 | if (serial_in || serial_out) { 189 | if (!serial_in) { 190 | serial_in = "/dev/null"; 191 | } 192 | if (!serial_out) { 193 | serial_out = "/dev/null"; 194 | } 195 | risc_set_serial(risc, raw_serial_new(serial_in, serial_out)); 196 | } 197 | 198 | if (SDL_Init(SDL_INIT_VIDEO) != 0) { 199 | fail(1, "Unable to initialize SDL: %s", SDL_GetError()); 200 | } 201 | atexit(SDL_Quit); 202 | SDL_EnableScreenSaver(); 203 | SDL_ShowCursor(false); 204 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best"); 205 | 206 | int window_flags = SDL_WINDOW_HIDDEN; 207 | int display = 0; 208 | if (fullscreen) { 209 | window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; 210 | display = best_display(&risc_rect); 211 | } 212 | if (zoom == 0) { 213 | SDL_Rect bounds; 214 | if (SDL_GetDisplayBounds(display, &bounds) == 0 && 215 | bounds.h >= risc_rect.h * 2 && bounds.w >= risc_rect.w * 2) { 216 | zoom = 2; 217 | } else { 218 | zoom = 1; 219 | } 220 | } 221 | SDL_Window *window = SDL_CreateWindow("Project Oberon", 222 | SDL_WINDOWPOS_UNDEFINED_DISPLAY(display), 223 | SDL_WINDOWPOS_UNDEFINED_DISPLAY(display), 224 | (int)(risc_rect.w * zoom), 225 | (int)(risc_rect.h * zoom), 226 | window_flags); 227 | if (window == NULL) { 228 | fail(1, "Could not create window: %s", SDL_GetError()); 229 | } 230 | 231 | SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0); 232 | if (renderer == NULL) { 233 | fail(1, "Could not create renderer: %s", SDL_GetError()); 234 | } 235 | 236 | SDL_Texture *texture = SDL_CreateTexture(renderer, 237 | SDL_PIXELFORMAT_ARGB8888, 238 | SDL_TEXTUREACCESS_STREAMING, 239 | risc_rect.w, 240 | risc_rect.h); 241 | if (texture == NULL) { 242 | fail(1, "Could not create texture: %s", SDL_GetError()); 243 | } 244 | 245 | SDL_Rect display_rect; 246 | double display_scale = scale_display(window, &risc_rect, &display_rect); 247 | update_texture(risc, texture, &risc_rect); 248 | SDL_ShowWindow(window); 249 | SDL_RenderClear(renderer); 250 | SDL_RenderCopy(renderer, texture, &risc_rect, &display_rect); 251 | SDL_RenderPresent(renderer); 252 | 253 | bool done = false; 254 | bool mouse_was_offscreen = false; 255 | while (!done) { 256 | uint32_t frame_start = SDL_GetTicks(); 257 | 258 | SDL_Event event; 259 | while (SDL_PollEvent(&event)) { 260 | switch (event.type) { 261 | case SDL_QUIT: { 262 | done = true; 263 | break; 264 | } 265 | 266 | case SDL_WINDOWEVENT: { 267 | if (event.window.event == SDL_WINDOWEVENT_RESIZED) { 268 | display_scale = scale_display(window, &risc_rect, &display_rect); 269 | } 270 | break; 271 | } 272 | 273 | case SDL_MOUSEMOTION: { 274 | int scaled_x = (int)round((event.motion.x - display_rect.x) / display_scale); 275 | int scaled_y = (int)round((event.motion.y - display_rect.y) / display_scale); 276 | int x = clamp(scaled_x, 0, risc_rect.w - 1); 277 | int y = clamp(scaled_y, 0, risc_rect.h - 1); 278 | bool mouse_is_offscreen = x != scaled_x || y != scaled_y; 279 | if (mouse_is_offscreen != mouse_was_offscreen) { 280 | SDL_ShowCursor(mouse_is_offscreen); 281 | mouse_was_offscreen = mouse_is_offscreen; 282 | } 283 | risc_mouse_moved(risc, x, risc_rect.h - y - 1); 284 | break; 285 | } 286 | 287 | case SDL_MOUSEBUTTONDOWN: 288 | case SDL_MOUSEBUTTONUP: { 289 | bool down = event.button.state == SDL_PRESSED; 290 | risc_mouse_button(risc, event.button.button, down); 291 | break; 292 | } 293 | 294 | case SDL_KEYDOWN: 295 | case SDL_KEYUP: { 296 | bool down = event.key.state == SDL_PRESSED; 297 | switch (map_keyboard_event(&event.key)) { 298 | case ACTION_RESET: { 299 | risc_reset(risc); 300 | break; 301 | } 302 | case ACTION_TOGGLE_FULLSCREEN: { 303 | fullscreen ^= true; 304 | if (fullscreen) { 305 | SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); 306 | } else { 307 | SDL_SetWindowFullscreen(window, 0); 308 | } 309 | break; 310 | } 311 | case ACTION_QUIT: { 312 | SDL_PushEvent(&(SDL_Event){ .type=SDL_QUIT }); 313 | break; 314 | } 315 | case ACTION_FAKE_MOUSE1: { 316 | risc_mouse_button(risc, 1, down); 317 | break; 318 | } 319 | case ACTION_FAKE_MOUSE2: { 320 | risc_mouse_button(risc, 2, down); 321 | break; 322 | } 323 | case ACTION_FAKE_MOUSE3: { 324 | risc_mouse_button(risc, 3, down); 325 | break; 326 | } 327 | case ACTION_OBERON_INPUT: { 328 | uint8_t ps2_bytes[MAX_PS2_CODE_LEN]; 329 | int len = ps2_encode(event.key.keysym.scancode, down, ps2_bytes); 330 | risc_keyboard_input(risc, ps2_bytes, len); 331 | break; 332 | } 333 | } 334 | } 335 | } 336 | } 337 | 338 | risc_set_time(risc, frame_start); 339 | risc_run(risc, CPU_HZ / FPS); 340 | 341 | update_texture(risc, texture, &risc_rect); 342 | SDL_RenderClear(renderer); 343 | SDL_RenderCopy(renderer, texture, &risc_rect, &display_rect); 344 | SDL_RenderPresent(renderer); 345 | 346 | uint32_t frame_end = SDL_GetTicks(); 347 | int delay = frame_start + 1000/FPS - frame_end; 348 | if (delay > 0) { 349 | SDL_Delay(delay); 350 | } 351 | } 352 | return 0; 353 | } 354 | 355 | 356 | static int best_display(const SDL_Rect *rect) { 357 | int best = 0; 358 | int display_cnt = SDL_GetNumVideoDisplays(); 359 | for (int i = 0; i < display_cnt; i++) { 360 | SDL_Rect bounds; 361 | if (SDL_GetDisplayBounds(i, &bounds) == 0 && 362 | bounds.h == rect->h && bounds.w >= rect->w) { 363 | best = i; 364 | if (bounds.w == rect->w) { 365 | break; // exact match 366 | } 367 | } 368 | } 369 | return best; 370 | } 371 | 372 | static int clamp(int x, int min, int max) { 373 | if (x < min) return min; 374 | if (x > max) return max; 375 | return x; 376 | } 377 | 378 | static enum Action map_keyboard_event(SDL_KeyboardEvent *event) { 379 | for (size_t i = 0; i < sizeof(key_map) / sizeof(key_map[0]); i++) { 380 | if ((event->state == key_map[i].state) && 381 | (event->keysym.sym == key_map[i].sym) && 382 | ((key_map[i].mod1 == 0) || (event->keysym.mod & key_map[i].mod1)) && 383 | ((key_map[i].mod2 == 0) || (event->keysym.mod & key_map[i].mod2))) { 384 | return key_map[i].action; 385 | } 386 | } 387 | return ACTION_OBERON_INPUT; 388 | } 389 | 390 | static void show_leds(const struct RISC_LED *leds, uint32_t value) { 391 | printf("LEDs: "); 392 | for (int i = 7; i >= 0; i--) { 393 | if (value & (1 << i)) { 394 | printf("%d", i); 395 | } else { 396 | printf("-"); 397 | } 398 | } 399 | printf("\n"); 400 | } 401 | 402 | static double scale_display(SDL_Window *window, const SDL_Rect *risc_rect, SDL_Rect *display_rect) { 403 | int win_w, win_h; 404 | SDL_GetWindowSize(window, &win_w, &win_h); 405 | double oberon_aspect = (double)risc_rect->w / risc_rect->h; 406 | double window_aspect = (double)win_w / win_h; 407 | 408 | double scale; 409 | if (oberon_aspect > window_aspect) { 410 | scale = (double)win_w / risc_rect->w; 411 | } else { 412 | scale = (double)win_h / risc_rect->h; 413 | } 414 | 415 | int w = (int)ceil(risc_rect->w * scale); 416 | int h = (int)ceil(risc_rect->h * scale); 417 | *display_rect = (SDL_Rect){ 418 | .w = w, .h = h, 419 | .x = (win_w - w) / 2, 420 | .y = (win_h - h) / 2 421 | }; 422 | return scale; 423 | } 424 | 425 | // Only used in update_texture(), but some systems complain if you 426 | // allocate three megabyte on the stack. 427 | static uint32_t pixel_buf[MAX_WIDTH * MAX_HEIGHT]; 428 | 429 | static void update_texture(struct RISC *risc, SDL_Texture *texture, const SDL_Rect *risc_rect) { 430 | struct Damage damage = risc_get_framebuffer_damage(risc); 431 | if (damage.y1 <= damage.y2) { 432 | uint32_t *in = risc_get_framebuffer_ptr(risc); 433 | uint32_t out_idx = 0; 434 | 435 | for (int line = damage.y2; line >= damage.y1; line--) { 436 | int line_start = line * (risc_rect->w / 32); 437 | for (int col = damage.x1; col <= damage.x2; col++) { 438 | uint32_t pixels = in[line_start + col]; 439 | for (int b = 0; b < 32; b++) { 440 | pixel_buf[out_idx] = (pixels & 1) ? WHITE : BLACK; 441 | pixels >>= 1; 442 | out_idx++; 443 | } 444 | } 445 | } 446 | 447 | SDL_Rect rect = { 448 | .x = damage.x1 * 32, 449 | .y = risc_rect->h - damage.y2 - 1, 450 | .w = (damage.x2 - damage.x1 + 1) * 32, 451 | .h = (damage.y2 - damage.y1 + 1) 452 | }; 453 | SDL_UpdateTexture(texture, &rect, pixel_buf, rect.w * 4); 454 | } 455 | } 456 | -------------------------------------------------------------------------------- /src/sdl-ps2.c: -------------------------------------------------------------------------------- 1 | // Translate SDL scancodes to PS/2 codeset 2 scancodes. 2 | 3 | #include 4 | #include "sdl-ps2.h" 5 | 6 | struct k_info { 7 | unsigned char code; 8 | unsigned char type; 9 | }; 10 | 11 | enum k_type { 12 | K_UNKNOWN = 0, 13 | K_NORMAL, 14 | K_EXTENDED, 15 | K_NUMLOCK_HACK, 16 | K_SHIFT_HACK, 17 | }; 18 | 19 | static struct k_info keymap[SDL_NUM_SCANCODES]; 20 | 21 | int ps2_encode(int sdl_scancode, bool make, uint8_t out[static MAX_PS2_CODE_LEN]) { 22 | int i = 0; 23 | struct k_info info = keymap[sdl_scancode]; 24 | switch (info.type) { 25 | case K_UNKNOWN: { 26 | break; 27 | } 28 | 29 | case K_NORMAL: { 30 | if (!make) { 31 | out[i++] = 0xF0; 32 | } 33 | out[i++] = info.code; 34 | break; 35 | } 36 | 37 | case K_EXTENDED: { 38 | out[i++] = 0xE0; 39 | if (!make) { 40 | out[i++] = 0xF0; 41 | } 42 | out[i++] = info.code; 43 | break; 44 | } 45 | 46 | case K_NUMLOCK_HACK: { 47 | // This assumes Num Lock is always active 48 | if (make) { 49 | // fake shift press 50 | out[i++] = 0xE0; 51 | out[i++] = 0x12; 52 | out[i++] = 0xE0; 53 | out[i++] = info.code; 54 | } else { 55 | out[i++] = 0xE0; 56 | out[i++] = 0xF0; 57 | out[i++] = info.code; 58 | // fake shift release 59 | out[i++] = 0xE0; 60 | out[i++] = 0xF0; 61 | out[i++] = 0x12; 62 | } 63 | break; 64 | } 65 | 66 | case K_SHIFT_HACK: { 67 | SDL_Keymod mod = SDL_GetModState(); 68 | if (make) { 69 | // fake shift release 70 | if (mod & KMOD_LSHIFT) { 71 | out[i++] = 0xE0; 72 | out[i++] = 0xF0; 73 | out[i++] = 0x12; 74 | } 75 | if (mod & KMOD_RSHIFT) { 76 | out[i++] = 0xE0; 77 | out[i++] = 0xF0; 78 | out[i++] = 0x59; 79 | } 80 | out[i++] = 0xE0; 81 | out[i++] = info.code; 82 | } else { 83 | out[i++] = 0xE0; 84 | out[i++] = 0xF0; 85 | out[i++] = info.code; 86 | // fake shift press 87 | if (mod & KMOD_RSHIFT) { 88 | out[i++] = 0xE0; 89 | out[i++] = 0x59; 90 | } 91 | if (mod & KMOD_LSHIFT) { 92 | out[i++] = 0xE0; 93 | out[i++] = 0x12; 94 | } 95 | } 96 | break; 97 | } 98 | } 99 | return i; 100 | } 101 | 102 | static struct k_info keymap[SDL_NUM_SCANCODES] = { 103 | [SDL_SCANCODE_A] = { 0x1C, K_NORMAL }, 104 | [SDL_SCANCODE_B] = { 0x32, K_NORMAL }, 105 | [SDL_SCANCODE_C] = { 0x21, K_NORMAL }, 106 | [SDL_SCANCODE_D] = { 0x23, K_NORMAL }, 107 | [SDL_SCANCODE_E] = { 0x24, K_NORMAL }, 108 | [SDL_SCANCODE_F] = { 0x2B, K_NORMAL }, 109 | [SDL_SCANCODE_G] = { 0x34, K_NORMAL }, 110 | [SDL_SCANCODE_H] = { 0x33, K_NORMAL }, 111 | [SDL_SCANCODE_I] = { 0x43, K_NORMAL }, 112 | [SDL_SCANCODE_J] = { 0x3B, K_NORMAL }, 113 | [SDL_SCANCODE_K] = { 0x42, K_NORMAL }, 114 | [SDL_SCANCODE_L] = { 0x4B, K_NORMAL }, 115 | [SDL_SCANCODE_M] = { 0x3A, K_NORMAL }, 116 | [SDL_SCANCODE_N] = { 0x31, K_NORMAL }, 117 | [SDL_SCANCODE_O] = { 0x44, K_NORMAL }, 118 | [SDL_SCANCODE_P] = { 0x4D, K_NORMAL }, 119 | [SDL_SCANCODE_Q] = { 0x15, K_NORMAL }, 120 | [SDL_SCANCODE_R] = { 0x2D, K_NORMAL }, 121 | [SDL_SCANCODE_S] = { 0x1B, K_NORMAL }, 122 | [SDL_SCANCODE_T] = { 0x2C, K_NORMAL }, 123 | [SDL_SCANCODE_U] = { 0x3C, K_NORMAL }, 124 | [SDL_SCANCODE_V] = { 0x2A, K_NORMAL }, 125 | [SDL_SCANCODE_W] = { 0x1D, K_NORMAL }, 126 | [SDL_SCANCODE_X] = { 0x22, K_NORMAL }, 127 | [SDL_SCANCODE_Y] = { 0x35, K_NORMAL }, 128 | [SDL_SCANCODE_Z] = { 0x1A, K_NORMAL }, 129 | 130 | [SDL_SCANCODE_1] = { 0x16, K_NORMAL }, 131 | [SDL_SCANCODE_2] = { 0x1E, K_NORMAL }, 132 | [SDL_SCANCODE_3] = { 0x26, K_NORMAL }, 133 | [SDL_SCANCODE_4] = { 0x25, K_NORMAL }, 134 | [SDL_SCANCODE_5] = { 0x2E, K_NORMAL }, 135 | [SDL_SCANCODE_6] = { 0x36, K_NORMAL }, 136 | [SDL_SCANCODE_7] = { 0x3D, K_NORMAL }, 137 | [SDL_SCANCODE_8] = { 0x3E, K_NORMAL }, 138 | [SDL_SCANCODE_9] = { 0x46, K_NORMAL }, 139 | [SDL_SCANCODE_0] = { 0x45, K_NORMAL }, 140 | 141 | [SDL_SCANCODE_RETURN] = { 0x5A, K_NORMAL }, 142 | [SDL_SCANCODE_ESCAPE] = { 0x76, K_NORMAL }, 143 | [SDL_SCANCODE_BACKSPACE] = { 0x66, K_NORMAL }, 144 | [SDL_SCANCODE_TAB] = { 0x0D, K_NORMAL }, 145 | [SDL_SCANCODE_SPACE] = { 0x29, K_NORMAL }, 146 | 147 | [SDL_SCANCODE_MINUS] = { 0x4E, K_NORMAL }, 148 | [SDL_SCANCODE_EQUALS] = { 0x55, K_NORMAL }, 149 | [SDL_SCANCODE_LEFTBRACKET] = { 0x54, K_NORMAL }, 150 | [SDL_SCANCODE_RIGHTBRACKET] = { 0x5B, K_NORMAL }, 151 | [SDL_SCANCODE_BACKSLASH] = { 0x5D, K_NORMAL }, 152 | [SDL_SCANCODE_NONUSHASH] = { 0x5D, K_NORMAL }, // same key as BACKSLASH 153 | 154 | [SDL_SCANCODE_SEMICOLON] = { 0x4C, K_NORMAL }, 155 | [SDL_SCANCODE_APOSTROPHE] = { 0x52, K_NORMAL }, 156 | [SDL_SCANCODE_GRAVE] = { 0x0E, K_NORMAL }, 157 | [SDL_SCANCODE_COMMA] = { 0x41, K_NORMAL }, 158 | [SDL_SCANCODE_PERIOD] = { 0x49, K_NORMAL }, 159 | [SDL_SCANCODE_SLASH] = { 0x4A, K_NORMAL }, 160 | 161 | [SDL_SCANCODE_F1] = { 0x05, K_NORMAL }, 162 | [SDL_SCANCODE_F2] = { 0x06, K_NORMAL }, 163 | [SDL_SCANCODE_F3] = { 0x04, K_NORMAL }, 164 | [SDL_SCANCODE_F4] = { 0x0C, K_NORMAL }, 165 | [SDL_SCANCODE_F5] = { 0x03, K_NORMAL }, 166 | [SDL_SCANCODE_F6] = { 0x0B, K_NORMAL }, 167 | [SDL_SCANCODE_F7] = { 0x83, K_NORMAL }, 168 | [SDL_SCANCODE_F8] = { 0x0A, K_NORMAL }, 169 | [SDL_SCANCODE_F9] = { 0x01, K_NORMAL }, 170 | [SDL_SCANCODE_F10] = { 0x09, K_NORMAL }, 171 | [SDL_SCANCODE_F11] = { 0x78, K_NORMAL }, 172 | [SDL_SCANCODE_F12] = { 0x07, K_NORMAL }, 173 | 174 | // Most of the keys below are not used by Oberon 175 | 176 | [SDL_SCANCODE_INSERT] = { 0x70, K_NUMLOCK_HACK }, 177 | [SDL_SCANCODE_HOME] = { 0x6C, K_NUMLOCK_HACK }, 178 | [SDL_SCANCODE_PAGEUP] = { 0x7D, K_NUMLOCK_HACK }, 179 | [SDL_SCANCODE_DELETE] = { 0x71, K_NUMLOCK_HACK }, 180 | [SDL_SCANCODE_END] = { 0x69, K_NUMLOCK_HACK }, 181 | [SDL_SCANCODE_PAGEDOWN] = { 0x7A, K_NUMLOCK_HACK }, 182 | [SDL_SCANCODE_RIGHT] = { 0x74, K_NUMLOCK_HACK }, 183 | [SDL_SCANCODE_LEFT] = { 0x6B, K_NUMLOCK_HACK }, 184 | [SDL_SCANCODE_DOWN] = { 0x72, K_NUMLOCK_HACK }, 185 | [SDL_SCANCODE_UP] = { 0x75, K_NUMLOCK_HACK }, 186 | 187 | [SDL_SCANCODE_KP_DIVIDE] = { 0x4A, K_SHIFT_HACK }, 188 | [SDL_SCANCODE_KP_MULTIPLY] = { 0x7C, K_NORMAL }, 189 | [SDL_SCANCODE_KP_MINUS] = { 0x7B, K_NORMAL }, 190 | [SDL_SCANCODE_KP_PLUS] = { 0x79, K_NORMAL }, 191 | [SDL_SCANCODE_KP_ENTER] = { 0x5A, K_EXTENDED }, 192 | [SDL_SCANCODE_KP_1] = { 0x69, K_NORMAL }, 193 | [SDL_SCANCODE_KP_2] = { 0x72, K_NORMAL }, 194 | [SDL_SCANCODE_KP_3] = { 0x7A, K_NORMAL }, 195 | [SDL_SCANCODE_KP_4] = { 0x6B, K_NORMAL }, 196 | [SDL_SCANCODE_KP_5] = { 0x73, K_NORMAL }, 197 | [SDL_SCANCODE_KP_6] = { 0x74, K_NORMAL }, 198 | [SDL_SCANCODE_KP_7] = { 0x6C, K_NORMAL }, 199 | [SDL_SCANCODE_KP_8] = { 0x75, K_NORMAL }, 200 | [SDL_SCANCODE_KP_9] = { 0x7D, K_NORMAL }, 201 | [SDL_SCANCODE_KP_0] = { 0x70, K_NORMAL }, 202 | [SDL_SCANCODE_KP_PERIOD] = { 0x71, K_NORMAL }, 203 | 204 | [SDL_SCANCODE_NONUSBACKSLASH] = { 0x61, K_NORMAL }, 205 | [SDL_SCANCODE_APPLICATION] = { 0x2F, K_EXTENDED }, 206 | 207 | [SDL_SCANCODE_LCTRL] = { 0x14, K_NORMAL }, 208 | [SDL_SCANCODE_LSHIFT] = { 0x12, K_NORMAL }, 209 | [SDL_SCANCODE_LALT] = { 0x11, K_NORMAL }, 210 | [SDL_SCANCODE_LGUI] = { 0x1F, K_EXTENDED }, 211 | [SDL_SCANCODE_RCTRL] = { 0x14, K_EXTENDED }, 212 | [SDL_SCANCODE_RSHIFT] = { 0x59, K_NORMAL }, 213 | [SDL_SCANCODE_RALT] = { 0x11, K_EXTENDED }, 214 | [SDL_SCANCODE_RGUI] = { 0x27, K_EXTENDED }, 215 | }; 216 | -------------------------------------------------------------------------------- /src/sdl-ps2.h: -------------------------------------------------------------------------------- 1 | #ifndef SDL_PS2_H 2 | #define SDL_PS2_H 3 | 4 | #include 5 | #include 6 | 7 | #define MAX_PS2_CODE_LEN 8 8 | 9 | int ps2_encode(int sdl_scancode, bool make, uint8_t out[static MAX_PS2_CODE_LEN]); 10 | 11 | #endif // SDL_PS2_H 12 | -------------------------------------------------------------------------------- /tools/Makefile: -------------------------------------------------------------------------------- 1 | RUSTFLAGS = -O 2 | 3 | all: asciidecoder ob2unix 4 | 5 | clean: 6 | rm -f asciidecoder ob2unix 7 | 8 | %: %.rs 9 | rustc $(RUSTFLAGS) $< 10 | -------------------------------------------------------------------------------- /tools/asciidecoder.rs: -------------------------------------------------------------------------------- 1 | // Decodes 'AsciiCoder.DecodeFiles' archives 2 | 3 | use std::io::*; 4 | use std::fs; 5 | use std::env; 6 | use std::process::exit; 7 | 8 | trait ByteIterator: Iterator {} 9 | impl> ByteIterator for T {} 10 | 11 | fn skip_header(bytes: &mut T) -> bool { 12 | let command = b"AsciiCoder.DecodeFiles"; 13 | let mut idx = 0; 14 | while let Some(ch) = bytes.next() { 15 | if ch != command[idx] { 16 | idx = 0 17 | } 18 | if ch == command[idx] { 19 | idx += 1; 20 | if idx == command.len() { 21 | return true 22 | } 23 | } 24 | } 25 | false 26 | } 27 | 28 | fn decode(bytes: &mut T) -> Option> { 29 | const BASE: u8 = 48; 30 | let mut vec = Vec::new(); 31 | let mut bits = 0; 32 | let mut buf: u32 = 0; 33 | for ch in bytes.filter(|&a| a > 32) { 34 | if ch >= BASE && ch < BASE + 64 { 35 | buf |= ((ch - BASE) as u32) << bits; 36 | bits += 6; 37 | if bits >= 8 { 38 | vec.push((buf & 0xFF) as u8); 39 | buf >>= 8; 40 | bits -= 8; 41 | } 42 | } else { 43 | return match ch { 44 | b'#' if bits == 0 => Some(vec), 45 | b'%' if bits == 2 => Some(vec), 46 | b'$' if bits == 4 => Some(vec), 47 | _ => None 48 | } 49 | } 50 | } 51 | None 52 | } 53 | 54 | fn read_number(bytes: &mut T) -> Option { 55 | let mut n: i32 = 0; 56 | let mut bits = 0; 57 | while let Some(b) = bytes.next() { 58 | if b >= 0x80 { 59 | n |= ((b - 0x80) as i32) << bits; 60 | bits += 7; 61 | if bits >= 32 { 62 | return None 63 | } 64 | } else { 65 | n |= (((b as i32) ^ 0x40) - 0x40) << bits; 66 | return Some(n) 67 | } 68 | } 69 | None 70 | } 71 | 72 | fn decompress(bytes: &mut T) -> Option> { 73 | let size = match read_number(bytes) { 74 | Some(n) if n >= 0 => n, 75 | _ => return None 76 | }; 77 | 78 | const N: usize = 16384; 79 | let mut table = [0u8; N]; 80 | let mut vec = Vec::new(); 81 | let mut hash: usize = 0; 82 | let mut buf: u32 = 0; 83 | let mut bits = 0; 84 | 85 | for _ in 0..size { 86 | if bits == 0 { 87 | buf = match bytes.next() { 88 | Some(b) => b as u32, 89 | None => return None 90 | }; 91 | bits = 8 92 | } 93 | 94 | let misprediction = (buf & 1) != 0; 95 | buf >>= 1; 96 | bits -= 1; 97 | 98 | let data: u8; 99 | if !misprediction { 100 | data = table[hash] 101 | } else { 102 | let b = match bytes.next() { 103 | Some(b) => b, 104 | None => return None 105 | }; 106 | buf |= (b as u32) << bits; 107 | data = (buf & 0xFF) as u8; 108 | buf >>= 8; 109 | table[hash] = data; 110 | } 111 | vec.push(data); 112 | hash = (16 * hash + data as usize) % N; 113 | } 114 | Some(vec) 115 | } 116 | 117 | fn read_name(bytes: &mut T) -> Option { 118 | let vec = bytes.skip_while(|&b| b <= 32) 119 | .take_while(|&b| b > 32) 120 | .collect(); 121 | let name = String::from_utf8(vec).unwrap(); 122 | if name.is_empty() { None } else { Some(name) } 123 | } 124 | 125 | macro_rules! printerr { 126 | ($prog_name: expr, $err: expr, $fmt: expr) => ( 127 | (writeln!(&mut stderr(), 128 | concat!("{}: ", $fmt, ": {}"), 129 | $prog_name, err) 130 | .unwrap())); 131 | ($prog_name: expr, $err: expr, $fmt: expr, $($arg: tt)*) => ( 132 | (writeln!(&mut stderr(), 133 | concat!("{}: ", $fmt, ": {}"), 134 | $prog_name, $($arg)*, $err) 135 | .unwrap())) 136 | } 137 | 138 | macro_rules! printerrx { 139 | ($prog_name: expr, $fmt: expr) => ( 140 | (writeln!(&mut stderr(), 141 | concat!("{}: ", $fmt), 142 | $prog_name) 143 | .unwrap())); 144 | ($prog_name: expr, $fmt: expr, $($arg: tt)*) => ( 145 | (writeln!(&mut stderr(), 146 | concat!("{}: ", $fmt), 147 | $prog_name, $($arg)*) 148 | .unwrap())) 149 | } 150 | 151 | fn main() { 152 | let mut verbose = false; 153 | let mut directory = None; 154 | let mut input_file = None; 155 | 156 | let mut args = env::args(); 157 | let progname = args.next().unwrap().rsplit('/').next().unwrap().to_owned(); 158 | while let Some(opt) = args.next() { 159 | match opt.as_ref() { 160 | "-v" | "--verbose" => { 161 | verbose = true 162 | }, 163 | "-C" | "--directory" if directory.is_none() => { 164 | directory = args.next() 165 | }, 166 | s if !s.starts_with("-") && input_file.is_none() => { 167 | input_file = Some(opt.clone()) 168 | }, 169 | _ => { 170 | writeln!(&mut stderr(), "Usage: {} [-v] [-C DIR] [FILE]", progname).unwrap(); 171 | exit(1) 172 | } 173 | } 174 | } 175 | 176 | let input: Box = match input_file { 177 | None => Box::new(stdin()), 178 | Some(filename) => match fs::File::open(&filename) { 179 | Ok(f) => Box::new(BufReader::new(f)), 180 | Err(e) => { 181 | printerr!(progname, e, "can't open '{}'", filename); 182 | exit(1) 183 | } 184 | } 185 | }; 186 | let mut input: Box> = Box::new(input.bytes().map(|x| x.unwrap())); 187 | 188 | if let Some(directory) = directory { 189 | if let Err(e) = env::set_current_dir(&directory) { 190 | if e.kind() != ErrorKind::NotFound { 191 | printerr!(progname, e, "can't change to directory '{}'", directory); 192 | exit(1); 193 | } 194 | if let Err(e) = fs::create_dir_all(&directory) { 195 | printerr!(progname, e, "can't create directory '{}'", directory); 196 | exit(1); 197 | } 198 | if let Err(e) = env::set_current_dir(&directory) { 199 | printerr!(progname, e, "can't change to directory '{}'", directory); 200 | exit(1); 201 | } 202 | } 203 | } 204 | 205 | let mut compressed = false; 206 | let mut names = Vec::new(); 207 | if !skip_header(&mut input) { 208 | printerrx!(progname, "no AsciiCoder.DecodeFiles archive found"); 209 | exit(1); 210 | } 211 | while let Some(name) = read_name(&mut input) { 212 | match name.as_ref() { 213 | "~" => break, 214 | "%" => compressed = true, 215 | _ => names.push(name) 216 | } 217 | } 218 | 219 | for name in &names { 220 | if verbose { 221 | println!("{}", name); 222 | } 223 | 224 | let mut data = match decode(&mut input) { 225 | Some(vec) => vec, 226 | None => { 227 | printerrx!(progname, "can't decode '{}' (input file truncated?)", name); 228 | exit(1) 229 | } 230 | }; 231 | if compressed { 232 | data = match decompress(&mut data.into_iter()) { 233 | Some(vec) => vec, 234 | None => { 235 | printerrx!(progname, "can't decompress '{}'", name); 236 | exit(1) 237 | } 238 | } 239 | }; 240 | 241 | let mut file = match fs::File::create(&name) { 242 | Ok(f) => f, 243 | Err(e) => { 244 | printerr!(progname, e, "can't create file '{}'", name); 245 | continue; 246 | } 247 | }; 248 | 249 | if let Err(e) = file.write_all(&data) { 250 | printerr!(progname, e, "can't write file '{}'", name); 251 | fs::remove_file(&name).unwrap(); 252 | continue; 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /tools/ob2unix.rs: -------------------------------------------------------------------------------- 1 | // Dumps the ASCII content of Oberon texts. Doesn't properly 2 | // parse the files so doesn't work very well. 3 | 4 | use std::io::*; 5 | use std::process::exit; 6 | 7 | fn ob2unix() -> Result<()> { 8 | let mut input = stdin(); 9 | let mut output = stdout(); 10 | 11 | let mut buf = [0u8; 1024]; 12 | let res = try!(copy(&mut input.by_ref().take(6), 13 | &mut &mut buf[..])) as usize; 14 | let is_oberon = res == 6 && ((buf[0] == 240 && buf[1] == 1) 15 | || buf[0] == 1 && buf[1] == 240); 16 | 17 | if is_oberon { 18 | // skip header 19 | let size = (buf[2] as u64) | (buf[3] as u64) << 8 20 | | (buf[4] as u64) << 16 | (buf[5] as u64) << 24; 21 | try!(copy(&mut input.by_ref().take(size - 6), &mut sink())); 22 | 23 | // translate '\r' to '\n' 24 | loop { 25 | let res = try!(copy(&mut input.by_ref().take(buf.len() as u64), 26 | &mut &mut buf[..])) as usize; 27 | if res == 0 { 28 | break 29 | } 30 | for ch in &mut buf[..res] { 31 | if *ch == b'\r' { 32 | *ch = b'\n' 33 | } 34 | } 35 | try!(copy(&mut &buf[..res], &mut output)); 36 | }; 37 | 38 | } else { 39 | // Not an Oberon text file: copy input to output 40 | try!(copy(&mut &buf[..res], &mut output)); 41 | try!(copy(&mut input, &mut output)); 42 | } 43 | 44 | Ok(()) 45 | } 46 | 47 | fn main() { 48 | if let Err(e) = ob2unix() { 49 | writeln!(&mut stderr(), "ob2unix: {}", e).unwrap(); 50 | exit(1); 51 | } 52 | } 53 | --------------------------------------------------------------------------------