├── icon.bmp ├── ChangeLog ├── README ├── tools ├── Makefile.am ├── lmfuse │ └── Makefile.am ├── dumptape.c ├── maketape.c ├── disktool.c └── decode_lmfl.c ├── examples ├── ld.conf └── lam.yml ├── Makefile.am ├── src ├── Makefile.am ├── mem.h ├── tapemaster.h ├── smd.h ├── 3com.h ├── syms.h ├── nubus.h ├── nubus.c ├── vcmem.h ├── mem.c ├── sdu_hw.h ├── sdu.h ├── syms.c ├── ld.h ├── lpart.c ├── lambda_cpu.h └── vcmem.c ├── AUTHORS ├── .gitignore ├── doc ├── KEYCODES └── OLDCONF.md ├── NEWS ├── m4 ├── ax_check_compile_flag.m4 ├── sdl.m4 └── sdl2.m4 ├── configure.ac ├── README.md └── COPYING /icon.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dseagrav/ld/HEAD/icon.bmp -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | See the git repository for change tracking information. 2 | 3 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | The documentation has been markdown-ified: Please see README.md 2 | -------------------------------------------------------------------------------- /tools/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = decode_lmfl dumptape maketape disktool 2 | -------------------------------------------------------------------------------- /examples/ld.conf: -------------------------------------------------------------------------------- 1 | # LambdaDelta configuration file 2 | ether_iface ldtap 3 | ether_addr 00:02:9C:55:89:C6 4 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | if COND_LMFUSE 2 | MAYBE_LMFUSE = tools/lmfuse 3 | endif 4 | SUBDIRS = src tools $(MAYBE_LMFUSE) 5 | -------------------------------------------------------------------------------- /tools/lmfuse/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = lmfuse 2 | 3 | lmfuse_CFLAGS = $(fuse_CFLAGS) 4 | lmfuse_LDADD = $(fuse_LIBS) 5 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CFLAGS = -std=gnu99 -Wall -Wextra -ggdb 2 | AM_LDFLAGS = -ggdb -Og 3 | 4 | bin_PROGRAMS = lam lpart 5 | 6 | lam_SOURCES = 3com.c lambda_cpu.c mem.c sdu.c smd.c tapemaster.c kernel.c nubus.c sdu_hw.c syms.c vcmem.c 3com.h ld.h nubus.h sdu_hw.h syms.h vcmem.h lambda_cpu.h mem.h sdu.h smd.h tapemaster.h 7 | 8 | lpart_SOURCES = lpart.c 9 | 10 | lam_LDFLAGS = -lm 11 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | This file purports to list those whose code wound up here. 2 | 3 | THE RINGLEADERS: ("The Usual Suspects") 4 | 5 | Daniel Seagraves Chief Instigator 6 | Barry Silverman That Other Guy 7 | 8 | OUR VICTIMS: (We borrowed code from...) 9 | 10 | Brad Parker lpart tool 11 | Greg Gilley Symbol handling code 12 | Mike Chambers Fake86 CPU core 13 | 14 | SPECIAL THANKS TO: 15 | 16 | Richard Greenblatt 17 | The LMI employees who gave us a machine to emulate 18 | The MIT students and faculty who contributed to the LispM 19 | 20 | -------------------------------------------------------------------------------- /src/mem.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | 4 | This file is part of LambdaDelta. 5 | 6 | LambdaDelta is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | LambdaDelta is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with LambdaDelta. If not, see . 18 | */ 19 | 20 | void mem_init(); 21 | void mem_clock_pulse(); 22 | void debug_mem_write(uint32_t addr,uint8_t data); 23 | uint8_t debug_mem_read(uint32_t addr); 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Things for git to ignore 2 | # Object files 3 | *.o 4 | ld 5 | lam 6 | lpart 7 | tools/maketape 8 | tools/dumptape 9 | tools/decode_lmfl 10 | tools/disktool 11 | tools/lmfuse/lmfuse 12 | # Emacs backup files 13 | *~ 14 | # Trace logs 15 | foo* 16 | *.LOG 17 | # Dump files 18 | *.DUMP 19 | *SCREENSHOT*.BMP 20 | # Partition images 21 | ULM* 22 | LOD* 23 | *sym 24 | bootstrap* 25 | # The disk image file 26 | disk.img 27 | # CMOS image file 28 | CMOS.RAM 29 | # RTC RAM image file 30 | RTC.RAM 31 | # ROM image files 32 | roms/* 33 | # Disk and tape image files 34 | disks/* 35 | tapes/* 36 | # The configuration file 37 | ld.conf 38 | # Autoconf stuff 39 | aclocal.m4 40 | autom4te.cache/ 41 | compile 42 | config.status 43 | install-sh 44 | missing 45 | INSTALL 46 | depcomp 47 | Makefile.in 48 | src/Makefile.in 49 | configure 50 | Makefile 51 | config.log 52 | src/.deps/ 53 | src/Makefile 54 | src/config.h.in 55 | src/config.h 56 | src/stamp* 57 | tools/.deps/ 58 | tools/lmfuse/.deps/ 59 | -------------------------------------------------------------------------------- /src/tapemaster.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | 4 | This file is part of LambdaDelta. 5 | 6 | LambdaDelta is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | LambdaDelta is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with LambdaDelta. If not, see . 18 | */ 19 | 20 | void tapemaster_init(); 21 | int tapemaster_open_next(); 22 | void tapemaster_clock_pulse(); 23 | void tapemaster_reset(); 24 | void tapemaster_attn(); 25 | -------------------------------------------------------------------------------- /src/smd.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | 4 | This file is part of LambdaDelta. 5 | 6 | LambdaDelta is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | LambdaDelta is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with LambdaDelta. If not, see . 18 | */ 19 | 20 | void smd_clock_pulse(); 21 | int smd_init(); 22 | void smd_reset(); 23 | uint8_t smd_read(uint8_t addr); 24 | void smd_write(uint8_t addr,uint8_t data); 25 | #ifdef HAVE_YAML_H 26 | int yaml_disk_mapping_loop(yaml_parser_t *parser); 27 | int yaml_disk_sequence_loop(yaml_parser_t *parser); 28 | #endif 29 | -------------------------------------------------------------------------------- /src/3com.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | Barry Silverman 4 | 5 | This file is part of LambdaDelta. 6 | 7 | LambdaDelta is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | LambdaDelta is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with LambdaDelta. If not, see . 19 | */ 20 | 21 | /* 3Com 3C400 Multibus Ethernet */ 22 | 23 | void enet_clock_pulse(); 24 | void enet_reset(); 25 | uint8_t enet_read(uint16_t addr); 26 | void enet_write(uint16_t addr,uint8_t data); 27 | #ifdef HAVE_YAML_H 28 | int yaml_network_mapping_loop(yaml_parser_t *parser); 29 | #endif 30 | -------------------------------------------------------------------------------- /src/syms.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | Barry Silverman 4 | Greg Gilley 5 | 6 | This file is part of LambdaDelta. 7 | 8 | LambdaDelta is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | LambdaDelta is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with LambdaDelta. If not, see . 20 | */ 21 | 22 | struct sym_s { 23 | struct sym_s *next; 24 | char *name; 25 | unsigned int v; 26 | int mtype; 27 | }; 28 | 29 | struct symtab_s { 30 | char *name; 31 | struct sym_s *syms; 32 | int sym_count; 33 | struct sym_s **sorted_syms; 34 | }; 35 | 36 | int read_sym_files(void); 37 | 38 | int sym_find(int mcr, char *name, int *pval); 39 | char *sym_find_by_val(int mcr, int t, int v); 40 | char *sym_find_by_type_val(int mcr, int t, int v); 41 | char *sym_find_last(int mcr, int v, int *poffset); 42 | 43 | int _sym_read_file(struct symtab_s *tab, const char *filename); 44 | int _sym_sort(struct symtab_s *tab); 45 | -------------------------------------------------------------------------------- /doc/KEYCODES: -------------------------------------------------------------------------------- 1 | LMI LAMBDA KEYBOARD CODES 2 | 3 | Blank keyname = Unused code 4 | 5 | OCTAL KEY 6 | CODE NAME 7 | ---- ---- 8 | 9 | 000 10 | 001 ROMAN II 11 | 002 ROMAN IV 12 | 003 MODE LOCK 13 | 004 14 | 005 LEFT SUPER 15 | 006 16 | 007 17 | 010 18 | 011 4 19 | 012 R 20 | 013 F 21 | 014 V 22 | 015 ALT LOCK 23 | 016 24 | 017 HAND RIGHT 25 | 020 LEFT CONTROL 26 | 021 : 27 | 022 TAB 28 | 023 RUBOUT 29 | 024 LEFT SHIFT 30 | 025 RIGHT SHIFT 31 | 026 RIGHT CONTROL 32 | 027 33 | 030 HOLD OUTPUT 34 | 031 8 35 | 032 I 36 | 033 K 37 | 034 , 38 | 035 39 | 036 LINE 40 | 037 \ 41 | 040 TERMINAL 42 | 041 43 | 042 NETWORK 44 | 043 45 | 044 LEFT GREEK 46 | 045 LEFT META 47 | 046 STATUS 48 | 047 RESUME 49 | 050 FORM 50 | 051 6 51 | 052 Y 52 | 053 H 53 | 054 N 54 | 055 55 | 056 56 | 057 57 | 060 58 | 061 2 59 | 062 W 60 | 063 S 61 | 064 X 62 | 065 RIGHT SUPER 63 | 066 64 | 067 ABORT 65 | 070 66 | 071 9 67 | 072 O 68 | 073 L 69 | 074 . 70 | 075 71 | 076 72 | 077 ` 73 | 100 BACK-NEXT 74 | 101 ROMAN I 75 | 102 ROMAN III 76 | 103 77 | 104 LEFT TOP 78 | 105 79 | 106 HAND UP 80 | 107 CALL 81 | 110 CLEAR 82 | 111 5 83 | 112 T 84 | 113 G 85 | 114 B 86 | 115 REPEAT 87 | 116 HELP 88 | 117 HAND LEFT 89 | 120 QUOTE 90 | 121 1 91 | 122 Q 92 | 123 A 93 | 124 Z 94 | 125 95 | 126 = 96 | 127 97 | 130 98 | 131 - 99 | 132 ( 100 | 133 ' 101 | 134 SPACE 102 | 135 103 | 136 CR 104 | 137 ) 105 | 140 106 | 141 SYSTEM 107 | 142 108 | 143 ALTMODE 109 | 144 110 | 145 LEFT HYPER 111 | 146 } 112 | 147 113 | 150 114 | 151 7 115 | 152 U 116 | 153 J 117 | 154 M 118 | 155 RIGHT TOP 119 | 156 END 120 | 157 DELETE 121 | 160 OVERSTRIKE 122 | 161 3 123 | 162 E 124 | 163 D 125 | 164 C 126 | 165 RIGHT META 127 | 166 { 128 | 167 BREAK 129 | 170 STOP OUTPUT 130 | 171 0 131 | 172 P 132 | 173 ; 133 | 174 / 134 | 175 RIGHT HYPER 135 | 176 HAND DOWN 136 | 177 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | VERSION 0.98.1 - Initial release. 2 | This version is sufficient to run a single Lambda at reduced speed. 3 | 4 | KNOWN ISSUES: 5 | * The need-fetch bit is inoperative. Every macroinstruction causes a fetch. 6 | This incurs a heavy speed penalty. 7 | * SM clock stepping is incomplete, meaning many debugging and diagnostic 8 | functions will fail. 9 | * Cache is not implemented. This also incurs a speed penalty. 10 | 11 | VERSION 0.98.2 12 | This version is sufficient to run a single Lambda at reduced speed. 13 | 14 | IMPROVEMENTS: 15 | * The need-fetch mechanism is now working properly, as far as we can tell. 16 | * The RTC's RAM is now saved between runs, making it possible to change 17 | the site timezone without Lisp complaining 18 | * In situations where both Linux tuntap and BPF are available, 19 | tuntap is used. This prevents a conflict. 20 | * The telnet socket used for the SDU serial port has SO_REUSEADDR set. 21 | 22 | KNOWN ISSUES: 23 | * SM clock stepping is incomplete, meaning many debugging and diagnostic 24 | functions will fail. 25 | * Cache is not implemented. This incurs a speed penalty. 26 | 27 | VERSION 0.98.3 28 | This version is sufficient to run a single Lambda in real-time 29 | on a sufficiently fast machine. 30 | 31 | IMPROVEMENTS: 32 | * There have been many bug fixes. 33 | * Many items have been made configurable. 34 | * Support for physical LMI keyboards on serial interfaces has been added. 35 | * Tools for manipulating disk images, including a FUSE-based LMFS driver, 36 | were added. 37 | * A new YAML-based configuration replaced the old configuration file. 38 | * The documentation was Markdown-ified. 39 | 40 | KNOWN ISSUES: 41 | * SM clock stepping is still incomplete, meaning some debugging and 42 | diagnostic functions will fail. Most seen in the normal operation 43 | of the system work as intended. 44 | * Cache is still not implemented, which incurs a speed penalty 45 | when the Lambda must wait for the bus. 46 | -------------------------------------------------------------------------------- /m4/ax_check_compile_flag.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Check whether the given FLAG works with the current language's compiler 12 | # or gives an error. (Warnings, however, are ignored) 13 | # 14 | # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on 15 | # success/failure. 16 | # 17 | # If EXTRA-FLAGS is defined, it is added to the current language's default 18 | # flags (e.g. CFLAGS) when the check is done. The check is thus made with 19 | # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to 20 | # force the compiler to issue an error when a bad flag is given. 21 | # 22 | # INPUT gives an alternative input source to AC_COMPILE_IFELSE. 23 | # 24 | # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this 25 | # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. 26 | # 27 | # LICENSE 28 | # 29 | # Copyright (c) 2008 Guido U. Draheim 30 | # Copyright (c) 2011 Maarten Bosmans 31 | # 32 | # Copying and distribution of this file, with or without modification, are 33 | # permitted in any medium without royalty provided the copyright notice 34 | # and this notice are preserved. This file is offered as-is, without any 35 | # warranty. 36 | 37 | #serial 6 38 | 39 | AC_DEFUN([AX_CHECK_COMPILE_FLAG], 40 | [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF 41 | AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl 42 | AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ 43 | ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS 44 | _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" 45 | AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], 46 | [AS_VAR_SET(CACHEVAR,[yes])], 47 | [AS_VAR_SET(CACHEVAR,[no])]) 48 | _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) 49 | AS_VAR_IF(CACHEVAR,yes, 50 | [m4_default([$2], :)], 51 | [m4_default([$3], :)]) 52 | AS_VAR_POPDEF([CACHEVAR])dnl 53 | ])dnl AX_CHECK_COMPILE_FLAGS 54 | -------------------------------------------------------------------------------- /src/nubus.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | Barry Silverman 4 | 5 | This file is part of LambdaDelta. 6 | 7 | LambdaDelta is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | LambdaDelta is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with LambdaDelta. If not, see . 19 | */ 20 | 21 | /* Bus structures */ 22 | 23 | typedef union rnuAddr { 24 | uint32_t raw; 25 | uint16_t hword[2]; 26 | struct { 27 | uint8_t Byte:2; 28 | uint32_t word:22; 29 | uint8_t Card:8; 30 | } __attribute__((packed)); 31 | struct { 32 | uint32_t Addr:24; 33 | } __attribute__((packed)); 34 | // For matching SDU mapping registers 35 | struct { 36 | uint32_t Offset:10; 37 | uint32_t Page:22; 38 | } __attribute__((packed)); 39 | } nuAddr; 40 | 41 | typedef union rnuData { 42 | uint32_t word; 43 | uint8_t byte[4]; 44 | uint16_t hword[2]; 45 | } nuData; 46 | 47 | /* Operations */ 48 | // CPU MASTER WORD IO 49 | #define VM_READ 0x00 50 | #define VM_WRITE 0x01 51 | // CPU MASTER BYTE IO 52 | #define VM_BYTE_READ 0x02 53 | #define VM_BYTE_WRITE 0x03 54 | 55 | // BUS MASTER WORD IO 56 | #define NB_READ 0x10 57 | #define NB_WRITE 0x11 58 | // BUS MASTER BYTE IO 59 | #define NB_BYTE_READ 0x12 60 | #define NB_BYTE_WRITE 0x13 61 | 62 | /* NuBus card IDs */ 63 | 64 | /* NUbus Interface */ 65 | extern volatile int NUbus_error; 66 | extern volatile int NUbus_Busy; 67 | extern volatile int NUbus_acknowledge; 68 | extern volatile int NUbus_master; 69 | extern volatile nuAddr NUbus_Address; 70 | extern volatile nuData NUbus_Data; 71 | extern volatile int NUbus_Request; 72 | extern volatile int NUbus_trace; 73 | /* Functions */ 74 | void nubus_clock_pulse(); 75 | void nubus_io_request(int access, int master, uint32_t address, uint32_t data); 76 | 77 | -------------------------------------------------------------------------------- /src/nubus.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | Barry Silverman 4 | 5 | This file is part of LambdaDelta. 6 | 7 | LambdaDelta is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | LambdaDelta is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with LambdaDelta. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "ld.h" 28 | #include "nubus.h" 29 | 30 | /* NUbus Interface */ 31 | volatile int NUbus_Busy; 32 | volatile int NUbus_error; 33 | volatile int NUbus_acknowledge; 34 | volatile int NUbus_master; 35 | volatile int NUbus_trace; 36 | volatile int NUbus_Request; 37 | volatile nuAddr NUbus_Address; 38 | volatile nuData NUbus_Data; 39 | 40 | void nubus_clock_pulse(){ 41 | if(NUbus_Busy > 0){ 42 | NUbus_Busy--; 43 | // Anyone take this request? 44 | if(NUbus_Busy == 1 && NUbus_acknowledge == 0){ 45 | // No. Light the error bit for at least one cycle. 46 | // Doing this before releasing the bus allows 47 | // the master a chance to learn their request timed out 48 | // because otherwise it would be clobbered if another master 49 | // grabs the bus first. 50 | NUbus_error = 1; 51 | logmsgf(LT_NUBUS,1,"NUBUS: Timed Out: Master 0x%X Request %o Addr 0x%X (0%o) w/ data 0x%X (0%o) Ack %o\n", 52 | NUbus_master,NUbus_Request,NUbus_Address.raw,NUbus_Address.raw, 53 | NUbus_Data.word,NUbus_Data.word,NUbus_acknowledge); 54 | } 55 | } 56 | } 57 | 58 | // Put a request on the bus 59 | void nubus_io_request(int access, int master, uint32_t address, uint32_t data){ 60 | // Clear flags 61 | NUbus_error = 0; 62 | NUbus_acknowledge = 0; 63 | // Log 64 | if(NUbus_trace == 1){ 65 | logmsgf(LT_NUBUS,10,"NUBUS: Request %o Addr 0x%X (0%o) w/ data 0x%X (0%o) by dev 0x%X\n", 66 | access,address,address,data,data,master); 67 | } 68 | // During the PROM run, it expects to read MD one instruction after the read request. 69 | NUbus_Busy = 3; 70 | NUbus_Address.raw = address; 71 | NUbus_master = master; 72 | NUbus_Request = access; 73 | NUbus_Data.word = data; 74 | } 75 | -------------------------------------------------------------------------------- /src/vcmem.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | Barry Silverman 4 | 5 | This file is part of LambdaDelta. 6 | 7 | LambdaDelta is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | LambdaDelta is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with LambdaDelta. If not, see . 19 | */ 20 | 21 | void vcmem_init(int vn,int slot); 22 | void vcmem_clock_pulse(int vn); 23 | void vcmem_kb_int(int vn); 24 | 25 | // Register definitions 26 | 27 | // Function Register 28 | typedef union rFunctionReg { 29 | uint16_t raw; 30 | uint8_t byte[2]; 31 | struct { 32 | uint8_t Reset:1; 33 | uint8_t BoardEnable:1; 34 | uint8_t LED:1; 35 | uint8_t Function:2; // 0 = XOR, 1 = OR, 2 = AND, 3 = STORE 36 | // Rest not documented/accessed? 37 | uint16_t Spare:11; 38 | } __attribute__((packed)); 39 | } FunctionReg; 40 | 41 | // Memory Control Register 42 | typedef union rMemoryControlReg { 43 | uint16_t raw; 44 | uint8_t byte[2]; 45 | struct { 46 | uint8_t RefreshCycles:2; // Refresh cycles per horizontal line 47 | uint8_t MemBank:1; 48 | uint8_t MemCopy:1; // Enable Bank A -> Bank B copying 49 | uint8_t ReverseVideo:1; 50 | uint8_t InterruptEnabled:1; 51 | uint8_t NUBusSelect:1; 52 | } __attribute__((packed)); 53 | } MemoryControlReg; 54 | 55 | // Interrupt Status Register 56 | typedef union rInterruptStatusReg { 57 | uint32_t raw; 58 | uint8_t byte[4]; 59 | struct { 60 | // Status Register 0 61 | uint8_t VerticalBlank:1; 62 | uint8_t FieldSelect:1; 63 | uint8_t HorizontalBlank:1; 64 | uint8_t HorizontalSelect:1; 65 | uint8_t VerticalSync:1; 66 | uint8_t CompositeSync:1; 67 | uint8_t CompositeBlank:1; 68 | uint8_t TTLVideoOutput:1; 69 | // Status Register 1 70 | uint8_t SerialParityError:1; 71 | uint8_t SerialFrameError:1; 72 | uint8_t SerialOverrunError:1; 73 | uint8_t SerialXmitDataEmpty:1; 74 | uint8_t SerialXmitEmpty:1; 75 | uint8_t SerialFIFOEmpty:1; 76 | uint8_t SerialFIFOFull:1; 77 | // Spare 78 | uint32_t Spare:17; 79 | } __attribute__((packed)); 80 | } InterruptStatusReg; 81 | 82 | // Serial port control register 83 | typedef union rSerialControlReg { 84 | uint16_t raw; 85 | uint8_t byte[2]; 86 | struct { 87 | uint8_t ParityInhibit:1; 88 | uint8_t Spare:7; 89 | uint8_t BaudRate:4; 90 | uint8_t StopBit:1; 91 | uint8_t EvenParity:1; 92 | uint8_t WordLength:2; 93 | } __attribute__((packed)); 94 | } SerialControlReg; 95 | 96 | // Framebuffer and scanline table sizes 97 | #define FB_SIZE (1024*128) 98 | #define SLT_SIZE (0x800) 99 | 100 | // Card state 101 | struct vcmemState { 102 | // Memories 103 | uint8_t AMemory[FB_SIZE]; 104 | uint8_t BMemory[FB_SIZE]; 105 | uint32_t SLT[SLT_SIZE]; // Scanline Table 106 | // Register storage 107 | FunctionReg Function; 108 | MemoryControlReg MemoryControl; 109 | uint32_t InterruptAddr; 110 | InterruptStatusReg InterruptStatus; 111 | SerialControlReg SerialControl; 112 | // Timing 113 | uint32_t cycle_count; 114 | // Software state 115 | uint8_t Card; 116 | }; 117 | -------------------------------------------------------------------------------- /tools/dumptape.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int tape_fd = -1; // FD for tape 11 | int out_fd = -1; // FD for output 12 | int tape_eot = 0; // At end of tape 13 | int tape_fm = 0; // At filemark 14 | int tape_reclen = 0; // Tape record length 15 | int tape_error = 0; // Tapemaster error code 16 | char tape_block[64*1024]; // Buffer 17 | 18 | char tfname[512]; 19 | char ofname[512]; 20 | int ofnumber = 0; 21 | 22 | int tape_read(){ 23 | int reclen; 24 | ssize_t rv=0; 25 | tape_eot = 0; 26 | tape_fm = 0; 27 | tape_error = 0; 28 | tape_reclen = 0; 29 | // Obtain record length 30 | rv = read(tape_fd,(unsigned char *)&reclen,4); 31 | if(rv == -1){ 32 | perror("tape:read"); 33 | tape_error = 0x0A; // IO error 34 | return(0); 35 | } 36 | if(rv < 4){ 37 | // Found EOT 38 | tape_eot = 1; 39 | tape_error = 0x09; // Unexpected EOT 40 | return(0); 41 | } 42 | // printf("TAPE READ: Reclen %d\n",reclen); 43 | tape_reclen = reclen; 44 | if(reclen == 0){ 45 | // Found file mark 46 | tape_fm = 1; 47 | // tape_error = 0x15; // Unexpected file mark 48 | return(0); 49 | } 50 | rv = read(tape_fd,tape_block,reclen); 51 | if(rv == -1){ 52 | perror("tape:read"); 53 | tape_error = 0x0A; // IO error 54 | tape_reclen = 0; 55 | return(0); 56 | } 57 | if(rv < reclen){ 58 | // Found EOT 59 | tape_eot = 1; 60 | tape_error = 0x09; // Unexpected EOT 61 | tape_reclen = 0; 62 | return(0); 63 | } 64 | // Obtain trailing record length 65 | rv = read(tape_fd,(unsigned char *)&reclen,4); 66 | if(rv == -1){ 67 | perror("tape:read"); 68 | tape_error = 0x0A; // IO error 69 | tape_reclen = 0; 70 | return(0); 71 | } 72 | if(rv < 4){ 73 | // Found EOT 74 | tape_eot = 1; 75 | tape_error = 0x09; // Unexpected EOT 76 | tape_reclen = 0; 77 | return(0); 78 | } 79 | if(reclen != tape_reclen){ 80 | printf("tape:reclen mismatch: %d vs %d\n",tape_reclen,reclen); 81 | close(tape_fd); 82 | if(out_fd != -1){ 83 | close(out_fd); 84 | } 85 | exit(-1); 86 | return(tape_reclen); 87 | } 88 | return(reclen); 89 | } 90 | 91 | int main(int argc, char *argv[]){ 92 | // Handle command-line options 93 | if(argc != 2){ 94 | printf("Usage: dumptape (tape file name)\n"); 95 | return(0); 96 | }else{ 97 | strncpy(tfname,argv[1],512); 98 | printf("Dumping %s\n",tfname); 99 | } 100 | 101 | tape_fd = open(tfname,O_RDONLY); 102 | if(tape_fd < 0){ 103 | perror("Tape:open"); 104 | return(-1); 105 | } 106 | 107 | while(tape_eot != 1){ 108 | tape_read(); 109 | if(tape_reclen == 0){ 110 | if(out_fd != -1){ 111 | printf("EOF/EOT\n"); 112 | close(out_fd); 113 | out_fd = -1; 114 | ofnumber++; 115 | } 116 | }else{ 117 | // We have a block 118 | ssize_t rv; 119 | if(out_fd == -1){ 120 | sprintf(ofname,"file-%d.dat",ofnumber); 121 | out_fd = open(ofname,O_RDWR|O_CREAT,0660); 122 | if(out_fd < 0){ 123 | perror("Output:open"); 124 | close(tape_fd); 125 | return(-1); 126 | } 127 | printf("Writing %s (blocksize %d)...\n",ofname,tape_reclen); 128 | } 129 | // Write it 130 | rv = write(out_fd,tape_block,tape_reclen); 131 | if(rv != tape_reclen){ 132 | perror("Output:write"); 133 | close(tape_fd); 134 | close(out_fd); 135 | return(-1); 136 | } 137 | } 138 | } 139 | 140 | return(0); 141 | } 142 | -------------------------------------------------------------------------------- /tools/maketape.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int tape_fd = -1; // FD for tape 11 | int in_fd = -1; // FD for output 12 | int in_reclen = -1; // Input record length 13 | int zero_reclen = 0; // For writing EOF/EOT 14 | char tape_block[64*1024]; // Buffer 15 | 16 | char tfname[512]; 17 | char ifname[512]; 18 | 19 | int main(int argc, char *argv[]){ 20 | // Handle command-line options 21 | if(argc < 4 || ((argc&0x01) != 0x00)){ 22 | printf("Usage: maketape (tape file name) [(input file name) (block size)]...\n"); 23 | return(0); 24 | }else{ 25 | int ifnumber = 0; 26 | ssize_t rv = 0; 27 | strncpy(tfname,argv[1],512); 28 | printf("Creating tape %s\n",tfname); 29 | tape_fd = open(tfname,O_RDWR|O_CREAT,0660); 30 | if(tape_fd < 0){ 31 | perror("Tape:open"); 32 | return(-1); 33 | } 34 | while(2+(ifnumber*2) < argc){ 35 | int eof = 0; 36 | strncpy(ifname,argv[2+(ifnumber*2)],512); 37 | in_reclen = atoi(argv[3+(ifnumber*2)]); 38 | if(in_reclen > 0){ 39 | printf("Adding %s with blocksize %d\n",ifname,in_reclen); 40 | in_fd = open(ifname,O_RDONLY); 41 | if(in_fd < 0){ 42 | perror("Input:open"); 43 | close(tape_fd); 44 | return(-1); 45 | } 46 | }else{ 47 | printf("Writing extra EOF\n"); 48 | eof = 1; 49 | } 50 | while(eof == 0){ 51 | rv = read(in_fd,tape_block,in_reclen); 52 | if(rv == -1){ 53 | perror("Input:read"); 54 | return(0); 55 | } 56 | if(rv == 0){ 57 | // Found EOF, no block 58 | eof = 1; 59 | }else{ 60 | if(rv < in_reclen){ 61 | // Found EOF, short block 62 | printf("Short read: Zeroing %d bytes after end of file\n", 63 | in_reclen-rv); 64 | bzero(tape_block+rv,in_reclen-rv); 65 | eof = 1; 66 | } 67 | // Write block 68 | rv = write(tape_fd,(unsigned char *)&in_reclen,4); 69 | if(rv != 4){ 70 | perror("Output:write"); 71 | close(tape_fd); 72 | close(in_fd); 73 | return(-1); 74 | } 75 | rv = write(tape_fd,tape_block,in_reclen); 76 | if(rv != in_reclen){ 77 | perror("Output:write"); 78 | close(tape_fd); 79 | close(in_fd); 80 | return(-1); 81 | } 82 | rv = write(tape_fd,(unsigned char *)&in_reclen,4); 83 | if(rv != 4){ 84 | perror("Output:write"); 85 | close(tape_fd); 86 | close(in_fd); 87 | return(-1); 88 | } 89 | } 90 | } 91 | // EOF 92 | write(tape_fd,(unsigned char *)&zero_reclen,4); 93 | ifnumber++; 94 | } 95 | printf("Writing EOT...\n"); 96 | rv = write(tape_fd,(unsigned char *)&zero_reclen,4); 97 | if(rv != 4){ 98 | perror("Output:write"); 99 | close(tape_fd); 100 | close(in_fd); 101 | return(-1); 102 | } 103 | rv = write(tape_fd,(unsigned char *)&zero_reclen,4); 104 | if(rv != 4){ 105 | perror("Output:write"); 106 | close(tape_fd); 107 | close(in_fd); 108 | return(-1); 109 | } 110 | rv = write(tape_fd,(unsigned char *)&zero_reclen,4); 111 | if(rv != 4){ 112 | perror("Output:write"); 113 | close(tape_fd); 114 | close(in_fd); 115 | return(-1); 116 | } 117 | rv = write(tape_fd,(unsigned char *)&zero_reclen,4); 118 | if(rv != 4){ 119 | perror("Output:write"); 120 | close(tape_fd); 121 | close(in_fd); 122 | return(-1); 123 | } 124 | printf("Done!\n"); 125 | } 126 | // Done 127 | return(0); 128 | } 129 | -------------------------------------------------------------------------------- /examples/lam.yml: -------------------------------------------------------------------------------- 1 | # Example LambdaDelta YAML Configuration 2 | # This file is documentation; It is not expected to be usable as-is. 3 | 4 | # Top-level keys control who parses what. 5 | # There are no values at the top level, only sequences or mappings. 6 | 7 | # Global LambdaDelta settings 8 | lam: 9 | # Working Directory (where to chdir for to access disk/rom/tape images) 10 | wd: /usr/local/whatever/etc 11 | # File to use for physical keyboard IO 12 | kb_file: /dev/ttySX 13 | # Baud rate to use for physical keyboard IO 14 | kb_baud: 9600 15 | 16 | # Logging settings 17 | log: 18 | # Key is either a logtype (see logtype_name in kernel.c) or ALL 19 | # Value is the logging level to use 20 | # Value of 0 = Minimal logging 21 | # Value of 10 = Maximum logging 22 | ALL: 0 23 | 24 | # Keyboard settings 25 | keyboard: 26 | # Whether or not to respect the SDL_QUIT event. (on/true/yes or off/false/no) 27 | quit: on 28 | # keyboard/mouse input frame rate 29 | input_fps: 60 30 | # Map decimal SDL keycode or key name "foo" (see the SDL documentation) 31 | # to octal Lambda keycode or key name "bar" (see doc/KEYCODES) 32 | map: foo bar 33 | map: F8 Page 34 | # Mapping as a sequence can also go here 35 | # Values as above 36 | mapping: 37 | - sdl: F8 38 | lambda: Page 39 | - sdl: x 40 | lambda: a 41 | - sdl: y 42 | lambda: b 43 | - sdl: z 44 | lambda: c 45 | 46 | # SDU settings 47 | sdu: 48 | # The SDU rotary switch position (0 = new install, 1 = default, other positions non-normal - see SDU documentation) 49 | switch: 1 50 | 51 | # Video settings 52 | video: 53 | # SDL video frame rate 54 | video_fps: 20 55 | # SDL framebuffer pixel values 56 | # Note that on/off means 1/0 in the Lambda's 1bpp framebuffer, 57 | # not the actually displayed value when the Lambda framebuffer's 58 | # polarity switch is taken into consideration. 59 | pixel-on: 0xFFFFFFFF 60 | pixel-off: 0x00000000 61 | # window height. 800 is the default, and the other useful value is 1024 (to get larger, microcode changes are needed). 62 | # Use (TV:SET-CONSOLE-SIZE width height) to change the LispM view. 63 | height: 800 64 | 65 | # Audio settings, only available when using SDL2 (where BEEP is implemented) 66 | audio: 67 | # Volume factor, from 0.0 to 1.0. I like 0.3. 68 | volume: 1.0 69 | 70 | # Mouse settings 71 | mouse: 72 | # Mouse mode: 73 | # 0 = direct (capture the mouse and drive the VCMEM serial port when it moves) 74 | # 1 = shared (push the host's window-relative mouse position into A-memory) 75 | mode: 1 76 | # A-Memory locations for Mode 1. 77 | # lambda: which processor (for 2x2) 78 | # xloc: X position location 79 | # yloc: Y position location 80 | # wake: Actvity flag location 81 | locs: 82 | - lambda: 0 83 | xloc: 0156 84 | yloc: 0157 85 | wake: 0170 86 | - lambda: 1 87 | xloc: 0156 88 | yloc: 0157 89 | wake: 0170 90 | 91 | # Networking settings 92 | network: 93 | # The network interface to use 94 | interface: ldtap 95 | # The Lambda's MAC address 96 | address: 00:02:9C:55:89:C6 97 | # The Lambda's guest IP if you are using UTUN (otherwise undefined key) 98 | guest-ip: aaa.bbb.ccc.ddd 99 | 100 | # Disk settings 101 | disk: 102 | # Units can be specified in one line. 103 | # This is mostly so they can be specified on the command line. 104 | # Parameters are the unit number and image file name. 105 | image: 0 disk.img 106 | image: 1 disk2.img 107 | # They can also be specified as a sequence 108 | # unit = which drive 109 | # file = image file 110 | units: 111 | - unit: 0 112 | file: disk.img 113 | - unit: 1 114 | file: disk2.img 115 | 116 | -------------------------------------------------------------------------------- /doc/OLDCONF.md: -------------------------------------------------------------------------------- 1 | 2 | # Old Configuration File: 3 | **Note: This file is provided for people who do not have libyaml on their system and wish to configure LambdaDelta without that dependency. However, it will not continue to be supported.** 4 | 5 | The old configuration file is named "ld.conf" and must be in the current 6 | working directory. An example configuration is provided in the examples 7 | directory. Each line of the configuration file has a keyword followed by 8 | a number of parameters. The following are the supported keywords 9 | and their parameters: 10 | 11 | ether_iface IFACE 12 | 13 | Causes the ethernet interface to be bound to the specified interface. 14 | For tuntap, this is the name of the tuntap device to use. 15 | For BPF, this is the interface the BPF interface will be bound to. 16 | As this is used for the passing of raw frames, it must be a wired 17 | Ethernet interface (or something emulating one) - A wireless interface 18 | will not work. The default is "ldtap". 19 | 20 | ether_addr MAC-ADDRESS 21 | 22 | Sets the MAC address used by the Lambda. This must be unique on your 23 | LAN, so do not make it the same as the host! It is written in the 24 | canonical colon-separated hex format. 25 | The default is 00:02:9C:55:89:C6. 26 | 27 | disk N FNAME 28 | 29 | Sets the image file for disk unit N. The given FNAME must exist. 30 | The geometry of the disk is determined by settings in the SDU. 31 | The default is that unit 0 is disks/disk.img and all other units 32 | are absent. 33 | 34 | sdu_switch N 35 | 36 | Sets the position of the SDU's rotary switch. N is a single hex digit 0-F. 37 | Position 0 is used for initial software installation or recovery. 38 | Position 1 is the standard configuration. The other positions operate as 39 | determined by the SDU configuration. If the position is not 1, LambdaDelta 40 | will wait for a telnet connection on port 3637 after startup. This simulates 41 | a terminal connected to the SDU's serial port. 42 | The default is 0. 43 | 44 | mouse_mode N 45 | 46 | Sets the operation mode of the mouse emulation. In mode 0, the mouse 47 | protocol is emulated directly using SDL relative mouse movement data. 48 | The lisp mouse pointer moves independently of the host mouse pointer, 49 | and movement may be difficult or unpredictable on some platforms. 50 | In mode 1, the position of the host's mouse pointer is written 51 | directly into the Lambda processor's memory, causing the mouse pointer 52 | to remain in sync with the host but requiring knowledge of the offsets 53 | at which the pointer data resides. For more information, see the usage 54 | instructions later in this document. 55 | The default is 1. 56 | 57 | mouse_x_loc_0 N 58 | mouse_y_loc_0 N 59 | mouse_wake_loc_0 N 60 | mouse_x_loc_1 N 61 | mouse_y_loc_1 N 62 | mouse_wake_loc_1 N 63 | 64 | Sets the memory offsets used for mouse operation mode 1. The 0 or 1 65 | indicates Lambda 0 or Lambda 1. N is an octal offset into A-memory. The 66 | offsets are for the X coordinate, the Y coordinate, and the mouse updation 67 | flag. You should only have to modify this if you modify the microcode. 68 | The defaults are 0156, 0157, and 0170 for both processors. 69 | 70 | pixel_on 0xVVVVVVVV 71 | pixel_off 0xVVVVVVVV 72 | 73 | Sets the pixel values used for on and off. The standard pixel format is 74 | AARRGGBB where AA is Alpha, RR is Red, GG is Green, BB is Blue. The default 75 | pixel values are 0xFFFFFFFF for on and 0x00000000 for off. 76 | 77 | input_fps N 78 | video_fps N 79 | 80 | Sets the framerate used for video output or user input. These are updated 81 | in the decisecond loop, so values less than ten will not work as expected, 82 | and will be set to ten. The default values are 60 input frames per second 83 | and 10 video frames per second. 84 | Note that the video frame rate does not affect updation of the title bar, 85 | only the rate at which the SDL framebuffer is refreshed. 86 | 87 | sdl_quit STATE 88 | 89 | Enables or disables handling of the platform-specific SDL_QUIT event. 90 | (Alt-F4,Command-Q,etc.) STATE is one of on,off,yes,no,true,or false. 91 | The default is enabled. 92 | 93 | map_key HV LV 94 | 95 | Maps host key HV to Lambda key LV. 96 | HV is a decimal SDL key code, LV is an octal Lambda key code. 97 | See the KEYCODES file in the doc directory for a list of Lambda key codes. 98 | See the SDL documentation for your SDL version to find the SDL key codes. 99 | SDL1 codes start with SDLK_ and SDL2 codes start with SDL_SCANCODE_. 100 | The default keymap is described later in this document. 101 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ([2.69]) 5 | AC_INIT([ld], [0.98.3], [dseagrav@lunar-tokyo.net]) 6 | AC_CONFIG_SRCDIR([src/kernel.c]) 7 | AC_CONFIG_HEADERS([src/config.h]) 8 | AC_CONFIG_MACRO_DIR([m4]) 9 | AM_INIT_AUTOMAKE 10 | 11 | # Checks for programs. 12 | AC_PROG_CC 13 | 14 | # Conditionalize XBEEP 15 | AC_ARG_ENABLE([use_xbeep], 16 | [AS_HELP_STRING([--enable-use-xbeep], [Enable superior XBEEP hack @<:@default=yes@:>@])]) 17 | 18 | AS_IF([test "x$enable_use_xbeep" = "xno"], [], [ 19 | CFLAGS="$CFLAGS -DXBEEP" 20 | ]) 21 | 22 | # Conditionalize UTUN 23 | AC_ARG_ENABLE([use_utun], 24 | [AS_HELP_STRING([--enable-use-utun], [Use UTUN frame diverter with BPF @<:@default=no@:>@])]) 25 | 26 | AS_IF([test "x$enable_use_utun" = "xyes"], [ 27 | CFLAGS="$CFLAGS -DUSE_UTUN" 28 | ]) 29 | 30 | # Conditionalize 2x2 support 31 | AC_ARG_ENABLE([config_2x2], 32 | [AS_HELP_STRING([--enable-config-2x2], [Use 2x2 Lambda configuration @<:@default=no@:>@])]) 33 | 34 | AS_IF([test "x$enable_config_2x2" = "xyes"], [ 35 | CFLAGS="$CFLAGS -DCONFIG_2X2" 36 | ]) 37 | 38 | # Conditionalize physical keyboard support 39 | AC_ARG_ENABLE([config_physkbd], 40 | [AS_HELP_STRING([--enable-config-physkbd], [Use physical Lambda keyboard @<:@default=no@:>@])]) 41 | 42 | AS_IF([test "x$enable_config_physkbd" = "xyes"], [ 43 | CFLAGS="$CFLAGS -DCONFIG_PHYSKBD" 44 | ]) 45 | 46 | # Conditionalize physical mouse support 47 | AC_ARG_ENABLE([config_physms], 48 | [AS_HELP_STRING([--enable-config-physms], [Use physical Mouse Systems mouse @<:@default=no@:>@])]) 49 | 50 | AS_IF([test "x$enable_config_physms" = "xyes"], [ 51 | CFLAGS="$CFLAGS -DCONFIG_PHYSMS" 52 | ]) 53 | 54 | # Checks for libraries. 55 | AC_ARG_WITH([SDL1], 56 | [AS_HELP_STRING([--with-SDL1], 57 | [Use SDL1 for console @<:@default=check@:>@])], 58 | [], 59 | [with_SDL1=check]) 60 | 61 | AC_ARG_WITH([SDL2], 62 | [AS_HELP_STRING([--with-SDL2], 63 | [Use SDL2 for console @<:@default=check@:>@])], 64 | [], 65 | [with_SDL2=check]) 66 | 67 | AC_ARG_WITH([FUSE], 68 | [AS_HELP_STRING([--with-FUSE], 69 | [support FUSE-based LMFS filesystem @<:@default=check@:>@])], 70 | [], 71 | [with_FUSE=check]) 72 | 73 | # Checks for SDL1 74 | SDL_VERSION=1.2.0 75 | LIBSDL1= 76 | AS_IF([test "x$with_SDL1" != xno], 77 | [AM_PATH_SDL($SDL_VERSION,:, 78 | AS_IF([test "x$with_SDL1" == xyes],[AC_MSG_ERROR([*** SDL1 version $SDL_VERSION not found!])]) 79 | ) 80 | GFX_SDL1=TRUE 81 | CFLAGS="$CFLAGS $SDL_CFLAGS -DSDL1" 82 | LIBS="$LIBS $SDL_LIBS" 83 | ]) 84 | 85 | # Checks for SDL2 86 | SDL2_VERSION=2.0.0 87 | LIBSDL2= 88 | AS_IF([test "x$with_SDL2" != xno], 89 | [AM_PATH_SDL2($SDL2_VERSION,:, 90 | AS_IF([test "x$with_SDL2" == xyes],[AC_MSG_ERROR([*** SDL2 version $SDL_VERSION not found!])]) 91 | ) 92 | GFX_SDL2=TRUE 93 | CFLAGS="$CFLAGS $SDL_CFLAGS -DSDL2" 94 | LIBS="$LIBS $SDL_LIBS" 95 | ]) 96 | 97 | # Blow up if we ended up with both SDLs or neither SDL 98 | AS_IF([test "$GFX_SDL1" = TRUE], 99 | [AS_IF([test "$GFX_SDL2" = TRUE], 100 | [AC_MSG_ERROR([*** You may use SDL1 or SDL2 but not both!])])], 101 | [AS_IF([test "$GFX_SDL2" = TRUE], 102 | [], 103 | [AC_MSG_ERROR([*** You must have either SDL1 or SDL2!])])]) 104 | 105 | # Check for FUSE 106 | LIBFUSE= 107 | AS_IF([test "x$with_FUSE" != xno], 108 | [PKG_CHECK_MODULES([fuse], [fuse], 109 | [LIB_FUSE=TRUE], 110 | [if test "x$with_FUSE" != xcheck; then 111 | AC_MSG_FAILURE([--with-FUSE was given, but test for FUSE failed]) 112 | fi])]) 113 | 114 | # Checks for header files. 115 | AC_HEADER_STDC 116 | AC_CHECK_HEADERS([fcntl.h netdb.h netinet/in.h arpa/inet.h stddef.h stdint.h stdlib.h string.h strings.h]) 117 | AC_CHECK_HEADERS([ sys/ioctl.h sys/socket.h sys/time.h unistd.h termios.h utime.h]) 118 | AC_CHECK_HEADERS([linux/if.h], [], [], 119 | [[#ifdef HAVE_SYS_SOCKET_H 120 | #include 121 | #endif 122 | ]]) 123 | AC_CHECK_HEADERS([linux/if_tun.h], [], [], 124 | [[#ifdef HAVE_SYS_SOCKET_H 125 | #include 126 | #endif 127 | ]]) 128 | AC_CHECK_HEADERS([net/bpf.h]) 129 | 130 | # Check for structure members 131 | AC_CHECK_MEMBERS([struct stat.st_blksize]) 132 | 133 | # Do we have libyaml? 134 | AC_CHECK_HEADERS([yaml.h], [LIBS="$LIBS -lyaml"]) 135 | 136 | # Checks for typedefs, structures, and compiler characteristics. 137 | AC_CHECK_HEADER_STDBOOL 138 | AC_TYPE_INT32_T 139 | AC_TYPE_INT64_T 140 | AC_TYPE_OFF_T 141 | AC_TYPE_SIZE_T 142 | AC_TYPE_SSIZE_T 143 | AC_TYPE_UINT16_T 144 | AC_TYPE_UINT32_T 145 | AC_TYPE_UINT64_T 146 | AC_TYPE_UINT8_T 147 | AC_TYPE_UID_T 148 | 149 | AX_CHECK_COMPILE_FLAG([-Wno-packed-bitfield-compat], [CFLAGS="$CFLAGS -Wno-packed-bitfield-compat"], [], [-Werror]) 150 | AX_CHECK_COMPILE_FLAG([-Og], [CFLAGS="$CFLAGS -Og"], [], [-Werror]) 151 | 152 | # Checks for library functions. 153 | AC_FUNC_MALLOC 154 | AC_FUNC_REALLOC 155 | AC_STRUCT_ST_BLOCKS 156 | AC_CHECK_FUNCS([atexit bzero inet_ntoa gethostbyname memset socket strcasecmp strdup strtol strchr]) 157 | AC_CHECK_FUNCS([gettimeofday mkdir strerror strstr utime]) 158 | 159 | # Path propagation 160 | CFLAGS="$CFLAGS -DSYSCONFDIR=${sysconfdir}" 161 | 162 | # Done 163 | AC_CONFIG_FILES([Makefile 164 | src/Makefile]) 165 | # Also make tools 166 | AC_CONFIG_FILES([tools/Makefile]) 167 | # If we have FUSE, make lmfuse 168 | AM_CONDITIONAL([COND_LMFUSE],[test "$LIB_FUSE" = TRUE]) 169 | AC_CONFIG_FILES([tools/lmfuse/Makefile]) 170 | AC_OUTPUT 171 | -------------------------------------------------------------------------------- /src/mem.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | Barry Silverman 4 | 5 | This file is part of LambdaDelta. 6 | 7 | LambdaDelta is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | LambdaDelta is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with LambdaDelta. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "ld.h" 29 | #include "nubus.h" 30 | 31 | // Board is 16MB 32 | // WAIT A MINUTE! 16MB IS THE ENTIRE SLOT SPACE! WHAT GIVES? 33 | // Easy - The board isn't really 16MB! The top 4K is omitted! 34 | #define RAM_TOP 0xFFF000 // "16MB" 35 | 36 | // Memory 37 | #ifdef CONFIG_2X2 38 | uint8_t MEM_RAM[2][RAM_TOP]; 39 | #else 40 | uint8_t MEM_RAM[1][RAM_TOP]; 41 | #endif 42 | static uint8_t rom_string[0x1B] = "LMI 16-MEGABYTE MEMORY V1.0"; 43 | // Don't use the ROM image anymore, no longer needed. 44 | // uint8_t MEM_ROM[2048]; // TI ROMs are 2KB 45 | // uint8_t MEM_ROM[512]; // LMI ROMs are 512 bytes 46 | 47 | // Externals 48 | extern int ld_die_rq; 49 | 50 | // Functions 51 | void mem_init(){ 52 | bzero(MEM_RAM[0],RAM_TOP); 53 | #ifdef CONFIG_2X2 54 | bzero(MEM_RAM[1],RAM_TOP); 55 | #endif 56 | } 57 | 58 | uint8_t debug_mem_read(uint32_t addr){ 59 | return(MEM_RAM[0][addr]); 60 | }; 61 | 62 | void debug_mem_write(uint32_t addr, uint8_t data){ 63 | MEM_RAM[0][addr] = data; 64 | }; 65 | 66 | void mem_clock_pulse(){ 67 | // If the bus is busy and not acknowledged... 68 | if(NUbus_Busy == 2 && NUbus_acknowledge == 0){ 69 | // Is it us? 70 | if(NUbus_Address.Card == 0xF9 71 | #ifdef CONFIG_2X2 72 | || NUbus_Address.Card == 0xFC 73 | #endif 74 | ){ 75 | int Card = 0; 76 | switch(NUbus_Address.Card){ 77 | case 0xF9: 78 | Card = 0; break; 79 | #ifdef CONFIG_2X2 80 | case 0xFC: 81 | Card = 1; break; 82 | #endif 83 | } 84 | // Yes, answer 85 | switch(NUbus_Address.Addr){ 86 | case 0x000000 ... RAM_TOP: 87 | if(NUbus_Request == VM_READ){ // Read four bytes 88 | switch(NUbus_Address.Byte){ 89 | case 1: // Read Low Half 90 | NUbus_Data.hword[0] = *(uint16_t *)(MEM_RAM[Card]+(NUbus_Address.Addr-1)); 91 | break; 92 | 93 | case 2: // Block Transfer (ILLEGAL) 94 | logmsgf(LT_MEM,0,"MEM8: BLOCK WRITE REQUESTED\n"); 95 | exit(-1); 96 | break; 97 | 98 | case 3: // Read High Half 99 | NUbus_Data.hword[1] = *(uint16_t *)(MEM_RAM[Card]+(NUbus_Address.Addr-1)); 100 | break; 101 | 102 | case 0: 103 | // Full word read 104 | NUbus_Data.word = *(uint32_t *)(MEM_RAM[Card]+NUbus_Address.Addr); 105 | break; 106 | } 107 | NUbus_acknowledge=1; 108 | return; 109 | } 110 | if(NUbus_Request == VM_WRITE){ 111 | switch(NUbus_Address.Byte){ 112 | case 1: // Write low half 113 | *(uint16_t *)(MEM_RAM[Card]+(NUbus_Address.Addr-1)) = NUbus_Data.hword[0]; 114 | break; 115 | 116 | case 2: // BLOCK TRANSFER (ILLEGAL) 117 | logmsgf(LT_MEM,0,"MEM8: BLOCK WRITE REQUESTED\n"); 118 | exit(-1); 119 | break; 120 | 121 | case 3: // Write high half 122 | *(uint16_t *)(MEM_RAM[Card]+(NUbus_Address.Addr-1)) = NUbus_Data.hword[1]; 123 | break; 124 | 125 | case 0: // Full Word 126 | *(uint32_t *)(MEM_RAM[Card]+NUbus_Address.Addr) = NUbus_Data.word; 127 | break; 128 | } 129 | NUbus_acknowledge=1; 130 | return; 131 | } 132 | if(NUbus_Request == VM_BYTE_READ){ 133 | // BYTE READ 134 | NUbus_Data.byte[NUbus_Address.Byte] = MEM_RAM[Card][NUbus_Address.Addr]; 135 | NUbus_acknowledge=1; 136 | return; 137 | } 138 | if(NUbus_Request == VM_BYTE_WRITE){ 139 | MEM_RAM[Card][NUbus_Address.Addr] = NUbus_Data.byte[NUbus_Address.Byte]; 140 | NUbus_acknowledge=1; 141 | return; 142 | } 143 | break; 144 | 145 | // Some kind of configuration register 146 | case 0xFFF7FC ... 0xFFF7FF: 147 | if((NUbus_Request == VM_READ || NUbus_Request == VM_BYTE_READ)){ 148 | NUbus_Data.word = 0; 149 | NUbus_acknowledge=1; 150 | return; 151 | } 152 | if((NUbus_Request == VM_WRITE || NUbus_Request == VM_BYTE_WRITE)){ 153 | // Bit 0x08 is LED? 154 | if(!(NUbus_Data.word == 0x08 && NUbus_Address.Byte == 0) && NUbus_Data.word != 0x00){ 155 | logmsgf(LT_MEM,0,"MEM: CONF REG WRITE: DATA = 0x%X\n",NUbus_Data.word); 156 | } 157 | NUbus_acknowledge=1; 158 | return; 159 | } 160 | break; 161 | 162 | // Configuration ROM 163 | case 0xFFF800 ... 0xFFFFFF: 164 | if((NUbus_Request == VM_READ || NUbus_Request == VM_BYTE_READ) 165 | && NUbus_Address.Byte == 0){ 166 | uint32_t rom_addr = (NUbus_Address.Addr-0xfff800)/4; 167 | if(rom_addr <= 0x1B){ 168 | NUbus_Data.word = rom_string[rom_addr]; 169 | }else{ 170 | NUbus_Data.word = 0; 171 | } 172 | NUbus_acknowledge=1; 173 | return; 174 | } 175 | break; 176 | 177 | // Uhoh! 178 | default: 179 | logmsgf(LT_MEM,0,"RAM: Unimplemented address 0x%X (0x%X), op %X\n", 180 | NUbus_Address.Addr,NUbus_Address.raw,NUbus_Request); 181 | // lambda_dump(DUMP_ALL); 182 | ld_die_rq=1; 183 | break; 184 | } 185 | } 186 | // Request finished or not ours 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/sdu_hw.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | Barry Silverman 4 | Mike Chambers 5 | 6 | This file is part of LambdaDelta and includes code from Fake86. 7 | 8 | LambdaDelta is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | LambdaDelta is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with LambdaDelta. If not, see . 20 | */ 21 | 22 | void reset86(); 23 | void i8086_clockpulse(); 24 | 25 | #define regax 0 26 | #define regcx 1 27 | #define regdx 2 28 | #define regbx 3 29 | #define regsp 4 30 | #define regbp 5 31 | #define regsi 6 32 | #define regdi 7 33 | #define reges 0 34 | #define regcs 1 35 | #define regss 2 36 | #define regds 3 37 | 38 | #ifdef __BIG_ENDIAN__ 39 | #define regal 1 40 | #define regah 0 41 | #define regcl 3 42 | #define regch 2 43 | #define regdl 5 44 | #define regdh 4 45 | #define regbl 7 46 | #define regbh 6 47 | #else 48 | #define regal 0 49 | #define regah 1 50 | #define regcl 2 51 | #define regch 3 52 | #define regdl 4 53 | #define regdh 5 54 | #define regbl 6 55 | #define regbh 7 56 | #endif 57 | 58 | union _bytewordregs_ { 59 | uint16_t wordregs[8]; 60 | uint8_t byteregs[8]; 61 | }; 62 | 63 | #ifdef CPU_ADDR_MODE_CACHE 64 | struct addrmodecache_s { 65 | uint16_t exitcs; 66 | uint16_t exitip; 67 | uint16_t disp16; 68 | uint32_t len; 69 | uint8_t mode; 70 | uint8_t reg; 71 | uint8_t rm; 72 | uint8_t forcess; 73 | uint8_t valid; 74 | }; 75 | #endif 76 | 77 | #define StepIP(x) ip += x 78 | #define getmem8(x, y) read86(segbase(x) + y) 79 | #define getmem16(x, y) readw86(segbase(x) + y) 80 | #define putmem8(x, y, z) write86(segbase(x) + y, z) 81 | #define putmem16(x, y, z) writew86(segbase(x) + y, z) 82 | #define signext(value) (int16_t)(int8_t)(value) 83 | #define signext32(value) (int32_t)(int16_t)(value) 84 | #define getreg16(regid) regs.wordregs[regid] 85 | #define getreg8(regid) regs.byteregs[byteregtable[regid]] 86 | #define putreg16(regid, writeval) regs.wordregs[regid] = writeval 87 | #define putreg8(regid, writeval) regs.byteregs[byteregtable[regid]] = writeval 88 | #define getsegreg(regid) segregs[regid] 89 | #define putsegreg(regid, writeval) segregs[regid] = writeval 90 | #define segbase(x) ((uint32_t) x << 4) 91 | 92 | #define makeflagsword() \ 93 | ( \ 94 | 2 | (uint16_t) cf | ((uint16_t) pf << 2) | ((uint16_t) af << 4) | ((uint16_t) zf << 6) | ((uint16_t) sf << 7) | \ 95 | ((uint16_t) tf << 8) | ((uint16_t) ifl << 9) | ((uint16_t) df << 10) | ((uint16_t) of << 11) \ 96 | ) 97 | 98 | #define decodeflagsword(x) { \ 99 | temp16 = x; \ 100 | cf = temp16 & 1; \ 101 | pf = (temp16 >> 2) & 1; \ 102 | af = (temp16 >> 4) & 1; \ 103 | zf = (temp16 >> 6) & 1; \ 104 | sf = (temp16 >> 7) & 1; \ 105 | tf = (temp16 >> 8) & 1; \ 106 | ifl = (temp16 >> 9) & 1; \ 107 | df = (temp16 >> 10) & 1; \ 108 | of = (temp16 >> 11) & 1; \ 109 | } 110 | 111 | #define modregrm() { \ 112 | addrbyte = getmem8(segregs[regcs], ip); \ 113 | StepIP(1); \ 114 | mode = addrbyte >> 6; \ 115 | reg = (addrbyte >> 3) & 7; \ 116 | rm = addrbyte & 7; \ 117 | switch(mode) \ 118 | { \ 119 | case 0: \ 120 | if(rm == 6) { \ 121 | disp16 = getmem16(segregs[regcs], ip); \ 122 | StepIP(2); \ 123 | } \ 124 | if(((rm == 2) || (rm == 3)) && !segoverride) { \ 125 | useseg = segregs[regss]; \ 126 | } \ 127 | break; \ 128 | \ 129 | case 1: \ 130 | disp16 = signext(getmem8(segregs[regcs], ip)); \ 131 | StepIP(1); \ 132 | if(((rm == 2) || (rm == 3) || (rm == 6)) && !segoverride) { \ 133 | useseg = segregs[regss]; \ 134 | } \ 135 | break; \ 136 | \ 137 | case 2: \ 138 | disp16 = getmem16(segregs[regcs], ip); \ 139 | StepIP(2); \ 140 | if(((rm == 2) || (rm == 3) || (rm == 6)) && !segoverride) { \ 141 | useseg = segregs[regss]; \ 142 | } \ 143 | break; \ 144 | \ 145 | default: \ 146 | disp8 = 0; \ 147 | disp16 = 0; \ 148 | } \ 149 | } 150 | 151 | // 8259 PIC 152 | typedef struct tag_i8259 { 153 | uint8_t ISR; // In-Service Register 154 | uint8_t IRR; // Int Request Register 155 | uint8_t IMR; // Int Mask Register 156 | int State; // 0 = init, 1-3 = ICWx, 4 = Operating 157 | int ICW4; // Do or don't use ICW4 158 | int ReadReg; // Which reg to read out 159 | uint8_t Base; // Set in ICW2 160 | uint8_t IRQ; // Interrupt Request Lines 161 | uint8_t Last_IRQ; // Last IRQ status 162 | uint8_t NSMR; // Not-Slave Mask Register 163 | uint8_t SFNM; // Special Fully Nested Mode (used with NSMR) 164 | uint8_t Pending_ISR; // ISR was refreshed after EOI, reservice 165 | } i8259; 166 | 167 | // 8253 PIT 168 | typedef struct tag_i8253 { 169 | uint16_t Counter[3]; // Binary counter 170 | double RCounter[3]; // Real Counter 171 | uint8_t Output[3]; // Output line 172 | uint8_t Output_Ticks[3]; // Count since last change 173 | uint16_t IV[3]; // Initial value 174 | uint8_t LoadHW[3]; // Load Halfword 175 | uint8_t Format[3]; // Format of count 176 | uint8_t Mode[3]; // Mode byte 177 | uint8_t Control[3]; // Control byte 178 | uint8_t State[3]; // State 179 | } i8253; 180 | -------------------------------------------------------------------------------- /src/sdu.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | 4 | This file is part of LambdaDelta. 5 | 6 | LambdaDelta is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | LambdaDelta is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with LambdaDelta. If not, see . 18 | */ 19 | 20 | void sdu_init(); 21 | void sdu_clock_pulse(); 22 | #ifdef HAVE_YAML_H 23 | int yaml_sdu_mapping_loop(yaml_parser_t *parser); 24 | #endif 25 | void boot_lambda(int cp,int step); 26 | void dump_lisp_start_state(int I); 27 | 28 | // 8086 processor address 29 | typedef union ri8086_Addr { 30 | uint32_t raw; 31 | uint16_t hw[2]; 32 | uint8_t byte[4]; 33 | struct { 34 | uint16_t Offset; 35 | uint16_t Segment; 36 | } __attribute__((packed)); 37 | } i8086_Addr; 38 | 39 | // Multibus address 40 | typedef union rmbAddr { 41 | uint32_t raw; 42 | uint8_t byte[4]; 43 | struct { 44 | uint32_t Offset:10; 45 | uint32_t Page:10; 46 | } __attribute__((packed)); 47 | } mbAddr; 48 | 49 | // Multibus to Nubus map entry 50 | typedef union rMNAMap_Ent { 51 | uint32_t word; 52 | uint8_t byte[4]; 53 | struct { 54 | uint32_t NUbus_Page:22; 55 | uint8_t Spare:1; 56 | uint8_t Enable:1; 57 | } __attribute__((packed)); 58 | } MNAMap_Ent; 59 | 60 | // interfaces to things 61 | uint8_t multibus_read(mbAddr addr); 62 | uint16_t multibus_word_read(mbAddr addr); 63 | void multibus_write(mbAddr addr,uint8_t data); 64 | void multibus_word_write(mbAddr addr,uint16_t data); 65 | void multibus_interrupt(int irq); 66 | void clear_multibus_interrupt(int irq); 67 | uint8_t i8088_port_read(uint32_t addr); 68 | void i8088_port_write(uint32_t addr,uint8_t data); 69 | uint16_t pic_chk(); 70 | void sducons_rx_int(); 71 | 72 | typedef struct tagsystem_configuration_qs { 73 | uint32_t version_number; 74 | uint32_t size; 75 | uint32_t number_of_processors; 76 | uint32_t processor_block_size; 77 | uint32_t share_struct_pointer; 78 | uint32_t debug_level; 79 | uint32_t lock; 80 | uint32_t ethernet_owner; 81 | uint32_t tapemaster_owner; 82 | uint32_t mti_8_line_owner; 83 | uint32_t mti_16_line_owner; 84 | uint32_t quarter_inch_tape_owner; 85 | uint32_t sdu_serial_a_owner; 86 | uint32_t sdu_serial_b_owner; 87 | uint32_t share_tty[3]; 88 | uint32_t grey_owner; 89 | uint32_t grey_slot; 90 | uint32_t number_of_share_ttys; 91 | uint32_t number_of_share_tty_pages; 92 | uint32_t global_shared_base; 93 | uint32_t global_shared_size; 94 | uint32_t excelan_owner; 95 | uint32_t excelan_2_owner; 96 | uint32_t shared_excelan_pointer; 97 | uint32_t excelan_2_initted; 98 | uint32_t sdu_interrupt_map; 99 | uint32_t tapemaster_base_multibus_map; 100 | uint32_t tapemaster_multibus_map_size; 101 | uint32_t titn_owner; 102 | uint32_t system_console; 103 | uint32_t sdu_nubus_base; 104 | uint32_t sdu_nubus_size; 105 | uint32_t multibus_tapemaster_parameter_block; 106 | uint32_t excelan_base_multibus_map_block; 107 | uint32_t excelan_multibus_map_size; 108 | uint32_t cmos_clock_chip_owner; 109 | uint32_t user_base_multibus_map; 110 | uint32_t user_multibus_map_size; 111 | uint32_t second_grey_owner; 112 | uint32_t second_grey_slot; 113 | uint32_t default_grey_owner; 114 | uint32_t default_second_grey_owner; 115 | uint32_t flavors_bus_link_owner; 116 | uint32_t flavors_bus_link_slot; 117 | uint32_t second_flavors_bus_link_owner; 118 | uint32_t second_flavors_bus_link_slot; 119 | uint32_t newboot_version_number; 120 | uint32_t sdu_rom_version_number; 121 | uint32_t burr_brown_owner; 122 | uint32_t second_burr_brown_owner; 123 | uint32_t interphase_2181_owner; 124 | uint32_t disk_unit_0_initialized; 125 | uint32_t disk_unit_1_initialized; 126 | uint32_t disk_unit_2_initialized; 127 | uint32_t disk_unit_3_initialized; 128 | uint32_t disk_unit_4_initialized; 129 | uint32_t disk_unit_5_initialized; 130 | uint32_t disk_unit_6_initialized; 131 | uint32_t disk_unit_7_initialized; 132 | uint32_t nubus_disk_owner; 133 | uint32_t newboot_idle_count; 134 | uint32_t chaos_sharedev_buffer_size_in_bytes; 135 | uint32_t lmi_debug_board_owner; 136 | uint32_t lmi_debug_board_slot; 137 | uint32_t second_lmi_debug_board_owner; 138 | uint32_t second_lmi_debug_board_slot; 139 | } __attribute__((packed)) system_configuration_qs; 140 | 141 | typedef struct tagprocessor_configuration_qs { 142 | uint32_t sys_conf_ptr; 143 | uint32_t slot_number; 144 | uint32_t major_version; 145 | uint32_t minor_version; 146 | uint32_t starting_processor_switches; 147 | uint32_t share_runme; 148 | uint32_t share_slot; 149 | uint32_t share_type; 150 | uint32_t share_iopb; 151 | uint32_t share_interrupt_addr; 152 | uint32_t share_spare_1; 153 | uint32_t share_spare_2; 154 | uint32_t share_spare_3; 155 | uint32_t share_spare_4; 156 | uint32_t chaos_address; 157 | uint32_t send_chaos_share_dev; 158 | uint32_t rcv_chaos_share_dev; 159 | uint32_t memory_base[10]; 160 | uint32_t memory_bytes[10]; 161 | uint32_t vcmem_slot; 162 | uint32_t processor_type; 163 | uint32_t micro_band; 164 | uint32_t load_band; 165 | uint32_t paging_band; 166 | uint32_t file_band; 167 | uint32_t base_multibus_mapping_register; 168 | uint32_t boot_status; 169 | uint32_t chaos_share[5]; 170 | uint32_t parity_enables; 171 | uint32_t vcmem_words_per_line; 172 | uint32_t number_of_multibus_maps; 173 | uint32_t boot_command; 174 | uint32_t boot_mode; 175 | uint32_t console; 176 | uint32_t console_baud_rate; 177 | uint32_t watchdog; 178 | uint32_t intmap_multibus_map; 179 | uint32_t n_aux_devs; 180 | uint32_t aux_dev[2]; 181 | uint32_t excelan_multibus_map_base; 182 | uint32_t excelan_multibus_map_size; 183 | } __attribute__((packed)) processor_configuration_qs; 184 | 185 | #define MAX_SHARE_IOPB 8 186 | 187 | // share structure 188 | typedef struct tagshare_struct_qs { 189 | uint32_t lock; 190 | uint32_t max_iopbs; 191 | uint32_t current_iopb; 192 | // IOPB table here 193 | uint32_t valid_siopb[MAX_SHARE_IOPB]; 194 | uint32_t siopb_ptr[MAX_SHARE_IOPB]; 195 | } __attribute__((packed)) share_struct_qs; 196 | -------------------------------------------------------------------------------- /src/syms.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | Barry Silverman 4 | Greg Gilley 5 | 6 | This file is part of LambdaDelta. 7 | 8 | LambdaDelta is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | LambdaDelta is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with LambdaDelta. If not, see . 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | struct sym_s { 28 | struct sym_s *next; 29 | char *name; 30 | unsigned int v; 31 | int mtype; 32 | }; 33 | 34 | struct symtab_s { 35 | char *name; 36 | struct sym_s *syms; 37 | int sym_count; 38 | struct sym_s **sorted_syms; 39 | }; 40 | 41 | 42 | struct symtab_s sym_prom; 43 | struct symtab_s sym_mcr; 44 | 45 | 46 | static int 47 | _sym_add(struct symtab_s *tab, int memory, char *name, int v) 48 | { 49 | struct sym_s *s; 50 | 51 | if (0) printf("%d %s %o\n", memory, name, v); 52 | 53 | s = (struct sym_s *)malloc(sizeof(struct sym_s)); 54 | if (s) { 55 | tab->sym_count++; 56 | 57 | s->name = strdup(name); 58 | s->v = v; 59 | s->mtype = memory; 60 | 61 | s->next = tab->syms; 62 | tab->syms = s; 63 | } 64 | 65 | return 0; 66 | } 67 | 68 | static char * 69 | _sym_find_by_val(struct symtab_s *tab, int memory, unsigned int v) 70 | { 71 | struct sym_s *s; 72 | 73 | for (s = tab->syms; s; s = s->next) { 74 | if (s->v == v && s->mtype == memory) 75 | return s->name; 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | 82 | char * 83 | _sym_find_last(struct symtab_s *tab, int memory, unsigned int v, int *poffset) 84 | { 85 | int i; 86 | struct sym_s *s; 87 | 88 | if (tab->sorted_syms == 0) 89 | return 0; 90 | 91 | for (i = 0; i < tab->sym_count; i++) { 92 | 93 | s = tab->sorted_syms[i]; 94 | 95 | if (s->mtype != memory) 96 | continue; 97 | 98 | if (v == s->v) { 99 | *poffset = 0; 100 | return s->name; 101 | } 102 | 103 | if (v < s->v || i == tab->sym_count-1) { 104 | while (tab->sorted_syms[i-1]->mtype != memory) 105 | i--; 106 | 107 | s = tab->sorted_syms[i-1]; 108 | *poffset = v - s->v; 109 | return s->name; 110 | } 111 | } 112 | 113 | return 0; 114 | } 115 | 116 | int 117 | _sym_find(struct symtab_s *tab, char *name, int *pval) 118 | { 119 | struct sym_s *s; 120 | 121 | for (s = tab->syms; s; s = s->next) { 122 | if (0) printf("%s %s\n", name, s->name); 123 | if (strcasecmp(name, s->name) == 0) { 124 | *pval = s->v; 125 | return 0; 126 | } 127 | } 128 | 129 | return -1; 130 | } 131 | 132 | /* 133 | * read a cadr mcr symbol file 134 | */ 135 | int 136 | _sym_read_file(struct symtab_s *tab, const char *filename) 137 | { 138 | int first = 1; 139 | FILE *f; 140 | char line[8*1024]; 141 | char linebuf[8*1024]; 142 | 143 | if (0) printf("tab %p, filename %s\n", tab, filename); 144 | 145 | f = fopen(filename, "r"); 146 | if (f == NULL) 147 | return -1; 148 | 149 | tab->name = strdup(filename); 150 | 151 | while (fgets(line, sizeof(line), f) != NULL) { 152 | char sym[64], symtype[64]; 153 | int loc, n; 154 | char *rv; 155 | 156 | if (first) { 157 | if (line[0] == 'I'){ 158 | // Added all this rv stuff to make gcc happy, but we really don't care if it fails. 159 | while(line[0] != '-'){ 160 | rv = fgets(line, sizeof(line), f); 161 | if(rv == NULL){ 162 | // Error 163 | } 164 | } 165 | first = 0; 166 | rv = fgets(line, sizeof(line), f); 167 | if(rv == NULL){ 168 | // Error 169 | } 170 | } else { 171 | rv = fgets(line, sizeof(line), f); 172 | if(rv == NULL){ } 173 | rv = fgets(line, sizeof(line), f); 174 | if(rv == NULL){ } 175 | rv = fgets(line, sizeof(line), f); 176 | if(rv == NULL){ } 177 | rv = fgets(line, sizeof(line), f); 178 | if(rv == NULL){ } 179 | // OS X will force an abort if the source and destination overlap, so a buffer must be used. 180 | strcpy(linebuf, &line[3]); 181 | strcpy(line, linebuf); 182 | first = 0; 183 | } 184 | } 185 | 186 | if (0) printf("'%s'\n", line); 187 | 188 | n = sscanf(line, "%s %s %o", sym, symtype, &loc); 189 | if (n == 3) { 190 | n = 0; 191 | if (strcmp(symtype, "I-MEM") == 0) n = 1; 192 | if (strcmp(symtype, "D-MEM") == 0) n = 2; 193 | if (strcmp(symtype, "A-MEM") == 0) n = 4; 194 | if (strcmp(symtype, "M-MEM") == 0) n = 5; 195 | if (strcmp(symtype, "NUMBER") == 0) n = 6; 196 | 197 | if (n == 0) printf("? %s", symtype); 198 | 199 | _sym_add(tab, n, sym, loc); 200 | } 201 | } 202 | 203 | fclose(f); 204 | return 0; 205 | } 206 | 207 | static int 208 | _sym_loc_compare(const void *p1, const void *p2) 209 | { 210 | struct sym_s *s1 = *(struct sym_s **)p1; 211 | struct sym_s *s2 = *(struct sym_s **)p2; 212 | 213 | if (s1->v < s2->v) 214 | return -1; 215 | 216 | if (s1->v > s2->v) 217 | return 1; 218 | 219 | return 0; 220 | } 221 | 222 | int 223 | _sym_sort(struct symtab_s *tab) 224 | { 225 | struct sym_s *s; 226 | int i; 227 | 228 | /* make vector of ptrs to syms */ 229 | tab->sorted_syms = (struct sym_s **)malloc(sizeof(struct sym_s *) * tab->sym_count); 230 | if (tab->sorted_syms == 0) 231 | return -1; 232 | 233 | /* fill in vector */ 234 | i = 0; 235 | for (s = tab->syms; s; s = s->next) { 236 | tab->sorted_syms[i++] = s; 237 | } 238 | 239 | printf("[%s] sort %d symbols (originally %d)\n", tab->name, i, tab->sym_count); 240 | 241 | /* sort the vector */ 242 | qsort((void *)tab->sorted_syms, tab->sym_count, sizeof(void *), _sym_loc_compare); 243 | 244 | #if 0 245 | for (i = 0; i < tab->sym_count; i++) { 246 | printf("%s %o\n", tab->sorted_syms[i]->name, tab->sorted_syms[i]->v); 247 | } 248 | #endif 249 | 250 | return 0; 251 | } 252 | 253 | int 254 | read_sym_files(void) 255 | { 256 | _sym_read_file(&sym_mcr, "bootstrap.lam-uload"); 257 | _sym_read_file(&sym_mcr, "ulambda.lmc-sym"); 258 | _sym_sort(&sym_mcr); 259 | 260 | return 0; 261 | } 262 | 263 | 264 | /* ------------------------------------------------------------- */ 265 | 266 | int 267 | sym_find(int mcr, char *name, int *pval) 268 | { 269 | if (mcr) 270 | return _sym_find(&sym_mcr, name, pval); 271 | 272 | return _sym_find(&sym_prom, name, pval); 273 | } 274 | 275 | char * 276 | sym_find_by_val(int mcr, int t, int v) 277 | { 278 | if (mcr) 279 | return _sym_find_by_val(&sym_mcr, t, v); 280 | 281 | return _sym_find_by_val(&sym_prom, t, v); 282 | } 283 | 284 | char * 285 | sym_find_by_type_val(int mcr, int t, int v) 286 | { 287 | if (mcr) 288 | return _sym_find_by_val(&sym_mcr, t, v); 289 | 290 | return _sym_find_by_val(&sym_prom, t, v); 291 | } 292 | 293 | char * 294 | sym_find_last(int mcr, int v, int *poffset) 295 | { 296 | if (mcr) 297 | return _sym_find_last(&sym_mcr, 1 /* I-MEM */, v, poffset); 298 | 299 | return _sym_find_last(&sym_prom, 1 /* I-MEM */, v, poffset); 300 | } 301 | 302 | 303 | -------------------------------------------------------------------------------- /m4/sdl.m4: -------------------------------------------------------------------------------- 1 | # Configure paths for SDL 2 | # Sam Lantinga 9/21/99 3 | # stolen from Manish Singh 4 | # stolen back from Frank Belew 5 | # stolen from Manish Singh 6 | # Shamelessly stolen from Owen Taylor 7 | 8 | # serial 1 9 | 10 | dnl AM_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) 11 | dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS 12 | dnl 13 | AC_DEFUN([AM_PATH_SDL], 14 | [dnl 15 | dnl Get the cflags and libraries from the sdl-config script 16 | dnl 17 | AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], 18 | sdl_prefix="$withval", sdl_prefix="") 19 | AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], 20 | sdl_exec_prefix="$withval", sdl_exec_prefix="") 21 | AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], 22 | , enable_sdltest=yes) 23 | 24 | if test x$sdl_exec_prefix != x ; then 25 | sdl_config_args="$sdl_config_args --exec-prefix=$sdl_exec_prefix" 26 | if test x${SDL_CONFIG+set} != xset ; then 27 | SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config 28 | fi 29 | fi 30 | if test x$sdl_prefix != x ; then 31 | sdl_config_args="$sdl_config_args --prefix=$sdl_prefix" 32 | if test x${SDL_CONFIG+set} != xset ; then 33 | SDL_CONFIG=$sdl_prefix/bin/sdl-config 34 | fi 35 | fi 36 | 37 | as_save_PATH="$PATH" 38 | if test "x$prefix" != xNONE && test "$cross_compiling" != yes; then 39 | PATH="$prefix/bin:$prefix/usr/bin:$PATH" 40 | fi 41 | AC_PATH_PROG(SDL_CONFIG, sdl-config, no, [$PATH]) 42 | PATH="$as_save_PATH" 43 | min_sdl_version=ifelse([$1], ,0.11.0,$1) 44 | AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) 45 | no_sdl="" 46 | if test "$SDL_CONFIG" = "no" ; then 47 | no_sdl=yes 48 | else 49 | SDL_CFLAGS=`$SDL_CONFIG $sdl_config_args --cflags` 50 | SDL_LIBS=`$SDL_CONFIG $sdl_config_args --libs` 51 | 52 | sdl_major_version=`$SDL_CONFIG $sdl_config_args --version | \ 53 | sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` 54 | sdl_minor_version=`$SDL_CONFIG $sdl_config_args --version | \ 55 | sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` 56 | sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \ 57 | sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` 58 | if test "x$enable_sdltest" = "xyes" ; then 59 | ac_save_CFLAGS="$CFLAGS" 60 | ac_save_CXXFLAGS="$CXXFLAGS" 61 | ac_save_LIBS="$LIBS" 62 | CFLAGS="$CFLAGS $SDL_CFLAGS" 63 | CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" 64 | LIBS="$LIBS $SDL_LIBS" 65 | dnl 66 | dnl Now check if the installed SDL is sufficiently new. (Also sanity 67 | dnl checks the results of sdl-config to some extent 68 | dnl 69 | rm -f conf.sdltest 70 | AC_TRY_RUN([ 71 | #include 72 | #include 73 | #include 74 | #include "SDL.h" 75 | 76 | char* 77 | my_strdup (char *str) 78 | { 79 | char *new_str; 80 | 81 | if (str) 82 | { 83 | new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char)); 84 | strcpy (new_str, str); 85 | } 86 | else 87 | new_str = NULL; 88 | 89 | return new_str; 90 | } 91 | 92 | int main (int argc, char *argv[]) 93 | { 94 | int major, minor, micro; 95 | char *tmp_version; 96 | 97 | /* This hangs on some systems (?) 98 | system ("touch conf.sdltest"); 99 | */ 100 | { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); } 101 | 102 | /* HP/UX 9 (%@#!) writes to sscanf strings */ 103 | tmp_version = my_strdup("$min_sdl_version"); 104 | if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { 105 | printf("%s, bad version string\n", "$min_sdl_version"); 106 | exit(1); 107 | } 108 | 109 | if (($sdl_major_version > major) || 110 | (($sdl_major_version == major) && ($sdl_minor_version > minor)) || 111 | (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) 112 | { 113 | return 0; 114 | } 115 | else 116 | { 117 | printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); 118 | printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro); 119 | printf("*** best to upgrade to the required version.\n"); 120 | printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n"); 121 | printf("*** to point to the correct copy of sdl-config, and remove the file\n"); 122 | printf("*** config.cache before re-running configure\n"); 123 | return 1; 124 | } 125 | } 126 | 127 | ],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) 128 | CFLAGS="$ac_save_CFLAGS" 129 | CXXFLAGS="$ac_save_CXXFLAGS" 130 | LIBS="$ac_save_LIBS" 131 | fi 132 | fi 133 | if test "x$no_sdl" = x ; then 134 | AC_MSG_RESULT(yes) 135 | ifelse([$2], , :, [$2]) 136 | else 137 | AC_MSG_RESULT(no) 138 | if test "$SDL_CONFIG" = "no" ; then 139 | echo "*** The sdl-config script installed by SDL could not be found" 140 | echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" 141 | echo "*** your path, or set the SDL_CONFIG environment variable to the" 142 | echo "*** full path to sdl-config." 143 | else 144 | if test -f conf.sdltest ; then 145 | : 146 | else 147 | echo "*** Could not run SDL test program, checking why..." 148 | CFLAGS="$CFLAGS $SDL_CFLAGS" 149 | CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" 150 | LIBS="$LIBS $SDL_LIBS" 151 | AC_TRY_LINK([ 152 | #include 153 | #include "SDL.h" 154 | 155 | int main(int argc, char *argv[]) 156 | { return 0; } 157 | #undef main 158 | #define main K_and_R_C_main 159 | ], [ return 0; ], 160 | [ echo "*** The test program compiled, but did not run. This usually means" 161 | echo "*** that the run-time linker is not finding SDL or finding the wrong" 162 | echo "*** version of SDL. If it is not finding SDL, you'll need to set your" 163 | echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" 164 | echo "*** to the installed location Also, make sure you have run ldconfig if that" 165 | echo "*** is required on your system" 166 | echo "***" 167 | echo "*** If you have an old version installed, it is best to remove it, although" 168 | echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], 169 | [ echo "*** The test program failed to compile or link. See the file config.log for the" 170 | echo "*** exact error that occured. This usually means SDL was incorrectly installed" 171 | echo "*** or that you have moved SDL since it was installed. In the latter case, you" 172 | echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ]) 173 | CFLAGS="$ac_save_CFLAGS" 174 | CXXFLAGS="$ac_save_CXXFLAGS" 175 | LIBS="$ac_save_LIBS" 176 | fi 177 | fi 178 | SDL_CFLAGS="" 179 | SDL_LIBS="" 180 | ifelse([$3], , :, [$3]) 181 | fi 182 | AC_SUBST(SDL_CFLAGS) 183 | AC_SUBST(SDL_LIBS) 184 | rm -f conf.sdltest 185 | ]) 186 | -------------------------------------------------------------------------------- /m4/sdl2.m4: -------------------------------------------------------------------------------- 1 | # Configure paths for SDL 2 | # Sam Lantinga 9/21/99 3 | # stolen from Manish Singh 4 | # stolen back from Frank Belew 5 | # stolen from Manish Singh 6 | # Shamelessly stolen from Owen Taylor 7 | # 8 | # Changelog: 9 | # * also look for SDL2.framework under Mac OS X 10 | 11 | # serial 1 12 | 13 | dnl AM_PATH_SDL2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) 14 | dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS 15 | dnl 16 | AC_DEFUN([AM_PATH_SDL2], 17 | [dnl 18 | dnl Get the cflags and libraries from the sdl2-config script 19 | dnl 20 | AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], 21 | sdl_prefix="$withval", sdl_prefix="") 22 | AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], 23 | sdl_exec_prefix="$withval", sdl_exec_prefix="") 24 | AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], 25 | , enable_sdltest=yes) 26 | AC_ARG_ENABLE(sdlframework, [ --disable-sdlframework Do not search for SDL2.framework], 27 | , search_sdl_framework=yes) 28 | 29 | AC_ARG_VAR(SDL2_FRAMEWORK, [Path to SDL2.framework]) 30 | 31 | min_sdl_version=ifelse([$1], ,2.0.0,$1) 32 | 33 | if test "x$sdl_prefix$sdl_exec_prefix" = x ; then 34 | PKG_CHECK_MODULES([SDL], [sdl2 >= $min_sdl_version], 35 | [sdl_pc=yes], 36 | [sdl_pc=no]) 37 | else 38 | sdl_pc=no 39 | if test x$sdl_exec_prefix != x ; then 40 | sdl_config_args="$sdl_config_args --exec-prefix=$sdl_exec_prefix" 41 | if test x${SDL2_CONFIG+set} != xset ; then 42 | SDL2_CONFIG=$sdl_exec_prefix/bin/sdl2-config 43 | fi 44 | fi 45 | if test x$sdl_prefix != x ; then 46 | sdl_config_args="$sdl_config_args --prefix=$sdl_prefix" 47 | if test x${SDL2_CONFIG+set} != xset ; then 48 | SDL2_CONFIG=$sdl_prefix/bin/sdl2-config 49 | fi 50 | fi 51 | fi 52 | 53 | if test "x$sdl_pc" = xyes ; then 54 | no_sdl="" 55 | SDL2_CONFIG="pkg-config sdl2" 56 | else 57 | as_save_PATH="$PATH" 58 | if test "x$prefix" != xNONE && test "$cross_compiling" != yes; then 59 | PATH="$prefix/bin:$prefix/usr/bin:$PATH" 60 | fi 61 | AC_PATH_PROG(SDL2_CONFIG, sdl2-config, no, [$PATH]) 62 | PATH="$as_save_PATH" 63 | no_sdl="" 64 | 65 | if test "$SDL2_CONFIG" = "no" -a "x$search_sdl_framework" = "xyes"; then 66 | AC_MSG_CHECKING(for SDL2.framework) 67 | if test "x$SDL2_FRAMEWORK" != x; then 68 | sdl_framework=$SDL2_FRAMEWORK 69 | else 70 | for d in / ~/ /System/; do 71 | if test -d "$dLibrary/Frameworks/SDL2.framework"; then 72 | sdl_framework="$dLibrary/Frameworks/SDL2.framework" 73 | fi 74 | done 75 | fi 76 | 77 | if test x"$sdl_framework" != x && test -d "$sdl_framework"; then 78 | AC_MSG_RESULT($sdl_framework) 79 | sdl_framework_dir=`dirname $sdl_framework` 80 | SDL_CFLAGS="-F$sdl_framework_dir -Wl,-framework,SDL2 -I$sdl_framework/include" 81 | SDL_LIBS="-F$sdl_framework_dir -Wl,-framework,SDL2" 82 | else 83 | no_sdl=yes 84 | fi 85 | fi 86 | 87 | if test "$SDL2_CONFIG" != "no"; then 88 | if test "x$sdl_pc" = "xno"; then 89 | AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) 90 | SDL_CFLAGS=`$SDL2_CONFIG $sdl_config_args --cflags` 91 | SDL_LIBS=`$SDL2_CONFIG $sdl_config_args --libs` 92 | fi 93 | 94 | sdl_major_version=`$SDL2_CONFIG $sdl_config_args --version | \ 95 | sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` 96 | sdl_minor_version=`$SDL2_CONFIG $sdl_config_args --version | \ 97 | sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` 98 | sdl_micro_version=`$SDL2_CONFIG $sdl_config_args --version | \ 99 | sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` 100 | if test "x$enable_sdltest" = "xyes" ; then 101 | ac_save_CFLAGS="$CFLAGS" 102 | ac_save_CXXFLAGS="$CXXFLAGS" 103 | ac_save_LIBS="$LIBS" 104 | CFLAGS="$CFLAGS $SDL_CFLAGS" 105 | CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" 106 | LIBS="$LIBS $SDL_LIBS" 107 | dnl 108 | dnl Now check if the installed SDL is sufficiently new. (Also sanity 109 | dnl checks the results of sdl2-config to some extent 110 | dnl 111 | rm -f conf.sdltest 112 | AC_TRY_RUN([ 113 | #include 114 | #include 115 | #include 116 | #include "SDL.h" 117 | 118 | char* 119 | my_strdup (char *str) 120 | { 121 | char *new_str; 122 | 123 | if (str) 124 | { 125 | new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char)); 126 | strcpy (new_str, str); 127 | } 128 | else 129 | new_str = NULL; 130 | 131 | return new_str; 132 | } 133 | 134 | int main (int argc, char *argv[]) 135 | { 136 | int major, minor, micro; 137 | char *tmp_version; 138 | 139 | /* This hangs on some systems (?) 140 | system ("touch conf.sdltest"); 141 | */ 142 | { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); } 143 | 144 | /* HP/UX 9 (%@#!) writes to sscanf strings */ 145 | tmp_version = my_strdup("$min_sdl_version"); 146 | if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { 147 | printf("%s, bad version string\n", "$min_sdl_version"); 148 | exit(1); 149 | } 150 | 151 | if (($sdl_major_version > major) || 152 | (($sdl_major_version == major) && ($sdl_minor_version > minor)) || 153 | (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) 154 | { 155 | return 0; 156 | } 157 | else 158 | { 159 | printf("\n*** 'sdl2-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); 160 | printf("*** of SDL required is %d.%d.%d. If sdl2-config is correct, then it is\n", major, minor, micro); 161 | printf("*** best to upgrade to the required version.\n"); 162 | printf("*** If sdl2-config was wrong, set the environment variable SDL2_CONFIG\n"); 163 | printf("*** to point to the correct copy of sdl2-config, and remove the file\n"); 164 | printf("*** config.cache before re-running configure\n"); 165 | return 1; 166 | } 167 | } 168 | 169 | ],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) 170 | CFLAGS="$ac_save_CFLAGS" 171 | CXXFLAGS="$ac_save_CXXFLAGS" 172 | LIBS="$ac_save_LIBS" 173 | 174 | fi 175 | if test "x$sdl_pc" = "xno"; then 176 | if test "x$no_sdl" = "xyes"; then 177 | AC_MSG_RESULT(no) 178 | else 179 | AC_MSG_RESULT(yes) 180 | fi 181 | fi 182 | fi 183 | fi 184 | if test "x$no_sdl" = x ; then 185 | ifelse([$2], , :, [$2]) 186 | else 187 | if test "$SDL2_CONFIG" = "no" ; then 188 | echo "*** The sdl2-config script installed by SDL could not be found" 189 | echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" 190 | echo "*** your path, or set the SDL2_CONFIG environment variable to the" 191 | echo "*** full path to sdl2-config." 192 | else 193 | if test -f conf.sdltest ; then 194 | : 195 | else 196 | echo "*** Could not run SDL test program, checking why..." 197 | CFLAGS="$CFLAGS $SDL_CFLAGS" 198 | CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" 199 | LIBS="$LIBS $SDL_LIBS" 200 | AC_TRY_LINK([ 201 | #include 202 | #include "SDL.h" 203 | 204 | int main(int argc, char *argv[]) 205 | { return 0; } 206 | #undef main 207 | #define main K_and_R_C_main 208 | ], [ return 0; ], 209 | [ echo "*** The test program compiled, but did not run. This usually means" 210 | echo "*** that the run-time linker is not finding SDL or finding the wrong" 211 | echo "*** version of SDL. If it is not finding SDL, you'll need to set your" 212 | echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" 213 | echo "*** to the installed location Also, make sure you have run ldconfig if that" 214 | echo "*** is required on your system" 215 | echo "***" 216 | echo "*** If you have an old version installed, it is best to remove it, although" 217 | echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], 218 | [ echo "*** The test program failed to compile or link. See the file config.log for the" 219 | echo "*** exact error that occured. This usually means SDL was incorrectly installed" 220 | echo "*** or that you have moved SDL since it was installed. In the latter case, you" 221 | echo "*** may want to edit the sdl2-config script: $SDL2_CONFIG" ]) 222 | CFLAGS="$ac_save_CFLAGS" 223 | CXXFLAGS="$ac_save_CXXFLAGS" 224 | LIBS="$ac_save_LIBS" 225 | fi 226 | fi 227 | SDL_CFLAGS="" 228 | SDL_LIBS="" 229 | ifelse([$3], , :, [$3]) 230 | fi 231 | AC_SUBST(SDL_CFLAGS) 232 | AC_SUBST(SDL_LIBS) 233 | rm -f conf.sdltest 234 | ]) 235 | -------------------------------------------------------------------------------- /src/ld.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | Barry Silverman 4 | 5 | This file is part of LambdaDelta. 6 | 7 | LambdaDelta is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | LambdaDelta is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with LambdaDelta. If not, see . 19 | */ 20 | 21 | /* LambdaDelta Global Header */ 22 | 23 | // Kernel items 24 | void lambda_dump(int opts); 25 | #ifdef BURR_BROWN 26 | void debug_tx_rq(uint8_t rq,uint32_t addr,uint32_t data); 27 | void debug_connect(); 28 | void debug_clockpulse(); 29 | #endif 30 | void nubus_cycle(int sdu); 31 | void sducons_write(char data); 32 | 33 | // Host framebuffer interface 34 | void framebuffer_update_word(int vn,uint32_t addr,uint32_t data); 35 | void framebuffer_update_hword(int vn,uint32_t addr,uint16_t data); 36 | void framebuffer_update_byte(int vn,uint32_t addr,uint8_t data); 37 | void set_bow_mode(int vn,int mode); 38 | 39 | #ifdef SDL2 40 | // xbeep 41 | void xbeep(int halfwavelength, int duration); 42 | #else 43 | // Old hack 44 | // beep on/off (called when vcmem keyboard control register 5 toggles "send break" bit. 45 | void audio_control(int onoff); 46 | #endif 47 | 48 | // Host ethernet interface 49 | void ether_tx_pkt(uint8_t *data,uint32_t len); 50 | uint32_t ether_rx_pkt(); 51 | 52 | // Mouse interface callback 53 | void warp_mouse_callback(int cp); 54 | 55 | // Logging stuff 56 | int logmsgf(int type, int level, const char *format, ...); 57 | 58 | // Type numbers 59 | // Make sure these stay in sync with the array logtype_name in kernel.c 60 | // I can't initialize that here because gcc whines about it (sigh) 61 | #define LT_SYSTEM 0 62 | #define LT_SDU 1 63 | #define LT_LAMBDA 2 64 | #define LT_NUBUS 3 65 | #define LT_MULTIBUS 4 66 | #define LT_RTC 5 67 | #define LT_VCMEM 6 68 | #define LT_3COM 7 69 | #define LT_SMD 8 70 | #define LT_TAPEMASTER 9 71 | #define LT_MEM 10 72 | #define LT_LISP 11 73 | // If you let MAX_LOGTYPE get above the actual number of initialized types, you're gonna have a bad time 74 | #define MAX_LOGTYPE 12 75 | 76 | // Dump options bits 77 | #define DUMP_A_MEM 0x00000001 78 | #define DUMP_M_MEM 0x00000002 79 | #define DUMP_U_STACK 0x00000004 80 | #define DUMP_PDL 0x00000008 81 | #define DUMP_P_MEM 0x00000010 82 | #define DUMP_MID_MEM 0x00000020 83 | #define DUMP_SHADOW_MEM 0x00000040 84 | #define DUMP_T_MEM 0x00000080 85 | #define DUMP_NO_INC_SEQ 0x40000000 86 | #define DUMP_ALL 0x3FFFFFFF 87 | 88 | /* Macroinstruction */ 89 | typedef union rMI { 90 | uint16_t raw; 91 | // MISC/MISC1-OP 92 | // This was mine 93 | struct { 94 | uint8_t Bits:6; // ? 95 | uint16_t Opcode:10; // Opcode 96 | } __attribute__((packed)) Misc; 97 | // Fields from ulambda 98 | struct { 99 | uint16_t Adr:9; 100 | uint8_t Opcode:5; 101 | uint8_t Dest:2; 102 | } __attribute__((packed)); 103 | struct { 104 | uint8_t Displacement:6; 105 | uint8_t Register:3; 106 | uint8_t Padding:4; 107 | uint8_t Sub_Opcode:3; 108 | } __attribute__((packed)); 109 | } MI; 110 | 111 | /* Here is a Q */ 112 | typedef union rQ { 113 | uint32_t raw; 114 | // Q 115 | struct { 116 | uint32_t ptr:25; 117 | uint8_t dtp:5; 118 | uint8_t cdr:2; 119 | } __attribute__((packed)); 120 | // (25-bit) virtual memory address 121 | struct { 122 | uint8_t Offset:8; 123 | uint8_t VPage_Offset:5; 124 | uint16_t VPage_Block:12; 125 | } __attribute__((packed)) VM; 126 | // Shadow memory indexing 127 | struct { 128 | uint8_t Offset:8; 129 | uint32_t VPage:17; 130 | } __attribute__((packed)) SM; 131 | // For easier parsing of macroinstructions 132 | struct { 133 | MI mi[2]; 134 | } __attribute__((packed)); 135 | } Q; 136 | 137 | /* macroinstruction dispatch memory addressing */ 138 | typedef union rMIDAddress { 139 | uint16_t raw; 140 | struct { 141 | uint16_t Opcode:10; 142 | uint8_t Hi:2; 143 | } __attribute__((packed)); 144 | } MIDAddress; 145 | 146 | /* Dispatch word */ 147 | typedef union rDispatchWord { 148 | uint32_t raw; 149 | struct { 150 | uint32_t PC:16; 151 | uint8_t Operation:3; // R-P-N 152 | uint8_t StartRead:1; 153 | } __attribute__((packed)); 154 | } DispatchWord; 155 | 156 | /* Memory map entries */ 157 | /* Level 1 */ 158 | typedef union rlv1_ent { 159 | uint32_t raw; 160 | struct { 161 | uint8_t LV2_Block:7; 162 | uint8_t MB:2; // MB0, MB1 163 | uint8_t MB_Valid:1; 164 | } __attribute__((packed)); 165 | } lv1_ent; 166 | 167 | /* Level 2 Index */ 168 | typedef union rlv2_idx { 169 | uint16_t raw; 170 | struct { 171 | uint8_t VPage_Offset:5; 172 | uint8_t LV2_Block:7; 173 | } __attribute__((packed)); 174 | } lv2_idx; 175 | 176 | /* Level 2 Control */ 177 | typedef union rlv2_ctl_ent { 178 | uint32_t raw; 179 | struct { 180 | uint8_t Meta:6; 181 | uint8_t Status:3; 182 | uint8_t AccHi:1; // Hi bit of access (it overlaps, see next struct) 183 | uint8_t Force_Allowed:1; 184 | uint8_t Packet_Code:2; // Byte code? ("packet size") 185 | uint8_t Packetize_Writes:1; // ??? 186 | uint8_t Cache_Permit:1; 187 | uint8_t Lock_NUbus:1; 188 | uint32_t Unused:16; // Stop if written 189 | } __attribute__((packed)); 190 | struct { 191 | uint8_t Padding:8; 192 | uint8_t Access:2; 193 | } __attribute__((packed)); 194 | } lv2_ctl_ent; 195 | 196 | /* Level 2 Address */ 197 | typedef union rlv2_adr_ent { 198 | uint32_t raw; 199 | struct { 200 | uint32_t PPN:22; 201 | uint8_t Byte_Code:2; 202 | } __attribute__((packed)); 203 | } lv2_adr_ent; 204 | 205 | /* Physical Address (the result of the above mapping) */ 206 | typedef union rPhysAddr { 207 | uint32_t raw; 208 | struct { 209 | uint8_t Byte:2; 210 | uint8_t Offset:8; 211 | uint32_t PPN:22; 212 | } __attribute__((packed)); 213 | } PhysAddr; 214 | 215 | /* Destination selector fields (ALU/BYTE instructions) */ 216 | /* If A.Flag is set, this is an A-Memory address. 217 | If A.Flag is clear and F.Dest is set, this is a functional destination. 218 | if A.Flag is clear and F.Dest is clear, this is an A+M-Memory address. 219 | What happens if A.Flag is clear, F.Dest is set, and M.Addr is also set? Write both? 220 | */ 221 | typedef union rDestSelect { 222 | uint32_t raw; 223 | struct A { 224 | uint16_t Padding:14; 225 | uint16_t Addr:12; 226 | uint8_t Flag:1; 227 | } __attribute__((packed)) A; 228 | struct M { 229 | uint16_t Padding:14; 230 | uint8_t Addr:6; 231 | } __attribute__((packed)) M; 232 | struct F { 233 | uint16_t Padding:14; 234 | uint8_t Spare:6; 235 | uint8_t Dest:6; 236 | } __attribute__((packed)) F; 237 | } DestSelect; 238 | 239 | /* Microaddress */ 240 | typedef union rMicroAddr { 241 | uint32_t raw; 242 | struct { 243 | uint8_t Offset:4; 244 | uint16_t Page:12; 245 | } __attribute__((packed)); 246 | } MicroAddr; 247 | 248 | /* Left half of jump microinstruction */ 249 | typedef union rJumpInst { 250 | uint64_t raw; 251 | struct { 252 | uint8_t Cond:5; 253 | uint8_t Spare2:1; 254 | uint8_t Test:1; 255 | uint8_t Invert:1; 256 | uint8_t RPN:3; 257 | uint8_t LC_Increment:1; 258 | uint8_t Spare:2; 259 | uint32_t Address:17; 260 | } __attribute__((packed)); 261 | } JumpUInst; 262 | 263 | /* Left half of ALU microinstruction */ 264 | typedef union rALUInst { 265 | uint64_t raw; 266 | struct { 267 | uint8_t QControl:2; 268 | uint8_t Carry:1; 269 | uint8_t Operation:5; 270 | uint8_t Mask:1; 271 | uint8_t Output:3; 272 | uint8_t Misc:2; 273 | uint16_t Dest:13; 274 | uint8_t Spare:3; // Spare can be overwritten by Dest when Dest contains a CRAM address. 275 | } __attribute__((packed)); 276 | } ALUInst; 277 | 278 | /* Left half of Byte microinstruction */ 279 | typedef union rByteUInst { 280 | uint64_t raw; 281 | struct { 282 | uint8_t Pos:6; 283 | uint8_t Len:6; // Actual length is this + 1 284 | uint8_t Misc:2; 285 | uint16_t Dest:13; 286 | uint8_t Spare:1; 287 | uint8_t Rotate_Source:1; 288 | uint8_t Rotate_Mask:1; 289 | } __attribute__((packed)); 290 | } ByteUInst; 291 | 292 | typedef union rDispatchUInst { 293 | uint64_t raw; 294 | struct { 295 | uint8_t Pos:6; 296 | uint8_t Len:5; 297 | uint8_t Spare:3; 298 | uint16_t Constant:12; 299 | uint8_t LPC:1; 300 | uint8_t Write_VMA:1; 301 | uint8_t Enable_GC_Volatility_Meta:1; 302 | uint8_t Enable_Oldspace_Meta:1; 303 | } __attribute__((packed)); 304 | } DispatchUInst; 305 | 306 | /* Here is a Lambda microinstruction */ 307 | typedef union rUInst { 308 | uint64_t raw; 309 | uint32_t word[2]; 310 | uint8_t byte[8]; 311 | // Left Halves 312 | JumpUInst Jump; 313 | ALUInst ALU; 314 | ByteUInst Byte; 315 | DispatchUInst Dispatch; 316 | // ALU/Byte destination select 317 | DestSelect Destination; 318 | // Global (right half) 319 | struct { 320 | uint32_t rtHalf:30; 321 | uint8_t Opcode:2; 322 | uint8_t MSource:7; 323 | uint16_t ASource:12; 324 | uint8_t PopJ_After_Next:1; 325 | uint8_t Macro_IR_Disp:1; 326 | uint8_t Src_to_Macro_IR:1; 327 | uint8_t Macro_Stream_Advance:1; 328 | uint8_t Slow_Dest:1; 329 | uint8_t ILong:1; 330 | uint8_t Stat_Bit:1; 331 | uint8_t Clobbers_Mem_Subr_Bit:1; 332 | uint8_t Halt:1; 333 | uint8_t Parity:4; 334 | } __attribute__((packed)); 335 | } UInst; 336 | -------------------------------------------------------------------------------- /tools/disktool.c: -------------------------------------------------------------------------------- 1 | /* Lambda disk tool 2 | 3 | Copyright 2016-2018 4 | Daniel Seagraves 5 | Barry Silverman 6 | 7 | This file is part of LambdaDelta. 8 | 9 | LambdaDelta is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 2 of the License, or 12 | (at your option) any later version. 13 | 14 | LambdaDelta is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with LambdaDelta. If not, see . 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | int disk_fd = -1; // FD for disk image 33 | int file_fd = -1; // FD for source/target file 34 | char *disk_fname = NULL; // Disk image filename 35 | uint8_t DISK_BLOCK[1024]; // One disk block 36 | uint32_t label_block = 0; // Address of disk label (used for block offsets) 37 | 38 | // TRUE MINI LABEL (physical block 10) 39 | typedef struct rTrue_Mini_Label { 40 | uint32_t magic; // "MINI" 41 | uint32_t length; // in bytes 42 | uint32_t label_block; 43 | uint32_t backup_label_block; 44 | uint32_t bad_track_list; 45 | uint32_t spare1; 46 | uint32_t usable_tracks; 47 | uint32_t disk_type; 48 | uint32_t heads; 49 | uint32_t sectors; 50 | uint32_t cyls; 51 | uint32_t gap1; 52 | uint32_t gap2; 53 | uint32_t interleave; 54 | uint32_t skew; 55 | uint32_t sector_size; 56 | uint32_t bad_track_list_2; 57 | uint32_t backup_label_track; 58 | } __attribute__((packed)) True_Mini_Label; 59 | 60 | // PARTITION TABLE ENTRY (in MAIN LABEL) 61 | typedef struct rPartition { 62 | uint8_t name[4]; 63 | uint32_t start; 64 | uint32_t size; 65 | uint8_t comment[16]; 66 | } __attribute__((packed)) Partition; 67 | 68 | // MAIN LABEL 69 | typedef struct rDisk_Label { 70 | uint32_t magic; 71 | uint32_t version; 72 | uint32_t cyls; 73 | uint32_t heads; 74 | uint32_t sectors; 75 | uint32_t sectors_per_cyl; 76 | uint8_t microload[4]; 77 | uint8_t load[4]; 78 | uint8_t type[32]; 79 | uint8_t pack[32]; 80 | uint8_t comment[96]; 81 | uint8_t padding[320]; // Unused space 82 | uint32_t partitions; // Part count 83 | uint32_t partsize; // Words per partition entry 84 | Partition partent[29]; // Partitions 85 | } __attribute__((packed)) Disk_Label; 86 | 87 | int disk_block_read(int adr){ 88 | ssize_t io_res; // Result of read/write operations 89 | // Reposition the file pointer. 90 | off_t seek_res = lseek(disk_fd,(adr*0x400),SEEK_SET); 91 | if(seek_res < 0){ 92 | // Seek error! 93 | perror("disktool: disk lseek()"); 94 | return(-1); 95 | } 96 | // Read in a sector. 97 | io_res = read(disk_fd,DISK_BLOCK,1024); 98 | if(io_res < 0){ 99 | perror("disktool: disk read()"); 100 | return(-1); 101 | } 102 | return(io_res); 103 | } 104 | 105 | int disk_block_write(int adr){ 106 | ssize_t io_res; // Result of read/write operations 107 | // Reposition the file pointer. 108 | off_t seek_res = lseek(disk_fd,(adr*0x400),SEEK_SET); 109 | if(seek_res < 0){ 110 | // Seek error! 111 | perror("disktool: disk lseek()"); 112 | return(-1); 113 | } 114 | // Write out a sector. 115 | io_res = write(disk_fd,DISK_BLOCK,1024); 116 | if(io_res < 0){ 117 | perror("disktool: disk write()"); 118 | return(-1); 119 | } 120 | return(io_res); 121 | } 122 | 123 | int read_label_info(){ 124 | True_Mini_Label *TML = NULL; 125 | Disk_Label *Label = NULL; 126 | // Obtain TRUE MINI LABEL 127 | int rv = 0; 128 | rv = disk_block_read(10); 129 | if(rv < 0){ return(rv); } 130 | // Is this it? 131 | TML = (True_Mini_Label *)DISK_BLOCK; 132 | if(TML->magic != 0x494E494D){ 133 | printf("disktool: True Mini Label magic number mismatch: Expected 0x494E494D, got 0x%.8X\n",TML->magic); 134 | return(-1); 135 | } 136 | // This is it! Obtain actual label 137 | label_block = TML->label_block; 138 | rv = disk_block_read(label_block); 139 | if(rv < 0){ return(rv); } 140 | TML = NULL; 141 | Label = (Disk_Label *)DISK_BLOCK; 142 | if(Label->magic != 0x4C42414C){ 143 | printf("disktool: Label magic number mismatch: Expected 0x4C42414C, got 0x%.8X\n",Label->magic); 144 | return(-1); 145 | } 146 | if(Label->partsize != 7){ 147 | printf("disktool: Unexpected partition entry size: Expected 7, got %d\n",Label->partsize); 148 | return(-1); 149 | } 150 | return(0); 151 | } 152 | 153 | int disk_print(){ 154 | int x; 155 | Disk_Label *Label = NULL; 156 | // Obtain label 157 | x = read_label_info(); 158 | if(x < 0){ return(x); } 159 | Label = (Disk_Label *)DISK_BLOCK; 160 | // Print it 161 | printf("Disk label at block %.8X\n",label_block); 162 | printf("Disk from machine(s): %s\n",Label->pack); 163 | printf("%d partitions\n",Label->partitions); 164 | x = 0; 165 | if(Label->partitions > 0){ 166 | printf("NAME START SIZE COMMENT\n"); 167 | printf("---- -------- -------- ----------------\n"); 168 | while(x < Label->partitions){ 169 | char comment[17]; 170 | strncpy(comment,(char *)Label->partent[x].comment,16); 171 | comment[16] = 0; // Ensure null termination 172 | printf("%c%c%c%c %.8X %.8X %s\n", 173 | Label->partent[x].name[0],Label->partent[x].name[1],Label->partent[x].name[2],Label->partent[x].name[3], 174 | Label->partent[x].start,Label->partent[x].size,comment); 175 | x++; 176 | } 177 | } 178 | close(disk_fd); 179 | return(0); 180 | } 181 | 182 | int read_partition(char *pname,char *filename){ 183 | Disk_Label *Label = NULL; 184 | int pslot = -1; 185 | uint32_t block; 186 | uint32_t psize; 187 | int x; 188 | // Find source partition 189 | x = read_label_info(); 190 | if(x < 0){ return(x); } 191 | Label = (Disk_Label *)DISK_BLOCK; 192 | x = 0; 193 | while(x < Label->partitions){ 194 | if(strncmp((char *)Label->partent[x].name,pname,4)==0){ 195 | pslot = x; 196 | block = Label->partent[x].start+label_block; 197 | psize = Label->partent[x].size; 198 | break; 199 | } 200 | x++; 201 | } 202 | if(pslot < 0){ 203 | printf("disktool: read: Unable to find source partition\n"); 204 | return(-1); 205 | } 206 | // Open target 207 | file_fd = open(filename,O_RDWR|O_CREAT,0660); 208 | if(file_fd < 0){ 209 | perror("disktool: file open()"); 210 | return(-1); 211 | } 212 | x = 0; 213 | printf("Copying %s to %s (%.8X blocks)...\n",pname,filename,psize); 214 | while(x < psize){ 215 | int rv = 0; 216 | printf("\rBlock %.8X ",x); 217 | rv = disk_block_read(block+x); 218 | if(rv < 0){ 219 | printf("\n"); 220 | return(rv); 221 | } 222 | // Write out 223 | rv = write(file_fd,DISK_BLOCK,1024); 224 | if(rv < 0){ 225 | perror("disktool: file write()"); 226 | return(-1); 227 | } 228 | // Next! 229 | x++; 230 | } 231 | printf("\nDone\n"); 232 | close(file_fd); 233 | close(disk_fd); 234 | return(0); 235 | } 236 | 237 | int write_partition(char *pname,char *filename,char * comment){ 238 | Disk_Label *Label = NULL; 239 | int pslot = -1; 240 | uint32_t block; 241 | uint32_t psize; 242 | uint32_t fsize; 243 | int x; 244 | struct stat file_stat; 245 | 246 | // Open source 247 | file_fd = open(filename,O_RDONLY); 248 | if(file_fd < 0){ 249 | perror("disktool: file open()"); 250 | return(-1); 251 | } 252 | // Fill stat 253 | x = fstat(file_fd,&file_stat); 254 | if(x < 0){ 255 | perror("disktool: file stat()"); 256 | return(-1); 257 | } 258 | // Find target partition 259 | x = read_label_info(); 260 | if(x < 0){ return(x); } 261 | Label = (Disk_Label *)DISK_BLOCK; 262 | x = 0; 263 | while(x < Label->partitions){ 264 | if(strncmp((char *)Label->partent[x].name,pname,4)==0){ 265 | pslot = x; 266 | block = Label->partent[x].start+label_block; 267 | psize = Label->partent[x].size; 268 | // Will the file fit? 269 | fsize = file_stat.st_size/1024; 270 | if(fsize > psize){ 271 | printf("disktool: write: File of size %.8X blocks won't fit into partition of %.8X blocks\n",fsize,psize); 272 | return(-1); 273 | } 274 | // All good! Are we writing a comment? 275 | if(comment != NULL){ 276 | int rv; 277 | // Yes, do that 278 | strncpy((char *)Label->partent[x].comment,comment,16); 279 | // Write back label 280 | rv = disk_block_write(label_block); 281 | if(rv < 0){ return(rv); } 282 | } 283 | break; 284 | } 285 | x++; 286 | } 287 | if(pslot < 0){ 288 | printf("disktool: write: Unable to find target partition\n"); 289 | return(-1); 290 | } 291 | x = 0; 292 | printf("Copying %s to %s (%.8X blocks)...\n",filename,pname,psize); 293 | while(x < psize){ 294 | int rv = 0; 295 | printf("\rBlock %.8X ",x); 296 | if(x < fsize){ 297 | // Read in 298 | rv = read(file_fd,DISK_BLOCK,1024); 299 | if(rv < 0){ 300 | perror("disktool: file read()"); 301 | return(-1); 302 | } 303 | }else{ 304 | if(x == fsize){ 305 | // Zero buffer 306 | bzero(DISK_BLOCK,1024); 307 | } 308 | } 309 | // Write 310 | rv = disk_block_write(block+x); 311 | if(rv < 0){ 312 | printf("\n"); 313 | return(rv); 314 | } 315 | // Next! 316 | x++; 317 | } 318 | printf("\nDone\n"); 319 | close(file_fd); 320 | close(disk_fd); 321 | return(0); 322 | } 323 | 324 | int main(int argc, char *argv[]){ 325 | // Handle command-line options 326 | if(argc < 2 || strncmp(argv[1],"help",4) == 0 || strncmp(argv[1],"-?",2) == 0){ 327 | printf("Lambda Disktool v0.1\n"); 328 | printf("Usage: disktool (command) (disk image file name) [parameters]...\n"); 329 | printf(" Commands:\n"); 330 | printf(" help Prints this information\n"); 331 | printf(" print Prints the disk label(s) and partitition table, if present\n"); 332 | printf(" read Reads the given partition into the given file\n"); 333 | printf(" Parameters: (partition name) (partition image file name)\n"); 334 | printf(" write Writes the given file into the given partition, optionally setting the comment\n"); 335 | printf(" The remainder of the partition will be zeroed\n"); 336 | printf(" Parameters: (partition image file name) (partition name) [partition comment]\n"); 337 | return(0); 338 | } 339 | if(argc < 3){ 340 | printf("disktool: Disk image file name is required; See \"disktool help\" for usage information.\n"); 341 | return(-1); 342 | } 343 | // We have a disk filename, so open it. 344 | disk_fname = argv[2]; 345 | disk_fd = open(disk_fname,O_RDWR); 346 | if(disk_fd < 0){ 347 | perror("disktool: disk open()"); 348 | return(-1); 349 | } 350 | // Select option (or bail) 351 | if(argc >= 3){ 352 | if(strncmp(argv[1],"print",5) == 0){ 353 | return(disk_print()); 354 | } 355 | if(strncmp(argv[1],"read",4) == 0){ 356 | if(argc < 4){ 357 | printf("disktool: read: partition name is required\n"); 358 | } 359 | if(argc < 5){ 360 | printf("disktool: read: partition image name is required\n"); 361 | } 362 | return(read_partition(argv[3],argv[4])); 363 | } 364 | if(strncmp(argv[1],"write",5) == 0){ 365 | char * comment = NULL; 366 | if(argc < 4){ 367 | printf("disktool: write: partition image name is required\n"); 368 | } 369 | if(argc < 5){ 370 | printf("disktool: write: partition name is required\n"); 371 | } 372 | if(argc == 6){ 373 | comment = argv[5]; 374 | } 375 | return(write_partition(argv[4],argv[3],comment)); 376 | } 377 | printf("disktool: Unknown parameters; See \"disktool help\" for usage information.\n"); 378 | return(-1); 379 | } 380 | } 381 | -------------------------------------------------------------------------------- /src/lpart.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | Barry Silverman 4 | Robert Swindells 5 | Brad Parker 6 | 7 | This file is part of LambdaDelta. 8 | 9 | LambdaDelta is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 2 of the License, or 12 | (at your option) any later version. 13 | 14 | LambdaDelta is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with LambdaDelta. If not, see . 21 | */ 22 | 23 | #include "config.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define E_BLKSIZE 1024 33 | 34 | uint8_t block[E_BLKSIZE]; 35 | int fd; 36 | char *filename; 37 | int mark_expt_flag; 38 | int show_flag; 39 | int extract_flag; 40 | int load_flag; 41 | int active_flag; 42 | int deactive_flag; 43 | int debug_flag; 44 | int devblock; 45 | 46 | typedef unsigned long uint32; 47 | 48 | #if 0 49 | /* virtual format */ 50 | struct { 51 | uint32 rev; 52 | uint32 flags; 53 | char dev[32]; 54 | int bytes_per_block; 55 | int bytes_per_sector; 56 | int sectors_per_track; 57 | int reads; 58 | int cyls; 59 | int reserved_sectors; 60 | }; 61 | #endif 62 | 63 | #define E_PART_MAX 32 64 | struct { 65 | char name[5]; 66 | int start_block; 67 | int length_blocks; 68 | char comment[32]; 69 | } __attribute__((packed)) part_table[E_PART_MAX]; 70 | int part_table_size; 71 | 72 | /* raw, actual-bytes format */ 73 | struct raw_volume_label_s { 74 | uint8_t vl_labl[4]; 75 | uint8_t vl_rev[4]; 76 | uint8_t vl_cyls[4]; 77 | uint8_t vl_heads[4]; 78 | uint8_t vl_sectors_per_track[4]; 79 | uint8_t vl_block_per_cyl[4]; 80 | uint8_t vl_microload[4]; 81 | uint8_t vl_load_band[4]; 82 | uint8_t vl_disk_name[32]; 83 | uint8_t vl_machine[32]; 84 | uint8_t vl_comment[96]; 85 | } __attribute__((packed)); 86 | 87 | struct part_table_s { 88 | uint8_t pt_num_entries[4]; 89 | uint8_t pt_size_entry[4]; 90 | uint8_t pt_offset[4]; 91 | uint8_t pt_res[40]; 92 | } __attribute__((packed)); 93 | 94 | struct part_table_entry_s { 95 | uint8_t pte_name[4]; 96 | uint8_t pte_start_block[4]; 97 | uint8_t pte_length_blocks[4]; 98 | uint8_t pte_comment[32]; 99 | } __attribute__((packed)); 100 | 101 | char *part_gen_func_type[] = { 102 | /* 00 */ "load band", 103 | /* 01 */ "microload band", 104 | /* 02 */ "page band", 105 | /* 03 */ "file band", 106 | /* 04 */ "meter band", 107 | /* 05 */ "test zone band", 108 | /* 06 */ "format parameter band", 109 | /* 07 */ "volume label", 110 | /* 08 */ "save band", 111 | /* 09 */ "partition band", 112 | /* 0A */ "configuration band", 113 | /* 0B */ "user defined type #1", 114 | /* 0C */ "user defined type #2", 115 | /* 0D */ "user defined type #3", 116 | /* 0E */ "user defined type #4", 117 | /* 0F */ "user defined type #5", 118 | /* 10 */ "user defined type #6", 119 | /* 11 */ "user defined type #7", 120 | /* 12 */ "user defined type #8", 121 | /* 13 */ "user defined type #9", 122 | /* 14 */ "user defined type #10", 123 | "reserved" 124 | }; 125 | 126 | 127 | /* 128 | Volume Label 129 | Partition Table 130 | Save Partition 131 | Test Zone 132 | Format Information partition 133 | */ 134 | 135 | int 136 | open_file(char *filename, int need_rw) 137 | { 138 | if (debug_flag) printf("open '%s'\n", filename); 139 | fd = open(filename, need_rw ? O_RDWR : O_RDONLY); 140 | if (fd < 0) { 141 | perror(filename); 142 | return -1; 143 | } 144 | 145 | return 0; 146 | } 147 | 148 | int 149 | read_block(int blknum) 150 | { 151 | int ret; 152 | 153 | off_t offset = blknum * 1024; 154 | 155 | ret = lseek(fd, offset, SEEK_SET); 156 | if (ret < 0) 157 | return 1; 158 | 159 | ret = read(fd, block, E_BLKSIZE); 160 | if (ret != E_BLKSIZE) 161 | return -1; 162 | 163 | return 0; 164 | } 165 | 166 | int 167 | write_block(int blknum) 168 | { 169 | int ret; 170 | off_t offset = blknum * 1024; 171 | 172 | ret = lseek(fd, offset, SEEK_SET); 173 | if (ret < 0) 174 | return 1; 175 | 176 | ret = write(fd, block, E_BLKSIZE); 177 | if (ret != E_BLKSIZE) 178 | return -1; 179 | 180 | return 0; 181 | } 182 | 183 | unsigned int 184 | int4(uint8_t *p) 185 | { 186 | return (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | (p[0]); 187 | } 188 | 189 | unsigned int 190 | int2(uint8_t *p) 191 | { 192 | return (p[1] << 8) | (p[0]); 193 | } 194 | 195 | void 196 | swap4(uint8_t *s, uint8_t *d) 197 | { 198 | d[0] = s[2]; 199 | d[1] = s[3]; 200 | d[2] = s[0]; 201 | d[3] = s[1]; 202 | } 203 | 204 | int ptblknum; 205 | 206 | int 207 | read_part_table(char *filename) 208 | { 209 | struct raw_volume_label_s *vl; 210 | struct part_table_s *pt; 211 | struct part_table_entry_s *pe; 212 | int i, count, size_in_words, index; 213 | int rev, bytes_per_block; 214 | 215 | if (read_block(0)) 216 | return -1; 217 | 218 | vl = (struct raw_volume_label_s *)block; 219 | if (vl->vl_labl[0] == 'D' && vl->vl_labl[1] == 'A' && 220 | vl->vl_labl[2] == 'T' && vl->vl_labl[3] == 'A') 221 | { 222 | printf("%s: DATA disk, no LABL partition\n", filename); 223 | return -1; 224 | } 225 | 226 | if (vl->vl_labl[0] != 'L' || vl->vl_labl[1] != 'A' || 227 | vl->vl_labl[2] != 'B' || vl->vl_labl[3] != 'L') 228 | { 229 | printf("%s: missing LABL signature\n", filename); 230 | 231 | if (debug_flag) printf("found %c%c%c%c\n", 232 | vl->vl_labl[0], vl->vl_labl[1], 233 | vl->vl_labl[2], vl->vl_labl[3]); 234 | return -1; 235 | } 236 | 237 | rev = int4(vl->vl_rev); 238 | printf("rev: %08x\n", rev); 239 | printf("Disk: '%s'\n", vl->vl_disk_name); 240 | 241 | printf("Machine: '%s'\n", vl->vl_machine); 242 | 243 | bytes_per_block = E_BLKSIZE; 244 | printf("bytes/block: %d\n", bytes_per_block); 245 | 246 | printf("comment: '%s'\n", vl->vl_comment); 247 | 248 | pt = (struct part_table_s *)(&block[0x200]); 249 | count = int4(pt->pt_num_entries); 250 | size_in_words = int4(pt->pt_size_entry); 251 | index = 0; 252 | 253 | printf("ptable count: %d\n", count); 254 | printf("size (words): %d\n", size_in_words); 255 | 256 | part_table_size = count; 257 | 258 | pe = (struct part_table_entry_s *)(&pt->pt_offset); 259 | for (i = 0; i < count; i++) { 260 | int user_type, property; 261 | 262 | memcpy(part_table[index].name, 263 | pe->pte_name, 4); 264 | part_table[index].name[4] = 0; 265 | 266 | part_table[index].start_block = int4(pe->pte_start_block); 267 | part_table[index].length_blocks = int4(pe->pte_length_blocks); 268 | memset(part_table[index].comment, 0, 269 | sizeof(part_table[index].comment)); 270 | 271 | memcpy(part_table[index].comment, 272 | pe->pte_comment, sizeof(pe->pte_comment)); 273 | 274 | printf("%-4s %6d/%6d '%s' ", 275 | part_table[index].name, 276 | part_table[index].start_block, 277 | part_table[index].length_blocks, 278 | part_table[index].comment); 279 | 280 | printf("\n"); 281 | index++; 282 | pe = (struct part_table_entry_s *) 283 | (((uint8_t *)pe) + (size_in_words * sizeof(uint32_t))); 284 | } 285 | 286 | return 0; 287 | } 288 | 289 | int 290 | show_part_table(void) 291 | { 292 | return 0; 293 | } 294 | 295 | int 296 | extract_part(char *partition) 297 | { 298 | int i, index, blocks, fd2, bpblk, ret; 299 | off_t offset; 300 | 301 | index = -1; 302 | for (i = 0; i < part_table_size; i++) { 303 | if (strcmp(partition, part_table[i].name) == 0) { 304 | index = i; 305 | break; 306 | } 307 | } 308 | 309 | if (index < 0) { 310 | printf("can't find partition '%s'\n", partition); 311 | return -1; 312 | } 313 | 314 | bpblk = 1024; 315 | 316 | offset = part_table[i].start_block * bpblk; 317 | lseek(fd, offset, SEEK_SET); 318 | 319 | blocks = part_table[i].length_blocks; 320 | 321 | unlink(partition); 322 | fd2 = open(partition, O_WRONLY | O_CREAT, 0666); 323 | if (fd2 == 0) { 324 | perror(partition); 325 | return -1; 326 | } 327 | 328 | for (i = 0; i < blocks; i++) { 329 | ret = read(fd, block, bpblk); 330 | if (ret != bpblk) { 331 | printf("short read?\n"); 332 | break; 333 | } 334 | 335 | write(fd2, block, bpblk); 336 | } 337 | 338 | close(fd2); 339 | 340 | return 0; 341 | } 342 | 343 | int 344 | load_part(char *partition) 345 | { 346 | int i, j, index, blocks, fd2, swap; 347 | uint8_t sblock[E_BLKSIZE]; 348 | off_t offset; 349 | size_t bpblk; 350 | ssize_t ret; 351 | 352 | index = -1; 353 | for (i = 0; i < part_table_size; i++) { 354 | if (strcmp(partition, part_table[i].name) == 0) { 355 | index = i; 356 | break; 357 | } 358 | } 359 | 360 | if (index < 0) { 361 | printf("can't find partition '%s'\n", partition); 362 | return -1; 363 | } 364 | 365 | bpblk = 1024; 366 | 367 | offset = part_table[i].start_block * bpblk; 368 | lseek(fd, offset, SEEK_SET); 369 | 370 | blocks = part_table[i].length_blocks; 371 | 372 | fd2 = open(partition, O_RDONLY); 373 | if (fd2 == 0) { 374 | perror(partition); 375 | return -1; 376 | } 377 | 378 | for (i = 0; i < blocks; i++) { 379 | ret = read(fd2, block, bpblk); 380 | 381 | ret = write(fd, block, ret); 382 | if (ret != bpblk) { 383 | break; 384 | } 385 | } 386 | 387 | close(fd2); 388 | 389 | return 0; 390 | } 391 | 392 | int 393 | active_part(char *partition, int onoff) 394 | { 395 | #if 0 396 | struct part_table_s *pt; 397 | struct part_table_entry_s *pe; 398 | int i, count, size_in_words; 399 | 400 | if (read_block(ptblknum)) 401 | return -1; 402 | 403 | printf("\nset/clean active\n"); 404 | 405 | pt = (struct part_table_s *)block; 406 | pe = (struct part_table_entry_s *)(pt+1); 407 | 408 | count = int4(pt->pt_num_entries); 409 | size_in_words = int4(pt->pt_size_entry); 410 | 411 | for (i = 0; i < count; i++) { 412 | if (strcmp(part_table[i].name, partition) == 0) { 413 | 414 | if (onoff) { 415 | pe->pte_attributes[3] |= 0x04; 416 | printf("setting %s\n", part_table[i].name); 417 | } else { 418 | pe->pte_attributes[3] &= ~0x04; 419 | printf("clearing %s\n", part_table[i].name); 420 | } 421 | } 422 | 423 | pe = (struct part_table_entry_s *) 424 | (((uint8_t *)pe) + (size_in_words * sizeof(uint32_t))); 425 | } 426 | 427 | if (write_block(ptblknum)) 428 | return -1; 429 | #endif 430 | return 0; 431 | } 432 | 433 | int 434 | mark_expt(void) 435 | { 436 | #if 0 437 | struct part_table_s *pt; 438 | struct part_table_entry_s *pe; 439 | int i, count, size_in_words; 440 | 441 | if (read_block(ptblknum)) 442 | return -1; 443 | 444 | printf("\ntrying to mark expts\n"); 445 | 446 | pt = (struct part_table_s *)block; 447 | pe = (struct part_table_entry_s *)(pt+1); 448 | 449 | count = int4(pt->pt_num_entries); 450 | size_in_words = int4(pt->pt_size_entry); 451 | 452 | for (i = 0; i < count; i++) { 453 | if (strcmp(part_table[i].name, "EXPT") == 0) { 454 | 455 | if ((pe->pte_attributes[3] & 0x02) == 0) { 456 | pe->pte_attributes[3] |= 0x02; 457 | printf("marking %s\n", part_table[i].name); 458 | } 459 | } 460 | 461 | pe = (struct part_table_entry_s *) 462 | (((uint32 *)pe) + size_in_words); 463 | } 464 | 465 | if (write_block(ptblknum)) 466 | return -1; 467 | #endif 468 | return 0; 469 | } 470 | 471 | void 472 | usage(void) 473 | { 474 | fprintf(stderr, "usage:\n"); 475 | fprintf(stderr, "-d debug\n"); 476 | fprintf(stderr, "-e extract partition\n"); 477 | fprintf(stderr, "-l load partition\n"); 478 | fprintf(stderr, "-A set active flag\n"); 479 | fprintf(stderr, "-D clear active flag\n"); 480 | fprintf(stderr, "-m mark expt parition\n"); 481 | exit(1); 482 | } 483 | 484 | 485 | extern char *optarg; 486 | extern int optind; 487 | 488 | main(int argc, char *argv[]) 489 | { 490 | int c, need_write; 491 | char *partition; 492 | 493 | filename = "disk.img"; 494 | 495 | show_flag = 1; 496 | devblock = 0; 497 | 498 | while ((c = getopt(argc, argv, "de:l:mA:D:s:")) != -1) { 499 | switch (c) { 500 | case 'd': 501 | debug_flag++; 502 | break; 503 | case 'l': 504 | load_flag++; 505 | partition = strdup(optarg); 506 | break; 507 | case 'e': 508 | extract_flag++; 509 | partition = strdup(optarg); 510 | break; 511 | case 'A': 512 | active_flag++; 513 | partition = strdup(optarg); 514 | break; 515 | case 'D': 516 | deactive_flag++; 517 | partition = strdup(optarg); 518 | break; 519 | case 'm': 520 | mark_expt_flag++; 521 | break; 522 | default: 523 | usage(); 524 | } 525 | } 526 | 527 | if (argc > optind) 528 | filename = argv[optind]; 529 | 530 | need_write = mark_expt_flag || active_flag || deactive_flag || load_flag; 531 | 532 | if (open_file(filename, need_write) || 533 | read_part_table(filename)) 534 | { 535 | exit(1); 536 | } 537 | 538 | if (show_flag) { 539 | show_part_table(); 540 | } 541 | 542 | if (mark_expt_flag) { 543 | mark_expt(); 544 | } 545 | 546 | if (deactive_flag) { 547 | active_part(partition, 0); 548 | } 549 | 550 | if (active_flag) { 551 | active_part(partition, 1); 552 | } 553 | 554 | if (load_flag) { 555 | load_part(partition); 556 | } 557 | 558 | if (extract_flag) { 559 | extract_part(partition); 560 | } 561 | 562 | exit(0); 563 | } 564 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NOTICE OF MAINTENANCE/DEVELOPMENT HIATUS 2 | 3 | Real-life conditions have made it extremely unlikely I will be in any position 4 | to work on (let alone maintain) this project for an extended period of time. 5 | 6 | You are encouraged to fork and/or develop a replacement this software. 7 | 8 | 9 | **Table of Contents** 10 | 11 | - [LAMBDADELTA](#lambdadelta) 12 | - [About the Lambda](#about-the-lambda) 13 | - [Contact](#contact) 14 | - [Getting Started](#getting-started) 15 | - [Prerequisites](#prerequisites) 16 | - [Building LambdaDelta](#building-lambdadelta) 17 | - [Configuring LambdaDelta](#configuring-lambdadelta) 18 | - [Using LambdaDelta](#using-lambdadelta) 19 | - [Preparing ROM Images](#preparing-rom-images) 20 | - [Microcode Symbol Files](#microcode-symbol-files) 21 | - [Installing a New Machine](#installing-a-new-machine) 22 | 23 | 24 | # LAMBDADELTA 25 | 26 | LambdaDelta is an emulator of the LMI Lambda Lisp Machine. Its goal is to 27 | simulate the hardware sufficiently to run original microcode and binaries 28 | unmodified, with performance and capability comparable to the original 29 | hardware. 30 | 31 | LambdaDelta is written in C. It is intended to be portable to any reasonably 32 | Unix-like operating system, but is developed primarily on Linux. The initial 33 | versions will most likely fail to compile elsewhere, but attempts will be 34 | made to rectify this as soon as practical. 35 | 36 | SDL Version 1 or 2 is used for console IO, the Linux tuntap driver or 37 | Berkeley Packet Filter is used for network IO, and standard Unix file 38 | operations are used for disk and tape IO. LambdaDelta includes code from the 39 | Fake86 project by Mike Chambers in its SDU implementation. 40 | 41 | LambdaDelta is licensed under the GNU GPL Version 2 or later at your option. 42 | Please see the file COPYING for details. 43 | 44 | ## About the Lambda 45 | 46 | The LMI Lambda is a NuBus-based machine consisting of at least one Lambda 47 | processor, a System Diagnostic Unit, and various NuBus and Multibus 48 | peripherals. The System Diagnostic Unit has an Intel 8088 processor, and is 49 | responsible for bootstrapping the Lambda processor. The Lambda processor is 50 | made up of four cards in a specially wired area of the NuBus backplane. 51 | The standard Multibus peripherals are a 3Com ethernet interface, an 52 | Interphase SMD disk controller, a Tapemaster 9-track tape controller, 53 | and a Quarterback QIC tape controller (not emulated). The SDU software 54 | mediates sharing of Multibus peripherals. The SDU hardware provides a 55 | DMA path between Multibus space and NuBus space with mapping hardware 56 | for routing pages between targets on both busses. 57 | 58 | The NuBus peripherals are memory and the VCMEM console controller. There 59 | must be one VCMEM for each Lambda processor. The standard configuration 60 | has one pair. An optional configuration called LAMBDA-2x2 has two pairs. 61 | This enables two simultaneous and separate Lisp environments to share one 62 | backplane, memory, disk, and network interface. The result was a 63 | considerable cost savings over buying a second machine. The 2x2 machine 64 | was also used for operating system development. 65 | 66 | There was also an optional 68000-based Unix processor which could run 67 | V7 or SVR4. No attempt was made to emulate this. A configuration with a 68 | Unix processor was given the suffix "plus", making a "LAMBDA-PLUS" 69 | (or "LAMBDA+") a single-user Lambda with a Unix processor and a 70 | "LAMBDA-2X2-PLUS" a two-user Lambda with a Unix processor. 71 | 72 | ## Contact 73 | 74 | Questions, ideas, problems, solutions, praise, complaints, or baseless 75 | accusations may be directed to the LispM mailing list on tunes.org. 76 | See http://lists.tunes.org/mailman/listinfo/lispm/ for more information. 77 | 78 | All contributors to this project are listed in the AUTHORS file. 79 | 80 | # Getting Started 81 | 82 | ## Prerequisites 83 | 84 | LambdaDelta requires a set of ROM images from a physical Lambda, and 85 | either a disk image of an installed system or images of the installation 86 | and source tapes. At the time of release, these items are available from 87 | Bitsavers. See http://www.bitsavers.org/ for more information. 88 | 89 | Bogodyne ZetaLisp System 500.0 Release 1 can be found at https://www.bogodyne.com/download/download-zetalisp/ 90 | 91 | ## Building LambdaDelta 92 | 93 | LambdaDelta uses the GNU auto* tools for configuration and compilation. 94 | Most features and libraries should be configured automatically when 95 | the `configure` script is run. 96 | 97 | To generate the configure script from a git checkout, the usual recipe is 98 | `aclocal ; autoheader ; autoconf ; automake --add-missing`. Your platform 99 | may require different invocation(s) of these programs. 100 | 101 | If the generated configure script fails, try `autoreconf -i` and re-run it. 102 | (See dseagrav/ld#6 for the issue.) 103 | 104 | If your system has both SDL1 and SDL2, you will be required to explicitly disable 105 | the one you do not want. (use --`without-SDLx`). SDL1 seems to perform 106 | better over remote X11 connections, but SDL2 has better performance 107 | when running locally. 108 | 109 | If you wish to emulate the optional 2x2 configuration, run configure 110 | with the option `--enable-config-2x2=yes` 111 | 112 | If you are in possession of a physical Lambda keyboard and/or mouse, 113 | you may enable the use of these with the options `--enable-config-physkbd` 114 | and `--enable-config-physms`. You will need to provide serial interface 115 | hardware as required to communicate with these items. 116 | 117 | After compilation, run-time options are controlled by a configuration 118 | file. If you have the YAML library installed (and configure found it), 119 | it will use YAML configuration. Otherwise, it will use the old 120 | configuration file. 121 | 122 | # Configuring LambdaDelta 123 | This is the new configuration method that is intended to be used going 124 | forward. The YAML configuration file is named `lam.yml` and is searched 125 | for in the following places, in order: 126 | 127 | 128 | *Note:* If you do not have libyaml on your system (or do not wish to install it), please see doc/OLDCONF.md for the legacy configuration system.. 129 | 130 | 1. The directory specified in `SYSCONFDIR` if it was defined during build. 131 | 2. The user's home directory, as determined by: 132 | 1. The `$HOME` environment variable, or if that is not defined: 133 | 2. If we are root and were run by sudo, from the sudoer's passwd entry. 134 | 3. If we are root and were not run by sudo, root's passwd entry. 135 | 4. If we are not root, the user's passwd entry. 136 | 3. The current working directory. 137 | 138 | Any or all of these locations are valid; Configuration items in later 139 | files supersede those specified in earlier files. 140 | 141 | You may also specify a configuration file of any name on the command line 142 | by using the `-c` argument. This file, if provided, is loaded last. 143 | 144 | Single YAML key/value pairs may also be specified on the command line 145 | by specifying them in the form `--section-key value`. For example, the 146 | argument `--log-ALL 10` will enable maximum logging of all types. 147 | 148 | A full description of the YAML configuration file is beyond the scope 149 | of this document; See the "lam.yml" file in the examples directory. 150 | 151 | # Using LambdaDelta 152 | 153 | While the program is running, the window title bar has the following form: 154 | 155 | LambdaDelta: VC N | Tape: FNAME | STATUS | DT X 156 | 157 | N is the number of the active console, either 0 or 1. N is always zero when 158 | the 2x2 configuration is not in use. `FNAME` is the name of the active tape 159 | image file. `STATUS` is a string describing the state of the Lambda processor 160 | associated with this console. The strings have the following meanings: 161 | 162 | | Status string | Meaning | 163 | |------------------|----------------------------------------------------------------------------------------------------------------| 164 | | Cold Halted | The processor has been powered on and has no state. | 165 | | Cold Running | The halt flag is clear but the state is unknown. | 166 | | Cold Booting | The bootstrap is running. | 167 | | Lisp Boot Halted | The processor halted while Lisp was initializing. (This is an error condition.) | 168 | | Lisp Booting | Lisp is initializing or warm booting. | 169 | | Running | Lisp is running. | 170 | | Halted | Lisp has stopped running. The processor has valid state. If you did not halt lisp, this is an error condition. | 171 | 172 | After the status string is the time offset. X is a number in deciseconds 173 | which indicates how far apart real time and the emulator's time are. 174 | The emulator will try to hold this number at 0, but if your computer is 175 | not fast enough to run the emulator in real time this number will become 176 | negative and decrease further as the times diverge. If the number becomes 177 | positive and increases, the throttle is not operating properly. This is a 178 | bug which should be reported. 179 | 180 | The following keys control emulator functions are cannot be remapped: 181 | 182 | F9 switches the active console if the 2x2 configuration is enabled. 183 | If the standard configuration is in use, F9 may be remapped. 184 | F10 changes mouse operation according to the configured mouse mode. 185 | In mode 0, F10 toggles capture and release of the mouse pointer. Clicking 186 | inside the LambdaDelta window while the mouse is not captured will 187 | recapture it. 188 | In mode 1, F10 toggles visibility of the host mouse pointer. 189 | 190 | F11 simulates the "return to newboot" keyboard chord, which terminates Lisp 191 | and recalls the Newboot interface. Limitations of the keyboard emulation 192 | make typing the actual chord fail, so this directly sends the expected 193 | sequence of bytes to make things happen. 194 | 195 | F12 causes the active tape image file to rotate to the next file in 196 | ASCIIbetical order. Pressing control and F12 at the same time causes 197 | the emulator to dump its state into a bunch of .DUMP files. These files 198 | are human-readable but not necessarily human-understandable. (It is our 199 | understanding that whether or not the developers are classified as human 200 | is a subject of ongoing debate.) 201 | 202 | All other keys on the keyboard may be remapped using the map_key option 203 | described above. The standard mapping preserves the printed key label 204 | of the standard typewriter keys. The other keys are mapped as follows: 205 | 206 | | HOST KEY | LISPM KEY | NOTES | 207 | |------------|-------------|----------------------------------------| 208 | | [ | ( | [ is the shift state, ( is unshifted | 209 | | ] | ) | ] is the shift state, ) is unshifted | 210 | | RETURN | ENTER | | 211 | | BACKSPACE | RUBOUT | | 212 | | UP | HAND UP | | 213 | | DOWN | HAND DOWN | | 214 | | LEFT | HAND LEFT | | 215 | | RIGHT | HAND RIGHT | | 216 | | PAGE UP | ABORT | | 217 | | PAGE DOWN | RESUME | | 218 | | F1 | SYSTEM | | 219 | | F2 | NETWORK | | 220 | | F3 | STATUS | | 221 | | F4 | TERMINAL | | 222 | | F5 | HELP | | 223 | | F6 | CLEAR | | 224 | | F7 | BREAK | | 225 | | RIGHT CTRL | LEFT GREEK | | 226 | | RIGHT ALT | LEFT SUPER | | 227 | | LEFT ALT | LEFT META | | 228 | | RIGHT FLAG | RIGHT SUPER | Does not always work on some platforms | 229 | | LEFT FLAG | LEFT SUPER | Does not always work on some platforms | 230 | | MENU | RIGHT HYPER | | 231 | 232 | The default keymap is still under development and is subject to change. Feel 233 | free to make suggestions or comments. 234 | 235 | ## Preparing ROM Images 236 | 237 | The ROM images go in the "roms" subdirectory. The necessary files are: 238 | 239 | - `SDU.ROM`: 240 | The SDU's 8088 program ROM. 64K, merged from two chips on the board. 241 | Bitsavers has one with an MD5 checksum of `4795bf46168808d32012213c0b370f30` 242 | - `MEM.ROM`: 243 | The nubus-space configuration ROM for a memory card. 2K. 244 | Bitsavers has two, and their MD5 checksums are `21089f3b4082548865f8cda6017f302e` or `1f898d018a2e2ab29279ecf00c7a4c82`. 245 | Either one may be used, but the same one will be used for both cards simulated. 246 | - `VCMEM.ROM` - The VCMEM's nubus-space configuration ROM. 2K. 247 | This contains 8088 program code that will be run by the SDU. 248 | The one on Bitsavers has an MD5 checksum of `0e53416a49294f02c7fd925c9de47f5a`. 249 | 250 | These can be found in zip files on the PDFs side of Bitsavers. 251 | 252 | ## Microcode Symbol Files 253 | 254 | In the lisp source tree there are files named `bootstrap.lam-uload` and 255 | `ulambda.lmc-sym` which correspond to the Lambda bootstrap code downloaded 256 | by the SDU and the Lisp microcode. Place these in the LambdaDelta directory 257 | to provide symbols for debugging. These are optional. 258 | 259 | ## Installing a New Machine 260 | 261 | 1. Prepare your config file, ROM images, tape images, and create the disk 262 | image file. Ensure the SDU switch setting is zero in your configuration. 263 | 2. Start the emulator. Telnet to port 3637 on the host. The emulator should 264 | start, and you should have a SDU prompt in your telnet session. 265 | 266 | Ignore the graphical console for the now - It is inoperative until 267 | the SDU firmware partition is loaded and the CMOS contents are valid. 268 | 269 | 3. Type `init` and push enter to initialize the busses and SDU. The SDU will 270 | reboot. 271 | 272 | 4. Ensure the install tape is the active tape. 273 | 274 | 5. Give the SDU command `/tar/setup clear eagle sp shell`. Your CMOS should 275 | be initialized. 276 | 277 | 6. Give the SDU command `/tar/load` and follow the prompts. 278 | 279 | 7. When the `load >` prompt appears, give the command `install` and follow 280 | the prompts. 281 | 282 | 8. Give the command `q` to return to the SDU 283 | 284 | 9. Close the emulator by closing its window 285 | 286 | 10. Edit your config file file and change the SDU switch setting to 1 287 | 288 | 11. Restart the emulator. You should get a SDU prompt on the console. 289 | 290 | 12. Give the `config` command to initialize the Lambda configuration. 291 | When asked if you want to change anything, type y and press enter. 292 | 293 | 13. At the `cmd:` prompt, give the command `lambda`. 294 | 295 | 14. At the `lambda cmd:` prompt, give the command `switches` 296 | 297 | 15. At the `->` prompt, give the command `set use_multiplier` 298 | 299 | 16. Push enter to return to the lambda command level. 300 | 301 | 17. Give the command `x` until config reboots the SDU. 302 | 303 | 18. At this point your machine is configured and you can save backups of your 304 | ld.conf, disk image, and CMOS image file. 305 | 306 | 19. At the SDU prompt, give the command `newboot` to bring up the 307 | bootstrap program. 308 | 309 | 20. At the `Command:` prompt, give the command `boot` to start lisp. 310 | 311 | 21. When the REPL arrives, evaluate `(si:set-pack-name "LISPM-A")` to set 312 | the hostname. 313 | 314 | 22. Evaluate `(si:%halt)` to halt Lisp. 315 | 316 | 23. When Lisp halts, press F11 to summon Newboot again 317 | 318 | 24. At the `Command:` prompt, give the boot command to cold-boot Lisp. 319 | 320 | 25. When the REPL arrives, evaluate `(fs:initialize-file-system)` to 321 | format the LMFS. Answer `Yes` when prompted for confirmation. 322 | 323 | 26. Push F12 to switch tapes to your distribution tape, if you have one. 324 | 325 | 27. Type System-B to summon the Backup activity. 326 | 327 | At this point you can follow the instructions included with the 328 | distribution tape. If it is a backup tape, simply restore it. If it is 329 | an actual distribution tape, it may have special loading procedures. 330 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /src/lambda_cpu.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | 4 | This file is part of LambdaDelta. 5 | 6 | LambdaDelta is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | LambdaDelta is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with LambdaDelta. If not, see . 18 | */ 19 | 20 | // Register definitions 21 | 22 | /* 23 | THE DP MODE REGISTER IS MOSTLY UNUSED, BUT SOMEDAY MAY CONTAIN A GREAT DEAL MORE 24 | DEBUGGING INFORMATION. FOR NOW, IT ONLY HAS TWO SIGNALS, BUT IT CAN ALSO BE USED 25 | TO JUMPER INTERESTING SIGNALS TO SO THAT THEY CAN BE EXAMINED BY THE PROCESSOR WHILE 26 | IT'S RUNNING. NOTE THAT ONLY THE BOTTOM 6 BITS ARE READ/WRITE - THE TOP TWO BITS 27 | ARE READ ONLY. 28 | 29 | bit 7 : the divisor sign bit ... 0 = positive , 1 = negitive 30 | bits 6-1 are unused 31 | bit 0 : the PDL.address.high bit ... 0 = M memory (only low 64 locations normally used) 32 | 1 = PDL memory 33 | 34 | */ 35 | // With the PDL.address.high bit bootstrap says there is 2KW of PDL memory. 36 | typedef union rDP_Mode_Reg { 37 | uint8_t raw; 38 | struct { 39 | uint8_t PDL_Addr_Hi:1; 40 | uint8_t Unused:6; 41 | uint8_t Divisor_Sign:1; 42 | } __attribute__((packed)); 43 | } DP_Mode_Reg; 44 | 45 | /* 46 | THERE IS ALSO A 16 BIT RG MODE REGISTER WHICH HAS LOTS OF INTERESTING THINGS. 47 | IT CONTAINS MOST OF THE FUNCTIONALITY OF THE OLD CADR INTERRRUPT CONTROL REGISTER 48 | NOTE THAT ONLY THE TOP 12 BITS ARE READ/WRITE -- THE LOW 4 BITS ARE WRITE ONLY. 49 | ALSO NOTE THAT THESE 16 BITS ARE THE HIGH 16 BITS OF THE WORD {MFO.<31-16>}. 50 | 51 | AS OF VERSION 3 OF THE RG BOARD, THE RG MODE REGISTER IS NOW A FULL 32 BITS. IT 52 | RETAINS THE OLD DEFINITIONS AS BEFORE (EXCEPT THE AUX-STAT-COUNT-CONTROL BIT, WHICH 53 | IS NOW UNUSED), AND NOW HAS 4 MORE READ ONLY BITS (THE RG NUBUS SLOT ID) AND 12 54 | MORE READ/WRITE BITS. THE WHOLE RG MODE REGISTER NOW LOOKS LIKE 12 READ/WRITE BITS, 55 | 8 READ-ONLY BITS, AND THEN 12 MORE READ/WRITE BITS. 56 | */ 57 | typedef union rRG_Mode_Reg { 58 | uint32_t raw; 59 | struct { 60 | /* 61 | bits 2-0 determine what the aux stat-counter increments on. There are 8 possibilities. 62 | 7 = hi.c -- a permanently high signal, so that you can increment every 63 | time ... this allows ratios between the two stat counters 64 | 6 = microsecond.clock -- a microsecond clock for timing purposes 65 | 5 = t.statistics.bit -- a bit in the timing ram 66 | 4 = t.hold -- to determine how long we are sitting in "hang" states 67 | (note that you probably want to clock on sm.ticks) 68 | 3 = increment.LC -- goes high during fetches of the next macro-instruction 69 | 2 = csm.statistics.bit -- a bit in the cache state machine 70 | (usually signifies cache hits) 71 | 1 = memory.start.next.cycle -- goes high during memory fetches 72 | 0 = valid.statistics.bit -- a bit in the current microinstruction 73 | bit 2. : aux.stat.count.control 2 -- see above 74 | bit 1. : aux.stat.count.control 1 -- see above 75 | bit 0. : aux.stat.count.control 0 -- see above 76 | */ 77 | uint8_t Aux_Stat_Count_Control:3; 78 | // Bit 3 is unused? 79 | uint8_t Bit3:1; 80 | /* 81 | bit 4 selects clock for aux stat counter (this was mod 31 to rev 2 ww boards. 82 | the mod may not have been made to most rev 2 boards). 83 | */ 84 | uint8_t Aux_Stat_Count_Clock:1; 85 | // bits 11-5 are currently unused 86 | uint16_t Unused:7; 87 | /* 88 | bits 15-12 : rg.nubus.id.<3-0> -- the nubus slot id that the RG board is currently in. 89 | bit 15 : rg.nubus.id.3 -- bit 3 of the rg nubus slot id 90 | bit 14 : rg.nubus.id.2 -- bit 2 of the rg nubus slot id 91 | bit 13 : rg.nubus.id.1 -- bit 1 of the rg nubus slot id 92 | bit 12 : rg.nubus.id.0 -- bit 0 of the rg nubus slot id 93 | */ 94 | uint8_t NUbus_ID:4; 95 | /* 96 | bit 16 : need.macro.inst.fetch L -- asserted when you need another pair of 97 | macro-instructions (i.e., you don't need to 98 | memory fetch each one, you first check to see if 99 | you still haven't used the other one; if so, assert 100 | this bit and you will fetch from memory two more. 101 | */ 102 | uint8_t Need_Macro_Inst_Fetch:1; 103 | // bits 17-19 are unused read-only bits 104 | uint8_t Unused_RO:3; 105 | /* 106 | bits 22-20 determine what the main stat-counter increments on. There are 8 possibilities. 107 | 7 = hi.c -- a permanently high signal, so that you can increment every 108 | time ... this allows ratios between the two stat counters 109 | 6 = unused -- can be jumpered to anything needed to examine 110 | 5 = t.statistics.bit -- a bit in the timing ram 111 | 4 = t.hold -- to determine how long we are sitting in "hang" states 112 | (note that you probably want to clock on sm.ticks) 113 | 3 = increment.LC -- goes high during fetches of the next macro-instruction 114 | 2 = csm.statistics.bit -- a bit in the cache state machine 115 | (usually signifies cache hits) 116 | 1 = memory.start.next.cycle -- goes high during memory fetches 117 | 0 = valid.statistics.bit -- a bit in the current microinstruction 118 | bit 22. : main.stat.count.control 2 -- see above 119 | bit 21. : main.stat.count.control 1 -- see above 120 | bit 20. : main.stat.count.control 0 -- see above 121 | */ 122 | uint8_t Main_Stat_Count_Control:3; 123 | /* 124 | bit 23 : aux.stat.count.control -- determines whether we increment the aux stat-counter 125 | based on the statistic bit in the current microinstruction, or the signal 126 | memory.start.next.cycle, which comes on during memory fetches.... 127 | 0 = valid.statistics.bit causes increment 128 | 1 = memory.start.next.cycle causes increment 129 | Also see AUX-STAT-CLOCK-CONTROL. 130 | */ 131 | uint8_t Aux_Stat_Count_Control_2:1; 132 | /* 133 | bit 24 : main.stat.clock.control -- determines whether we clock the main stat counter on 134 | uinst.clocks (standard) or sm.clocks; 0 = sm.clocking, 1 = uinst.clocking 135 | */ 136 | uint8_t Main_Stat_Clock_Control:1; 137 | /* 138 | bit 25 : enable.misc.MID 139 | when HI let the macro instruction decode stuff dispatch on the whole MISC field 140 | of the instruction, if it is a MISC instruction to destination ignore (0). 141 | MID memory 6000 - 7777 is used to hold dispatch addresses for MISC (6000-6777) 142 | and MISC1 (7000 - 7777). 143 | On source cycles, if the 40 bit in M.source is set, the above areas are ref'ed on 144 | a misc instruction regardless of its destination. 145 | */ 146 | uint8_t Enable_Misc_MID:1; 147 | /* 148 | bit 26 : sequence.break L -- this bit is checked by the microcode for the instruction 149 | jump-on-sequence-break. 0 = set the sequence break, 1 = disable sequence breaks 150 | */ 151 | uint8_t Sequence_Break:1; 152 | /* 153 | bit 27 : interrupt.enable -- enables program interrupts; 0 = no interrupts, 154 | 1 = normal program interrupts 155 | */ 156 | uint8_t Interrupt_Enable:1; 157 | /* 158 | bits 28-29: mid.hi.adr.0 and mid.hi.adr.1 159 | high two bits of MID Ram address when others are comming from the high 10 bits of 160 | the macro.ir. These allow convenient addressing of the portion of the macro.ir 161 | used for MISC decodeing, etc. Should be 0 for normal operation. 162 | */ 163 | uint8_t MID_Hi_Adr:2; 164 | // bit 30 : 25.bit.virtual.address.mode L; 0 = 25 bit addresses, 1 = 24 bit addresses 165 | uint8_t VA_Mode:1; 166 | // bit 31 : single.step.macro.inst.mode; 0 = normal execution, 1 = macrocode single stepping 167 | uint8_t Single_Step_Macro_Inst:1; 168 | } __attribute__((packed)); 169 | } RG_Mode_Reg; 170 | 171 | /* Configuration Register */ 172 | typedef union rConfiguration_Reg { 173 | uint32_t word; 174 | uint8_t byte[4]; 175 | uint8_t writable_bits:4; 176 | struct { 177 | uint8_t Init:1; 178 | uint8_t Enable_NU_Master:1; 179 | uint8_t LED:1; 180 | uint8_t Enable_SM_Clock:1; 181 | // The next 4 bits are read-only, and are inverted from the signal on the board. 182 | uint8_t nop:1; 183 | uint8_t nop_next:1; 184 | uint8_t t_hold_l:1; 185 | uint8_t uinst_clock_l:1; 186 | // The remaining 24 bits are read-only, but are not inverted. 187 | uint8_t stat_halt_l:1; 188 | uint8_t halt_request:1; 189 | uint8_t any_parity_error:1; 190 | uint8_t hbus_present_l:1; 191 | uint8_t debug_clock_mode_synced:1; 192 | // This is actually what stops SM.clock. 193 | // When a parity error is detected, one more sm.clock happens before machine really stops completely. 194 | // (t.hold is asserted during this clock, however) 195 | // If the parity error goes away (during?) this last sm.clock, the machine will stop 196 | // with the any-parity-error-synced bit set, but any-parity-error no longer set. 197 | uint8_t any_parity_error_synced_l:1; 198 | // Remaining bits unused 199 | } __attribute__((packed)); 200 | } Configuration_Reg; 201 | 202 | 203 | /* This is the Processor Mode Register, accessed by the SPY interface */ 204 | typedef union rProcessor_Mode_Reg { 205 | uint32_t raw; 206 | uint8_t byte[4]; 207 | struct { 208 | uint8_t Stat_Halt_Enable:1; 209 | uint8_t Single_Step_Mode:1; // NOT USED IN V3.0 AND LATER 210 | uint8_t Fast_Clock_Enable:1; // Inverted 211 | uint8_t Reset_Interrupt_Counter:1; 212 | uint8_t Debug_Clock:1; // Drives SM clock when CONREG Enable_SM_Clock is clear. 213 | uint8_t Advance_UInst_Request:1; 214 | uint8_t Debug_Cache_Permit:1; 215 | uint8_t Parity_Stop_Enable:1; 216 | uint8_t Enable_MI_Parity:1; 217 | uint8_t Enable_CM_Parity:1; 218 | uint8_t Enable_DP_Parity:1; 219 | uint8_t Enable_MID_Parity:1; 220 | uint8_t Enable_TREG_Parity:1; 221 | uint8_t Spare0:1; 222 | uint8_t Clear_NOP:1; 223 | uint8_t Spare1:2; 224 | uint8_t Spy_Address_TRAM_L:1; 225 | uint8_t Force_T_Hold:1; 226 | uint8_t Force_MI_Reset_L:1; 227 | uint8_t Force_CSM_Use_Spy_Addr_L:1; 228 | uint8_t Allow_UInst_Clocks:1; 229 | uint8_t Spare2:2; 230 | } __attribute__((packed)); 231 | } Processor_Mode_Reg; 232 | 233 | // This is a TRAM word 234 | typedef union rTRAM_Word { 235 | uint32_t word; 236 | uint8_t byte[4]; 237 | struct { 238 | uint8_t state; 239 | uint8_t next_select:2; 240 | uint8_t hold_control:2; // Unused 241 | uint8_t M_address_control:2; 242 | uint8_t M_CS:1; 243 | uint8_t M_WE_L:1; 244 | uint8_t A_address_control:2; 245 | uint8_t A_WE_L:1; 246 | uint8_t A_clock_next:1; 247 | uint8_t L_to_A_L:1; 248 | uint8_t L_to_M_L:1; 249 | uint8_t source_cycle:1; // Also first.source.cycle.next and first.source.cycle.next-l 250 | uint8_t data_paths_to_MFO:1; 251 | uint8_t M_clock_next:1; 252 | uint8_t slow_dest_write:1; 253 | uint8_t Spare:1; 254 | uint8_t new_uinst:1; 255 | uint8_t next_cycle_number:2; // Shared with parity 256 | uint8_t parity:2; 257 | } __attribute__((packed)); 258 | } TRAM_Word; 259 | 260 | // CSM.ADR register 261 | typedef union rCSMAdr_Word { 262 | uint32_t raw; 263 | struct { 264 | uint16_t Addr:12; 265 | uint32_t Cached_Phy_Addr:18; 266 | uint8_t Memory_Cycle_Active:1; 267 | uint8_t Memory_Cycle_Pending:1; 268 | } __attribute__((packed)); 269 | } CSMAdr_Word; 270 | 271 | // Words in CSMRAM 272 | typedef union rCSM_Word { 273 | uint32_t raw; 274 | uint8_t byte[4]; 275 | struct { 276 | uint16_t State:11; 277 | uint8_t Condition:3; 278 | uint8_t Cache_Mode:2; // 0 = nothing, 1 = read, 2 = write if NU.xfer, 3 = write if cache hit 279 | uint8_t MD_to_NUDATA_bus:1; 280 | uint8_t RD_DATA_to_NUDATA_bus:1; 281 | uint8_t Cache_Adr_from_NUADR:1; 282 | uint8_t Cache_Hit_Enable:1; 283 | uint8_t Memory_Cycle_Ending_L:1; // Defaulted HI if not spec'd 284 | uint8_t Count_NUADR:1; 285 | uint8_t Virtual_Page_Group:1; 286 | uint8_t NU_Idle:1; 287 | uint8_t Statistics_Bit:1; 288 | uint8_t Lambda_STREQ:1; 289 | uint8_t Lambda_BREQ:1; 290 | uint8_t Request_Noted_L:1; // Defaulted HI if not spec'd 291 | uint8_t Parity:4; 292 | } __attribute__((packed)); 293 | } CSM_Word; 294 | 295 | /* Shadow Memory Page Map Entry */ 296 | typedef union ShadowMemoryPageEnt { 297 | uint8_t raw; 298 | struct { 299 | uint8_t Resident:1; // Resident in physical memory 300 | uint8_t Written:1; // Written to (other than paging in) 301 | uint8_t Paged:1; // Written to by being paged out 302 | // Resident == 0 with Written == 0 and Paged == 0 means unused page 303 | } __attribute__((packed)); 304 | } ShadowMemoryPageEnt; 305 | 306 | /* Processor State Structure */ 307 | struct lambdaState { 308 | /* Buses */ 309 | uint32_t Abus; // A-bus 310 | uint32_t Mbus; // M-bus 311 | uint32_t Obus; // O-bus 312 | uint32_t MFObus; // MFO-bus 313 | uint32_t Rbus; // R-bus 314 | /* Halt control */ 315 | int cpu_die_rq; 316 | // Memories 317 | UInst WCS[64*1024]; // Writable Control Store (16KW on Raven) 318 | uint32_t Amemory[1024*4]; // A-memory (1KW on Raven) 319 | uint32_t Mmemory[1024*4]; // M-memory and PDLmemory (see DP_Mode) 320 | uint32_t MIDmemory[1024*4]; // Macro-Instruction-Dispatch-memory 321 | 322 | MicroAddr loc_ctr_reg; // (Micro)Location Counter Register -- Address of NEXT instruction 323 | uint32_t loc_ctr_cnt; // (Micro)Location Counter Register -- Address of CURRENT instruction 324 | int32_t loc_ctr_nxt; // (Micro)Location Counter Register -- Address of AFTER-NEXT instruction 325 | int32_t popj_after_nxt; // POPJ-After-Next trigger 326 | int32_t macro_dispatch_inst; // Macro instruction dispatch trigger 327 | int last_loc_ctr; // Lpc 328 | lv1_ent vm_lv1_map[4096]; // VM Map, level 1 329 | lv2_ctl_ent vm_lv2_ctl[4096]; // VM Map, level 2, Control Storage 330 | lv2_adr_ent vm_lv2_adr[4096]; // VM Map, level 2, Address Storage 331 | lv2_idx vm_lv2_index; // VM Map, level 1 to level 2 index 332 | int vm_byte_mode; // Gets ORed with access type to implement byte mode 333 | int cached_gcv; // Cached GC Volatility 334 | PhysAddr vm_phys_addr; // VM Map, Generated Physical Address 335 | int Page_Fault; // VM Map Failure Flag 336 | uint16_t CRAM_map[4096]; // CRAM map. 337 | MIDAddress MIDAddr; // Macro Instruction Dispatch Address 338 | TRAM_Word TRAM[0x1000]; // Timing RAM 339 | TRAM_Word TREG; // Timing RAM output register 340 | uint32_t CSM_Output; // Cache State Machine output register 341 | CSM_Word CSMRAM[0x1000]; // Cache State Machine RAM 342 | 343 | uint8_t InterruptPending; // Interrupt Pending Counter 344 | uint8_t InterruptStatus[0x100]; // Interrupt states from the RG board 345 | uint8_t InterruptVector; // Which one is active? 346 | 347 | /* Mode registers */ 348 | DP_Mode_Reg DP_Mode; 349 | RG_Mode_Reg RG_Mode; 350 | Processor_Mode_Reg PMR; 351 | Configuration_Reg ConReg; 352 | 353 | /* MF-bus sources */ 354 | uint32_t pdl_ptr_reg; // PDL Pointer Register 355 | uint32_t pdl_index_reg; // PDL Index Register 356 | uint32_t uPCS_ptr_reg; // Microcode counter stack pointer 357 | uint32_t disp_constant_reg; // Dispatch constant register 358 | uint32_t stat_counter_main; // Main stat counter 359 | uint32_t stat_counter_aux; // Aux stat counter 360 | 361 | /* SPY items */ 362 | CSMAdr_Word CSM_Adr; // For hacking (CSMRAM?), also carries memory info on read 363 | uint16_t TRAM_Adr; // Timing RAM address register, 12 bits 364 | uint32_t SPY_Register; // Bond, James Bond 365 | uint32_t Parity_Vector; // Parity Vector 366 | uint16_t History_Pointer; // HPTR, 12 bits (docs say 10, diags test for 12) 367 | uint16_t History_RAM[0x1000]; // HRAM, 16 bits 368 | 369 | /* Others */ 370 | Q MIregister; // Macroinstruction Register 371 | uint8_t mirInvalid; // MIR validity 372 | Q MDregister; // Memory Data register 373 | Q VMAregister; // Virtual Memory Address 374 | Q LCregister; // Macro Location Counter register 375 | UInst Iregister; // 56b Instruction register 376 | uint32_t Qregister; // Q register 377 | uint32_t uPCS_stack[255]; // Microcode counter stack memory 378 | int64_t ALU_Result; // Result of ALU operation 379 | bool ALU_Carry_Out; // Carry-Out 380 | bool ALU_Fixnum_Oflow; // FIXNUM Overflow 381 | bool imod_en; // Enable IMOD 382 | bool spy_wrote_ireg; // Spy wrote the IR, so don't fetch over it. 383 | DispatchWord disp_word; // Dispatch operation word 384 | uint32_t imod_lo,imod_hi; // IMOD storage 385 | uint32_t Multiplier_Input; // Multiplier input 386 | uint32_t Multiplier_Output; // Multiplier output 387 | uint32_t Multiplier_FT; // Multiplier flow-through 388 | uint16_t TRAM_PC; // Timing RAM "PC" register, 12 bits 389 | uint64_t Obus_Input; // Obus Multiplexer Input 390 | 391 | bool NOP_Next; // Next microinstruction gets NOPed out 392 | bool cram_write_cyc; // CRAM write cycle (or map) 393 | 394 | int SM_Clock; // State Machine Clock 395 | 396 | // Software flags (Nonexistent on real hardware) 397 | uint8_t NUbus_ID; // Bus slot for this processor (RG board slot?) 398 | bool test_true; // Condition Test True 399 | bool slow_dest; // Slow destination flag 400 | bool long_inst; // long instruction flag 401 | bool exec_hold; // Execution hold flag, waiting for bus to free 402 | bool uI_Clock_Pulse; // Microinstruction clock pulsed 403 | bool wrote_uPC; // Did we write the uPC? 404 | bool microtrace; // Microcode tracing enable 405 | bool macrotrace; // Macrocode tracing enable 406 | uint16_t Next_History_Pointer; // Next HPTR, 12 bits 407 | 408 | // Performance monitoring 409 | volatile unsigned long cycle_count; 410 | volatile unsigned long stall_count; 411 | }; 412 | 413 | /* Functions */ 414 | 415 | void lambda_initialize(int I,int ID); 416 | void lambda_clockpulse(int I); 417 | void shadow_write(uint32_t addr,Q data); 418 | Q shadow_read(uint32_t addr); 419 | 420 | -------------------------------------------------------------------------------- /tools/decode_lmfl.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2015-2019 2 | Daniel Seagraves 3 | 4 | Decode LMFL, Version 16 5 | This file is part of LambdaDelta. 6 | 7 | LambdaDelta is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | LambdaDelta is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with LambdaDelta. If not, see . 19 | */ 20 | 21 | /* 22 | This expects to be run at the top of a directory tree containing a series 23 | of files to be extracted. The program will walk the tree and extract 24 | what it finds to a directory one level below CWD named "ext". 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | // Tracelog FH 38 | FILE *logfd; 39 | char logent[512]; 40 | 41 | // Directory timestamp list 42 | #define MAXDIR 5000 43 | struct dlistent { 44 | char ufn[512]; 45 | long ts; 46 | }; 47 | struct dlistent dlist[MAXDIR]; 48 | int topdir=0; 49 | 50 | // File directory list 51 | #define MAXFILE 15000 52 | struct filent { 53 | char ufn[512]; 54 | int version; 55 | }; 56 | struct filent fdir[MAXFILE]; 57 | int topfile=0; 58 | 59 | // File counter for defaulting name 60 | int filecounter=0; 61 | 62 | // Check existence of output path, create if needed 63 | void checkpath(char *path,long ts){ 64 | struct stat st; 65 | int res = stat(path,&st); 66 | int x=0; 67 | // Update directory list 68 | while(x < topdir){ 69 | if(strcmp(dlist[x].ufn,path) == 0){ 70 | break; 71 | } 72 | x++; 73 | } 74 | if(x == topdir){ 75 | // New dir 76 | strncpy(dlist[x].ufn,path,512); 77 | dlist[x].ts = ts; 78 | topdir++; 79 | }else{ 80 | if(dlist[x].ts < ts){ 81 | // "Older" dir 82 | dlist[x].ts = ts; 83 | } 84 | } 85 | if(res != 0){ 86 | // Is it something other than nonexistent? 87 | if(errno != ENOENT){ 88 | perror("stat()"); 89 | exit(-1); 90 | } 91 | // Doesn't exist, create it 92 | res = mkdir(path,0777); 93 | if(res != 0){ 94 | perror("mkdir()"); 95 | exit(-1); 96 | } 97 | }else{ 98 | // It exists, we should check for directory-ness but I am supremely lazy. 99 | } 100 | } 101 | 102 | // Process the file 103 | int process(char *name){ 104 | FILE *fd,*ofd,*of2; 105 | unsigned char block[514]; 106 | char *headerword; 107 | size_t inb; 108 | // Header information 109 | char opath[512]; // Original path 110 | char oname[64]; // Original name 111 | char otype[64]; // Original type 112 | char over[64]; // Original version 113 | int ovn; // Original version (number) 114 | long octs; // Original creation timestamp 115 | int obytes,oblocks; // Original file size in bytes/blocks 116 | int obsize; // Original Byte Size 117 | int xbytes,xblocks; // Bytes/blocks xferd 118 | int charmode = 0; // Character translation mode 119 | char ufn[512]; // Unix path and filename 120 | char uf2[512]; // Unix path and filename for of2 121 | int blksize = 514; // Read block size 122 | int x=0; // Scratch 123 | struct utimbuf utb; // utime() buffer 124 | 125 | of2 = NULL; // Clobber of2 126 | fd = fopen(name,"r"); 127 | if(fd == NULL){ 128 | perror("fopen()"); 129 | return(-1); 130 | } 131 | // There's only one header to a file. It's in the first block. 132 | // The LMI files were padded by two bytes every 512 due to an FTP transfer screw-up. The extra bytes should be CR/LF. 133 | // Oh, but some files are different! 134 | inb = fread(block,1,blksize,fd); 135 | if(inb < blksize){ 136 | if(feof(fd)){ 137 | printf("Unexpected EOF: Expected %d bytes, got %d\n",blksize,(int)inb); 138 | if(inb == 0){ 139 | printf("Skipping...\n"); 140 | fclose(fd); 141 | return(0); 142 | } 143 | }else{ 144 | perror("fread()"); 145 | } 146 | return(-1); 147 | } 148 | // Check for block tail 149 | if(block[512] != 0x0D && block[513] != 0x0A){ 150 | // Bad tail? 151 | if(block[512] == 0x0A && block[513] == 0x00){ 152 | // This is LF padding! 153 | blksize = 513; 154 | fseek(fd,-1,SEEK_CUR); // Back up one byte 155 | }else{ 156 | if(block[512] == 0x00 && block[513] == 0x00){ 157 | // No padding! 158 | blksize = 512; 159 | fseek(fd,-2,SEEK_CUR); // Back up two bytes 160 | }else{ 161 | printf("Unexpected tail, expected 0D 0A or 0A 00, got %X %X\n",block[512],block[513]); 162 | return(-1); 163 | } 164 | } 165 | } 166 | // Check for existence of header 167 | if(strncmp(block,"LMFL",4) != 0){ 168 | printf("Unexpected header, expected 4C 4D 46 4C, got %X %X %X %X\n",block[0],block[1],block[2],block[3]); 169 | printf("Skipping...\n"); 170 | fclose(fd); 171 | return(0); 172 | } 173 | // We have a valid header. Process it. 174 | // Make log entry... 175 | fprintf(logfd,"%s (%d-byte blocks)\n%s\n",name,blksize,block); 176 | /* 177 | A header looks like these: 178 | LMFL(:DEVICE "DSK" :DIRECTORY ("L" "NETWORK" "SERIAL-IP") :NAME "SERIAL-PROTOCOL" :TYPE "LISP" :VERSION 42 :CHARACTERS T :CREATION-DATE 2786122897 :AUTHOR "keith" :LENGTH-IN-BYTES 17028 :LENGTH-IN-BLOCKS 17 :BYTE-SIZE 8) 179 | LMFL(:DEVICE "DSK" :DIRECTORY ("L" "IO" "FILE") :NAME "ACCESS" :TYPE "QFASL" :VERSION 39 :CREATION-DATE 2803576763 :AUTHOR "keith" :LENGTH-IN-BYTES 28345 :LENGTH-IN-BLOCKS 56 :BYTE-SIZE 16) 180 | LMFL(:DEVICE "DSK" :DIRECTORY ("L" "IO" "FILE") :NAME "BALDIR" :TYPE "LISP" :VERSION 118 :DONT-DELETE T :CHARACTERS T :CREATION-DATE 2747449977 :AUTHOR "pld" :LENGTH-IN-BYTES 28438 :LENGTH-IN-BLOCKS 28 :BYTE-SIZE 8) 181 | The header is null-terminated in the block. 182 | */ 183 | // Default file name, type, and version 184 | filecounter++; 185 | sprintf(oname,"FILE%d",filecounter); 186 | strncpy(otype,"BIN",64); 187 | strncpy(over,"1",64); 188 | // Now parse header 189 | headerword = strtok(block+5," "); 190 | while(headerword != NULL){ 191 | int found = 0; 192 | if(strcmp(headerword,":DEVICE") == 0){ 193 | // Device name 194 | headerword = strtok(NULL,"\" "); 195 | // printf("DEVICE: %s\n",headerword); 196 | found = 1; 197 | } 198 | if(strcmp(headerword,":DIRECTORY") == 0){ 199 | // Directory path. This is NOT ALWAYS a list! 200 | if(headerword[11] == '('){ 201 | // List 202 | headerword = strtok(NULL,"()"); 203 | }else{ 204 | // String 205 | headerword = strtok(NULL," "); 206 | } 207 | printf("DIRECTORY: %s\n",headerword); 208 | strncpy(opath,headerword,512); 209 | found = 1; 210 | } 211 | if(strcmp(headerword,":NAME") == 0){ 212 | // File name. These can have embedded spaces. 213 | headerword = strtok(NULL,"\""); 214 | printf("NAME: %s\n",headerword); 215 | strncpy(oname,headerword,64); 216 | found = 1; 217 | } 218 | if(strcmp(headerword,":TYPE") == 0){ 219 | // File type. (the extension) 220 | headerword = strtok(NULL,"\" "); 221 | printf("TYPE: %s\n",headerword); 222 | strncpy(otype,headerword,64); 223 | found = 1; 224 | } 225 | if(strcmp(headerword,":VERSION") == 0){ 226 | // Version number 227 | headerword = strtok(NULL,"\" "); 228 | printf("VERSION: %s\n",headerword); 229 | strncpy(over,headerword,64); 230 | ovn = atoi(over); 231 | found = 1; 232 | } 233 | if(strcmp(headerword,":CHARACTERS") == 0){ 234 | // Indicates text-ness? 235 | headerword = strtok(NULL,"\" "); 236 | printf("CHARACTERS: %s\n",headerword); 237 | charmode = 1; 238 | found = 1; 239 | } 240 | if(strcmp(headerword,":CREATION-DATE") == 0){ 241 | // File creation date 242 | headerword = strtok(NULL,"\" "); 243 | // printf("CREATION-DATE: %s\n",headerword); 244 | octs = atol(headerword); // Get lisp timestamp 245 | octs -= 2208988800; // Convert to unix timestamp 246 | found = 1; 247 | } 248 | if(strcmp(headerword,":AUTHOR") == 0){ 249 | // Author 250 | headerword = strtok(NULL,"\" "); 251 | // printf("AUTHOR: %s\n",headerword); 252 | found = 1; 253 | } 254 | if(strcmp(headerword,":SIZE") == 0){ 255 | // From the dist tape. Size in 512-byte blocks. 256 | // Size will be doubled by byte-size modifier later. 257 | headerword = strtok(NULL,"\" "); 258 | printf("SIZE: %s\n",headerword); 259 | obytes = atoi(headerword); 260 | oblocks = obytes; 261 | obytes *= 512; 262 | found = 1; 263 | } 264 | if(strcmp(headerword,":LENGTH-IN-BYTES") == 0){ 265 | // Length in bytes 266 | headerword = strtok(NULL,"\" "); 267 | printf("LENGTH-IN-BYTES: %s\n",headerword); 268 | obytes = atoi(headerword); 269 | found = 1; 270 | } 271 | if(strcmp(headerword,":LENGTH-IN-BLOCKS") == 0){ 272 | // Length in blocks 273 | headerword = strtok(NULL,"\" "); 274 | printf("LENGTH-IN-BLOCKS: %s\n",headerword); 275 | oblocks = atoi(headerword); 276 | found = 1; 277 | } 278 | if(strcmp(headerword,":BYTE-SIZE") == 0){ 279 | // Size of a byte 280 | headerword = strtok(NULL,"\") "); 281 | printf("BYTE-SIZE: %s\n",headerword); 282 | obsize = atoi(headerword); 283 | if(obsize == 16){ 284 | obytes *= 2; 285 | } 286 | if(obsize != 8 && obsize != 16){ 287 | printf("Unsupported byte size %s\n",headerword); 288 | return(-1); 289 | } 290 | found = 1; 291 | } 292 | if(strcmp(headerword,":PROTECTION") == 0){ 293 | // Numeric protection mask 294 | headerword = strtok(NULL,"\" "); 295 | // printf("PROTECTION: %s\n",headerword); 296 | found = 1; 297 | } 298 | if(strcmp(headerword,":GENERATION-RETENTION-COUNT") == 0 || strcmp(headerword,":GENERATION-RETENTATION-COUNT") == 0){ 299 | // How many versions of this file to keep 300 | headerword = strtok(NULL,"\" "); 301 | // printf("GENERATION-RETENTION-COUNT: %s\n",headerword); 302 | found = 1; 303 | } 304 | if(strcmp(headerword,":BLOCK-SIZE") == 0){ 305 | // File block size, nothing to do with tape. 306 | headerword = strtok(NULL,"\" "); 307 | // printf("BLOCK-SIZE: %s\n",headerword); 308 | found = 1; 309 | } 310 | if(strcmp(headerword,":REFERENCE-DATE") == 0){ 311 | // ??? 312 | headerword = strtok(NULL,"\" "); 313 | // printf("REFERENCE-DATE: %s\n",headerword); 314 | found = 1; 315 | } 316 | if(strcmp(headerword,":ACCOUNT") == 0){ 317 | // Disk usage accounting? 318 | // Some wiseacre used this to put cute strings in the directory list. 319 | // :ACCOUNT "/^V/"W/^Vi/^Vn/^Vn/^Vi/^Vn/^Vg/^V A/^Vw/^Va/^Vy/^V!/^V/"" :C 320 | headerword = strtok(NULL,"\" "); 321 | // printf("ACCOUNT: %s\n",headerword); 322 | // Try to unscrew up that mess. We don't NEED the value so there's no point to dealing with all the escaping crud. 323 | // Obtain next token 324 | headerword = strtok(NULL," "); 325 | // If it's not the next keyword and we haven't hit the end of the string, try again 326 | while(headerword != NULL && headerword[0] != ':'){ 327 | headerword = strtok(NULL," "); 328 | } 329 | continue; 330 | } 331 | if(strcmp(headerword,":DONT-DELETE") == 0){ 332 | // Delete prevention flag? 333 | headerword = strtok(NULL,"\" "); 334 | // printf("DONT-DELETE: %s\n",headerword); 335 | found = 1; 336 | } 337 | if(strcmp(headerword,":T") == 0){ 338 | // No idea what this is 339 | headerword = strtok(NULL,"\" "); 340 | // printf("T: %s\n",headerword); 341 | found = 1; 342 | } 343 | if(strcmp(headerword,":PHYSICAL-VOLUME-FREE-BLOCKS") == 0){ 344 | // Why is this here? 345 | headerword = strtok(NULL,"\" "); 346 | // printf("PHYSICAL-VOLUME-FREE-BLOCKS: %s\n",headerword); 347 | found = 1; 348 | } 349 | if(strcmp(headerword,":QFASLP") == 0){ 350 | // QFASL-ness? 351 | headerword = strtok(NULL,"\" "); 352 | // printf("QFASLP: %s\n",headerword); 353 | found = 1; 354 | } 355 | if(strcmp(headerword,":BACKUP-DATE") == 0){ 356 | headerword = strtok(NULL,"\" "); 357 | // printf("BACKUP-DATE: %s\n",headerword); 358 | found = 1; 359 | } 360 | if(strcmp(headerword,":DONT-SUPERSEDE") == 0){ 361 | headerword = strtok(NULL,"\" "); 362 | // printf("DONT-SUPERSEDE: %s\n",headerword); 363 | found = 1; 364 | } 365 | if(strcmp(headerword,":LMI-TID") == 0){ 366 | headerword = strtok(NULL,"\" "); 367 | // printf("LMI-TID: %s\n",headerword); 368 | found = 1; 369 | } 370 | if(strcmp(headerword,":DONT-REAP") == 0){ 371 | // Did this come from ITS? 372 | headerword = strtok(NULL,"\" "); 373 | // printf("DONT-REAP: %s\n",headerword); 374 | found = 1; 375 | } 376 | if(strcmp(headerword,":NOT-BACKED-UP") == 0){ 377 | // It is now! 378 | headerword = strtok(NULL,"\" "); 379 | // printf("NOT-BACKED-UP: %s\n",headerword); 380 | found = 1; 381 | } 382 | if(strcmp(headerword,":TEMPORARY") == 0){ 383 | headerword = strtok(NULL,"\" "); 384 | // printf("TEMPORARY: %s\n",headerword); 385 | found = 1; 386 | } 387 | if(strcmp(headerword,":COMMENT") == 0){ 388 | // Partition comment 389 | headerword = strtok(NULL,"\""); 390 | // strncpy(oname,headerword,64); 391 | // printf("COMMENT: %s\n",headerword); 392 | found = 1; 393 | } 394 | if(strcmp(headerword,":PARTITION") == 0){ 395 | // Indicates this is a disk partition and not a file 396 | headerword = strtok(NULL,"\" "); 397 | strncpy(otype,"PART",64); 398 | strncpy(over,"1",64); 399 | ovn = 1; 400 | // printf("COMMENT: %s\n",headerword); 401 | found = 1; 402 | } 403 | if(strcmp(headerword,":TAPE-NUMBER") == 0){ 404 | headerword = strtok(NULL,"\" "); 405 | // printf("TAPE-NUMBER: %s\n",headerword); 406 | found = 1; 407 | } 408 | if(strcmp(headerword,":TAPE-ID") == 0){ 409 | headerword = strtok(NULL,"\""); 410 | // printf("TAPE-ID: %s\n",headerword); 411 | // strncpy(oname,headerword,64); 412 | found = 1; 413 | } 414 | if(strcmp(headerword,")") == 0){ 415 | // End of the header 416 | found = 1; 417 | } 418 | /* 419 | if(strcmp(headerword,":X") == 0){ 420 | // 421 | headerword = strtok(NULL,"\" "); 422 | printf(": %s\n",headerword); 423 | found = 1; 424 | } 425 | */ 426 | if(found == 1){ 427 | headerword = strtok(NULL," "); 428 | }else{ 429 | printf("Unknown header keyword %s\n",headerword); 430 | return(-1); 431 | } 432 | } 433 | // Check for mode screw 434 | if(strncmp(otype,"LISP",4) == 0 && charmode != 1){ 435 | printf("Filetype LISP without charmode: Forcing charmode\n"); 436 | charmode = 1; 437 | } 438 | // The header is read in. Now build the output path and filename, 439 | // creating directories (if necessary) as we go 440 | strcpy(ufn,"../ext/"); 441 | headerword = strtok(opath,"\""); 442 | while(headerword != NULL){ 443 | x=0; 444 | while(x < strlen(headerword)){ 445 | headerword[x] = tolower(headerword[x]); 446 | x++; 447 | } 448 | strcat(ufn,headerword); 449 | checkpath(ufn,octs); 450 | strcat(ufn,"/"); 451 | headerword = strtok(NULL,"\" "); 452 | } 453 | // Convert name and type to lowercase 454 | x=0; 455 | while(x < strlen(oname)){ 456 | oname[x] = tolower(oname[x]); 457 | x++; 458 | } 459 | x=0; 460 | while(x < strlen(otype)){ 461 | otype[x] = tolower(otype[x]); 462 | x++; 463 | } 464 | // Append filename, type 465 | strcat(ufn,oname); 466 | strcat(ufn,"."); 467 | strcat(ufn,otype); 468 | // At this point, check if we already wrote this file 469 | x = 0; 470 | while(x < topfile){ 471 | if(strcmp(fdir[x].ufn,ufn) == 0){ 472 | break; 473 | } 474 | x++; 475 | } 476 | if(x == topfile){ 477 | // This is a new file. 478 | strncpy(fdir[x].ufn,ufn,512); 479 | fdir[x].version = ovn; 480 | topfile++; 481 | strncpy(uf2,ufn,512); 482 | of2 = fopen(uf2,"w"); 483 | if(of2 == NULL){ 484 | perror("fopen()"); 485 | exit(-1); 486 | } 487 | }else{ 488 | if(fdir[x].version < ovn){ 489 | // This is a newer file 490 | fdir[x].version = ovn; 491 | strncpy(uf2,ufn,512); 492 | of2 = fopen(uf2,"w"); 493 | if(of2 == NULL){ 494 | perror("fopen()"); 495 | exit(-1); 496 | } 497 | } 498 | } 499 | // Append version number 500 | strcat(ufn,"."); 501 | strcat(ufn,over); 502 | printf("OFN: %s\n",ufn); 503 | fprintf(logfd," -> %s\n\n",ufn); 504 | // Obtain output FD 505 | ofd = fopen(ufn,"w"); 506 | if(ofd == NULL){ 507 | perror("fopen()"); 508 | exit(-1); 509 | } 510 | xbytes = 0; xblocks=1; 511 | while(xbytes < obytes){ 512 | size_t outb,xsize; 513 | // Obtain input 514 | inb = fread(block,1,blksize,fd); 515 | if(inb < blksize){ 516 | if(feof(fd)){ 517 | printf("Unexpected EOF: Expected %d bytes, got %d\n",blksize,(int)inb); 518 | }else{ 519 | perror("fread()"); 520 | } 521 | return(-1); 522 | } 523 | xblocks++; 524 | // Data starts 8 blocks in? 525 | if(xblocks < 9){ continue; } 526 | // Generate output 527 | if(xbytes+512 > obytes){ 528 | xsize = obytes-xbytes; 529 | }else{ 530 | xsize = 512; 531 | } 532 | // Charmode translation 533 | if(charmode == 1){ 534 | x=0; 535 | while(x < xsize){ 536 | if((block[x]&0x80) != 0){ 537 | // Just unset the high bit? 538 | block[x] &= 0x7F; 539 | // CR becomes LF 540 | if(block[x] == 0x0D){ block[x] = 0x0A; } 541 | } 542 | x++; 543 | } 544 | } 545 | // printf("BLOCK %d: XByte %d OByte %d (Xsize %d)\n",xblocks,xbytes,obytes,(int)xsize); 546 | outb = fwrite(block,1,xsize,ofd); 547 | if(of2 != NULL){ 548 | outb = fwrite(block,1,xsize,of2); 549 | } 550 | xbytes += xsize; 551 | } 552 | // Fill out times 553 | utb.actime = octs; 554 | utb.modtime = octs; 555 | // All done. Close files and set times. 556 | fclose(ofd); 557 | if(octs > 0){ 558 | utime(ufn,&utb); 559 | if(of2 != NULL){ 560 | fclose(of2); 561 | utime(uf2,&utb); 562 | } 563 | } 564 | // Next file! 565 | fclose(fd); 566 | return(0); 567 | } 568 | 569 | // Walk a directory looking for files we want 570 | void walkdir(DIR *d,char *path){ 571 | char nextpath[1024]; 572 | DIR *nextdir = NULL; 573 | struct dirent *ent; 574 | while(ent = readdir(d)){ 575 | // Is this a directory? (linux/bsd specific) 576 | if(ent->d_type == DT_DIR){ 577 | // If it starts with a dot, disregard it. 578 | if(ent->d_name[0] == '.'){ continue; } 579 | // Walk it 580 | sprintf(nextpath,"%s/%s",path,ent->d_name); 581 | nextdir = opendir(nextpath); 582 | if(nextdir == NULL){ 583 | perror("opendir()"); 584 | exit(-1); 585 | } 586 | walkdir(nextdir,nextpath); 587 | closedir(nextdir); 588 | }else{ 589 | // It's probably a normal file. Does the name end in .DAT? 590 | char ext[5]; 591 | strncpy(ext,ent->d_name+(strlen(ent->d_name)-4),4); 592 | if(strcasecmp(ext,".DAT")==0){ 593 | // Yes, this is what we want 594 | sprintf(nextpath,"%s/%s",path,ent->d_name); 595 | if(process(nextpath) < 0){ 596 | printf("Failed to process file %s\n",nextpath); 597 | exit(-1); 598 | } 599 | } 600 | } 601 | } 602 | } 603 | 604 | // Main execution 605 | int main(){ 606 | int x=0; 607 | struct utimbuf utb; // utime() buffer 608 | 609 | // Create output path if it doesn't exist 610 | checkpath("../ext",0); 611 | // Create log file 612 | logfd = fopen("../ext/decode_lmfl.log","w"); 613 | if(logfd == NULL){ 614 | perror("fopen()"); 615 | exit(-1); 616 | } 617 | // Open directory 618 | DIR *d = opendir("."); 619 | if(d == NULL){ 620 | perror("opendir()"); 621 | return(-1); 622 | } 623 | // These boots were made fer walkin'... 624 | walkdir(d,"."); 625 | // All done! 626 | closedir(d); 627 | // Write back directory times 628 | while(x < topdir){ 629 | if(dlist[x].ts > 0){ 630 | utb.actime = utb.modtime = dlist[x].ts; 631 | utime(dlist[x].ufn,&utb); 632 | } 633 | x++; 634 | } 635 | // Finished 636 | fclose(logfd); 637 | return(0); 638 | } 639 | -------------------------------------------------------------------------------- /src/vcmem.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 2 | Daniel Seagraves 3 | Barry Silverman 4 | 5 | This file is part of LambdaDelta. 6 | 7 | LambdaDelta is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | LambdaDelta is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with LambdaDelta. If not, see . 19 | */ 20 | 21 | /* VCMEM (console) Implementation */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "ld.h" 30 | #include "nubus.h" 31 | #include "vcmem.h" 32 | 33 | // State for two controllers 34 | struct vcmemState vcS[2]; 35 | // PROM 36 | uint8_t VCMEM_ROM[2048]; 37 | // static uint8_t prom_string[0x12] = "PROTOTYPE VCMEM"; 38 | // Externals 39 | extern int ld_die_rq; 40 | // Kernel interface items 41 | extern int cp_state[2]; 42 | extern uint8_t keyboard_io_ring[2][0x100]; 43 | extern uint8_t keyboard_io_ring_top[2],keyboard_io_ring_bottom[2]; 44 | extern uint8_t mouse_io_ring[2][0x100]; 45 | extern uint8_t mouse_io_ring_top[2],mouse_io_ring_bottom[2]; 46 | 47 | // Functions 48 | void vcmem_init(int vn,int slot){ 49 | vcS[vn].Card = slot; 50 | vcS[vn].cycle_count = 0; 51 | // The SDU is supposed to have initialized the vcmem before we get here. 52 | vcS[vn].MemoryControl.MemCopy = 1; 53 | int x = 0; 54 | // Clobber A memory 55 | while(x < FB_SIZE){ 56 | vcS[vn].AMemory[x] = 0; 57 | x++; 58 | } 59 | } 60 | 61 | void kb_put_char(int vn,uint8_t ch){ 62 | keyboard_io_ring[vn][keyboard_io_ring_top[vn]] = ch; 63 | keyboard_io_ring_top[vn]++; 64 | } 65 | 66 | void vcmem_kb_int(int vn){ 67 | // We should test the global enable in the function register first, 68 | // but it hasn't been touched yet 69 | if(vcS[vn].MemoryControl.InterruptEnabled != 0){ 70 | vcS[vn].InterruptStatus.SerialFIFOFull = 1; 71 | if(NUbus_Busy == 0){ 72 | nubus_io_request(VM_WRITE,0xF4,vcS[vn].InterruptAddr,0xFFFFFFFF); 73 | // logmsgf(LT_VCMEM,,"VCMEM: Int generated\n"); 74 | }else{ 75 | logmsgf(LT_VCMEM,0,"VCMEM: KB int while bus busy\n"); 76 | } 77 | } 78 | } 79 | // has last write whatsoever (except 0 and 0x70) 80 | unsigned char last_kbd_ctrl_write = 0; 81 | 82 | void vcmem_clock_pulse(int vn){ 83 | // Time has passed... 84 | vcS[vn].cycle_count++; 85 | // There are 5000000 cycles per second, so 83335 per blank 86 | if(vcS[vn].cycle_count >= 83335){ 87 | // We should test the global enable in the function register first, but it hasn't been touched yet 88 | if(vcS[vn].MemoryControl.InterruptEnabled != 0){ 89 | // Vertical Blank 90 | vcS[vn].InterruptStatus.VerticalBlank = 1; 91 | if(NUbus_Busy == 0){ 92 | // We can has bus 93 | nubus_io_request(VM_WRITE,0xF4,vcS[vn].InterruptAddr,0xFFFFFFFF); 94 | vcS[vn].cycle_count = 0; 95 | // logmsgf(LT_VCMEM,,"VCMEM: VB Int generated\n"); 96 | } 97 | }else{ 98 | vcS[vn].cycle_count = 0; // No interrupt, carry on 99 | } 100 | } 101 | // *** NUBUS SLAVE *** 102 | // If the bus is busy and not acknowledged... 103 | if(NUbus_Busy == 2 && NUbus_acknowledge == 0){ 104 | // Is it us? 105 | if(NUbus_Address.Card == vcS[vn].Card){ 106 | switch(NUbus_Address.Addr){ 107 | // Registers 108 | // 00 = Function register 109 | case 0x00: // 00 = Function Register 110 | if(NUbus_Request == VM_BYTE_READ){ 111 | logmsgf(LT_VCMEM,10,"VCMEM: Function Reg Byte Read\n"); 112 | NUbus_Data.byte[0] = vcS[vn].Function.byte[0]; 113 | NUbus_acknowledge=1; 114 | return; 115 | } 116 | if(NUbus_Request == VM_BYTE_WRITE){ 117 | vcS[vn].Function.byte[0] = NUbus_Data.byte[0]; 118 | if(NUbus_trace == 1){ 119 | logmsgf(LT_VCMEM,10,"VCMEM: Function Reg Byte Write: "); 120 | if(vcS[vn].Function.Reset != 0){ logmsgf(LT_VCMEM,10," RESET"); } 121 | if(vcS[vn].Function.BoardEnable != 0){ logmsgf(LT_VCMEM,10," ENABLE"); } 122 | if(vcS[vn].Function.LED != 0){ logmsgf(LT_VCMEM,10," LED"); } 123 | logmsgf(LT_VCMEM,10," FUNCTION=%d\n",vcS[vn].Function.Function); 124 | } 125 | NUbus_acknowledge=1; 126 | return; 127 | } 128 | 129 | break; 130 | case 0x01: 131 | if(NUbus_Request == VM_BYTE_READ){ 132 | logmsgf(LT_VCMEM,10,"VCMEM: Function Reg Hi Byte Read\n"); 133 | NUbus_Data.byte[1] = vcS[vn].Function.byte[1]; 134 | NUbus_acknowledge=1; 135 | return; 136 | } 137 | if(NUbus_Request == VM_BYTE_WRITE){ 138 | logmsgf(LT_VCMEM,10,"VCMEM: Function Reg Hi Byte Write\n"); 139 | vcS[vn].Function.byte[1] = NUbus_Data.byte[1]; 140 | NUbus_acknowledge=1; 141 | return; 142 | } 143 | break; 144 | 145 | case 0x04: // 04 = Memory Control register 146 | if(NUbus_Request == VM_READ || NUbus_Request == VM_BYTE_READ){ 147 | logmsgf(LT_VCMEM,10,"VCMEM: Memory Control Reg Read\n"); 148 | NUbus_Data.word = vcS[vn].MemoryControl.raw; 149 | NUbus_acknowledge=1; 150 | return; 151 | } 152 | if(NUbus_Request == VM_WRITE || NUbus_Request == VM_BYTE_WRITE){ 153 | logmsgf(LT_VCMEM,10,"VCMEM: Memory Control Reg Write: 0x%X\n",NUbus_Data.word); 154 | vcS[vn].MemoryControl.raw = NUbus_Data.word; 155 | set_bow_mode(vn,vcS[vn].MemoryControl.ReverseVideo ? 0 : 1); // Update black-on-white mode 156 | NUbus_acknowledge=1; 157 | return; 158 | } 159 | break; 160 | case 0x05: 161 | if(NUbus_Request == VM_BYTE_READ){ 162 | logmsgf(LT_VCMEM,10,"VCMEM: Memory Control Reg Hi Byte Read\n"); 163 | NUbus_Data.byte[1] = vcS[vn].MemoryControl.byte[1]; 164 | NUbus_acknowledge=1; 165 | return; 166 | } 167 | if(NUbus_Request == VM_BYTE_WRITE){ 168 | logmsgf(LT_VCMEM,10,"VCMEM: Memory Control Reg Hi Byte Write\n"); 169 | vcS[vn].MemoryControl.byte[1] = NUbus_Data.byte[1]; 170 | set_bow_mode(vn,vcS[vn].MemoryControl.ReverseVideo ? 0 : 1); // Update black-on-white mode 171 | NUbus_acknowledge=1; 172 | return; 173 | } 174 | break; 175 | 176 | 177 | case 0x08 ... 0x0B: // Interrupt Address 178 | if(NUbus_Request == VM_WRITE && NUbus_Address.Byte == 0){ 179 | logmsgf(LT_VCMEM,10,"VCMEM: Interrupt Address Reg Write: 0x%X\n",NUbus_Data.word); 180 | vcS[vn].InterruptAddr = NUbus_Data.word; 181 | NUbus_acknowledge=1; 182 | return; 183 | } 184 | if(NUbus_Request == VM_BYTE_READ){ 185 | uint32_t Word = 0; 186 | Word = vcS[vn].InterruptAddr; 187 | Word >>= (8*NUbus_Address.Byte); 188 | NUbus_Data.byte[NUbus_Address.Byte] = (Word&0xFF); 189 | logmsgf(LT_VCMEM,10,"VCMEM: Interrupt Address Reg Byte Read, returning 0x%X\n", 190 | NUbus_Data.byte[NUbus_Address.Byte]); 191 | NUbus_acknowledge=1; 192 | return; 193 | } 194 | if(NUbus_Request == VM_BYTE_WRITE){ 195 | uint32_t Word = NUbus_Data.byte[NUbus_Address.Byte]; 196 | uint32_t Mask = 0xFF; 197 | logmsgf(LT_VCMEM,10,"VCMEM: Interrupt Address Reg Byte Write: 0x%X\n", 198 | NUbus_Data.byte[NUbus_Address.Byte]); 199 | Mask <<= (8*NUbus_Address.Byte); 200 | vcS[vn].InterruptAddr &= ~Mask; 201 | Word <<= (8*NUbus_Address.Byte); 202 | vcS[vn].InterruptAddr |= Word; 203 | NUbus_acknowledge=1; 204 | return; 205 | } 206 | break; 207 | 208 | case 0x0C ... 0x0F: // Interrupt Status Register 209 | if(NUbus_Request == VM_READ && NUbus_Address.Byte == 0){ 210 | if(NUbus_trace == 1){ 211 | logmsgf(LT_VCMEM,10,"VCMEM: Interrupt Status Reg Read\n"); 212 | } 213 | NUbus_Data.word = vcS[vn].InterruptStatus.raw; 214 | NUbus_acknowledge=1; 215 | return; 216 | } 217 | if(NUbus_Request == VM_BYTE_READ){ 218 | if(NUbus_trace == 1){ 219 | logmsgf(LT_VCMEM,10,"VCMEM: Interrupt Status Reg Byte Read\n"); 220 | } 221 | NUbus_Data.byte[NUbus_Address.Byte] = vcS[vn].InterruptStatus.byte[NUbus_Address.Byte]; 222 | NUbus_acknowledge=1; 223 | return; 224 | } 225 | break; 226 | 227 | case 0x10: // Serial Port Control Register 228 | if(NUbus_Request == VM_WRITE){ 229 | logmsgf(LT_VCMEM,10,"VCMEM: Serial Port Control Reg Write: 0x%X\n",NUbus_Data.word); 230 | vcS[vn].SerialControl.raw = NUbus_Data.word; 231 | NUbus_acknowledge=1; 232 | return; 233 | } 234 | if(NUbus_Request == VM_BYTE_WRITE){ 235 | logmsgf(LT_VCMEM,10,"VCMEM: Serial Port Control Reg Byte Write: 0x%X\n",NUbus_Data.word); 236 | vcS[vn].SerialControl.byte[0] = NUbus_Data.byte[0]; 237 | NUbus_acknowledge=1; 238 | return; 239 | } 240 | if(NUbus_Request == VM_BYTE_READ){ 241 | logmsgf(LT_VCMEM,10,"VCMEM: Serial Port Control Reg Byte Read\n"); 242 | NUbus_Data.byte[NUbus_Address.Byte] = vcS[vn].SerialControl.byte[NUbus_Address.Byte]; 243 | NUbus_acknowledge=1; 244 | return; 245 | } 246 | break; 247 | case 0x11: // Serial Port Control Register (Hi) 248 | if(NUbus_Request == VM_BYTE_WRITE){ 249 | logmsgf(LT_VCMEM,10,"VCMEM: Serial Port Control Reg (Hi) Byte Write: 0x%X\n",NUbus_Data.word); 250 | vcS[vn].SerialControl.byte[1] = NUbus_Data.byte[1]; 251 | NUbus_acknowledge=1; 252 | return; 253 | } 254 | if(NUbus_Request == VM_BYTE_READ){ 255 | logmsgf(LT_VCMEM,10,"VCMEM: Serial Port Control Reg Byte Read\n"); 256 | NUbus_Data.byte[NUbus_Address.Byte] = vcS[vn].SerialControl.byte[NUbus_Address.Byte]; 257 | NUbus_acknowledge=1; 258 | return; 259 | } 260 | break; 261 | 262 | case 0x14: // Serial Port Transmit (keytty?) 263 | if(NUbus_Request == VM_BYTE_WRITE){ 264 | logmsgf(LT_VCMEM,10,"VCMEM: Serial Port Transmit Reg Byte Write: 0x%X\n",NUbus_Data.word); 265 | // Discard data 266 | NUbus_acknowledge=1; 267 | return; 268 | } 269 | if(NUbus_Request == VM_BYTE_READ){ 270 | logmsgf(LT_VCMEM,10,"VCMEM: Serial Port Transmit Reg Read\n"); 271 | if(keyboard_io_ring_top[vn] != keyboard_io_ring_bottom[vn]){ 272 | NUbus_Data.word = 1; // We have a key 273 | }else{ 274 | NUbus_Data.word = 0; 275 | } 276 | NUbus_acknowledge=1; 277 | return; 278 | } 279 | break; 280 | 281 | case 0x18: // Serial Port Recieve (keytty keyboard?) 282 | if(NUbus_Request == VM_BYTE_READ){ 283 | if(NUbus_trace == 1){ 284 | logmsgf(LT_VCMEM,10,"VCMEM: Serial Port Recieve Reg Byte Read\n"); 285 | } 286 | NUbus_Data.word = 0; 287 | NUbus_acknowledge=1; 288 | return; 289 | } 290 | break; 291 | 292 | case 0x30: // Serial Port A (Keyboard) Data 293 | // See vcmem-serial-set-up-port 294 | if(NUbus_Request == VM_READ || NUbus_Request == VM_BYTE_READ){ 295 | if(NUbus_trace == 1){ 296 | logmsgf(LT_VCMEM,10,"VCMEM: Serial Port A Data Read\n"); 297 | } 298 | if(keyboard_io_ring_top[vn] != keyboard_io_ring_bottom[vn]){ 299 | NUbus_Data.word = keyboard_io_ring[vn][keyboard_io_ring_bottom[vn]]; 300 | // writeOct(keyboard_io_ring[keyboard_io_ring_bottom]); 301 | // logmsgf(LT_VCMEM,," from "); 302 | // writeDec(keyboard_io_ring_bottom); 303 | keyboard_io_ring_bottom[vn]++; 304 | }else{ 305 | // logmsgf(LT_VCMEM,,"0 from nowhere"); 306 | NUbus_Data.word = 0; 307 | } 308 | // logmsgf(LT_VCMEM,,"\n"); 309 | NUbus_acknowledge=1; 310 | return; 311 | } 312 | if(NUbus_Request == VM_WRITE || NUbus_Request == VM_BYTE_WRITE){ 313 | if(NUbus_trace == 1){ 314 | logmsgf(LT_VCMEM,10,"VCMEM: Serial Port A Data = 0x%X\n",NUbus_Data.byte[0]); 315 | } 316 | NUbus_acknowledge=1; 317 | return; 318 | } 319 | break; 320 | 321 | case 0x34: // Serial Port A (Keyboard) Command 322 | if(NUbus_Request == VM_WRITE || NUbus_Request == VM_BYTE_WRITE){ 323 | if(NUbus_trace == 1){ 324 | logmsgf(LT_VCMEM,10,"VCMEM: Serial Port A Command = 0x%X\n",NUbus_Data.byte[0]); 325 | } 326 | #ifndef XBEEP 327 | // Old beep hack, replaced by actual audio in SDL2 328 | // BV tracing beep, starting from uc-hacks: 329 | // It seems every other write is a register number, and the other is the value. 330 | // Register 5 controls the beep (at least): 331 | // #xea to turn it off, and #xfa to "click" ("send break" bit, cf uc-hacks) 332 | // A tv:beep (with default args) generates 78 times 0x05, 0xfa, 0x05, 0xea + ending 0x05, 0xea 333 | // Possible hack: track 0xea/0xfa writes, output \a when two successive 0xea 334 | // That works, but only for default tv:%beep args 335 | if(NUbus_Data.byte[0] != 0){ 336 | // many continuous 0 writes, it seems - ignore those 337 | if (last_kbd_ctrl_write == 0x05) { // last write whatsoever (except 0) 338 | // let kernel decide what to do 339 | audio_control(NUbus_Data.byte[0] == 0xfa); 340 | } 341 | last_kbd_ctrl_write = NUbus_Data.byte[0]; // remember last write 342 | } 343 | #endif 344 | NUbus_acknowledge=1; 345 | return; 346 | } 347 | if(NUbus_Request == VM_READ || NUbus_Request == VM_BYTE_READ){ 348 | if(NUbus_trace == 1){ 349 | logmsgf(LT_VCMEM,10,"VCMEM: Serial Port A Command Read: 0x"); 350 | } 351 | if(keyboard_io_ring_top[vn] != keyboard_io_ring_bottom[vn]){ 352 | NUbus_Data.word = 0x5; // We have a key, tx ready 353 | }else{ 354 | NUbus_Data.word = 0x4; // No key, tx ready 355 | } 356 | if(NUbus_trace == 1){ 357 | logmsgf(LT_VCMEM,10,"%X\n",NUbus_Data.word); 358 | } 359 | NUbus_acknowledge=1; 360 | return; 361 | } 362 | break; 363 | 364 | case 0x38: // Serial Port B (Mouse) Data 365 | if(NUbus_Request == VM_READ){ 366 | // logmsgf(LT_VCMEM,,"VCMEM: Serial Port B Data Read: Taking code "); 367 | if(mouse_io_ring_top[vn] != mouse_io_ring_bottom[vn]){ 368 | NUbus_Data.word = mouse_io_ring[vn][mouse_io_ring_bottom[vn]]; 369 | // writeOct(mouse_io_ring[mouse_io_ring_bottom]); 370 | // logmsgf(LT_VCMEM,," from "); 371 | // writeDec(mouse_io_ring_bottom); 372 | mouse_io_ring_bottom[vn]++; 373 | }else{ 374 | // logmsgf(LT_VCMEM,,"0 from nowhere"); 375 | NUbus_Data.word = 0; 376 | } 377 | // logmsgf(LT_VCMEM,,"\n"); 378 | NUbus_acknowledge=1; 379 | return; 380 | } 381 | break; 382 | 383 | case 0x3C: // Serial Port B (Mouse) Command 384 | if(NUbus_Request == VM_WRITE){ 385 | if(NUbus_trace == 1){ 386 | logmsgf(LT_VCMEM,10,"VCMEM: Serial Port B Command = 0x%X\n",NUbus_Data.byte[0]); 387 | } 388 | if(cp_state[vn] == 2){ 389 | cp_state[vn] = 3; 390 | logmsgf(LT_VCMEM,1,"MOUSE: INIT\n"); 391 | } 392 | NUbus_acknowledge=1; 393 | return; 394 | } 395 | if(NUbus_Request == VM_READ){ 396 | if(NUbus_trace == 1){ 397 | logmsgf(LT_VCMEM,10,"VCMEM: Serial Port B Command Read\n"); 398 | } 399 | if(mouse_io_ring_top[vn] != mouse_io_ring_bottom[vn]){ 400 | NUbus_Data.word = 1; // We have a key 401 | }else{ 402 | NUbus_Data.word = 0; 403 | } 404 | NUbus_acknowledge=1; 405 | return; 406 | } 407 | break; 408 | 409 | // VCMEM has two memories, A and B. 410 | // The A memory is attached to the nubus and the B memory is displayed. 411 | 412 | // The scanline table is at 0x6000-0x7FFC 413 | // Each entry is the offset of the start of that scanline. 414 | case 0x6000 ... (0x6000+4*(SLT_SIZE)-1): 415 | if(NUbus_Request == VM_READ){ 416 | uint32_t Scanline = ((NUbus_Address.Addr-0x6000)>>2); 417 | // This is a mono framebuffer, and our resolution is 800 x 1024. 418 | // That's 80 bytes per line. 419 | NUbus_Data.word = vcS[vn].SLT[Scanline]; // 0x20000+(80*Scanline); 420 | if(NUbus_trace == 1){ 421 | logmsgf(LT_VCMEM,10,"VCMEM: SLT Read: Line 0x%X, returning 0x%X\n", 422 | Scanline,NUbus_Data.word); 423 | } 424 | NUbus_acknowledge=1; 425 | return; 426 | } 427 | if(NUbus_Request == VM_WRITE){ 428 | uint32_t Scanline = ((NUbus_Address.Addr-0x6000)>>2); 429 | if(NUbus_trace == 1){ 430 | logmsgf(LT_VCMEM,10,"VCMEM: SLT Write: Line 0x%X w/ data 0x%X\n", 431 | Scanline,NUbus_Data.word); 432 | } 433 | vcS[vn].SLT[Scanline] = NUbus_Data.word; 434 | NUbus_acknowledge=1; 435 | return; 436 | } 437 | if(NUbus_Request == VM_BYTE_READ){ 438 | uint32_t Scanline = ((NUbus_Address.Addr-0x6000)>>2); 439 | uint32_t Word = 0; 440 | Word = vcS[vn].SLT[Scanline]; 441 | Word >>= (8*NUbus_Address.Byte); 442 | NUbus_Data.byte[NUbus_Address.Byte] = (Word&0xFF); 443 | if(NUbus_trace == 1){ 444 | logmsgf(LT_VCMEM,10,"VCMEM: SLT Byte Read: Line 0x%X, returning 0x%X\n", 445 | Scanline,NUbus_Data.byte[NUbus_Address.Byte]); 446 | } 447 | NUbus_acknowledge=1; 448 | return; 449 | } 450 | if(NUbus_Request == VM_BYTE_WRITE){ 451 | uint32_t Scanline = ((NUbus_Address.Addr-0x6000)>>2); 452 | uint32_t Word = NUbus_Data.byte[NUbus_Address.Byte]; 453 | uint32_t Mask = 0xFF; 454 | Mask <<= (8*NUbus_Address.Byte); 455 | vcS[vn].SLT[Scanline] &= ~Mask; 456 | Word <<= (8*NUbus_Address.Byte); 457 | vcS[vn].SLT[Scanline] |= Word; 458 | if(NUbus_trace == 1){ 459 | logmsgf(LT_VCMEM,10,"VCMEM: SLT Byte Write: Line 0x%X w/ data 0x%X\n", 460 | Scanline,NUbus_Data.byte[NUbus_Address.Byte]); 461 | } 462 | NUbus_acknowledge=1; 463 | return; 464 | } 465 | break; 466 | 467 | // Framebuffer is at 0x20000-0x3FFFF 468 | case 0x20000 ... 0x20000+FB_SIZE-1: 469 | if(NUbus_Request == VM_READ || NUbus_Request == VM_BYTE_READ){ 470 | uint32_t FBAddr = NUbus_Address.Addr-0x20000; 471 | if(NUbus_Request == VM_READ){ // Read four bytes 472 | switch(NUbus_Address.Byte){ 473 | case 1: // Read Low Half 474 | NUbus_Data.hword[0] = *(uint16_t *)(vcS[vn].AMemory+(FBAddr-1)); 475 | break; 476 | 477 | case 2: // Block Transfer (ILLEGAL) 478 | logmsgf(LT_VCMEM,0,"VCMEM: BLOCK READ REQUESTED\n"); 479 | ld_die_rq=1; 480 | break; 481 | 482 | case 3: // Read High Half 483 | NUbus_Data.hword[1] = *(uint16_t *)(vcS[vn].AMemory+(FBAddr-1)); 484 | break; 485 | 486 | case 0: 487 | // Full word read 488 | NUbus_Data.word = *(uint32_t *)(vcS[vn].AMemory+FBAddr); 489 | break; 490 | } 491 | }else{ 492 | // BYTE READ 493 | NUbus_Data.byte[NUbus_Address.Byte] = vcS[vn].AMemory[FBAddr]; 494 | } 495 | NUbus_acknowledge=1; 496 | return; 497 | } 498 | if(NUbus_Request == VM_WRITE || NUbus_Request == VM_BYTE_WRITE){ 499 | uint32_t FBAddr = NUbus_Address.Addr-0x20000; 500 | if(NUbus_Request == VM_BYTE_WRITE){ 501 | switch(vcS[vn].Function.Function){ 502 | case 0: // XOR 503 | vcS[vn].AMemory[FBAddr] ^= NUbus_Data.byte[NUbus_Address.Byte]; 504 | break; 505 | case 1: // OR 506 | vcS[vn].AMemory[FBAddr] |= NUbus_Data.byte[NUbus_Address.Byte]; 507 | break; 508 | case 2: // AND 509 | vcS[vn].AMemory[FBAddr] &= ~NUbus_Data.byte[NUbus_Address.Byte]; 510 | break; 511 | case 3: // STORE 512 | vcS[vn].AMemory[FBAddr] = NUbus_Data.byte[NUbus_Address.Byte]; 513 | break; 514 | } 515 | framebuffer_update_byte(vn,FBAddr,vcS[vn].AMemory[FBAddr]); 516 | }else{ 517 | // WORD WRITE 518 | switch(NUbus_Address.Byte){ 519 | case 1: // Write low half 520 | switch(vcS[vn].Function.Function){ 521 | case 0: // XOR 522 | *(uint16_t *)(vcS[vn].AMemory+(FBAddr-1)) ^= NUbus_Data.hword[0]; 523 | break; 524 | case 1: // OR 525 | *(uint16_t *)(vcS[vn].AMemory+(FBAddr-1)) |= NUbus_Data.hword[0]; 526 | break; 527 | case 2: // AND 528 | *(uint16_t *)(vcS[vn].AMemory+(FBAddr-1)) &= ~NUbus_Data.hword[0]; 529 | break; 530 | case 3: // STORE 531 | *(uint16_t *)(vcS[vn].AMemory+(FBAddr-1)) = NUbus_Data.hword[0]; 532 | break; 533 | } 534 | framebuffer_update_hword(vn,FBAddr-1,NUbus_Data.hword[0]); 535 | break; 536 | 537 | case 2: // BLOCK TRANSFER (ILLEGAL) 538 | logmsgf(LT_VCMEM,0,"VCMEM: BLOCK TRANSFER REQUESTED\n"); 539 | ld_die_rq=1; 540 | break; 541 | 542 | case 3: // Write high half 543 | switch(vcS[vn].Function.Function){ 544 | case 0: // XOR 545 | *(uint16_t *)(vcS[vn].AMemory+(FBAddr-1)) ^= NUbus_Data.hword[1]; 546 | break; 547 | case 1: // OR 548 | *(uint16_t *)(vcS[vn].AMemory+(FBAddr-1)) |= NUbus_Data.hword[1]; 549 | break; 550 | case 2: // AND 551 | *(uint16_t *)(vcS[vn].AMemory+(FBAddr-1)) &= ~NUbus_Data.hword[1]; 552 | break; 553 | case 3: // STORE 554 | *(uint16_t *)(vcS[vn].AMemory+(FBAddr-1)) = NUbus_Data.hword[1]; 555 | break; 556 | } 557 | framebuffer_update_hword(vn,FBAddr-1,NUbus_Data.hword[1]); 558 | break; 559 | 560 | case 0: // Full Word 561 | switch(vcS[vn].Function.Function){ 562 | case 0: // XOR 563 | *(uint32_t *)(vcS[vn].AMemory+FBAddr) ^= NUbus_Data.word; 564 | break; 565 | case 1: // OR 566 | *(uint32_t *)(vcS[vn].AMemory+FBAddr) |= NUbus_Data.word; 567 | break; 568 | case 2: // AND 569 | *(uint32_t *)(vcS[vn].AMemory+FBAddr) &= ~NUbus_Data.word; 570 | break; 571 | case 3: // STORE 572 | *(uint32_t *)(vcS[vn].AMemory+FBAddr) = NUbus_Data.word; 573 | break; 574 | } 575 | framebuffer_update_word(vn,FBAddr,NUbus_Data.word); 576 | break; 577 | } 578 | } 579 | NUbus_acknowledge=1; 580 | return; 581 | } 582 | break; 583 | 584 | // Configuration PROM 585 | case 0xFFE000 ... 0xFFFFFF: 586 | if((NUbus_Request == VM_READ || NUbus_Request == VM_BYTE_READ) && NUbus_Address.Byte == 0){ 587 | uint32_t rom_addr = (NUbus_Address.Addr-0xffe000)/4; 588 | NUbus_Data.word = VCMEM_ROM[rom_addr]; 589 | /* 590 | logmsgf(LT_VCMEM,,"VCM: ROM READ 0x"); 591 | writeH32(NUbus_Address.Addr); 592 | logmsgf(LT_VCMEM,," = ROM ADDR "); 593 | writeH32(rom_addr); 594 | logmsgf(LT_VCMEM,,"\n"); 595 | */ 596 | /* 597 | uint8_t prom_addr = (NUbus_Address.Addr-0xfff800)/4; 598 | if(prom_addr <= 0x12){ 599 | NUbus_Data.word = prom_string[prom_addr]; 600 | }else{ 601 | NUbus_Data.word = 0; 602 | } 603 | */ 604 | NUbus_acknowledge=1; 605 | return; 606 | } 607 | break; 608 | 609 | // Serial number? Model number? 610 | /* 611 | case 0xFFFF64 ... 0xFFFFFF: 612 | if(NUbus_Request == VM_READ){ 613 | NUbus_Data.word = 0; 614 | NUbus_acknowledge=1; 615 | return; 616 | } 617 | break; 618 | */ 619 | 620 | default: 621 | logmsgf(LT_VCMEM,0,"VCMEM: Unimplemented address 0x%X\n",NUbus_Address.Addr); 622 | ld_die_rq = 1; 623 | } 624 | } 625 | } 626 | } 627 | --------------------------------------------------------------------------------