├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── editors └── ga-mode.el ├── examples ├── 3-node-ram-ASM.ga ├── 3-node-ram.ga ├── 600serial.ga ├── 708serial-ASM.ga ├── 708serial.ga ├── 715crystal.ga ├── README.md ├── adc_617.ga ├── counter.ga ├── fast-ram-node.ga ├── fibonacci.ga ├── hmm-sim.ga ├── port-execution.ga ├── racecar.ga ├── square_waves.ga ├── sram-demo.ga ├── sram-minimal-master.ga ├── sram.ga └── variables.ga ├── ga ├── ga_tools ├── __init__.py ├── assembler.py ├── bootstream.py ├── defs.py ├── f18a_asm.py ├── ga144_asm.py ├── ga144_rom.py ├── ga_serial.py ├── parse.py └── word.py ├── images ├── logo.svg ├── logo_150.png └── logo_v2.svg ├── setup.py └── tests ├── _test-include.ga ├── _test-include2.ga ├── test-chip.py └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | build 3 | *.pyc 4 | 5 | dist 6 | MANIFEST 7 | 8 | .emacs.desktop 9 | .emacs.desktop.lock 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | python3 setup.py build 4 | 5 | install: 6 | python3 setup.py install 7 | 8 | upload: 9 | python3 setup.py sdist 10 | twine upload dist/* 11 | 12 | test: 13 | python3 tests/test.py 14 | 15 | .PHONY: clean 16 | 17 | clean: 18 | rm -rf build 19 | rm -rf dist 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![logo](/images/logo_150.png) 3 | --- 4 | 5 | Alternative tools for the [GA144](http://www.greenarraychips.com/home/products/index.html) multi-computer chip using Python3. 6 | 7 | These tools are provided as a CLI script and a Python3 library. 8 | They support two forms of assembly, bootstream generation and loading. 9 | 10 | 1. [Installation](#Installation) 11 | 2. [First program](#First-program) 12 | 3. [Usage](#Usage) 13 | 1. [Use with Greenarrays evalboard](#Use-with-Greenarrays-evalboard) 14 | 4. [Assembly syntax](#Assembly-syntax) 15 | 1. [Aforth syntax](#aforth-syntax) 16 | 2. [ASM syntax](#ASM-syntax) 17 | 5. [Boot streams](#Boot-streams) 18 | 6. [Documentation](#Documentation) 19 | 7. [Example code](#Example-code) 20 | 8. [GA144 Simulation](#GA144-Simulation) 21 | 22 | # Installation 23 | To install from pypi, run: `pip3 install ga-tools` 24 | 25 | To install from source, first clone the repository and then run: `python setup.py install` 26 | 27 | This will install the `ga` cli script and the `ga_tools` Python3 library. 28 | 29 | # First program 30 | To check that everything works, first connect the GA144 eval board or chip. 31 | On GNU/Linux run `dmesg` to find the serial port it is connected on. 32 | Try running the fibonacci.ga example: 33 | ``` 34 | ga examples/fibonacci.ga --port /dev/ttyUSB0 35 | ``` 36 | Replace `/dev/ttyUSB0` with the correct serial port. 37 | This should print out the first 15 numbers of the fibonacci 38 | series before exiting. 39 | 40 | # Usage 41 | `ga -h` prints a summary of the cli options. 42 | 43 | ## Print assembly 44 | `ga FILE.ga --print` prints a summary of the assembled program 45 | for each node alongside its disassembly. 46 | 47 | Add the `--node N` option to print a single node. 48 | 49 | ## Program loading 50 | `ga FILE.ga --port NAME` streams the program into serial port NAME. 51 | 52 | Use `--bootstream TYPE` to specify the boot stream type, default is '708'. 53 | See [Boot streams](#Boot_streams) for more on the different boot stream types. 54 | 55 | `--baud` sets the serial baud rate. Default is 460800. 56 | The rate can range from 9600bps to 1Mbps. 57 | 58 | By default `ga` will listen for and print data words sent back from the ga144 after loading the problem. 59 | To disable that behavior, use option `--no-listen`. 60 | 61 | ## Show program size 62 | `ga FILE --size` prints the RAM usage of each node. 63 | ## JSON output 64 | `ga FILE.ga --json` prints the assembled program as JSON. 65 | The bootstream is included with the `--bootstream TYPE` option. 66 | 67 | Use the `--outfile NAME` option to direct the JSON output to a file. 68 | 69 | ## Use with Greenarrays evalboard 70 | 71 | To load code into the target chip via port A, or into the host 72 | chip via port C, use the 'async' boot stream: 73 | `ga FILE.ga --port /dev/ttyUSB0 --bootstream async` 74 | 75 | To load code into the target chip via port A, use the 'target' 76 | boot stream: 77 | `ga FILE.ga --port /dev/ttyUSB0 --bootstream target` 78 | 79 | ## Disabling optimization 80 | The `--disable-0-opt` option prevents compiling '0' as 'dup dup or'. 81 | The `--disable-plus-opt` options prevents automatic 82 | insertions of no-ops before the + or +* instructions. 83 | 84 | # Assembly syntax 85 | These tools provide support for two forms of assembly. 86 | The default form is called `aforth` and is similar to 87 | arrayforth supported by the tools and documentation 88 | provided by Greenarrays. 89 | 90 | The second form is called `ASM` and is closer to a traditional assembly 91 | language, with one line of source for every machine word. 92 | This form is used when full transparency is needed 93 | and can be useful for writing some kinds of optimizations. 94 | 95 | Both forms support the same basic instruction set. 96 | Read about it [here](https://mschuldt.github.io/www.colorforth.com/inst.htm) or in the [F18a Product Brief](http://www.greenarraychips.com/home/documents/greg/PB003-110412-F18A.pdf). 97 | 98 | Both forms are case insensitive. 99 | 100 | ## Aforth syntax 101 | Aforth provides convenient layer above raw assembly, 102 | bringing its syntax closer to that of the Forth. 103 | It allows reuse of existing code for the ga144 and helps with 104 | the use and comprehension of the valuable documentation from Greenarrays. 105 | Additionally aforth syntax is easier for compilers to generate. 106 | 107 | Aforth is intended to remain compatible with the output of the [chlorophyll](https://github.com/mangpo/chlorophyll) compiler. 108 | 109 | TODO 110 | ## ASM syntax 111 | To use asm syntax instead of aforth, use the `ASM` directive 112 | after specifying the node coordinate, for example: 113 | ``` 114 | node 508 ASM 115 | (...) 116 | ``` 117 | Each node that uses the asm syntax must be marked with `asm`. 118 | 119 | `node`, `chip`, and other directives must start on a new line. 120 | 121 | TODO 122 | # Boot streams 123 | 124 | A Boot stream is an instruction stream used to load programs 125 | into the ga144. 126 | You can read about the boot protocols [here](http://www.greenarraychips.com/home/documents/greg/BOOT-02.pdf) 127 | 128 | You need to pick the correct boot stream type depending 129 | what node you are loading the code into, and the chip topology 130 | when using multiple chips. 131 | 132 | The boot streams names for the `--bootstream` option are named 133 | after the node they are loaded into. 134 | The following types are supported: 135 | - `708` (alias `async`) Asynchronous serial boot. 136 | - `708-300` (alias `target`) For loading code into node 300 via 137 | another ga144 which is loaded from node 708. Use this for the 138 | target chip on the Greenarrays evalboard. 139 | - `300` 2-wire synchronous boot. Used by the `708-300` stream 140 | 141 | The boot stream type defaults to `708` with one chip 142 | and `708-300` with two chips. 143 | 144 | If stream `708-300` is used to load code into both chips, 145 | they must be named "host" and "target". The "target" chip 146 | gets its bootstream loaded through the "host" chip. 147 | 148 | 149 | # Documentation 150 | 151 | Greenarrays [documentation](http://www.greenarraychips.com/home/documents/index.html) is the valuable resource. 152 | 153 | For a quick intro read the [GA144 Product Brief](http://www.greenarraychips.com/home/documents/greg/PB001-100503-GA144-1-10.pdf) 154 | and the [F18a Product Brief](http://www.greenarraychips.com/home/documents/greg/PB003-110412-F18A.pdf) 155 | 156 | For more serious programming the [F18A Technology reference](http://www.greenarraychips.com/home/documents/greg/DB001-171107-F18A.pdf) 157 | and [GA144 Chip Reference](http://www.greenarraychips.com/home/documents/greg/DB002-110705-G144A12.pdf) 158 | are very useful. 159 | 160 | colorforth.com contained many pages useful for programming the ga144. 161 | The site is down so links here are for a mirrored copy, 162 | a list of useful links has been collected [here](https://github.com/mschuldt/www.colorforth.com). 163 | 164 | - [Instructions](https://mschuldt.github.io/www.colorforth.com/inst.htm) 165 | 166 | - [Arithmetic routines](https://mschuldt.github.io/www.colorforth.com/arith.htm) 167 | 168 | # GA144 Simulation 169 | 170 | These tools do not currently provide a simulator, but they do 171 | provide support for this emacs-based simulator: 172 | [github.com/mschuldt/ga144-sim](https://github.com/mschuldt/ga144-sim) 173 | 174 | After installation of that simulator, it can be run with: 175 | `ga FILENAME --sim` 176 | 177 | If the `--bootstream` option is provided it will simulate the 178 | loading of the entire boot stream.This is disabled by default 179 | as it's not usually interesting and is very slow. 180 | The boot stream is only supported through node 708 in simulation. 181 | 182 | # Example code 183 | The examples/ directory provides numerous example programs 184 | for the GA144. 185 | 186 | See [examples/README.md](examples/README.md) for a summary of each one. 187 | 188 | # Comparison to arrayforth 189 | This version of aforth differs from arrayforth supported by Greenarrays 190 | in several ways. Knowing the differences is helpful if you already 191 | know arrayforth or if you want to use the Greenarrays documentation. 192 | 193 | - No semantic color 194 | - standard forth syntax for words and comments 195 | - hex,bin literals: 0xN, 0bN 196 | - boot descriptors and other yellow words are reserved keywords. 197 | - `north`, `east`, `south`, and `west` 198 | get resolved to correct ports: `up`, `down`, `left`, or `right` 199 | - Each node has a seporate namespace 200 | - word@coord compiles a call to =word= in node =coord=. 201 | - The word `reclaim` has no use. 202 | - Automatic nop insertion. 203 | - Can be disabled. 204 | - Currently inserts nops even when not actually needed 205 | - Arguments follow the yellow words. 206 | For example, use `'node 715'` instead of `715 node'`. 207 | - Generalized host computations during compilation are not supported. 208 | The compiler is not a forth interpreter. 209 | - There are no grey words 210 | - Automatically shift words when destination address does not fit in word. 211 | arrayforth does not compile in such situations, manual word alignment is necessary 212 | - words may be called before their definition 213 | - aforth does not support multiple labels at the same location 214 | 215 | # Other GA144 tools 216 | 217 | * ga144tools [https://github.com/jamesbowman/ga144tools](https://github.com/jamesbowman/ga144tools) 218 | - Assembler similar to the ASM supported here. 219 | - Interesting examples including code for virtual machines and VGA. 220 | - Includes a flash based virtual machine and compiler for it. 221 | 222 | * Chlorophyll [https://github.com/mangpo/chlorophyll](https://github.com/mangpo/chlorophyll) 223 | - A high level c-like language with automatic partitioning for the ga144 224 | 225 | * Arrayforth [from Greenarrays](http://www.greenarraychips.com/home/support/download-02b.html) 226 | 227 | # Todo 228 | 229 | * Test Windows/mac support 230 | * Proper guide to using aforth 231 | * Fix line/col numbers in error messages 232 | * GA144 Simulator 233 | -------------------------------------------------------------------------------- /editors/ga-mode.el: -------------------------------------------------------------------------------- 1 | (require 'font-lock) 2 | 3 | (defvar ga-mode-hook nil) 4 | 5 | (setq ga-instructions-1 '( "ret" "nop""ex" "jump" "call" "unext" "next" "if" 6 | "and" "or" "drop" "dup" "pop" "over" "a" "push" )) 7 | 8 | (setq ga-instructions-2 '("@+" "!+" "+*" "2*" "2/" "!p" "!b" "!" "-if" 9 | ";" "." "-" "+" "@p" "@b" "@" "b!" "a!")) 10 | 11 | (setq ga-ports '("up" "left" "down" "right" "io" 12 | "north" "east" "south" "west" 13 | "ldata" "data" "warp" "center" "top" "side" "corner" 14 | )) 15 | 16 | (setq ga-directives '( "start" "for" "begin" "then" "here" 17 | "while" "reclaim" "leap" "boot")) 18 | 19 | (setq ga-directives2 '(".." "#swap" "-while" "," "-until" 20 | "---u" "--l-" "--lu" "-d--" "-d-u" 21 | "-dl-" "-dlu" "r---" "r--u" "r-l-" 22 | "r-lu" "rd--" "rd-u" "rdl-" "rdlu")) 23 | 24 | ;;directives that take an argument 25 | (setq ga-directives-3 '("node" "org" "include")) 26 | 27 | (setq boot-descriptors '("/b" "/a" "/io" "/p" "/stack")) 28 | 29 | (defun ga-make-regexp (strings &optional word) 30 | (let ((x (concat "\\(" 31 | (mapconcat 'regexp-quote strings "\\|") 32 | "\\)"))) 33 | (if word 34 | (concat "\\<" x "\\>") 35 | x))) 36 | 37 | (setq ga-number-regexp 38 | "\\<\\(\\(0x[0-9a-fA-F]+\\)\\|\\(0b[01]+\\)\\|[0-9]+\\)\\>") 39 | (setq ga-instruction-regexp-1 (ga-make-regexp ga-instructions-1 t)) 40 | (setq ga-instruction-regexp-2 (ga-make-regexp ga-instructions-2)) 41 | (setq ga-directive-regexp (ga-make-regexp ga-directives t)) 42 | (setq ga-directive-regexp-2 (ga-make-regexp ga-directives2)) 43 | (setq ga-ports-regexp (ga-make-regexp ga-ports t)) 44 | (setq ga-boot-descriptor-regex (ga-make-regexp boot-descriptors)) 45 | (setq ga-directive-regexp-3 46 | (concat "\\(" (mapconcat (lambda (x) 47 | (format "%s[ ]+[a-zA-Z0-9_.-]+" x)) 48 | ga-directives-3 49 | "\\|") 50 | "\\)")) 51 | (ga-make-regexp '("b!")) 52 | 53 | (defface ga-instruction-face '((((background light)) (:foreground "green4")) 54 | (((background dark)) (:foreground "green"))) 55 | "Default face for arrayforth instructions") 56 | 57 | (defface ga-directive-face '((((background light)) (:foreground "DarkGoldenrod")) 58 | (((background dark)) (:foreground "yellow"))) 59 | "Default face for arrayforth compiler directives") 60 | 61 | (defface ga-number-face '((((background light)) (:foreground "green3")) 62 | (((background dark)) (:foreground "LightGreen"))) 63 | "Default face for arrayforth numbers") 64 | 65 | (defface ga-word-face '((((background light)) (:foreground "red")) 66 | (((background dark)) (:foreground "red"))) 67 | "Default face for arrayforth word definitions") 68 | 69 | (defface ga-boot-descriptor-face '((((background light)) 70 | (:foreground "maroon")) 71 | (((background dark)) 72 | (:foreground "maroon"))) 73 | "Default face for arrayforth boot descriptors") 74 | 75 | (defface ga-word-reference-face '((((background light)) 76 | (:foreground "DeepPink")) 77 | (((background dark)) 78 | (:foreground "DeepPink"))) 79 | "Default face for arrayforth boot descriptors") 80 | 81 | (setq ga-instruction-face 'ga-instruction-face) 82 | (setq ga-directive-face 'ga-directive-face) 83 | (setq ga-word-face 'ga-word-face) 84 | (setq ga-number-face 'ga-number-face) 85 | (setq ga-boot-descriptor-face 'ga-boot-descriptor-face) 86 | (setq ga-word-reference-face 'ga-word-reference-face) 87 | 88 | (setq ga-indent-words '((("if" "begin" "for") (0 . 2) (0 . 2)) 89 | (("then" "next" "unext") (-2 . 0) (0 . -2)) 90 | (("while" "-while") (-2 . 4) (0 . 2)))) 91 | 92 | (setq ga-font-lock-keywords 93 | `(;;word definitions 94 | ("\\(::\\)[ \t\n]+\\([a-zA-Z0-9_+!@.*/\-]+\\)" 95 | (1 ga-directive-face) 96 | (2 ga-directive-face)) 97 | ("\\(:\\)[ \t\n]+\\([a-zA-Z0-9_+!@.*/\-]+\\)" 98 | (1 ga-word-face) 99 | (2 ga-word-face)) 100 | ("&[a-zA-Z0-9]+" . ga-word-reference-face) 101 | (,ga-boot-descriptor-regex . ga-boot-descriptor-face) 102 | (,ga-directive-regexp-2 . ga-directive-face) 103 | (,ga-directive-regexp . ga-directive-face) 104 | (,ga-directive-regexp-3 . ga-directive-face) 105 | (,ga-instruction-regexp-2 . ga-instruction-face) 106 | (,ga-instruction-regexp-1 . ga-instruction-face) 107 | (,ga-ports-regexp . ga-instruction-face) 108 | (,ga-number-regexp . ga-number-face) 109 | )) 110 | 111 | (defvar ga-mode-syntax-table 112 | (let ((table (make-syntax-table))) 113 | (modify-syntax-entry ?\\ "<" table) 114 | (modify-syntax-entry ?\n ">" table) 115 | (modify-syntax-entry ?\( "<1" table) 116 | (modify-syntax-entry ?\) ">4" table) 117 | (modify-syntax-entry ?\: "(" table) 118 | (modify-syntax-entry ?\; ")" table) 119 | table) 120 | "Syntax table in use in ga buffers") 121 | 122 | ;; imenu support 123 | 124 | (defvar ga-defining-words 125 | '(":") 126 | "List of words, that define the following word. 127 | Used for imenu index generation.") 128 | 129 | (defvar ga-defining-words-regexp nil 130 | "Regexp that's generated for matching `ga-defining-words'") 131 | 132 | (defun ga-next-definition-starter () 133 | (progn 134 | (let* ((pos (re-search-forward ga-defining-words-regexp (point-max) t))) 135 | (if pos 136 | (if (or (text-property-not-all (match-beginning 0) (match-end 0) 137 | 'ga-parsed nil) 138 | (text-property-not-all (match-beginning 0) (match-end 0) 139 | 'ga-state nil) 140 | nil) 141 | (ga-next-definition-starter) 142 | t) 143 | nil)))) 144 | 145 | (defun ga-create-index () 146 | (let* ((ga-defining-words-regexp 147 | ;;(concat "\\<\\(" (regexp-opt ga-defining-words) "\\)\\>") 148 | (concat "\\(" (regexp-opt ga-defining-words) "\\)") 149 | ) 150 | (index nil)) 151 | (goto-char (point-min)) 152 | (while (ga-next-definition-starter) 153 | (if (looking-at "[ \t]*\\([^ \t\n]+\\)") 154 | (setq index (cons (cons (match-string 1) (point)) index)))) 155 | index)) 156 | 157 | (defun ga-fill-paragraph (&rest args) 158 | (let ((fill-paragraph-function nil) 159 | (fill-paragraph-handle-comment t) 160 | (comment-start "\\ ") 161 | (comment-end "")) 162 | (apply #'fill-paragraph args))) 163 | 164 | (defun ga-comment-region (&rest args) 165 | (let ((comment-start "\\ ") 166 | (comment-end "")) 167 | (apply #'comment-region-default args))) 168 | 169 | (defun ga-beginning-of-defun (arg) 170 | (and (re-search-backward "^\\s *: \\_<" nil t (or arg 1)) 171 | (beginning-of-line))) 172 | 173 | (define-derived-mode ga-mode prog-mode "ga" 174 | "Major mode for editing ga files" 175 | (setq font-lock-defaults '((ga-font-lock-keywords))) 176 | 177 | (set-syntax-table ga-mode-syntax-table) 178 | (setq-local parse-sexp-lookup-properties t) 179 | (setq-local fill-paragraph-function #'ga-fill-paragraph) 180 | (setq-local ga-of-defun-function #'ga-beginning-of-defun) 181 | (setq-local comment-start "( ") 182 | (setq-local comment-end " )") 183 | (setq-local comment-start-skip "[(\\][ \t*]+") 184 | (setq-local comment-region-function #'ga-comment-region) 185 | 186 | (setq imenu-create-index-function 'ga-create-index) 187 | (run-hooks 'ga-mode-hook) 188 | ) 189 | 190 | (add-to-list 'auto-mode-alist '("\\.ga\\'" . ga-mode)) 191 | 192 | (provide 'ga-mode) 193 | -------------------------------------------------------------------------------- /examples/3-node-ram-ASM.ga: -------------------------------------------------------------------------------- 1 | 2 | \ See 3node-ram.ga for description 3 | \ 4 | \ Taken from: https://github.com/jamesbowman/ga144tools/blob/master/src/nt.ga 5 | 6 | node 505 ASM 7 | jump east 8 | 9 | node 507 ASM 10 | jump west 11 | 12 | node 406 ASM 13 | jump north 14 | 15 | node 506 ASM 16 | boot 17 | @p b! 18 | north 19 | : main 20 | jump north 21 | : addr \ ( -- o ) 22 | \ fetch byte address 0-383 23 | \ set A to bank 24 | \ o to offset 0-63 25 | dup 2/ 26 | 2/ 2/ 2/ 27 | 2/ 2/ call tab 28 | west \ 3 bank nodes 29 | south 30 | east 31 | : tab 32 | pop + a! 33 | @ a! @p 34 | 63 35 | and ; 36 | 37 | : read 38 | @b call addr 39 | @p ! ! 40 | @p a! @ !p 41 | @ !b ; 42 | 43 | : write 44 | @b call addr 45 | @p ! ! @p 46 | @p a! . . 47 | @p ! . . 48 | ! @b ! ; 49 | 50 | : read_byte 51 | @b dup dup 52 | call addr 53 | @p ! ! 54 | @p a! @ !p 55 | 2/ 2* or \ low bit of addr 56 | 2* 2* 2* \ 0 or 8 57 | push @ next hibyte 58 | : lo8 59 | @p and !b ; 60 | 255 61 | : hibyte 62 | 2/ unext jump lo8 63 | 64 | : write_byte 65 | @b dup 66 | call addr 67 | @p ! ! 68 | @p a! . . 69 | ! @b ! ; 70 | 71 | : erase 72 | @b push @b 73 | : loop 74 | dup call addr 75 | @p ! ! @p 76 | @p a! dup 77 | or ! 78 | ! @p . + 79 | 1 80 | next loop 81 | ; 82 | 83 | 84 | \ test section ------------------ 85 | \ ( same as 3-node-ram.ga ) 86 | node 606 \ test node 87 | /a south /b north 88 | : write ( v a - ) 89 | { write@506 } ! ! ! ; 90 | : read ( a - v ) 91 | { read@506 } ! ! @ ; 92 | : erase ( a n - ) 93 | { erase@506 } ! ! ! ; 94 | 95 | : out !b ; 96 | : main 97 | \ write a=a*a, for each address a in ram 98 | 191 for 99 | pop dup push 100 | dup dup dup + over write 101 | next 102 | 103 | \ read and print values 104 | 191 for 105 | pop dup push dup read out 106 | next 107 | 108 | node 708 109 | /a west 110 | include 708serial.ga 111 | : main 112 | 191 for @ send next exit 113 | 114 | node 706 wire south east 115 | node 707 wire west east 116 | -------------------------------------------------------------------------------- /examples/3-node-ram.ga: -------------------------------------------------------------------------------- 1 | \ 3 node ram cluster. 192 addressable words. Word and byte addressable. 2 | \ 3 | \ Pulled from: https://github.com/jamesbowman/ga144tools/blob/master/src/nt.ga 4 | \ 5 | \ Includes test code to write, read, and send test values. 6 | \ See 3-node-ram.ga for a version using ASM syntax. 7 | \ 8 | \ Output: 9 | \ 382 10 | \ 380 11 | \ 378 12 | \ ... 13 | \ 4 14 | \ 2 15 | \ 0 16 | \ [exit] 17 | 18 | \ ram data nodes 19 | node 505 /p east 20 | node 507 /p west 21 | node 406 /p north 22 | 23 | \ ram control node 24 | node 506 25 | /b north /p north 26 | : addr ( -- o ) 27 | \ fetch byte address 0-383 28 | \ set A to bank 29 | \ o to offset 0-63 30 | dup 2/ 2/ 2/ 2/ 2/ 2/ 31 | tab 32 | , west 33 | , south 34 | , east 35 | 36 | : tab 37 | pop +, a! @ a! 63 and ; 38 | 39 | : read 40 | @b addr 41 | { @p a! @ !p } ! ! 42 | @ !b ; 43 | 44 | : write 45 | @b addr 46 | { @p a! } ! ! 47 | { @p ! } ! @b ! ; 48 | 49 | : read_byte 50 | @b dup dup addr 51 | { @p a! @ !p } ! ! 52 | 2/ 2* or \ low bit of addr 53 | 2* 2* 2* \ 0 or 8 54 | push @ next: hibyte 55 | 56 | : lo8 57 | 255 and !b ; 58 | 59 | : hibyte 60 | begin 2/ unext lo8 ; 61 | 62 | : write_byte 63 | @b dup addr 64 | { @p a! } ! ! 65 | ! @b ! ; 66 | 67 | : erase 68 | @b push @b 69 | begin 70 | dup addr 71 | { @p a! dup } ! ! 72 | { or ! } ! 73 | 1 . + 74 | next ; 75 | 76 | \ test section ------------------ 77 | 78 | node 606 \ test node 79 | /a south /b north 80 | : write ( v a - ) 81 | { write@506 } ! ! ! ; 82 | : read ( a - v ) 83 | { read@506 } ! ! @ ; 84 | : erase ( a n - ) 85 | { erase@506 } ! ! ! ; 86 | 87 | : out !b ; 88 | : main 89 | \ write a=a*a, for each address a in ram 90 | 191 for 91 | pop dup push 92 | dup dup dup + over write 93 | next 94 | 95 | \ read and print values 96 | 191 for 97 | pop dup push dup read out 98 | next 99 | 100 | node 708 101 | /a west 102 | include 708serial.ga 103 | : main 104 | 191 for @ send next exit 105 | 106 | node 706 wire south east 107 | node 707 wire west east 108 | -------------------------------------------------------------------------------- /examples/600serial.ga: -------------------------------------------------------------------------------- 1 | \ Used in the same way as 708serial.ga, but for node 600 2 | 3 | \ wire pin J30.4 to pin J23.11 4 | \ J23.11 is async out for port C 5 | \ J30.4 is for 600.17 6 | 7 | : val 1 and if 0x20000 ; then 0x30000 ; 8 | : out18 0 out8 drop out8 out8 9 | : out8 0 out1 7 for dup out1 2/ next 1 10 | : out1 val !b drop unext_baud for unext ; 11 | : exit 1 out8 12 | -------------------------------------------------------------------------------- /examples/708serial-ASM.ga: -------------------------------------------------------------------------------- 1 | 2 | \ see 708serial.ga for description 3 | 4 | \ node 708 ASM 5 | : send ( n - n ) 6 | dup dup or 7 | call _send8 8 | drop call _send8 9 | call _send8 10 | : _send8 ( n - n ) 11 | dup dup or 12 | call _send1 13 | @p push 14 | 7 15 | : _loop 16 | dup call _send1 17 | 2/ next _loop 18 | @p 19 | 1 20 | : _send1 ( n ) 21 | @p and @p 22 | 1 23 | 3 24 | or !b @p 25 | unext_baud 26 | push 27 | unext ; 28 | : exit 29 | @p jump _send8 30 | 1 31 | -------------------------------------------------------------------------------- /examples/708serial.ga: -------------------------------------------------------------------------------- 1 | 2 | \ Sends 18 bit words from node 708 3 | \ b must be set to io 4 | \ 5 | \ Example: 6 | \ 7 | \ node 708 8 | \ include 708serial.ga 9 | \ : main 10 | \ 124 send exit 11 | 12 | : send ( n - n ) 13 | \ 18bit words sent using 4 bytes in order: 14 | \ wordcode lower8 middle8 upper2 15 | 0 _send8 drop 16 | _send8 _send8 17 | : _send8 ( n - n ) 18 | 0 _send1 19 | 7 for dup _send1 2/ next 1 20 | : _send1 ( n ) 21 | 1 and 3 or !b 22 | unext_baud for unext ; 23 | : exit 1 _send8 ; 24 | -------------------------------------------------------------------------------- /examples/715crystal.ga: -------------------------------------------------------------------------------- 1 | \ ga 715crystal-test.aforth --port /dev/ttyUSB0 2 | 3 | \ Software-defined crystal! find resonant frequency of a 4 | \ watch crystal and maintain oscillation. 5 | 6 | \ Requires a 32.768 khz watch crystal from 715.17 to gnd 7 | 8 | \ node 715 is a good node for driving the crystal as 9 | \ the timing pulses can be read from the shared pins 10 | \ by nodes 709, 713, and 717 11 | 12 | \ counter.ga provides an example of monitoring the 13 | \ timing pulses from node 709 14 | 15 | \ From colorforth block 980 16 | 17 | node 715 18 | 19 | : -osc ( kn-f ) 20 | \ -osc tries exciting the crystal with n cycles of 21 | \ period k returning nonzero if it didn't come back 22 | \ high after last cycle 23 | io b! for 24 | 0x30000 !b dup .. 2/ dup for unext 25 | 0x20000 !b .. over 1 and .. + for unext next 26 | dup or !b dup 30000 for 27 | drop @b - -while next ; 28 | then dup or pop drop ; 29 | : main 30 | : clang 31 | \ clang searches for resonant frequency over a reasonable range. 32 | \ Initially we use 5000 cycles and may be able to shorten this. 33 | \ When we find resonance, falls thru into 'prep' which sets up 34 | \ registers and finally we camp in 'run' which is the low power, 35 | \ low duty cycle oscillator 36 | 12895 ( 12700) 200 for dup 5000 -osc while 37 | drop 1 . + next clang ; then 38 | : prep 0 0x20000 0x800 0x30800 0 0x20000 0x800 0x30800 39 | dup up a! drop 40 | : run !b !b @ drop run ; 41 | 42 | \ : try ( test code for finding resonance) 43 | \ dup 5000 -osc over 1 . + ; 44 | \ 45 | \ do not connect any kind of conventional probe to the crystal; 46 | \ this oscillator 47 | \ will not work if you load it down even that much 48 | \ => successfully tested with the Agilent MSO-X 2014A oscilloscope 49 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | ## fibonacci.ga 4 | 5 | Prints the first 15 numbers of the Fibonacci sequence 6 | 7 | ## port-execution.ga 8 | Simple example of using port execution to 9 | utilize an adjacent node as a random access 64 word array. 10 | 11 | ## 3-node-ram.ga, 3-node-ram-ASM.ga 12 | 3 node ram cluster. 13 | 192 addressable words, word and byte addressable. 14 | 15 | 3-node-ram-ASM.ga is the same thing with ASM syntax 16 | 17 | ## square_waves.ga 18 | Various ways of toggling a pin, with timing info. 19 | 20 | ## sram-demo.ga 21 | example reading and writing SRAM 22 | 23 | ## variables.ga 24 | Simple example showing one way of implementing variables 25 | 26 | ## counter.ga 27 | Uses a crystal to maintain time. 28 | Prints counter and debug values over serial. 29 | 30 | ## fast-ram-node.ga 31 | 60 word RAM node with small client code (2 words) 32 | 33 | ## racecar.ga 34 | An example using all the nodes. Used to measure the time to 35 | send a number around the chip in a loop. 36 | 37 | # library examples 38 | 39 | ## 708serial.ga 40 | serial communciation from node 708 41 | 42 | ## 708serial-ASM.ga 43 | ASM syntax version of 708serial.ga 44 | 45 | ## 600serial.ga 46 | Like 708serial.ga, but for node 600 47 | 48 | ## 715crystal.ga 49 | Drives a 32.768 khz watch crystal from pin 715.17 50 | 51 | see `counter.ga` for a running example that uses the same 52 | technique. 53 | 54 | ## sram.ga, sram-minimal-master.ga 55 | 56 | `sram.ga` main sram control. 57 | 58 | `sram-minimal-master.ga` minimal capability SRAM master. 59 | 60 | See `sram-demo.ga` for example usage of these files. 61 | -------------------------------------------------------------------------------- /examples/adc_617.ga: -------------------------------------------------------------------------------- 1 | 2 | node 708 3 | boot io b! south a! 4 | include 708serial.ga 5 | 6 | : main 7 | 555 send 8 | : loop 9 | @ send loop ; 10 | 11 | node 608 12 | wire east north 13 | 14 | node 609-616 15 | wire east west 16 | 17 | node 617 ASM 18 | 19 | : main 20 | @p a! 21 | ldata 22 | @p b! 23 | io 24 | 25 | @p !b 26 | 0x0000 27 | jump adc 28 | 29 | : adc 30 | \ sample 31 | @p dup push 32 | 9800 33 | ! @ dup 34 | unext 35 | ! @ 36 | - . + 37 | \ calib 38 | @p - . + 39 | 85010 40 | 2/ 2/ 2/ 41 | \ send 42 | @p b! !b 43 | WEST 44 | @p b! 45 | io 46 | jump adc 47 | -------------------------------------------------------------------------------- /examples/counter.ga: -------------------------------------------------------------------------------- 1 | \ ga counter.ga --port /dev/ttyUSB0 2 | 3 | \ Uses a crystal to maintain time. 4 | \ Sends the debug values then counter value over serial once per second 5 | 6 | \ The first values printed are port coordinates (debug values) 7 | \ Then the period delay values used for pumping the crystal are printed. 8 | \ Then the second counter is printed when it is incremented 9 | 10 | \ Requires a 32.768 khz watch crystal from 715.17 to gnd 11 | 12 | \ See 715crystal.ga for code comments 13 | 14 | node 715 15 | : -osc over ! 16 | io b! for 17 | 0x30000 !b dup .. 2/ dup for unext 18 | 0x20000 !b .. over 1 and .. + for unext next 19 | dup or !b dup 30000 for 20 | drop @b - -while next ; 21 | then dup or pop drop ; 22 | : clang 23 | 12895 ( 12700) 2000 for dup 5000 -osc while 24 | drop 1 . + next clang ; then 25 | : prep west a! ! 0 ! 26 | 0 0x20000 0x800 0x30800 0 0x20000 0x800 0x30800 27 | dup up a! drop 28 | : run !b !b @ drop run ; 29 | : main 30 | west a! 715 ! 31 | clang ; 32 | 33 | node 710-714 34 | : loop @ !b loop ; 35 | : main east a! west b! coord !b loop ; 36 | 37 | node 709 38 | : monitor 39 | ( print the test oscillation periods) 40 | @ dup !b 41 | if monitor then ( 0 means oscillation found) 42 | io b! 0 ( seconds ) 0 ( clock edge) 43 | : count ( counts seconds ) 44 | 0 !b up a! dup dup ! drop 45 | 0x800 !b up a! dup dup ! drop 46 | dup 32000 - 1 . + . + 47 | -if drop 1 . + count ; then 48 | drop over 1 . + dup west a! ! 0 count ; 49 | : main 50 | east a! west b! 709 !b 51 | monitor ; 52 | 53 | node 708 54 | include 708serial.ga 55 | : main io b! east a! 56 | : loop @ send loop ; 57 | -------------------------------------------------------------------------------- /examples/fast-ram-node.ga: -------------------------------------------------------------------------------- 1 | \ 2 | \ 60 word ram node 3 | \ minimizes code needed in the client 4 | \ 5 | \ ga fast-ram-node.ga --port /dev/ttyUSB0 6 | \ Output: 7 | \ 2222 8 | \ 1111 9 | \ [exit] 10 | 11 | \ RAM node 12 | node 508 13 | /b north 14 | org 60 \ Place code in upper 4 words of RAM 15 | : read 16 | a! @ !b 17 | : main 18 | @b -if: read 19 | - a! @b ! main ; 20 | 21 | 22 | \ Test client -------------------- 23 | node 608 24 | /b south /a north 25 | : write ( xa - ) 26 | - !b !b ; 27 | : read ( a - x ) 28 | !b @b ; 29 | 30 | : main 31 | 1111 5 write 32 | 2222 3 write 33 | 34 | 3 read ! 35 | 5 read ! 36 | 0 ! \ exit 37 | 38 | node 708 39 | include 708serial.ga 40 | : main 41 | south a! @ 42 | if send main then exit ; 43 | -------------------------------------------------------------------------------- /examples/fibonacci.ga: -------------------------------------------------------------------------------- 1 | \ Prints the first 15 numbers of the Fibonacci sequence 2 | \ Run with: 3 | \ ga fibonacci.ga --port /dev/ttyUSB0 4 | 5 | node 708 6 | include 708serial.ga 7 | 8 | : main 9 | io b! south a! 10 | 14 for @ send drop next 11 | exit 12 | 13 | node 608 14 | north a! 1 0 15 | : lucas over over + dup ! lucas 16 | -------------------------------------------------------------------------------- /examples/hmm-sim.ga: -------------------------------------------------------------------------------- 1 | \ HMM setup for simulation 2 | \ Output of the chlorophyll compiler: https://github.com/mangpo/chlorophyll 3 | \ This file from: https://github.com/mangpo/chlorophyll/blob/master/examples/sensors/compiled-code/hmm-sim.aforth 4 | 5 | node 705 6 | warm 7 | 8 | node 607 ( core 115 ) 9 | org 0 10 | , -192 , 2096 , -19536 11 | , -192 , 2096 , -19536 12 | , 1440 , -592 , -12560 13 | , 1088 , -3824 , -21248 14 | , -240 , 3376 , -20176 15 | , 2016 , 32 , -13248 16 | , -992 , -1280 , -20608 17 | , 1664 , -272 , -13312 18 | , 896 , 848 , -22928 19 | : main up b! dup or a! 26 for @+ !b next warm ; 20 | 21 | node 602 ( core 110 ) 22 | /a up org 0 23 | : main @ left b! !b main ; 24 | 25 | node 601 ( core 109 ) 26 | /a right org 0 27 | : main up b! @b ! left b! @b ! main ; 28 | 29 | node 600 ( core 108 ) 30 | /a io org 0 31 | , 0 32 | : 1if 33 | .. if drop 131072 ! ; then drop 196608 ! ; 34 | : emit_1 dup 1 and 1if 35 | 904 for 36 | unext drop ; 37 | : emit_8 dup dup or emit_1 38 | 7 for 39 | dup emit_1 dup 2/ push drop pop next 1 emit_1 dup push drop pop ; 40 | : print dup dup or emit_8 dup dup or b! !b dup emit_8 push drop pop dup emit_8 push drop pop dup emit_8 push drop pop drop ; 41 | : main right b! @b print right b! @b print main ; 42 | 43 | node 511 ( core 101 ) 44 | /a right org 0 45 | , 0 46 | , 0 47 | , 0 48 | : 23rep - 1 . + . + dup ! ; 49 | : 22rep drop @b 6554 . + @ 23rep drop ; 50 | : 21rep 22rep @ ; 51 | : 20rep b! @b 6554 - 1 . + . + 23rep ; 52 | : 1if 53 | .. if drop @ dup dup or 20rep 21rep 1 20rep 21rep 2 20rep 22rep ; then drop ; 54 | : 2if 55 | .. if drop @ dup dup or b! !b @ 1 b! !b @ 2 b! !b ; then drop ; 56 | : filter @ 1if @ 2if ; 57 | : main 58 | 999 for 59 | filter @ dup down b! !b drop next main ; 60 | 61 | node 510 ( core 100 ) 62 | /a right org 0 63 | , 0 64 | , 0 65 | , 0 66 | : 19rep 67 | .. -if drop 1 push drop pop ; then drop ; 68 | : 7if 69 | .. if drop dup dup or push drop pop dup dup or b! @b ! @ 19rep @b ! @ 19rep 1 b! @b ! @ 19rep @b ! @ 19rep 2 b! @b ! @ 19rep @b ! @ 19rep ; then drop ; 70 | : 8if 71 | .. if drop dup dup or b! @b ! 1 b! @b ! 2 b! @b ! ; then drop ; 72 | : filter 73 | a push 74 | dup dup or a! left b! 75 | @b !+ @b !+ @b !+ 76 | pop a! 77 | @b dup dup ! 7if dup dup ! 8if dup left b! !b drop ; 78 | : main 79 | 999 for 80 | filter @b dup ! drop next main ; 81 | 82 | node 509 ( core 99 ) 83 | /a right org 0 84 | , 0 85 | : 18rep 86 | .. -if drop 1 push drop pop ; then drop ; 87 | : 3if 88 | .. if drop @ down b! !b @ !b @ !b ; then drop ; 89 | : filter @ dup dup or b! !b dup dup or dup dup or push drop pop 2949 @b - 1 . + . + 18rep @b 328 - 1 . + . + 18rep @ left b! !b @ !b @ !b dup !b @b ! drop ; 90 | : main 91 | 999 for 92 | filter @ dup down b! !b dup left b! !b 3if next main ; 93 | 94 | node 508 ( core 98 ) 95 | /a left org 0 96 | , 0 97 | , 0 98 | : 16rep 99 | for 100 | +* unext ; 101 | : 17rep 102 | .. -if - 1 . + then dup a push a! dup dup or 17 16rep push dup or pop 14 16rep drop drop a pop a! ; 103 | : 1if 104 | .. if drop @ !b @ !b @ !b @ down b! !b ; then drop ; 105 | : filter a push dup dup or a! push !+ !+ pop pop a! 1 b! @b 17rep dup dup or b! @b 17rep . + over 17rep . + right b! !b 1 b! @b right b! !b dup dup or b! @b right b! !b dup !b @b ! drop ; 106 | : main 107 | 999 for 108 | @ @ @ filter @ dup !b 1if next main ; 109 | 110 | node 507 ( core 97 ) 111 | /a left org 0 112 | , 0 113 | , 0 114 | , 0 115 | : 15rep dup dup or b! @b ! 1 b! @b ! 2 b! @b ! ; 116 | : 1while dup dup or over - 1 . + . + dup right b! !b 117 | .. -if drop dup 2* push drop pop 1while ; then drop ; 118 | : 2if 119 | .. if drop dup dup or b! @b right b! !b 1 b! @b right b! !b 2 b! @b right b! !b 15rep down b! @b @b over - and . + push drop pop 1while @b ! ; then drop ; 120 | : filter @ ; 121 | : main dup dup or 122 | 999 for 123 | a push 124 | dup dup or a! up b! 125 | @b !+ @b !+ @b !+ 126 | pop a! 127 | 15rep drop filter dup ! dup right b! !b dup down b! !b 2if next main ; 128 | 129 | node 506 ( core 96 ) 130 | /a right org 0 131 | : 1while @ 132 | .. -if drop dup 1 . + push drop pop 1while ; then drop ; 133 | : 2if 134 | .. -if drop dup 3 - 1 . + . + push drop pop ; then drop dup dup or push drop pop ; 135 | : 3if 136 | .. if drop @ !b @ !b @ !b dup dup or push drop pop 1while 3 over - 1 . + . + 2if dup !b dup ! ; then drop ; 137 | : main dup dup or 138 | 999 for 139 | @ dup left b! !b 3if next main ; 140 | 141 | node 505 ( core 95 ) 142 | /a left org 0 143 | : 1if 144 | .. if drop @ !b @ !b @ !b @ right b! !b ; then drop ; 145 | : main 146 | 999 for 147 | @ dup right b! !b dup down b! !b 1if next main ; 148 | 149 | node 504 ( core 94 ) 150 | /a left org 0 151 | : 1if 152 | .. if drop @b ! ; then drop ; 153 | : main 154 | 999 for 155 | right b! @b dup ! 1if next down b! @b ! main ; 156 | 157 | node 503 ( core 93 ) 158 | /a left org 0 159 | : 1if 160 | .. if drop @ !b ; then drop ; 161 | : main 162 | 999 for 163 | @ dup down b! !b 1if next @ right b! !b main ; 164 | 165 | node 502 ( core 92 ) 166 | /a up org 0 167 | : main right b! @b ! main ; 168 | 169 | node 501 ( core 91 ) 170 | /a down org 0 171 | : main @ up b! !b main ; 172 | 173 | node 412 ( core 84 ) 174 | /a up org 0 175 | : main 176 | 999 for 177 | left b! @b dup ! drop next main ; 178 | 179 | node 411 ( core 83 ) 180 | /a up org 0 181 | : main 182 | 999 for 183 | down b! @b dup left b! !b dup ! drop next main ; 184 | 185 | node 409 ( core 81 ) 186 | /a up org 0 187 | : 1if 188 | .. if drop @b ! @b ! @b ! right b! @b ! ; then drop ; 189 | : main 190 | 999 for 191 | down b! @b dup ! 1if next main ; 192 | 193 | node 408 ( core 80 ) 194 | /a up org 0 195 | : 1if 196 | .. if drop @ push drop pop dup !b down b! @b right b! !b ; then drop ; 197 | : main dup dup or 198 | 999 for 199 | left b! @b dup ! 1if next main ; 200 | 201 | node 407 ( core 79 ) 202 | /a down org 0 203 | : 1if 204 | .. if drop @b ! left b! @b ! ; then drop ; 205 | : main 206 | 999 for 207 | @ dup left b! !b dup right b! !b 1if next up b! @b right b! !b main ; 208 | 209 | node 406 ( core 78 ) 210 | /a right org 0 211 | : 1if 212 | .. if drop left b! @b push drop pop dup ! ; then drop ; 213 | : main dup dup or 214 | 999 for 215 | @ 1if next @ left b! !b main ; 216 | 217 | node 405 ( core 77 ) 218 | /a up org 0 219 | : 1if 220 | .. if drop @b ! @b ! @b ! @ left b! !b ; then drop ; 221 | : main 222 | 999 for 223 | down b! @b dup ! 1if next left b! @b right b! !b main ; 224 | 225 | node 404 ( core 76 ) 226 | /a down org 0 227 | : main right b! @b ! main ; 228 | 229 | node 403 ( core 75 ) 230 | /a down org 0 231 | : 1if 232 | .. if drop @ up b! !b ; then drop ; 233 | : main 234 | 999 for 235 | @ 1if next main ; 236 | 237 | node 401 ( core 73 ) 238 | /a up org 0 239 | : main @ down b! !b main ; 240 | 241 | node 313 ( core 67 ) 242 | org 0 243 | , 10809 244 | , 7643 245 | , 0 246 | , -7643 247 | , -10809 248 | , -7643 249 | , 1908 250 | , 6946 251 | , 0 252 | , 0 253 | , 0 254 | , 0 255 | , 0 256 | , 0 257 | : 14rep 258 | for 259 | +* unext ; 260 | : hmm2_derive_group dup dup or down b! @b push drop pop 261 | dup dup or a! 13 for 262 | @+ over - 1 . + . + 263 | .. -if - 1 . + then dup a push a! dup dup or 17 14rep push dup or pop 14 14rep drop drop a pop a! !b next drop ; 264 | : main hmm2_derive_group main ; 265 | 266 | node 312 ( core 66 ) 267 | /a down org 0 268 | : hmm2_forward_proc_inc @b ! ; 269 | : hmm2_input left b! @b ! @b ! @b ! hmm2_forward_proc_inc ; 270 | : 1if 271 | .. if drop hmm2_input @ !b ; then drop ; 272 | : main 273 | 999 for 274 | up b! @b dup ! 1if next main ; 275 | 276 | node 311 ( core 65 ) 277 | /a right org 0 278 | : hmm2_forward_proc_inc @ !b ; 279 | : hmm2_input @ left b! !b @ !b @ !b hmm2_forward_proc_inc ; 280 | : 1if 281 | .. if drop hmm2_input @b ! ; then drop ; 282 | : main 283 | 999 for 284 | up b! @b 1if next main ; 285 | 286 | node 310 ( core 64 ) 287 | /a left org 0 288 | : hmm2_forward_proc_inc down b! @b right b! !b ; 289 | : hmm2_input @ right b! !b @ !b @ !b hmm2_forward_proc_inc ; 290 | : 1if 291 | .. if drop hmm2_input @b ! @ down b! !b ; then drop ; 292 | : main 293 | 999 for 294 | @ 1if next main ; 295 | 296 | node 309 ( core 63 ) 297 | /a left org 0 298 | , 0 299 | , 0 300 | : hmm2_forward_proc_out dup dup or dup dup or push drop pop 301 | 7 for 302 | dup down b! @b . + push drop pop next dup push drop pop ; 303 | : hmm2_input a push dup dup or a! !+ push !+ pop pop a! 1 b! @b ! dup ! dup dup or b! @b ! drop ; 304 | : 1if 305 | .. if drop up b! @b @b @b hmm2_input @ right b! !b up b! @b ! ; then drop ; 306 | : main 307 | 999 for 308 | up b! @b dup ! dup down b! !b 1if next hmm2_forward_proc_out right b! !b main ; 309 | 310 | node 308 ( core 62 ) 311 | /a up org 0 312 | : 1if 313 | .. if drop right b! @b ! ; then drop ; 314 | : main 315 | 999 for 316 | @ 1if next right b! @b left b! !b main ; 317 | 318 | node 307 ( core 61 ) 319 | /a up org 0 320 | : main left b! @b ! main ; 321 | 322 | node 306 ( core 60 ) 323 | org 0 324 | , 11685 325 | , 1 326 | , 0 327 | , -8263 328 | , -11685 329 | , -8263 330 | , 658 331 | , 8263 332 | , 0 333 | , 0 334 | , 0 335 | , 0 336 | , 0 337 | , 0 338 | : 13rep 339 | for 340 | +* unext ; 341 | : hmm1_derive_group dup dup or down b! @b push drop pop 342 | dup dup or a! 13 for 343 | @+ over - 1 . + . + 344 | .. -if - 1 . + then dup a push a! dup dup or 17 13rep push dup or pop 14 13rep drop drop a pop a! !b next drop ; 345 | : main hmm1_derive_group main ; 346 | 347 | node 305 ( core 59 ) 348 | /a right org 0 349 | : hmm1_forward_proc_inc @ !b ; 350 | : hmm1_input @ down b! !b @ !b @ !b hmm1_forward_proc_inc ; 351 | : 1if 352 | .. if drop up b! @b ! @b ! @b ! hmm1_input @b up b! !b ; then drop ; 353 | : main 354 | 999 for 355 | up b! @b dup ! dup down b! !b 1if next main ; 356 | 357 | node 304 ( core 58 ) 358 | /a left org 0 359 | : hmm1_forward_proc_inc @ !b ; 360 | : hmm1_input @ !b @ !b @ !b hmm1_forward_proc_inc ; 361 | : 1if 362 | .. if drop @b ! @b ! @b ! hmm1_input ; then drop ; 363 | : main 364 | 999 for 365 | right b! @b dup ! 1if next main ; 366 | 367 | node 303 ( core 57 ) 368 | /a left org 0 369 | : hmm1_forward_proc_inc down b! @b ! ; 370 | : hmm1_input @b ! @b ! @b ! hmm1_forward_proc_inc ; 371 | : 1if 372 | .. if drop @ right b! !b @ !b @ !b hmm1_input up b! @b down b! !b ; then drop ; 373 | : main 374 | 999 for 375 | @ dup right b! !b dup down b! !b 1if next main ; 376 | 377 | node 302 ( core 56 ) 378 | /a right org 0 379 | , 0 380 | , 0 381 | : hmm1_forward_proc_out dup dup or dup dup or push drop pop 382 | 7 for 383 | dup down b! @b . + push drop pop next dup push drop pop ; 384 | : hmm1_input a push dup dup or a! !+ !+ push pop pop a! dup ! 1 b! @b ! dup dup or b! @b ! drop ; 385 | : 1if 386 | .. if drop @ @ @ hmm1_input ; then drop ; 387 | : main 388 | 999 for 389 | @ dup down b! !b 1if next hmm1_forward_proc_out left b! !b main ; 390 | 391 | node 301 ( core 55 ) 392 | /a up org 0 393 | : main left b! @b ! main ; 394 | 395 | node 214 ( core 50 ) 396 | org 0 397 | , 0 398 | , 0 399 | , 0 400 | , 0 401 | , 0 402 | , 0 403 | , -698 404 | , -73 405 | , 10809 406 | , 7643 407 | , -7643 408 | , -10809 409 | , -7643 410 | , 7643 411 | : 12rep 412 | for 413 | +* unext ; 414 | : hmm2_derive_group dup dup or left b! @b push drop pop 415 | dup dup or a! 13 for 416 | @+ over - 1 . + . + 417 | .. -if - 1 . + then dup a push a! dup dup or 17 12rep push dup or pop 14 12rep drop drop a pop a! !b next drop ; 418 | : main hmm2_derive_group main ; 419 | 420 | node 213 ( core 49 ) 421 | /a right /p right org 0 422 | , 0 423 | , 0 424 | , 0 425 | , 0 426 | , 0 427 | , 0 428 | , 0 429 | , 0 430 | , 0 431 | : 1if 432 | .. -if drop dup !b 8 b! @b 4 b! !b ; then drop ; 433 | : hmm2_derive_group a push dup dup or a! !+ !+ !+ pop a! 2 b! @b down b! !b 1 b! @b left b! !b dup dup or b! @b up b! !b 98304 3 b! !b dup dup or 434 | dup dup or 8 b! !b 13 for 435 | down b! @b left b! @b up b! @b . + . + push drop pop dup 3 b! @b - 1 . + . + 1if 8 b! @b 1 . + !b next 4 b! @b ! drop ; 436 | : act49 @ @ @ hmm2_derive_group ; 437 | 438 | node 211 ( core 47 ) 439 | /a up org 0 440 | : main left b! @b ! @b ! main ; 441 | 442 | node 210 ( core 46 ) 443 | org 0 444 | , 0 445 | , 0 446 | , 0 447 | , 0 448 | , 0 449 | , 0 450 | , 0 451 | , 0 452 | : 11rep drop 453 | dup dup or a! 7 for 454 | up b! @b !+ next ; 455 | : 1if 456 | .. -if 11rep ; then 11rep ; 457 | : hmm2_forward_proc_inc @b dup left b! !b 1if @b down b! !b ; 458 | : hmm2_swap_shift 459 | dup dup or a! 7 for 460 | @+ over 461 | .. if -1 . + 462 | for 463 | 2* unext dup then drop left b! !b next drop ; 464 | : 2if 465 | .. if drop hmm2_forward_proc_inc @b hmm2_swap_shift ; then drop ; 466 | : main 467 | 999 for 468 | left b! @b dup up b! !b 2if next main ; 469 | 470 | node 209 ( core 45 ) 471 | org 0 472 | , 0 473 | , 0 474 | , 0 475 | , 0 476 | , 0 477 | , 0 478 | , 0 479 | , 0 480 | : 1if 481 | .. -if drop 482 | 7 for 483 | 484 | dup dup or a! 7 for 485 | @+ up b! !b next next ; then drop ; 486 | : hmm2_forward_proc_inc @b 1if ; 487 | : hmm2_forward_proc_out 488 | dup dup or a! 7 for 489 | @+ down b! !b next ; 490 | : hmm2_swap_shift 491 | dup dup or a! 7 for 492 | left b! @b !+ next ; 493 | : 2if 494 | .. if drop hmm2_forward_proc_inc hmm2_swap_shift ; then drop ; 495 | : main 496 | 999 for 497 | down b! @b dup left b! !b 2if next hmm2_forward_proc_out main ; 498 | 499 | node 208 ( core 44 ) 500 | /a up /p up org 0 501 | , 0 502 | , 0 503 | , 0 504 | , 43691 505 | , 43691 506 | , 43691 507 | , 0 508 | , 0 509 | , 0 510 | , 0 511 | , 0 512 | , 0 513 | , 43691 514 | , 43691 515 | , 43691 516 | , 0 517 | , 0 518 | , 0 519 | , 0 520 | , 0 521 | , 0 522 | , 43691 523 | , 43691 524 | , 43691 525 | , 0 526 | , 0 527 | , 0 528 | , 0 529 | , 0 530 | , 0 531 | , 65536 532 | , 65536 533 | , 0 534 | , 0 535 | , 0 536 | , 0 537 | , 0 538 | , 0 539 | , 0 540 | , 131071 541 | : hmm2_get_a2 dup -24 . + b! @b ! drop ; 542 | : act44 @ hmm2_get_a2 ; 543 | 544 | node 207 ( core 43 ) 545 | org 0 546 | , 0 547 | , 1 548 | , 0 549 | , 0 550 | , 0 551 | , 0 552 | , -648 553 | , 0 554 | , 11685 555 | , 8263 556 | , -8263 557 | , -11685 558 | , -8263 559 | , 8263 560 | : 10rep 561 | for 562 | +* unext ; 563 | : hmm1_derive_group dup dup or right b! @b push drop pop 564 | dup dup or a! 13 for 565 | @+ over - 1 . + . + 566 | .. -if - 1 . + then dup a push a! dup dup or 17 10rep push dup or pop 14 10rep drop drop a pop a! !b next drop ; 567 | : main hmm1_derive_group main ; 568 | 569 | node 206 ( core 42 ) 570 | /a left /p left org 0 571 | , 0 572 | , 0 573 | , 0 574 | , 0 575 | , 0 576 | , 0 577 | , 0 578 | , 0 579 | , 0 580 | : 1if 581 | .. -if drop dup !b 8 b! @b 4 b! !b ; then drop ; 582 | : hmm1_derive_group a push dup dup or a! !+ !+ !+ pop a! 2 b! @b down b! !b 1 b! @b right b! !b dup dup or b! @b up b! !b 98304 3 b! !b dup dup or 583 | dup dup or 8 b! !b 13 for 584 | down b! @b right b! @b up b! @b . + . + push drop pop dup 3 b! @b - 1 . + . + 1if 8 b! @b 1 . + !b next 4 b! @b ! drop ; 585 | : act42 @ @ @ hmm1_derive_group ; 586 | 587 | node 204 ( core 40 ) 588 | /a up org 0 589 | : main right b! @b ! @b ! main ; 590 | 591 | node 203 ( core 39 ) 592 | org 0 593 | , 0 594 | , 0 595 | , 0 596 | , 0 597 | , 0 598 | , 0 599 | , 0 600 | , 0 601 | : 9rep drop 602 | dup dup or a! 7 for 603 | up b! @b !+ next ; 604 | : 1if 605 | .. -if 9rep ; then 9rep ; 606 | : hmm1_forward_proc_inc @b dup right b! !b 1if @b down b! !b ; 607 | : hmm1_swap_shift 608 | dup dup or a! 7 for 609 | @+ over 610 | .. if -1 . + 611 | for 612 | 2* unext dup then drop right b! !b next drop ; 613 | : 2if 614 | .. if drop hmm1_forward_proc_inc @b hmm1_swap_shift ; then drop ; 615 | : main 616 | 999 for 617 | down b! @b dup up b! !b 2if next main ; 618 | 619 | node 202 ( core 38 ) 620 | org 0 621 | , 0 622 | , 0 623 | , 0 624 | , 0 625 | , 0 626 | , 0 627 | , 0 628 | , 0 629 | : 1if 630 | .. -if drop 631 | 7 for 632 | 633 | dup dup or a! 7 for 634 | @+ up b! !b next next ; then drop ; 635 | : hmm1_forward_proc_inc right b! @b 1if ; 636 | : hmm1_forward_proc_out 637 | dup dup or a! 7 for 638 | @+ down b! !b next ; 639 | : hmm1_swap_shift 640 | dup dup or a! 7 for 641 | right b! @b !+ next ; 642 | : 2if 643 | .. if drop hmm1_forward_proc_inc hmm1_swap_shift ; then drop ; 644 | : main 645 | 999 for 646 | down b! @b 2if next hmm1_forward_proc_out main ; 647 | 648 | node 201 ( core 37 ) 649 | /a up /p up org 0 650 | , 0 651 | , 0 652 | , 0 653 | , 43691 654 | , 43691 655 | , 43691 656 | , 0 657 | , 0 658 | , 0 659 | , 0 660 | , 0 661 | , 0 662 | , 43691 663 | , 43691 664 | , 43691 665 | , 0 666 | , 0 667 | , 0 668 | , 0 669 | , 0 670 | , 0 671 | , 43691 672 | , 43691 673 | , 43691 674 | , 0 675 | , 0 676 | , 0 677 | , 0 678 | , 0 679 | , 0 680 | , 65536 681 | , 65536 682 | , 0 683 | , 0 684 | , 0 685 | , 0 686 | , 0 687 | , 0 688 | , 0 689 | , 131071 690 | : hmm1_get_a2 dup -24 . + b! @b ! drop ; 691 | : act37 @ hmm1_get_a2 ; 692 | 693 | node 113 ( core 31 ) 694 | org 0 695 | , 0 696 | , 7643 697 | , 10809 698 | , 7643 699 | , 0 700 | , -7643 701 | , -20033 702 | , -12226 703 | , 0 704 | , 7643 705 | , 7643 706 | , 0 707 | , -7643 708 | , -7643 709 | : 8rep 710 | for 711 | +* unext ; 712 | : hmm2_derive_group dup dup or up b! @b push drop pop 713 | dup dup or a! 13 for 714 | @+ over - 1 . + . + 715 | .. -if - 1 . + then dup a push a! dup dup or 17 8rep push dup or pop 14 8rep drop drop a pop a! !b next drop ; 716 | : main hmm2_derive_group main ; 717 | 718 | node 112 ( core 30 ) 719 | org 0 720 | , 131071 721 | , 0 722 | , 0 723 | , 0 724 | , 0 725 | , 0 726 | , 0 727 | , 0 728 | : 1if 729 | .. -if drop ; then drop 730 | dup dup or a! 7 for 731 | @+ !b next ; 732 | : hmm2_forward_proc_inc dup dup or left b! @b 1if drop ; 733 | : main hmm2_forward_proc_inc main ; 734 | 735 | node 110 ( core 28 ) 736 | org 0 737 | , 0 738 | , 0 739 | : 1if 740 | .. -if drop 741 | 7 for 742 | left b! @b right b! @b a push *.17 push drop pop pop a! 1 b! !b @b up b! !b dup 1 b! @b over - and . + push drop pop next ; then drop -1 dup dup or b! !b 743 | 7 for 744 | right b! @b up b! !b next -131072 push drop pop ; 745 | : hmm2_forward_proc_inc dup dup or dup dup or push drop pop dup dup or b! @b dup up b! !b dup right b! !b dup left b! !b 1if dup up b! !b drop ; 746 | : hmm2_forward_proc_out dup dup or dup dup or b! !b ; 747 | : 2if 748 | .. if drop hmm2_forward_proc_inc ; then drop ; 749 | : main 750 | 999 for 751 | up b! @b 2if next hmm2_forward_proc_out main ; 752 | 753 | node 108 ( core 26 ) 754 | /a up /p right org 0 755 | , 43691 756 | , 43691 757 | , 43691 758 | , 0 759 | , 0 760 | , 0 761 | , 0 762 | , 0 763 | , 0 764 | , 43691 765 | , 43691 766 | , 43691 767 | , 0 768 | , 0 769 | , 0 770 | , 0 771 | , 0 772 | , 0 773 | , 43691 774 | , 43691 775 | , 43691 776 | , 0 777 | , 0 778 | , 0 779 | , 0 780 | : hmm2_get_a2 @ ; 781 | : 1if 782 | .. -if drop dup b! @b 24 b! !b ; then drop @p .. act44@208 .. ! dup ! hmm2_get_a2 24 b! !b ; 783 | : hmm2_get_a dup 24 - 1 . + . + 1if @b right b! !b drop ; 784 | : act26 right b! @b hmm2_get_a ; 785 | 786 | node 109 ( core 27 ) 787 | org 0 788 | , 0 789 | , 0 790 | : hmm2_get_a @b ; 791 | : 1if 792 | .. -if drop 793 | dup dup or dup dup or b! !b 7 for 794 | dup dup or push drop pop 795 | dup dup or 1 b! !b 7 for 796 | dup up b! @b right b! @p .. act26@108 .. !b 1 b! @b 2* 2* 2* dup dup or b! @b . + right b! !b hmm2_get_a a push *.17 push drop pop pop a! . + push drop pop 1 b! @b 1 . + !b next dup left b! !b dup dup or b! @b 1 . + !b next ; then drop ; 797 | : hmm2_forward_proc_inc dup dup or left b! @b 1if drop ; 798 | : main hmm2_forward_proc_inc main ; 799 | 800 | node 106 ( core 24 ) 801 | org 0 802 | , 0 803 | , 1 804 | , 11685 805 | , 8263 806 | , 0 807 | , -8263 808 | , -17708 809 | , -8263 810 | , 0 811 | , 8263 812 | , 8263 813 | , 0 814 | , -8263 815 | , -8263 816 | : 4rep 817 | for 818 | +* unext ; 819 | : hmm1_derive_group dup dup or up b! @b push drop pop 820 | dup dup or a! 13 for 821 | @+ over - 1 . + . + 822 | .. -if - 1 . + then dup a push a! dup dup or 17 4rep push dup or pop 14 4rep drop drop a pop a! !b next drop ; 823 | : main hmm1_derive_group main ; 824 | 825 | node 105 ( core 23 ) 826 | org 0 827 | , 131071 828 | , 0 829 | , 0 830 | , 0 831 | , 0 832 | , 0 833 | , 0 834 | , 0 835 | : 1if 836 | .. -if drop ; then drop 837 | dup dup or a! 7 for 838 | @+ !b next ; 839 | : hmm1_forward_proc_inc right b! @b 1if ; 840 | : main hmm1_forward_proc_inc main ; 841 | 842 | node 103 ( core 21 ) 843 | org 0 844 | , 0 845 | , 0 846 | : 1if 847 | .. -if drop 848 | 7 for 849 | right b! @b left b! @b a push *.17 push drop pop pop a! 1 b! !b @b up b! !b dup 1 b! @b over - and . + push drop pop next ; then drop -1 dup dup or b! !b 850 | 7 for 851 | left b! @b up b! !b next -131072 push drop pop ; 852 | : hmm1_forward_proc_inc dup dup or dup dup or push drop pop dup dup or b! @b dup up b! !b dup left b! !b dup right b! !b 1if dup up b! !b drop ; 853 | : hmm1_forward_proc_out dup dup or dup dup or b! !b ; 854 | : 2if 855 | .. if drop hmm1_forward_proc_inc ; then drop ; 856 | : main 857 | 999 for 858 | up b! @b 2if next hmm1_forward_proc_out main ; 859 | 860 | node 101 ( core 19 ) 861 | /a up /p left org 0 862 | , 43691 863 | , 43691 864 | , 43691 865 | , 0 866 | , 0 867 | , 0 868 | , 0 869 | , 0 870 | , 0 871 | , 43691 872 | , 43691 873 | , 43691 874 | , 0 875 | , 0 876 | , 0 877 | , 0 878 | , 0 879 | , 0 880 | , 43691 881 | , 43691 882 | , 43691 883 | , 0 884 | , 0 885 | , 0 886 | , 0 887 | : hmm1_get_a2 @ ; 888 | : 1if 889 | .. -if drop dup b! @b 24 b! !b ; then drop @p .. act37@201 .. ! dup ! hmm1_get_a2 24 b! !b ; 890 | : hmm1_get_a dup 24 - 1 . + . + 1if @b left b! !b drop ; 891 | : act19 left b! @b hmm1_get_a ; 892 | 893 | node 102 ( core 20 ) 894 | org 0 895 | , 0 896 | , 0 897 | , 0 898 | : hmm1_get_a @b ; 899 | : 1if 900 | .. -if drop 901 | dup dup or 1 b! !b 7 for 902 | dup dup or push drop pop 903 | dup dup or 2 b! !b 7 for 904 | dup up b! @b left b! @p .. act19@101 .. !b 2 b! @b 2* 2* 2* 1 b! @b . + left b! !b hmm1_get_a a push *.17 push drop pop pop a! . + push drop pop 2 b! @b 1 . + !b next dup right b! !b 1 b! @b 1 . + !b next ; then drop ; 905 | : hmm1_forward_proc_inc dup dup or right b! @b 1if drop ; 906 | : main hmm1_forward_proc_inc main ; 907 | 908 | node 8 ( core 8 ) 909 | /a right /p right org 0 910 | , 0 911 | , 0 912 | , 0 913 | , 0 914 | , 0 915 | , 0 916 | , 0 917 | , 0 918 | , 0 919 | , 0 920 | , 0 921 | , 0 922 | , 0 923 | , 0 924 | , 0 925 | , 0 926 | , 0 927 | , 0 928 | , 0 929 | , 0 930 | , 0 931 | , 0 932 | , 0 933 | , 0 934 | , 0 935 | , 0 936 | , 0 937 | , 0 938 | : hmm2_get_b4 dup -84 . + b! @b ! drop ; 939 | : act8 @ hmm2_get_b4 ; 940 | 941 | node 9 ( core 9 ) 942 | /a right /p left org 0 943 | , 43973 944 | , 52282 945 | , 53199 946 | , 55317 947 | , 55803 948 | , 56026 949 | , 55177 950 | , 50721 951 | , 0 952 | , 0 953 | , 0 954 | , 0 955 | , 0 956 | , 0 957 | , 0 958 | , 0 959 | , 0 960 | , 0 961 | , 0 962 | , 0 963 | , 0 964 | , 0 965 | , 0 966 | , 0 967 | , 0 968 | , 0 969 | , 0 970 | , 0 971 | , 0 972 | : hmm2_get_b4 @ ; 973 | : 1if 974 | .. -if drop dup -56 . + b! @b 28 b! !b ; then drop @p .. act8@8 .. ! dup ! hmm2_get_b4 28 b! !b ; 975 | : hmm2_get_b3 dup 84 - 1 . + . + 1if @b left b! !b drop ; 976 | : act9 left b! @b hmm2_get_b3 ; 977 | 978 | node 10 ( core 10 ) 979 | /a left /p right org 0 980 | , 0 981 | , 0 982 | , 0 983 | , 0 984 | , 0 985 | , 0 986 | , 0 987 | , 0 988 | , 0 989 | , 0 990 | , 0 991 | , 0 992 | , 0 993 | , 0 994 | , 0 995 | , 0 996 | , 0 997 | , 0 998 | , 0 999 | , 0 1000 | , 87071 1001 | , 78512 1002 | , 77313 1003 | , 74366 1004 | , 73266 1005 | , 72372 1006 | , 73356 1007 | , 79267 1008 | , 0 1009 | : hmm2_get_b3 @ ; 1010 | : 1if 1011 | .. -if drop dup -28 . + b! @b 28 b! !b ; then drop @p .. act9@9 .. ! dup ! hmm2_get_b3 28 b! !b ; 1012 | : hmm2_get_b2 dup 56 - 1 . + . + 1if @b right b! !b drop ; 1013 | : act10 right b! @b hmm2_get_b2 ; 1014 | 1015 | node 11 ( core 11 ) 1016 | /a down /p down org 0 1017 | , 28 1018 | , 278 1019 | , 560 1020 | , 1389 1021 | , 2003 1022 | , 2674 1023 | , 2539 1024 | , 1084 1025 | , 0 1026 | , 0 1027 | , 0 1028 | , 0 1029 | , 0 1030 | , 0 1031 | , 0 1032 | , 0 1033 | , 0 1034 | , 0 1035 | , 0 1036 | , 0 1037 | , 0 1038 | , 0 1039 | , 0 1040 | , 0 1041 | , 0 1042 | , 0 1043 | , 0 1044 | , 0 1045 | , 0 1046 | : hmm2_get_b2 @b ; 1047 | : 1if 1048 | .. -if drop dup b! @b 28 b! !b ; then drop right b! @p .. act10@10 .. !b dup right b! !b hmm2_get_b2 28 b! !b ; 1049 | : hmm2_get_b dup 28 - 1 . + . + 1if @b ! drop ; 1050 | : act11 @ hmm2_get_b ; 1051 | 1052 | node 111 ( core 29 ) 1053 | /p up org 0 1054 | , 0 1055 | : 7rep right b! !b dup dup or b! @b 1 . + !b ; 1056 | : 5rep down b! @p .. act11@11 .. !b ; 1057 | : hmm2_get_b @b ; 1058 | : 6rep 2* 2* 2* dup dup or b! @b . + down b! !b hmm2_get_b ; 1059 | : 1if 1060 | .. -if drop 1061 | dup dup or dup dup or b! !b 7 for 1062 | 5rep dup 6rep 7rep next ; then drop 1063 | dup dup or dup dup or b! !b 7 for 1064 | left b! @b 5rep over 6rep a push *.17 push drop pop pop a! 7rep next ; 1065 | : hmm2_forward_proc_inc right b! @b dup left b! !b 1if drop ; 1066 | : act29 up b! @b hmm2_forward_proc_inc ; 1067 | 1068 | node 212 ( core 48 ) 1069 | /a down org 0 1070 | : hmm2_forward_proc_inc @ ; 1071 | : hmm2_derive_group @b ; 1072 | : hmm2_input left b! @p .. act29@111 .. !b right b! @p .. act49@213 .. !b @ right b! !b @ !b @ !b hmm2_derive_group left b! !b hmm2_forward_proc_inc ; 1073 | : 1if 1074 | .. if drop hmm2_input ! ; then drop ; 1075 | : main 1076 | 999 for 1077 | @ 1if next main ; 1078 | 1079 | node 1 ( core 1 ) 1080 | /a left /p left org 0 1081 | , 0 1082 | , 0 1083 | , 0 1084 | , 0 1085 | , 0 1086 | , 0 1087 | , 0 1088 | , 0 1089 | , 0 1090 | , 0 1091 | , 0 1092 | , 0 1093 | , 0 1094 | , 0 1095 | , 0 1096 | , 0 1097 | , 0 1098 | , 0 1099 | , 0 1100 | , 0 1101 | , 0 1102 | , 0 1103 | , 0 1104 | , 0 1105 | , 0 1106 | , 0 1107 | , 0 1108 | , 0 1109 | : hmm1_get_b4 dup -84 . + b! @b ! drop ; 1110 | : act1 @ hmm1_get_b4 ; 1111 | 1112 | node 2 ( core 2 ) 1113 | /a left /p right org 0 1114 | , 9 1115 | , 111 1116 | , 262 1117 | , 782 1118 | , 1376 1119 | , 2355 1120 | , 3363 1121 | , 3832 1122 | , 0 1123 | , 0 1124 | , 0 1125 | , 0 1126 | , 0 1127 | , 0 1128 | , 0 1129 | , 0 1130 | , 0 1131 | , 0 1132 | , 0 1133 | , 0 1134 | , 0 1135 | , 0 1136 | , 0 1137 | , 0 1138 | , 0 1139 | , 0 1140 | , 0 1141 | , 0 1142 | , 0 1143 | : hmm1_get_b4 @ ; 1144 | : 1if 1145 | .. -if drop dup -56 . + b! @b 28 b! !b ; then drop @p .. act1@1 .. ! dup ! hmm1_get_b4 28 b! !b ; 1146 | : hmm1_get_b3 dup 84 - 1 . + . + 1if @b right b! !b drop ; 1147 | : act2 right b! @b hmm1_get_b3 ; 1148 | 1149 | node 3 ( core 3 ) 1150 | /a right /p left org 0 1151 | , 0 1152 | , 0 1153 | , 0 1154 | , 0 1155 | , 0 1156 | , 0 1157 | , 0 1158 | , 0 1159 | , 0 1160 | , 0 1161 | , 0 1162 | , 0 1163 | , 0 1164 | , 0 1165 | , 0 1166 | , 0 1167 | , 0 1168 | , 0 1169 | , 0 1170 | , 0 1171 | , 128821 1172 | , 126453 1173 | , 127767 1174 | , 130249 1175 | , 129580 1176 | , 128382 1177 | , 126500 1178 | , 117937 1179 | , 0 1180 | : hmm1_get_b3 @ ; 1181 | : 1if 1182 | .. -if drop dup -28 . + b! @b 28 b! !b ; then drop @p .. act2@2 .. ! dup ! hmm1_get_b3 28 b! !b ; 1183 | : hmm1_get_b2 dup 56 - 1 . + . + 1if @b left b! !b drop ; 1184 | : act3 left b! @b hmm1_get_b2 ; 1185 | 1186 | node 4 ( core 4 ) 1187 | /a down /p down org 0 1188 | , 0 1189 | , 0 1190 | , 0 1191 | , 0 1192 | , 0 1193 | , 0 1194 | , 0 1195 | , 0 1196 | , 2241 1197 | , 4508 1198 | , 3043 1199 | , 41 1200 | , 117 1201 | , 335 1202 | , 1209 1203 | , 9303 1204 | , 0 1205 | , 0 1206 | , 0 1207 | , 0 1208 | , 0 1209 | , 0 1210 | , 0 1211 | , 0 1212 | , 0 1213 | , 0 1214 | , 0 1215 | , 0 1216 | , 0 1217 | : hmm1_get_b2 @b ; 1218 | : 1if 1219 | .. -if drop dup b! @b 28 b! !b ; then drop left b! @p .. act3@3 .. !b dup left b! !b hmm1_get_b2 28 b! !b ; 1220 | : hmm1_get_b dup 28 - 1 . + . + 1if @b ! drop ; 1221 | : act4 @ hmm1_get_b ; 1222 | 1223 | node 104 ( core 22 ) 1224 | /p up org 0 1225 | , 0 1226 | : 3rep left b! !b dup dup or b! @b 1 . + !b ; 1227 | : 1rep down b! @p .. act4@4 .. !b ; 1228 | : hmm1_get_b @b ; 1229 | : 2rep 2* 2* 2* dup dup or b! @b . + down b! !b hmm1_get_b ; 1230 | : 1if 1231 | .. -if drop 1232 | dup dup or dup dup or b! !b 7 for 1233 | 1rep dup 2rep 3rep next ; then drop 1234 | dup dup or dup dup or b! !b 7 for 1235 | right b! @b 1rep over 2rep a push *.17 push drop pop pop a! 3rep next ; 1236 | : hmm1_forward_proc_inc left b! @b dup right b! !b 1if drop ; 1237 | : act22 up b! @b hmm1_forward_proc_inc ; 1238 | 1239 | node 205 ( core 41 ) 1240 | /a down org 0 1241 | : hmm1_forward_proc_inc @ ; 1242 | : hmm1_derive_group @b ; 1243 | : hmm1_input right b! @p .. act22@104 .. !b left b! @p .. act42@206 .. !b @ left b! !b @ !b @ !b hmm1_derive_group right b! !b hmm1_forward_proc_inc ; 1244 | : 1if 1245 | .. if drop hmm1_input ! ; then drop ; 1246 | : main 1247 | 999 for 1248 | @ 1if next main ; 1249 | 1250 | -------------------------------------------------------------------------------- /examples/port-execution.ga: -------------------------------------------------------------------------------- 1 | \ code from AB004: 2 | \ http://www.greenarraychips.com/home/documents/greg/AB004-141021-PORTEX.pdf 3 | \ Read that document if this code does not make sense. 4 | 5 | \ Node 608 is in port execution mode and is used by node 708 6 | \ as a random access 64 word array 7 | 8 | \ Write test values into the ram, then read them back and send 9 | \ them back via 708 serial. 10 | 11 | \ Output: numbers even numbers from 0 to 20 12 | 13 | node 608 14 | : main r--- 15 | 16 | 17 | node 708 18 | : set ( a ) 19 | @p ! ! ; 20 | .. @p a! .. 21 | : @next ( -n ) 22 | @p ! @ ; 23 | .. @+ !p .. 24 | : !next ( n ) 25 | @p ! ! ; 26 | .. @p !+ .. 27 | : fetch ( a-n ) set @next ; 28 | : store ( na ) set !next ; 29 | 30 | \ --------------------------------- 31 | \ test section 32 | include 708serial.ga 33 | : set-array \ store 2*i at index i 34 | \ Could also use 'set' and '!next' here 35 | \ instead of manually handling the index 36 | 0 ( index) 37 | 10 for 38 | dup dup . + ( 2*index ) 39 | over ( index ) 40 | store 41 | 1 . + ( increment index ) 42 | next ; 43 | 44 | : print-array \ read and print the stored values 45 | 0 set 46 | 10 for 47 | @next send 48 | next ; 49 | 50 | : main 51 | right a! 52 | set-array 53 | print-array 54 | exit 55 | -------------------------------------------------------------------------------- /examples/racecar.ga: -------------------------------------------------------------------------------- 1 | 2 | \ Every node is a wire forming a loop that weaves its way 3 | \ through the chip. The number 1 is the race car, it 4 | \ races around the chip on its wire track. 5 | 6 | \ Node 517 passes the number and inverts pin 517.17 7 | \ allowing us to measure the time to complete one lap. 8 | 9 | node 517 \ J36.2 10 | : pin io b! !b ; 11 | : pass west b! @ !b ; 12 | : main south a! west b! 1 !b 13 | : loop pass 0x30000 pin pass 0x20000 pin loop ; 14 | \ 840Khz 15 | 16 | node 1-16,202-216,402-416,602-616 17 | wire west east 18 | 19 | node 102-116,302-316,502-516,701-716 20 | wire east west 21 | 22 | node 17,217,417,617 23 | wire west north 24 | 25 | node 117,317,717 26 | wire south west 27 | 28 | node 101,301,501 29 | wire east north 30 | 31 | node 201,401,601 32 | wire south east 33 | 34 | node 700 35 | wire east south 36 | 37 | node 600-100 38 | wire north south 39 | 40 | node 0 41 | wire north east 42 | -------------------------------------------------------------------------------- /examples/square_waves.ga: -------------------------------------------------------------------------------- 1 | 2 | node 517 \ J36.2 3 | boot io b! 4 | : delay 0x3ffff for . . . unext ; 5 | : setup 0x30000 0x20000 over over over over over over over over ; 6 | 7 | \ 307Hz 8 | : 3_2ms setup : loop !b delay loop ; 9 | 10 | \ 37.2Mhz 11 | : 27ns 0x30000 !b 0x20000 !b 27ns ; 12 | 13 | \ 58.8Mhz 14 | : 17ns setup : 17ns_loop !b 17ns_loop ; 15 | 16 | \ 82.7Mhz 17 | : 12ns setup : 12ns_loop 0x3ffff for !b unext 12ns_loop ; 18 | 19 | \ 82.7Mhz, less jitter 20 | \ 104.3Mhz with double !b, but asymmetrical 21 | : best 22 | setup 23 | 0x3ffff dup push dup push dup push dup push 24 | dup push dup push dup push dup push push 25 | \ drop ( for larger high period with two double !b ) 26 | .. !b ( !b ) unext, unext, 27 | 28 | : main \ select square wave here 29 | 3_2ms 30 | \ 27ns 31 | \ 17ns 32 | \ 12ns 33 | \ best 34 | ; 35 | -------------------------------------------------------------------------------- /examples/sram-demo.ga: -------------------------------------------------------------------------------- 1 | 2 | \ save words to sram then read them back 3 | 4 | \ 708 - async, print all words it receives from south port 5 | \ 106 - for n from 1 to 10 write sram[n] = 2*n 6 | \ for n from 1 to 15 read sram[n], sent to node 708 7 | \ 107,7,8,9 - sram control 8 | \ 608...206 - wire nodes for sending data from sram to async port 9 | 10 | \ run with: 11 | \ ga sram-demo.ga --port /dev/ttyUSB0 12 | \ 13 | \ Expected output: 14 | \ 0 15 | \ 2 16 | \ 4 17 | \ 6 18 | \ 8 19 | \ 10 20 | \ 12 21 | \ 14 22 | \ 16 23 | \ 18 24 | \ 20 25 | \ ... 26 | \ ... 27 | \ ... 28 | \ ... 29 | \ ... 30 | \ [exit] 31 | \ Where ... are random numbers 32 | 33 | 34 | \ block 278: 35 | \ example code for memory master nodes. 36 | \ memory-access words assume that addresses and 37 | \ data are 16-bit parameters with the upper two 38 | \ bits zero and pages are 4-bits with the upper 39 | \ 14 bits zero. p.a is thus a 20-bit address. 40 | \ 41 | \ ex@ a p - w fetch w from p.a 42 | \ ex! w a p store w at p.a 43 | \ mk! w f -0 set masks from w per f. 44 | \ cx? w a p n - f comp-and-exch 45 | \ 46 | \ cx? compares value at p.a to n. if same, write 47 | \ s w to p.a and returns true. otherwise, only 48 | \ returns false. x@ and x! are 16-bit versions to 49 | \ access the lowest 64k of available memory. 50 | \ 51 | \ mk! sets mask from w when f is 0; 52 | \ posts stimuli when f is 1. ) 53 | \ - user 54 | node 106 ( node 106, 108, or 207.) 55 | : x! ( wa) ( 39) dup dup or 56 | : ex! ( wap) : mk! ( mfp') ( 3A) - !b - !b !b ; 57 | : x@ ( a-w) ( 3C) dup dup or 58 | : ex@ ( ap-w) ( 3D) !b !b @b ; 59 | : cx? ( wapn-f) ( 3E) - !b !b !b !b @b ; ( 40) 60 | : main 61 | north a! east b! 62 | 0 10 for dup dup . + over x! 1 . + next 63 | 0 15 for dup dup x@ ! 1 . + next 64 | 65 | 66 | include sram.ga 67 | include sram-minimal-master.ga 68 | 69 | 70 | node 708 71 | include 708serial.ga 72 | : main 73 | io b! south a! 74 | 15 for @ send next 75 | exit 76 | 77 | 78 | node 608 : main north b! south a! : loop @ !b loop ; 79 | node 508 : main north b! south a! : loop @ !b loop ; 80 | node 408 : main north b! south a! : loop @ !b loop ; 81 | node 308 : main north b! west a! : loop @ !b loop ; 82 | node 307 : main west a! east b! : loop @ !b loop ; 83 | node 306 : main south a! east b! : loop @ !b loop ; 84 | node 206 : main south a! north b! : loop @ !b loop ; 85 | -------------------------------------------------------------------------------- /examples/sram-minimal-master.ga: -------------------------------------------------------------------------------- 1 | 2 | \ node 107 minimal capability version. 3 | \ single master, no polling, no stimuli. 4 | \ maximum speed, minimum power. 5 | \ 6 | \ all requests are atomic. passes ex@ and ex! 7 | \ requests on to node 007, performs cx? locally 8 | \ using those primitives. 9 | \ 10 | \ requests are variable length messages decoded 11 | \ as shown below where - means 18-bit inverse of 12 | \ 16 bit argument. 13 | \ 14 | \ ex@ +p +a fetch 15 | \ cx? -w1 +p a w2 comp-and-exch 16 | \ ex! -p -a w store 17 | \ 280 list degenerate sram 107 node 18 | 19 | node 107 org 0 20 | 21 | : cx ( wp-) over push @ dup 22 | ( a) !b over ( p) !b @b ( w) pop - ( w1) or if 23 | ( ne) @ ( w2) dup or ( ff) ! ; 24 | ( eq) then drop ( a) !b - ( -p) !b @ ( w2) !b 0xffff ! ; 25 | 26 | : cmd @ -if @ ' cx -until ( .e!) - !b !b @ !b ; 27 | then @ ( .. here to conform with softsim ) ( .e@) ( a) !b ( p) !b @b ( w) ! ; 28 | \ TODO: in softsim the '@' after 'then' is in its own word, why? 29 | org 0x17 30 | 31 | : main : start down b! right a! 32 | : run cmd run ; 33 | -------------------------------------------------------------------------------- /examples/sram.ga: -------------------------------------------------------------------------------- 1 | \ SRAM Control Cluster 2 | \ AP003 http://www.greenarraychips.com/home/documents/greg/AP003-110810-SRAM.pdf 3 | 4 | \ sram driver nodes 7, 8, 9 5 | 6 | \ block 270: 7 | \ node 9 suspends while waiting for a16. it uses 8 | \ the two lower page bits to output an 18-bit address. 9 | \ 10 | \ a16 xx.aaaa.aaaa.aaaa.aaaa 11 | \ p04 00.0000.0000.0000.pppp 12 | \ a18 aa.aaaa.aaaa.aaaa.aapp 13 | \ 14 | \ the code is written to minimize/equalize the time 15 | \ to output the address, which must be stable 16 | \ when node8 stores the 'start' command ) 17 | 18 | node 9 ( sram.16 address-bus ) 19 | org 0x20 20 | : main 21 | : start west ( right) b! .. data a! .. 0x3 ( mask) 22 | : cmd ( m) @b ( a16) 2* 2* over @b -if 23 | - ( p04) and or ( a18) ! cmd ; 24 | then ( p04) and or .. ( a18) ! cmd ; 25 | 26 | 27 | 28 | \ ( block 272: 29 | \ node8 is fed a stop command during start-up, then 30 | \ suspends while waiting for a16. after starting 31 | \ the read or write, it again suspends while 32 | \ waiting for the stop command. 33 | \ 34 | \ bits 4..2 of the /possibly inverted/ page value 35 | \ are used 'as-is' to index into the start table, 36 | \ setting two address bits, write enable, and chip 37 | \ enable. ** note that reads and writes are swapped 38 | \ if the page 'overflows' into bit4, with 39 | \ disastrous results ** 40 | \ 41 | \ cmd index .lit. pin17 pin05 pin03 pin01 42 | \ w00 .0111 2556A a19-0 a18-0 /we-0 /ce-0 43 | \ r00 .0000 2556E a19-0 a18-0 /we-1 /ce-0 44 | \ w01 .0110 2557A a19-0 a18-1 /we-0 /ce-0 45 | \ r01 .0001 2557E a19-0 a18-1 /we-1 /ce-0 46 | \ w10 .0101 3556A a19-1 a18-0 /we-0 /ce-0 47 | \ r10 .0010 3556E a19-1 a18-0 /we-1 /ce-0 48 | \ w11 .0100 3557A a19-1 a18-1 /we-0 /ce-0 49 | \ r11 .0011 3557E a19-1 a18-1 /we-1 /ce-0 ) 50 | \ 51 | node 8 ( control-pins ) 52 | org 0 53 | \ :: 'r-l- 0x1F5 lit ; ( TODO: why? 54 | ( 'start' pin control table 0-7) 55 | , 0x2556E ( r00) , 0x2557E ( r01) 56 | , 0x3556E ( r10) , 0x3557E ( r11) 57 | , 0x3557A ( w11) , 0x3556A ( w10) 58 | , 0x2557A ( w01) , 0x2556A ( w00) 59 | org 0x20 60 | 61 | : main 62 | : start ( 'r-l-) 0x1F5 b! io a! 63 | : cmd @b ( stop) ! a push 0x7 ( mask) .. 64 | @b ( a16) !b @b ( +p/-p) dup !b 65 | 2/ 2/ and ( i3) a! .. @ ( ctrl) pop a! 66 | ( start) ! cmd ; 67 | 68 | 69 | 70 | ( ( block 274: 71 | ( node7 suspends waiting for a16, passes it and 72 | ( page/r/w to nodes 8 and 9, finally controlling 73 | ( the data transfer and timing until sending the 74 | ( stop command. 75 | ( 76 | ( the literals needed for writing are loaded 77 | ( onto the stack and used circularly to save 78 | ( time. /read's drops are free./ 79 | ( 80 | ( ---- .lit. pin17 pin05 pin03 pin01 81 | ( stop 3557F a19-1 a18-1 /we-1 /ce-1 ) 82 | 83 | node 7 ( data-bus) 84 | \ Greenarrys arrayforth wraps the words when length exceeds 64 85 | \ That is not supported here, so don't org out that far 86 | \ org 0x20 87 | org 29 88 | 89 | \ :: in 0x14555 lit ; 90 | \ :: out 0x15555 lit ; 91 | \ :: stop 0x3557F lit ; 92 | 93 | : main 94 | : start east ( left) b! ( out) 0x15555 io data ( stop) 0x3557F 95 | ( out) 0x15555 io data ( stop) 0x3557F ( in) 0x14555 io a! ( in) ! 96 | north ( down) a! ( stop) !b 97 | : cmd ( /soid/) @ ( a16 ) !b @ ( +p/-p ) -if 98 | 99 | : w16 ( /soid/p-) ( +p/-p) !b 100 | ( /- setup + 45ns) @ ( w) a push push ( data) a! 101 | pop ! ( io) a! ( out) ! ( 40) 13 for unext ( stop) !b 102 | ( -/) ( in) 0x14555 ! pop a! cmd ; 103 | 104 | : r16 ( /soid/p-) then ( +p/-p) !b 105 | ( /- setup + 55ns) a push ( data) a! 106 | ( io) drop ( out) drop ( 50) 40 for unext ( stop) !b ( -/ ) 107 | @ ( w) pop a! ! cmd ; 108 | -------------------------------------------------------------------------------- /examples/variables.ga: -------------------------------------------------------------------------------- 1 | 2 | node 1 3 | : name! @p drop !p ; 4 | : name@ 0 ; 5 | 6 | : main 7 | 5 name! \ set variable 8 | name@ \ read variable 9 | 10 | name@ 1 . + name! \ increment 11 | -------------------------------------------------------------------------------- /ga: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- python -*- 3 | 4 | # ::::GA* 5 | # ::::Tools 6 | 7 | import argparse 8 | import os 9 | 10 | import ga_tools 11 | 12 | #print('::::GA*Tools Version', ga_tools.version) 13 | parser = argparse.ArgumentParser() 14 | parser.add_argument('file', nargs='?', help='input file') 15 | parser.add_argument('--bootstream', 16 | nargs=1, 17 | help='Bootstream type. Default=708') 18 | parser.add_argument('--port', 19 | nargs=1, 20 | help='Serial port') 21 | parser.add_argument('--baud', 22 | nargs=1, 23 | default=[460800], 24 | help='Serial port baud rate. Default=460800') 25 | parser.add_argument('--json', 26 | nargs=-1, 27 | help='Dump compiled data in json format') 28 | parser.add_argument('--outfile', 29 | nargs=1, 30 | help='filename to write json data to') 31 | parser.add_argument('--print', 32 | nargs=-1, 33 | help='Pretty print assembled node data') 34 | parser.add_argument('--no-listen', 35 | nargs=-1, 36 | help='Wait for and print data from serial port') 37 | parser.add_argument('--size', 38 | nargs=-1, 39 | help='Count node ram usage') 40 | parser.add_argument('--disable-0-opt', 41 | nargs=-1, 42 | help="Don't compile '0' as 'dup dup or'") 43 | parser.add_argument('--disable-plus-opt', 44 | nargs=-1, 45 | help="Don't auto insert nops before + or +*") 46 | parser.add_argument('--print-simple', 47 | nargs=-1, 48 | help='Simple assembly output usefull for diffs') 49 | parser.add_argument('--node', 50 | nargs=1, 51 | help='Selects a single node for print options') 52 | parser.add_argument('--sim', 53 | nargs=-1, 54 | help='Run the GA144 simulator') 55 | parser.add_argument('--dis', 56 | nargs=1, 57 | help='Disassemble an 18-bit word') 58 | parser.add_argument('--version', 59 | nargs=-1, 60 | help='GA-tools version') 61 | args = parser.parse_args() 62 | 63 | if args.version is not None: 64 | print(':::GA*Tools', ga_tools.version) 65 | exit() 66 | 67 | if args.dis: 68 | word = int(args.dis[0]) & 0x3ffff 69 | print(word, ' ', ga_tools.disasm_to_str(word)) 70 | exit() 71 | 72 | print_json = args.json is not None 73 | print_pretty = args.print is not None or args.print_simple is not None 74 | print_size = args.size is not None 75 | select_node = None 76 | outfile = args.outfile[0] if args.outfile else None 77 | 78 | if args.node: 79 | select_node = int(args.node[0]) 80 | if not ga_tools.valid_coord(select_node): 81 | print('Invalid --node coordinate:', select_node) 82 | exit() 83 | 84 | ga_tools.optimize_0(args.disable_0_opt is None) 85 | ga_tools.optimize_plus(args.disable_plus_opt is None) 86 | 87 | if not args.file: 88 | parser.print_help() 89 | exit() 90 | 91 | ga_tools.set_baud(int(args.baud[0])) 92 | 93 | ga_tools.include_file(args.file) 94 | ga_tools.do_compile() 95 | 96 | chips = list(ga_tools.get_chips().values()) 97 | 98 | if len(chips) > 2: 99 | raise Exception('More than 2 chips are not supported yet') 100 | 101 | multiple_chips = len(chips) > 1 102 | if not chips: 103 | exit() # empty file 104 | 105 | bootstream_type = args.bootstream 106 | if bootstream_type is None: 107 | bootstream_type = '708-300' if multiple_chips else '708' 108 | else: 109 | bootstream_type = args.bootstream[0] 110 | 111 | if print_size: 112 | for chip in chips: 113 | chip.print_size(multiple_chips) 114 | exit() 115 | 116 | def do_pretty_print(): 117 | ga_tools.print_nodes(args.print_simple is not None, select_node) 118 | 119 | if print_pretty: 120 | do_pretty_print() 121 | exit() 122 | 123 | if args.sim is not None: 124 | if multiple_chips: 125 | print('Multiple chips are not supported in simulation') 126 | exit() 127 | import tempfile 128 | print_json = True 129 | outfile = tempfile.NamedTemporaryFile().name 130 | 131 | if print_json: 132 | #TODO: json for multiple chips 133 | chip = chips[0] 134 | import json 135 | bs = None 136 | if args.bootstream is not None: 137 | bs = bootstream_type 138 | data = ga_tools.chip_json(chip, bs, args.sim is not None) 139 | data['version'] = ga_tools.version 140 | data['file'] = os.path.abspath(args.file) 141 | dump = json.dumps(data) 142 | if outfile: 143 | f = open(outfile, 'w') 144 | f.write(dump) 145 | f.close() 146 | else: 147 | print(dump) 148 | if args.sim is None: 149 | exit() 150 | 151 | if args.sim is not None: 152 | import subprocess 153 | subprocess.call(["ga-sim", outfile]) 154 | exit() 155 | 156 | if not args.port: 157 | do_pretty_print() 158 | exit() 159 | 160 | serial = ga_tools.GA144Serial(chips, args.port[0], args.baud[0]) 161 | serial.write_bootstream(bootstream_type) 162 | if args.no_listen is None: 163 | serial.listen() 164 | -------------------------------------------------------------------------------- /ga_tools/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from .ga144_asm import * 3 | from .assembler import * 4 | from .ga_serial import * 5 | from .bootstream import * 6 | 7 | version = 0.2 8 | 9 | 10 | def chip_json(chip, bootstream_type=None, simulation=False): 11 | data = {'nodes': {coord:node.json() for coord, node 12 | in chip.nodes.items()}} 13 | if bootstream_type: 14 | bs = make_bootstream(bootstream_type, [chip]) 15 | data['bootstream'] = {'type':bootstream_type, 16 | 'node': bs.start_coord, 17 | 'stream': bs.stream(not simulation)} 18 | return data 19 | 20 | -------------------------------------------------------------------------------- /ga_tools/assembler.py: -------------------------------------------------------------------------------- 1 | 2 | # ::::GA* 3 | # ::::Tools 4 | 5 | # GA144/F18a assembler 6 | 7 | from .defs import * 8 | from .ga144_asm import * 9 | from .parse import * 10 | 11 | import re 12 | 13 | chip = None 14 | node = None 15 | 16 | directives = {} 17 | asm_directives = {} 18 | 19 | compile_0_as_dup_dup_or = True 20 | auto_nop_insert = True 21 | 22 | baud=None 23 | 24 | node_stack = [] 25 | 26 | def directive(name, asm_supported=False): 27 | def decorator(fn): 28 | directives[name] = fn 29 | if asm_supported: 30 | asm_directives[name] = fn 31 | return decorator 32 | 33 | def op_directive(op): 34 | def fn(_): 35 | node.compile_op(op) 36 | return fn 37 | 38 | for op in opcodes: 39 | directives[op] = op_directive(op) 40 | 41 | @directive(':', True) 42 | def start_def(p): 43 | name = p.read_word() 44 | if name in directives: 45 | throw_error("name '{}' is reserved (directive)".format(name)) 46 | # Removed this check for the SRAM code from greenarrays 47 | # elif name in node.rom_names: 48 | # throw_error("name '{}' already defined (rom)".format(name)) 49 | node.start_def(name) 50 | 51 | @directive('boot', True) 52 | def _start_boot(_): 53 | if not node.current_word.empty() or node.current_word.prev: 54 | throw_error('boot directive must be first in node definition') 55 | node.boot_code = True 56 | node.boot = node.current_word 57 | 58 | @directive('if') 59 | def _if(_): 60 | node.compile_if('if') 61 | 62 | @directive('-if') 63 | def _if(_): 64 | node.compile_if('-if') 65 | 66 | @directive('if:') 67 | def _if_label(p): 68 | node.compile_call('if', node.make_ref(p.read_word())) 69 | 70 | @directive('-if:') 71 | def __if_label(p): 72 | node.compile_call('-if', node.make_ref(p.read_word())) 73 | 74 | @directive('then') 75 | def _if(_): 76 | node.compile_then() 77 | 78 | @directive('while') 79 | def _while(_): 80 | node.compile_if('if') 81 | node.swap() 82 | 83 | @directive('-while') 84 | def __while(_): 85 | node.compile_if('-if') 86 | node.swap() 87 | 88 | def here(): 89 | node.fill_rest_with_nops() 90 | node.push(node.current_word) 91 | 92 | @directive('here') 93 | def _here(_): 94 | here() 95 | 96 | @directive('begin') 97 | def _begin(_): 98 | here() 99 | 100 | @directive('for') 101 | def _for(_): 102 | node.compile_op('push') 103 | here() 104 | 105 | @directive('next') 106 | def _next(_): 107 | node.compile_next('next') 108 | 109 | @directive('next:') 110 | def _if_label(p): 111 | node.compile_call('next', node.make_ref(p.read_word())) 112 | 113 | @directive('unext') 114 | def _unext(_): 115 | node.compile_op('unext') 116 | node.pop() 117 | 118 | @directive('unext,') 119 | def _unext_comma(_): 120 | node.compile_op('unext') 121 | 122 | def compile_without_nop(op): 123 | x = node.auto_nop_insert 124 | node.auto_nop_insert = False 125 | node.compile_op(op) 126 | node.auto_nop_insert = x 127 | 128 | @directive('+,') 129 | def _plus(_): 130 | compile_without_nop('+') 131 | 132 | @directive('+*,') 133 | def _plus(_): 134 | compile_without_nop('+*') 135 | 136 | @directive('end') 137 | def _end(_): 138 | node.compile_next('end') 139 | 140 | @directive('until') 141 | def _until(_): 142 | node.compile_next('if') 143 | 144 | @directive('-until') 145 | def __until(_): 146 | node.compile_next('-if') 147 | 148 | @directive('..') 149 | def _align(_): 150 | node.fill_rest_with_nops() 151 | 152 | @directive('+cy', True) 153 | def _pcy(_): 154 | node.fill_rest_with_nops() 155 | node.extended_arith = 0x200 156 | 157 | @directive('-cy', True) 158 | def _mcy(_): 159 | node.fill_rest_with_nops() 160 | node.extended_arith = 0 161 | 162 | def compile_const_directive(const): 163 | def fn(_): 164 | node.compile_constant(const) 165 | return fn 166 | 167 | for name, addr in named_addresses.items(): 168 | directives[name] = compile_const_directive(addr) 169 | 170 | def compile_port_directive(port): 171 | def fn(_): 172 | node.compile_port(port) 173 | return fn 174 | 175 | for port_index, name in enumerate(('north', 'east', 'south', 'west')): 176 | directives[name] = compile_port_directive(port_index) 177 | 178 | def optimize_0(x): 179 | global compile_0_as_dup_dup_or 180 | compile_0_as_dup_dup_or = x 181 | if node: 182 | node.compile_0_as_dup_dup_or = x 183 | 184 | def optimize_plus(x): 185 | global auto_nop_insert 186 | auto_nop_insert = x 187 | if node: 188 | node.auto_nop_insert = x 189 | 190 | @directive('enable_0_opt') 191 | def _enable_0_opt(_): 192 | optimize_0(True) 193 | 194 | @directive('disable_0_opt') 195 | def _disable_0_opt(_): 196 | optimize_0(False) 197 | 198 | @directive('enable_plus_opt') 199 | def _enable_plus_opt(_): 200 | optimize_plus(True) 201 | 202 | @directive('disable_plus_opt') 203 | def _disable_plus_opt(_): 204 | optimize_plus(False) 205 | 206 | @directive('org', True) 207 | def _org(p): 208 | node.move_forward(p.read_int()) 209 | 210 | def read_ref(p): 211 | return node.make_ref(p.read_word()) 212 | 213 | @directive(',') 214 | def _comma(p): 215 | node.set_next_const(read_ref(p)) 216 | 217 | @directive("'") 218 | def _tick(p): 219 | word = p.read_word() 220 | # symbols.get is type Word, read_ref is type Ref 221 | # functions like compile_next expect a word to be on the stack, 222 | # not a reference. 223 | # todo - fix this by using references for everything. 224 | value = node.symbols.get(word) 225 | if not value: 226 | throw_error('tick: undefined word ' + str(word)) 227 | node.push(value) 228 | 229 | @directive("lit") 230 | def _lit(p): 231 | #TODO: values from ' are Words so this does not work 232 | node.compile_constant(node.pop()) 233 | 234 | @directive('coord') 235 | def _coord(_): 236 | node.compile_constant(node.coord) 237 | 238 | @directive('/a', True) 239 | def _init_a(p): 240 | node.init_a = read_ref(p) 241 | 242 | @directive('/b', True) 243 | def _init_b(p): 244 | node.init_b = read_ref(p) 245 | 246 | @directive('/p', True) 247 | def _init_p(p): 248 | node.init_p = read_ref(p) 249 | 250 | @directive('/io', True) 251 | def _init_io(p): 252 | node.init_io = read_ref(p) 253 | 254 | def read_stream_options(r): 255 | into, thru = None, None 256 | while True: 257 | val = r.peak() 258 | if val == 'into': 259 | r.read_word() #discard 260 | into = r.read_coord() 261 | continue 262 | if val == 'thru': 263 | r.read_word() #discard 264 | thru = r.read_word() 265 | if thru not in ['!', '!b', '!p']: 266 | throw_error("invalid value for 'thru': " + thru) 267 | continue 268 | if into is None: 269 | throw_error("'stream{' requires 'node' argument") 270 | return into, thru 271 | 272 | @directive('stream{') 273 | def _start_stream(r): 274 | global node 275 | node_stack.append(node) 276 | into, thru = read_stream_options(r) 277 | stream = chip.new_stream(into, thru) 278 | node.start_stream(stream) 279 | node = stream 280 | 281 | @directive('}stream') 282 | def _end_stream(r): 283 | global node 284 | assert node.stream 285 | if not node_stack: 286 | throw_error('unmatched }stream') 287 | node.fill_rest_with_nops() 288 | node = node_stack.pop() 289 | 290 | # { and } are implemented as a stream that have a limit 291 | # to a single word and the current node as the destination node. 292 | 293 | @directive('{') 294 | def _start_asm(r): 295 | global node 296 | node_stack.append(node) 297 | stream = chip.new_stream(node.coord, None) 298 | stream.single_word = True 299 | node.start_stream1(stream) 300 | node = stream 301 | 302 | @directive('}') 303 | def _end_asm(r): 304 | global node 305 | assert node.stream 306 | if not node_stack: 307 | throw_error('unmatched }') 308 | node.fill_rest_with_nops() 309 | node = node_stack.pop() 310 | 311 | def error_directive(msg): 312 | def fn(_): 313 | throw_error(msg) 314 | return fn 315 | 316 | # These words should be handled by the parser 317 | for word in ('include', 'chip', 'node', 'asm', 318 | '\n', '(', '\\', 'wire', 'into', 'thru'): 319 | directives[word] = error_directive('parser error: ' + word) 320 | 321 | def set_baud(n): 322 | global baud 323 | baud = n 324 | 325 | @directive('baud') 326 | def _baud(_): 327 | node.compile_constant(baud) 328 | 329 | @directive('unext_baud') 330 | def _unext_baud(_): 331 | node.compile_constant(int((1/baud)/(2.4*10**-9))) 332 | 333 | def set_chip(name): 334 | global chip 335 | chip = get_chips().get(name) 336 | if chip is None: 337 | chip = GA144(name) 338 | chip = chip 339 | 340 | def set_node(coord): 341 | global node 342 | assert chip 343 | node = chip.set_node(coord) 344 | if node.finished: 345 | throw_error('Repeated node {}'.format(coord)) 346 | node.compile_0_as_dup_dup_or = compile_0_as_dup_dup_or 347 | node.auto_nop_insert = auto_nop_insert 348 | 349 | def process_call(reader, word): 350 | ref = node.make_ref(word) 351 | if ref.node == node: 352 | if word not in node.symbol_names and not node.stream: 353 | #TODO: handle streams 354 | m = "node {} - name '{}' is not defined" 355 | throw_error(m.format(node.coord, word)) 356 | next_word = reader.peak() 357 | if next_word == ';': 358 | node.compile_call('jump', ref) 359 | reader.read_word() # discard ; 360 | return 361 | node.compile_call('call', ref) 362 | 363 | def process_aforth(coord, data): 364 | reader = data.token_reader(node) 365 | while True: 366 | w = reader.read_word() 367 | if not w: 368 | break 369 | fn = directives.get(w) 370 | if fn: 371 | fn(reader) 372 | continue 373 | n = parse_int(w) 374 | if n is None: 375 | process_call(reader, w) 376 | else: 377 | node.compile_constant(n) 378 | 379 | def process_asm(coord, data): 380 | node.asm_node = True 381 | for line in data.tokens: 382 | if type(line) is not list: 383 | throw_error('asm mode parser error: expected type list') 384 | if len(line) >= 1: 385 | w = line[0].value 386 | fn = asm_directives.get(w) 387 | if fn: 388 | reader = TokenReader(None, line[1:]) 389 | fn(reader) 390 | if reader.index < reader.last: 391 | throw_error('ASM mode directives must be on their own line') 392 | continue 393 | 394 | ops = [t.value for t in line] 395 | node.asm_word(ops, add_to_node=True) 396 | 397 | def process_chip(nodes): 398 | for coord, data in nodes.items(): 399 | set_node(coord) 400 | node.symbol_names.extend(data.symbols) 401 | if data.asm: 402 | process_asm(coord, data) 403 | else: 404 | process_aforth(coord, data) 405 | node.finish() 406 | 407 | def process_include(parser, top_level=True): 408 | tokens = parser.parse() 409 | for chip, nodes in tokens.items(): 410 | set_chip(chip) 411 | process_chip(nodes) 412 | 413 | def include_file(filename, top_level=True): 414 | p = Parser() 415 | p.include_file(filename) 416 | process_include(p, top_level) 417 | 418 | def include_string(string, source_file=None): 419 | p = Parser() 420 | p.include_string(string, source_file) 421 | process_include(p, True) 422 | 423 | def do_compile(): 424 | for chip in get_chips().values(): 425 | chip.compile_nodes() 426 | 427 | def compile_file(filename): 428 | clear_chips() 429 | include_file(filename) 430 | do_compile() 431 | return get_chips() 432 | 433 | def compile_string(filename): 434 | clear_chips() 435 | include_string(filename) 436 | do_compile() 437 | return get_chips() 438 | 439 | def print_nodes(simple=False, coord=None): 440 | chips = get_chips() 441 | print_names = len(chips) > 1 442 | for name, chip in chips.items(): 443 | if print_names: 444 | print('\n\nchip:', name) 445 | nodes = list(chip.nodes.values()) 446 | nodes.sort(key=lambda x: x.coord) 447 | for node in nodes: 448 | if coord is None or node.coord == coord: 449 | node.print(simple) 450 | -------------------------------------------------------------------------------- /ga_tools/bootstream.py: -------------------------------------------------------------------------------- 1 | 2 | from .defs import * 3 | from .assembler import compile_string 4 | 5 | class Bootstream: 6 | def __init__(self, chip): 7 | self.chip = chip 8 | 9 | @staticmethod 10 | def port_pump(port, stream_len): 11 | return [('@p', 'dup', 'a!', '.'), 12 | ('call', port), 13 | ('@p', 'push', '!', '.'), 14 | stream_len - 1, 15 | ('@p', '!', 'unext', '.')] 16 | 17 | @staticmethod 18 | def load_pump(code_len): 19 | if code_len == 0: 20 | return [(';')] 21 | return [('@p', 'a!', '@p', '.'), 22 | 0, 23 | code_len - 1, 24 | ('push', '.', '.', '.'), 25 | ('@p', '!+', 'unext', '.')] 26 | 27 | @staticmethod 28 | def set_a(node): 29 | return [('@p', 'a!', '.', '.'), 30 | node.get_init_a()] 31 | 32 | @staticmethod 33 | def set_b(node): 34 | return [('@p', 'b!', '.', '.'), 35 | node.get_init_b()] 36 | 37 | @staticmethod 38 | def set_io(node): 39 | return [('@p', '@p', 'b!', '.'), 40 | node.get_init_io(), 41 | 0x15D, # io 42 | ('!b', '.', '.', '.')] 43 | 44 | def start_node(self): 45 | return self.chip.node(self.start_coord) 46 | 47 | def ordered_nodes(self): 48 | # constructs a list of nodes in bootstream order 49 | coord_changes = (100, 1, -100, -1) 50 | coord = self.start_coord 51 | nodes = [] 52 | for direction in self.path: 53 | coord += coord_changes[direction] 54 | nodes.append(self.chip.node(coord)) 55 | return reversed(nodes) 56 | 57 | def add_init_code(self, stream, node): 58 | if node.init_a: 59 | stream.extend(node.asm_words(self.set_a(node))) 60 | if node.init_io: 61 | stream.extend(node.asm_words(self.set_io(node))) 62 | if node.init_b: 63 | stream.extend(node.asm_words(self.set_b(node))) 64 | return stream 65 | 66 | def node_code(self, node, iport, oport, stream): 67 | # IPORT stream input port. OPORT output port 68 | stream_len = len(stream) 69 | s = [node.asm_word(('call', iport))] # focusing call 70 | # move previous code through this node 71 | if stream_len > 0: 72 | s.extend(node.asm_words(self.port_pump(oport, stream_len))) 73 | s.extend(stream) 74 | # then load this nodes code into ram 75 | code = node.assemble(check_size=True) 76 | s.extend(node.asm_words(self.load_pump(len(code)))) 77 | s.extend(code) 78 | s.extend(node.assemble_boot_code()) 79 | self.add_init_code(s, node) 80 | s.append(node.asm_word(('jump', node.start_addr()))) 81 | return s 82 | 83 | def head_frame(self): 84 | # create bootstream for all nodes except boot node 85 | stream = [] 86 | path = self.path 87 | if not path: 88 | return stream 89 | rpath = list(reversed(self.path)) 90 | for node, p, p_out in zip(self.ordered_nodes(), 91 | [None]+rpath, 92 | rpath+[self.path[0]]): 93 | iport = node.port_addr(p_out, opposite=True) 94 | oport = p is not None and node.port_addr(p) 95 | stream = self.node_code(node, iport, oport, stream) 96 | # create the bootframe 97 | # boot frame format: 98 | # 0 completion(jump) address 99 | # 1 transfer (store) address 100 | # 2 transfer size in words 101 | # 3+ data words (if size != 0) 102 | addr = self.start_node().port_addr(self.path[0]) 103 | return [0xae, addr, len(stream)] + stream 104 | 105 | def tail_frame(self): 106 | # create frame for boot node 107 | node = self.start_node() 108 | boot = node.assemble_boot_code() 109 | # TODO: Add this boot code earlier so it's visable with --print 110 | # TODO: how else to do this? 111 | boot.extend(self.add_init_code([],node)) 112 | code = node.assemble() 113 | start = node.start_addr() 114 | if boot: 115 | boot.append(node.asm_word(('jump', start))) 116 | start = max(len(code), 0) 117 | code.extend(boot) 118 | return [start, 0, len(code)] + code 119 | 120 | def stream(self, _=None): 121 | # creates the bootstream 122 | s = self.head_frame() 123 | s.extend(self.tail_frame()) 124 | return s 125 | 126 | def stream_str(self): 127 | return ''.join(map(chr, self.stream())) 128 | 129 | class AsyncBootstream(Bootstream): 130 | # Async bootstream for loading in node 708 131 | def __init__(self, chip): 132 | super(AsyncBootstream, self).__init__(chip) 133 | # DB004 page 31 134 | nenw = [NORTH] + [EAST]*16 + [NORTH] + [WEST]*16 135 | self.path = ([EAST]*9 + [SOUTH]*7 + [WEST]*17 136 | + nenw + nenw + nenw + [NORTH] + [EAST]*7) 137 | self.start_coord = 708 138 | 139 | def stream(self, serial_convert=True): 140 | s = super(AsyncBootstream, self).stream() 141 | if serial_convert: 142 | # Option to not do conversion for use by the simulator 143 | return self.sget_convert(s) 144 | return s 145 | 146 | def sget_convert(self, stream): 147 | new = [] 148 | for w in stream: 149 | new.append((((w << 6) & 0xc0) | 0x2d) ^ 0xff) 150 | new.append(((w >> 2) & 0xff) ^ 0xff) 151 | new.append(((w >> 10) & 0xff) ^ 0xff) 152 | return new 153 | 154 | class SyncBootstream(Bootstream): 155 | # Sync bootstream for loading in node 300 156 | def __init__(self, chip): 157 | super(SyncBootstream, self).__init__(chip) 158 | #path0 from DB004 page 31 159 | swse = [SOUTH] + [WEST]*16 + [SOUTH] + [EAST]*16 160 | sw = [SOUTH] + [WEST]*17 161 | self.path = ([NORTH]*4 + [EAST]*17 + swse + swse + sw 162 | + [SOUTH] + [EAST]*17 + sw) 163 | self.start_coord = 300 164 | 165 | class AsyncBootstream_GA4(AsyncBootstream): 166 | # Async bootstream for loading into GA4 node 0 167 | def __init__(self, chip): 168 | super(AsyncBootstream_GA4, self).__init__(chip) 169 | self.path = (NORTH, EAST, SOUTH) 170 | self.start_coord = 0 171 | 172 | host_loader_code = """ 173 | chip __host_loader__ 174 | node 608 175 | north a! west b! 176 | ( read stream length and pass a copy next node ) 177 | @ dup !b 178 | ( send stream to next node ) 179 | for @ !b unext 180 | node 607 east a! west b! @ dup !b for @ !b unext 181 | node 606 east a! west b! @ dup !b for @ !b unext 182 | node 605 east a! west b! @ dup !b for @ !b unext 183 | node 604 east a! west b! @ dup !b for @ !b unext 184 | node 603 east a! west b! @ dup !b for @ !b unext 185 | node 602 east a! west b! @ dup !b for @ !b unext 186 | node 601 east a! west b! @ dup !b for @ !b unext 187 | node 600 east a! south b! @ dup !b for @ !b unext 188 | node 500 0x20000 io b! !b ( reset target chip) 189 | 10000 for . . next 190 | 0 !b 191 | north a! south b! 192 | @ dup !b for @ !b unext 193 | node 400 north a! south b! @ dup !b for @ !b unext 194 | node 300 195 | ( reference: block 632 ) 196 | : dly !b 32 for unext ; 197 | : 1bt dup dly 0x10000 or dly ; 198 | : c+d+ 0x30003 1bt ; ( set clock high, data high, etc) 199 | : c+d- 0x30002 1bt ; 200 | : c-d+ 0x20003 1bt ; 201 | : c-d- 0x20002 1bt ; 202 | : bit0 203 | -if c+d+ ; then c+d- ; 204 | : bit1 205 | -if c-d+ ; then c-d- ; 206 | : send 207 | 8 for 208 | bit0 2* 209 | bit1 2* 210 | next ; 211 | : main 212 | north a! io b! 213 | @ push @ 0x30000 dly send 214 | pop -1 . + for @ send next 215 | """ 216 | 217 | class AsyncTargetBootstream(): 218 | # bootstream that is loaded into target chip through 219 | # node 300 sync port 220 | def __init__(self, chip): 221 | self.target = SyncBootstream(chip) 222 | self.loader = None 223 | 224 | def loader_stream(self, length): 225 | chips = compile_string(host_loader_code) 226 | self.loader = AsyncBootstream(chips['__host_loader__']) 227 | return self.loader.head_frame() 228 | 229 | def start_node(self): 230 | return self.loader.start_node() 231 | 232 | def stream(self, serial_convert=True): 233 | target_stream = self.target.stream() 234 | target_stream = [len(target_stream)-1] + target_stream 235 | loader_bs = self.loader_stream(len(target_stream)-1) 236 | addr = self.start_node().port_addr(SOUTH) 237 | target_bs = [0xae, addr, len(target_stream)] + target_stream 238 | s = loader_bs + target_bs 239 | if serial_convert: 240 | s = self.loader.sget_convert(s) 241 | return s 242 | 243 | class AsyncHostTargetBootstream(): 244 | # bootstream for loading both 'host' and 'target' chips 245 | def __init__(self, chips): 246 | assert len(chips) == 2 247 | chipmap = {c.name:c for c in chips} 248 | if 'target' not in chipmap or 'host' not in chipmap: 249 | print('Mulitple chips must have names "host" and "target"') 250 | exit() 251 | self.host = AsyncBootstream(chipmap['host']) 252 | self.target = AsyncTargetBootstream(chipmap['target']) 253 | 254 | def stream(self, serial_convert=True): 255 | host_stream = self.host.stream(serial_convert) 256 | target_stream = self.target.stream(serial_convert) 257 | return target_stream + host_stream 258 | 259 | bootstream_aliases = { 'async' : '708', 260 | 'async-target' :'708-300', 261 | 'target': '708-300' } 262 | 263 | def make_bootstream(bootstream_type, chips): 264 | chip = chips[0] 265 | multiple_chips = len(chips) > 1 266 | bootstream_type = bootstream_aliases.get(bootstream_type, 267 | bootstream_type) 268 | if bootstream_type == '708': 269 | assert not multiple_chips 270 | return AsyncBootstream(chip) 271 | if bootstream_type == '300': 272 | assert not multiple_chips 273 | return SyncBootstream(chip) 274 | if bootstream_type == '708-300': 275 | if multiple_chips: 276 | return AsyncHostTargetBootstream(chips) 277 | return AsyncTargetBootstream(chip) 278 | if bootstream_type == 'ga4': 279 | return AsyncBootstream_GA4(chip) 280 | raise Exception('Invalid bootstream type: ' + str(bootstream_type)) 281 | -------------------------------------------------------------------------------- /ga_tools/defs.py: -------------------------------------------------------------------------------- 1 | 2 | NOP='.' 3 | XOR = 'or' 4 | DUP = 'dup' 5 | READ_P = '@p' 6 | 7 | ops = (';', 'ex', 'jump', 'call', 'unext', 'next', 'if', 8 | '-if', READ_P, '@+', '@b', '@', '!p', '!+', '!b', '!', 9 | '+*', '2*', '2/', '-', '+', 'and', XOR, 'drop', 10 | DUP, 'pop', 'over', 'a', NOP, 'push', 'b!', 'a!') 11 | 12 | op_i = {op:i for i, op in enumerate(ops)} 13 | 14 | def get_op_i(op): 15 | x = op_i.get(op) 16 | return x 17 | 18 | OP_NOP = get_op_i(NOP) 19 | OP_READ_P = get_op_i(READ_P) 20 | 21 | opcodes = frozenset(ops) 22 | 23 | address_required = frozenset(['jump', 'call', 'next', 'if', '-if']) 24 | 25 | last_slot_ops = frozenset([';', 'unext', '@p', '!p', '+*', '+', 26 | 'dup', '.']) 27 | 28 | ops_preceded_by_nops = ('+', '+*') 29 | 30 | ops_completing_carry = ('.', 'call', ';', 'if', '-if', 'next', 'unext') 31 | 32 | ops_using_rest_of_word = (';', 'ex') 33 | 34 | named_addresses = { 'right': 0x1D5, 35 | 'down': 0x115, 36 | 'left': 0x175, 37 | 'up': 0x145, 38 | 'io': 0x15D, 39 | 'ldata': 0x171, 40 | 'data': 0x141, 41 | 'warp': 0x157, 42 | 'center': 0x1A5, 43 | 'top': 0x1B5, 44 | 'side': 0x185, 45 | 'corner': 0x195 } 46 | 47 | port_names = ('north', 'east', 'south', 'west') 48 | 49 | word_address_masks = (0x3ff, 0xff, 0x7) 50 | 51 | NORTH = 0 52 | EAST = 1 53 | SOUTH = 2 54 | WEST = 3 55 | 56 | UP = 0x145 57 | DOWN = 0x115 58 | RIGHT = 0x1d5 59 | LEFT = 0x175 60 | IO = 0x15d 61 | DATA = 0x141 62 | 63 | io_places = {'---u': 0x145, 64 | '--l-': 0x175, 65 | '--lu': 0x165, 66 | '-d--': 0x115, 67 | '-d-u': 0x105, 68 | '-dl-': 0x135, 69 | '-dlu': 0x125, 70 | 'r---': 0x1D5, 71 | 'r--u': 0x1C5, 72 | 'r-l-': 0x1F5, 73 | 'r-lu': 0x1E5, 74 | 'rd--': 0x195, 75 | 'rd-u': 0x185, 76 | 'rdl-': 0x1B5, 77 | 'rdlu': 0x1A5} 78 | 79 | def parse_int(s): 80 | try: 81 | return int(s, 0) 82 | except ValueError: 83 | return None 84 | 85 | def node_port_names(coord): 86 | x = coord % 100 87 | y = coord // 100 88 | return (('down', 'up')[y % 2], 89 | ('right', 'left')[x % 2], 90 | ('up', 'down')[y % 2], 91 | ('left', 'right')[x % 2]) 92 | 93 | def node_ports(coord): 94 | # Returns a tuple of addresses: (north, east, south, west) 95 | return tuple(named_addresses[a] for a in node_port_names(coord)) 96 | 97 | def check_op(op): 98 | if op not in op_i: 99 | throw_error('invalid op: ' + str(op)) 100 | 101 | current_token = None 102 | 103 | def set_current_token(t): 104 | global current_token 105 | if t and t != '\n': 106 | current_token = t 107 | 108 | def throw_error(msg, print_source=True, token=None): 109 | if current_token and print_source: 110 | print('Exception:', msg) 111 | print(' Source:' , current_token.source, 112 | 'line:', current_token.line, 113 | '~column:', current_token.col) 114 | exit() 115 | raise Exception(msg) 116 | 117 | def valid_coord(coord): 118 | return coord >= 0 and coord%100 < 18 and coord // 100 < 8 119 | -------------------------------------------------------------------------------- /ga_tools/f18a_asm.py: -------------------------------------------------------------------------------- 1 | 2 | import re 3 | 4 | from .defs import * 5 | from .word import * 6 | 7 | class F18a: 8 | def __init__(self, chip, coord): 9 | #self.rom = None 10 | self.symbols = {} # maps names to Word objects 11 | self.symbol_names = [] 12 | self.rom_names = [] 13 | self.boot = None # first boot word 14 | w = Word() 15 | self.ram = w # first instruction word 16 | self.current_word = w 17 | self.last_word = w 18 | self.current_slot = 0 19 | self.stack = [] 20 | self.prev_op = None 21 | self.boot_code = False 22 | self.chip = chip 23 | self.coord = coord 24 | self.port_addrs = node_ports(coord) 25 | self.node_port_names = node_port_names(coord) 26 | self.extended_arith = 0 27 | self.asm_node = False 28 | self.next_asm_symbol = None 29 | self.finished = False 30 | self.compile_0_as_dup_dup_or = True 31 | self.auto_nop_insert = True 32 | self.const_refs = [] 33 | self.init_a = None 34 | self.init_b = None 35 | self.init_p = None 36 | self.init_io = None 37 | self.stream = False 38 | self.name = None 39 | 40 | def pop(self): 41 | if not self.stack: 42 | throw_error('assembler stack underflow') 43 | return self.stack.pop(-1) 44 | 45 | def push(self, val): 46 | self.stack.append(val) 47 | 48 | def swap(self): 49 | t = self.pop() 50 | s = self.pop() 51 | self.push(t) 52 | self.push(s) 53 | 54 | def count_ram(self): 55 | word = self.ram 56 | count = 0 57 | last = None 58 | while word: 59 | if word.stream: 60 | count += word.stream.count_ram() 61 | else: 62 | count += 1 63 | last = word 64 | word = word.next 65 | if last and last.empty(): 66 | count -= 1 67 | return count 68 | 69 | def finish_word(self): 70 | # fill rest of current_word with nops 71 | pass 72 | 73 | def parse_ref(self, name): 74 | m = re.search('([^ ]+)@([0-9]+)', name) 75 | if m: 76 | return m.group(1), self.chip.node(int(m.group(2))) 77 | m = re.search('([0-9]+)[.]([^ ]+)', name) 78 | if m: 79 | return m.group(2), self.chip.node(int(m.group(1))) 80 | return name, self 81 | 82 | def make_ref(self, name): 83 | if type(name) is int: 84 | return Ref(node=self, value=name) 85 | try: 86 | return Ref(node=self, value=int(name, 0)) 87 | except ValueError as e: 88 | pass 89 | word, location = self.parse_ref(name) 90 | if word in port_names: 91 | word = self.node_port_names[port_names.index(name)] 92 | return Ref(node=location, name=word) 93 | 94 | def new_word(self, set_current=True): 95 | # start a new word. return it 96 | assert not self.finished 97 | w = Word() 98 | w.extended_arith = self.extended_arith 99 | w.prev = self.last_word 100 | self.last_word.next = w 101 | self.last_word = w 102 | if set_current: 103 | self.current_word = w 104 | self.current_slot = 0 105 | if not self.boot_code and not self.ram: 106 | self.ram = w 107 | return w 108 | 109 | def fill_rest_with_nops(self): 110 | # force word alignment. Fills rest of current word with nops 111 | while self.current_slot != 0: 112 | self.add_to_next_slot('.') 113 | 114 | def inc_slot(self): 115 | #TODO: should not be necessary. Needed now because of methods 116 | # like Word.compile_next that add ops to word without 117 | # using F18a.add_to_next_slot 118 | self.current_slot += 1 119 | if self.current_slot == 4: 120 | self.new_word() 121 | 122 | def add_to_next_slot(self, op): 123 | assert not self.finished 124 | self.current_word.set_op(op) 125 | self.prev_op = op 126 | self.inc_slot() 127 | 128 | def set_next_const(self, const): 129 | if self.current_slot == 0: 130 | self.current_word.set_const(const) 131 | w = self.current_word 132 | self.new_word() 133 | return w 134 | w = self.new_word(set_current=False) 135 | w.set_const(const) 136 | return w 137 | 138 | def maybe_insert_nop(self, op): 139 | if not self.auto_nop_insert: 140 | return 141 | if op not in ops_preceded_by_nops: 142 | return 143 | if self.prev_op in ops_completing_carry: 144 | return 145 | self.add_to_next_slot(NOP) 146 | 147 | def compile_op(self, op): 148 | check_op(op) 149 | self.maybe_insert_nop(op) 150 | if self.current_slot == 3 and op not in last_slot_ops: 151 | self.add_to_next_slot(NOP) 152 | self.add_to_next_slot(op) 153 | if op in ops_using_rest_of_word: 154 | self.fill_rest_with_nops() 155 | 156 | def compile_constant(self, const): 157 | if self.asm_node: 158 | self.set_next_const(const) 159 | return 160 | if const == 0 and self.compile_0_as_dup_dup_or: 161 | self.compile_op(DUP) 162 | self.compile_op(DUP) 163 | self.compile_op(XOR) 164 | else: 165 | self.compile_op(READ_P) 166 | self.set_next_const(const) 167 | 168 | def compile_call(self, op, ref): 169 | if self.current_slot == 3: 170 | self.fill_rest_with_nops() 171 | self.current_word.set_call(op, ref) 172 | self.prev_op = op 173 | self.new_word() 174 | 175 | def end_boot_code(self): 176 | node = self.current_word 177 | self.ram = node 178 | if node.prev: 179 | # unlink from boot code chain 180 | node.prev.next = None 181 | node.prev = None 182 | self.boot_code = False 183 | 184 | def start_def(self, name): 185 | self.fill_rest_with_nops() 186 | if self.boot_code: 187 | self.end_boot_code() 188 | self.symbols[name] = self.current_word 189 | 190 | def compile_if(self, op): 191 | if self.current_slot == 3: 192 | self.add_to_next_slot(NOP) 193 | self.current_word.set_if(op) 194 | self.prev_op = op 195 | self.push(self.current_word) 196 | self.new_word() 197 | 198 | def compile_then(self): 199 | self.fill_rest_with_nops() 200 | word = self.pop() 201 | word.dest_word = self.current_word 202 | 203 | def compile_next(self, op): 204 | if self.current_slot == 3: 205 | self.add_to_next_slot(NOP) 206 | self.current_word.set_next(op, self.pop()) 207 | self.inc_slot() 208 | if self.current_slot != 0: 209 | self.new_word() 210 | 211 | def compile_port(self, port): 212 | self.compile_constant(self.port_addrs[port]) 213 | 214 | def start_stream(self, stream): 215 | self.fill_rest_with_nops() 216 | self.current_word.stream = stream 217 | self.new_word() 218 | 219 | def start_stream1(self, stream): 220 | self.compile_op(READ_P) 221 | w = self.set_next_const(12345) 222 | w.stream = stream 223 | 224 | def port_addr(self, port, opposite=False): 225 | if opposite: 226 | port = (SOUTH, WEST, NORTH, EAST)[port] 227 | return self.port_addrs[port] 228 | 229 | def add_asm_word(self, w): 230 | self.asm.append(w) 231 | 232 | def finish(self): 233 | # Finish assembling this node 234 | # if self.current_word.empty(): 235 | # return 236 | if self.current_word.type == INST: 237 | self.fill_rest_with_nops() 238 | # can't trim word from nodes yet because the last 239 | # word might be the destination for an addresss node 240 | #self.trim_last_word() 241 | if self.asm_node: 242 | if self.next_asm_symbol: 243 | self.symbols[self.next_asm_symbol] = self.current_word 244 | if self.boot_code: 245 | self.end_boot_code() 246 | self.finished = True 247 | 248 | def trim_last_word(self): 249 | if self.last_word.empty(): 250 | if self.last_word.prev: 251 | self.last_word = self.last_word.prev 252 | self.last_word.next = None 253 | else: 254 | self.ram = None 255 | self.last_word = None 256 | self.finished = True 257 | 258 | def get_asm_ref(self, word): 259 | if type(word) == int: 260 | return Ref(node=self, value=word) 261 | if len(word)==1 and word[0] not in ops: 262 | return self.make_ref(word[0]) 263 | return None 264 | 265 | def do_asm_word(self, ops, word): 266 | const = self.get_asm_ref(ops) 267 | if const is not None: 268 | word.set_const(const) 269 | else: 270 | for op in ops: 271 | if op in address_required: 272 | word.set_call(op, self.make_ref(ops[-1])) 273 | return word 274 | else: 275 | word.set_op(op) 276 | if op in ops_using_rest_of_word: 277 | word.fill_rest_with_nops() 278 | return word 279 | word.fill_rest_with_nops() 280 | return word 281 | 282 | def asm_word(self, ops, add_to_node=False): 283 | if add_to_node: 284 | if ops[0] == ':': 285 | self.next_asm_symbol = ops[1] 286 | if self.boot_code: 287 | self.end_boot_code() 288 | return None 289 | if self.next_asm_symbol: 290 | self.symbols[self.next_asm_symbol] = self.current_word 291 | self.next_asm_symbol = None 292 | asm = self.do_asm_word(ops, self.current_word) 293 | self.new_word() 294 | return asm 295 | word = self.do_asm_word(ops, Word()) 296 | word.extended_arith = self.extended_arith 297 | if word.type == ADDR: 298 | word.resolve_symbol() 299 | return word.asm() 300 | 301 | def asm_words(self, words, add_to_node=False): 302 | #return [self.asm_word(w) for w in words] 303 | if add_to_node: 304 | for w in words: 305 | self.asm_word(w, add_to_node) 306 | return None 307 | for i, w in enumerate(words): 308 | words[i] = self.asm_word(w, add_to_node) 309 | return words 310 | 311 | def start_addr(self, with_extended_arith=True): 312 | # Returns the start address - address of 'main', from '/p', or 0 313 | w = self.symbols.get('main') 314 | if w: 315 | if self.init_p is not None: 316 | throw_error("conflicting /p and 'main'") 317 | addr = w.word_addr 318 | if with_extended_arith: 319 | addr |= w.extended_arith 320 | return addr 321 | if self.init_p: 322 | return self.init_p.resolve() 323 | return 0 324 | 325 | def get_init_a(self): 326 | return self.init_a.resolve() if self.init_a else None 327 | 328 | def get_init_b(self): 329 | return self.init_b.resolve() if self.init_b else None 330 | 331 | def get_init_io(self): 332 | return self.init_io.resolve() if self.init_io else None 333 | 334 | def symbol_addr(self, name): 335 | # return address of symbol NAME, or None if it's not known yet 336 | w = self.symbols.get(name) 337 | if w is None: 338 | return None 339 | return w.word_addr 340 | 341 | def do_set_word_addresses(self, word): 342 | # Set the address in ram of each word 343 | a = 0 344 | while word: 345 | word.word_addr = a 346 | word = word.next 347 | a += 1 348 | 349 | def set_word_addresses(self): 350 | # Set the address in ram of each word 351 | self.do_set_word_addresses(self.ram) 352 | self.do_set_word_addresses(self.boot) 353 | 354 | def do_resolve_transfers(self, word): 355 | # Set the transfer address in each word 356 | while word: 357 | if word.dest_word: 358 | addr = word.dest_word.word_addr 359 | assert addr is not None 360 | word.set_addr(addr) 361 | word = word.next 362 | 363 | def resolve_transfers(self): 364 | self.do_resolve_transfers(self.ram) 365 | self.do_resolve_transfers(self.boot) 366 | 367 | def assemble_list(self, lst): 368 | # Assembles a linked list of words 369 | ret = [] 370 | while lst: 371 | ret.append(lst.asm()) 372 | lst = lst.next 373 | # # Wrap words around for greenarray aforth compatibility 374 | # if len(ret) > 64: 375 | # last = ret[64:] 376 | # ret = last + ret[len(last):64] 377 | return ret 378 | 379 | def assemble(self, check_size=False): 380 | # returns list of assembled RAM 381 | asm = self.assemble_list(self.ram) 382 | if check_size and len(asm) > 64: 383 | m = 'node {} code is too large: {} words' 384 | throw_error(m.format(self.coord, len(asm)), False) 385 | return asm 386 | 387 | def assemble_boot_code(self): 388 | return self.assemble_list(self.boot) 389 | 390 | def aforth_list(self): 391 | ret = [] 392 | word = self.ram 393 | while word: 394 | ret.append(word.aforth_list()) 395 | word = word.next 396 | return ret 397 | 398 | def do_resolve_calls(self, word): 399 | # Set the symbol address in each word 400 | while word: 401 | if word.addr_sym: 402 | word.resolve_symbol() 403 | word = word.next 404 | 405 | def resolve_calls(self): 406 | self.do_resolve_calls(self.ram) 407 | self.do_resolve_calls(self.boot) 408 | 409 | def do_shift_addr_words(self, word): 410 | # Move transfer to new word if it doesn't fit. 411 | # It's fast enough 412 | while word: 413 | if not self.word_addr_fits(word): 414 | self.shift_addr_word(word) 415 | self.set_word_addresses() 416 | word = self.ram 417 | word = word.next 418 | 419 | def shift_addr_words(self): 420 | self.do_shift_addr_words(self.ram) 421 | self.do_shift_addr_words(self.boot) 422 | 423 | def word_addr_fits(self, word, p=None): 424 | # Return True if the address WORD its in the available slots 425 | if word.type != ADDR: 426 | return True 427 | sym = word.addr_sym 428 | dest_addr = sym.resolve() if sym else word.dest_word.word_addr 429 | mask = word_address_masks[word.op_index] 430 | _mask = ~mask & 0x3ffff 431 | p = word.word_addr + 1 if p is None else p 432 | p += word._slots.count(OP_READ_P) 433 | min_dest = _mask & p 434 | max_dest = (_mask & p) | (mask & dest_addr) 435 | return (dest_addr >= min_dest) & (dest_addr <= max_dest) 436 | 437 | def find_shift_dest(self, word): 438 | ret = word 439 | for i in range(4): 440 | if word._slots[i] == OP_READ_P: 441 | ret = ret.next 442 | return ret 443 | 444 | def shift_addr_word(self, word): 445 | # Creates a new word following WORD and moves the 446 | # transfer to the new word. 447 | # If instruction has @p instructions, insert the instruction 448 | # after the last word they read. 449 | new = Word() 450 | new.move_addr(word) 451 | after = self.find_shift_dest(word) 452 | after.next.prev = new 453 | new.prev = after 454 | new.next = after.next 455 | after.next = new 456 | if new.next is None: 457 | self.last_word = new 458 | 459 | def do_insert_streams(self, word): 460 | while word: 461 | if word.stream: 462 | self.insert_stream(word, word.stream) 463 | word = word.next 464 | 465 | def insert_streams(self): 466 | self.do_insert_streams(self.ram) 467 | self.do_insert_streams(self.boot) 468 | 469 | def insert_stream(self, word, stream): 470 | # replace WORD with the words in STREAM 471 | if stream.single_word: 472 | return self.insert_stream1(word, stream) 473 | first = self.get_stream_ram(stream) 474 | if word.prev: 475 | word.prev.next = first 476 | first.prev = word.prev 477 | else: 478 | self.asm = first 479 | last = stream.last_word 480 | stream.trim_last_word 481 | if word.next: 482 | word.next.prev = last 483 | last.next = word.next 484 | else: 485 | self.last_word = last 486 | 487 | def insert_stream1(self, word, stream): 488 | asm = stream.assemble() 489 | if len(asm) > 1: 490 | for i, word in enumerate(asm): 491 | print(i, word, disasm_to_str(word)) 492 | fmt = '{{...}} is too long (4 ops max): {}' 493 | throw_error(fmt.format(asm)) 494 | elif len(asm) == 0: 495 | throw_error('empty {...}') 496 | word.set_const(asm[0]) 497 | 498 | def get_stream_ram(self, stream): 499 | ram = stream.ram 500 | if stream.into: 501 | pass #TODO 502 | return ram 503 | 504 | def move_forward(self, n): 505 | if not self.current_word.empty(): 506 | self.fill_rest_with_nops() 507 | if self.boot_code: 508 | self.end_boot_code() 509 | length = self.count_ram() 510 | if n < length: 511 | throw_error("cannot 'org' backwards") 512 | for _ in range(n - length): 513 | self.new_word() 514 | 515 | def print_list(self, ll, names={}): 516 | a = 0 517 | while ll: 518 | s = names.get(a) 519 | if s: 520 | print(':', s) 521 | asm = ll.asm() 522 | disasm = disasm_to_str(asm) 523 | addr = hex(a)[2:].ljust(5) 524 | comp = str(ll).ljust(20) 525 | asm = str(asm).ljust(13) 526 | print(addr, comp, asm, disasm) 527 | ll = ll.next 528 | a += 1 529 | 530 | def print(self, simple=False): 531 | # pretty print this node 532 | # TODO: -should call self.assemble since that wraps the words 533 | # -handle the word wrapping better... 534 | if simple: 535 | return self.simple_print() 536 | print('\n'+'='*53) 537 | print(' Compiled Assembled Disassembled') 538 | print('node ', self.coord, ' ASM' if self.asm_node else '') 539 | p = self.start_addr() 540 | if p: 541 | print('/p', p) 542 | if self.init_a is not None: 543 | print('/a', self.init_a) 544 | if self.init_b is not None: 545 | print('/b', self.init_b) 546 | if self.boot: 547 | print('- boot', '- '*24) 548 | self.print_list(self.boot) 549 | print('- '*27) 550 | names = {w.word_addr:s for s,w in self.symbols.items()} 551 | self.print_list(self.ram, names) 552 | 553 | def simple_print(self): 554 | print('node', self.coord) 555 | word = self.ram 556 | while word: 557 | print(word.asm()) 558 | word = word.next 559 | 560 | def json(self): 561 | return {'coord': self.coord, 562 | 'ram': self.assemble(), 563 | 'forth': self.aforth_list(), 564 | 'boot_code': self.assemble_boot_code(), 565 | 'symbols': {n:w.word_addr 566 | for n,w in self.symbols.items()}, 567 | 'a': self.get_init_a(), 568 | 'b': self.get_init_b(), 569 | 'p': self.start_addr(False), 570 | 'io': self.get_init_io()} 571 | 572 | class Stream(F18a): 573 | counter = 0 574 | def __init__(self, chip, node, address=0x195, into=None): 575 | super(Stream, self).__init__(chip, node.coord) 576 | self.node = node 577 | self.address = address 578 | self.into = into # store instruction: ! !b !p 579 | self.symbol_names = node.symbol_names 580 | self.symbols = node.symbols 581 | self.rom_names = node.rom_names 582 | self.stream = True 583 | self.single_word = True 584 | self.name = 'Stream_{}_{}'.format(self.coord, Stream.counter) 585 | Stream.counter += 1 586 | 587 | def set_word_addresses(self): 588 | a = self.address 589 | word = self.ram 590 | while word: 591 | word.word_addr = a 592 | word = word.next 593 | 594 | def count_ram(self): 595 | count = super(Stream, self).count_ram() 596 | if self.into: 597 | # reserve space for stream logic 598 | count += 3 599 | return count 600 | -------------------------------------------------------------------------------- /ga_tools/ga144_asm.py: -------------------------------------------------------------------------------- 1 | 2 | # ::::GA* 3 | # ::::Tools 4 | 5 | chips = {} # maps names to GA144 instances 6 | 7 | from .defs import * 8 | from .f18a_asm import * 9 | from .ga144_rom import get_node_rom 10 | from .word import Word 11 | 12 | class GA144: 13 | def __init__(self, name): 14 | self.name = name 15 | self.nodes = {} 16 | self.current_node = None 17 | chips[name] = self 18 | 19 | def node(self, coord): 20 | n = self.nodes.get(coord) 21 | if not n: 22 | n = F18a(self, coord) 23 | self.set_rom(n) 24 | self.nodes[coord] = n 25 | return n 26 | 27 | def set_node(self, coord): 28 | if self.current_node == coord: 29 | return 30 | node = self.node(coord) 31 | self.current_node = node 32 | return node 33 | 34 | def compile_nodes(self): 35 | nodes = self.nodes.values() 36 | for node in nodes: 37 | node.set_word_addresses() 38 | aforth_nodes = [] 39 | for node in nodes: 40 | if node.asm_node: 41 | node.resolve_calls() 42 | node.trim_last_word() 43 | else: 44 | node.shift_addr_words() 45 | node.resolve_transfers() 46 | aforth_nodes.append(node) 47 | for node in aforth_nodes: 48 | node.resolve_calls() 49 | node.trim_last_word() 50 | for node in aforth_nodes: 51 | node.insert_streams() 52 | self.delete_streams() 53 | 54 | def set_rom(self, node): 55 | rom = get_node_rom(node.coord) 56 | rom.update(io_places) 57 | node.symbol_names.extend(list(rom.keys())) 58 | node.rom_names.extend(node.symbol_names) 59 | for name, addr in rom.items(): 60 | node.symbols[name] = Word(addr=addr) 61 | 62 | def new_stream(self, coord, into): 63 | s = Stream(self, self.node(coord), into=into) 64 | self.nodes[s.name] = s 65 | return s 66 | 67 | def delete_streams(self): 68 | # delete stream nodes so they don't show up in assembly output 69 | for coord, node in list(self.nodes.items()): 70 | if node.stream: 71 | del self.nodes[coord] 72 | 73 | def print_size(self, print_name=False): 74 | print('Node Size Percent') 75 | print('-------------------') 76 | if print_name: 77 | print('chip', self.name) 78 | nodes = list(self.nodes.items()) 79 | nodes.sort() 80 | for coord, node in nodes: 81 | n = len(node.assemble()) 82 | print(str(coord).ljust(5),str(n).ljust(5), str(n/64*100)+'%') 83 | 84 | def get_chips(): 85 | return chips 86 | 87 | def get_chip(name=None): 88 | if name is not None: 89 | return get_chips().get(name) 90 | chips = list(get_chips().values()) 91 | return len(chips) and chips[0] 92 | 93 | def clear_chips(): 94 | global chips 95 | chips = {} 96 | -------------------------------------------------------------------------------- /ga_tools/ga144_rom.py: -------------------------------------------------------------------------------- 1 | 2 | from .defs import named_addresses, io_places 3 | 4 | 5 | extra_names = dict(named_addresses) 6 | extra_names.update(io_places) 7 | 8 | def get_node_rom(coord): 9 | rom = node_rom_type.get(coord, basic_rom) 10 | rom.update(extra_names) 11 | return rom 12 | 13 | 14 | # block 1418 math rom anywhere 15 | 16 | basic_rom = {"relay": 0xa1, # 1388 17 | "warm": 0xa9, # warm 18 | "*.17": 0xb0, # 1390 multiply 19 | "*.": 0xb7, # 1396 fractional multiply 20 | "taps": 0xbc, # 1386 21 | "interp": 0xc4, # 1384 interpolate 22 | "triangle": 0xce, # 1394 23 | "clc": 0xd3, # 1398 24 | "--u/mod": 0x2d5, # 1398 25 | "-u/mod": 0x2d6, # 1398 26 | "poly": 0xaa} # 1382 polynomial approximation 27 | 28 | # block 1432 analog 29 | analog_rom = {"relay": 0xa1, # 1388 30 | "warm": 0xa9, # warm 31 | "*.17": 0xb0, # 1390 multiply 32 | "*.": 0xb7, # 1396 fractional multiply 33 | "-dac": 0xbc, # 1434 34 | "interp": 0xc4, # 1384 interpolate 35 | "triangle": 0xce, # 1394 36 | "clc": 0xd3, # 1398 37 | "--u/mod": 0x2d5, # 1398 38 | "-u/mod": 0x2d6, # 1398 39 | "poly": 0xaa} # 1382 polynomial approximation 40 | 41 | # block 1420 serdes top/bot 42 | serdes_boot_rom = {"relay": 0xa1, # 1388 43 | "warm": 0xa9, 44 | "cold": 0xaa, 45 | "*.17": 0xb0, # 1390 multiply 46 | "*.": 0xb7, # 1396 fractional multiply 47 | "taps": 0xbc, # 1386 48 | "interp": 0xc4, # 1384 interpolate 49 | "triangle": 0xce, # 1394 50 | "clc": 0xd3, # 1398 51 | "--u/mod": 0x2d5, # 1398 52 | "-u/mod": 0x2d6, # 1398 53 | "poly": 0xaa} # 1382 polynomial approximation 54 | 55 | # block 1422 sync serial boot side 56 | sync_boot_rom = {"relay": 0xa1, # 1388 57 | "warm": 0xa9, 58 | "cold": 0xaa, 59 | "ser-exec": 0xb6, 60 | "ser-copy": 0xb9, 61 | "sget": 0xbe, 62 | "6in": 0xc0, 63 | "2in": 0xc2, 64 | "*.17": 0xcc, # 1390 multiply 65 | "taps": 0xd3, # 1386 66 | "triangle": 0xdb} # 1394 67 | 68 | # block 1424 async serial boot top/bot 69 | async_boot_rom = {"relay": 0xa1, # 1388 70 | "warm": 0xa9, 71 | "cold": 0xaa, 72 | "ser-exec": 0xae, 73 | "ser-copy": 0xb3, 74 | "wait": 0xbb, 75 | "sync": 0xbe, 76 | "start": 0xc5, 77 | "delay": 0xc8, 78 | "18ibits": 0xcb, # 1426 79 | "byte": 0xd0, # 1426 80 | "4bits": 0xd2, # 1426 81 | "2bits": 0xd3, # 1426 82 | "1bit": 0xd4, # 1426 83 | "lsh": 0xd9, # 1392 84 | "rsh": 0xdb} 85 | # 1392 ;??????? 86 | 87 | # block 1428 spi boot top/bot 88 | spi_boot_rom = {"relay": 0xa1, # 1388 89 | "warm": 0xa9, 90 | "8obits": 0xc2, 91 | "ibit": 0xc7, 92 | "half": 0xca, 93 | "select": 0xcc, 94 | "obit": 0xd0, 95 | "rbit": 0xd5, 96 | "18ibits": 0xd9, 97 | #?? ibits, u2/ 98 | # block 1430 99 | "cold": 0xaa, 100 | "spi-boot": 0xb0, 101 | "spi-exec": 0xb6, 102 | "spi-copy": 0xbc} 103 | 104 | # block 1436 1-wire 105 | one_wire_rom = {"rcv": 0x9e, 106 | "bit": 0xa1, 107 | "warm": 0xa9, 108 | "cold": 0xaa, 109 | "triangle": 0xbe, # 1394 110 | "*.17": 0xc3, # 1390 111 | "*.": 0xca, # 1396 112 | "interp": 0xcf, # 1384 113 | "clc": 0xcf, # 1398 114 | "--u/mod": 0x2d1, # 1398 115 | "-u/mod": 0x2d2} # 1398 #TODO: check 116 | 117 | # node 9 block 1320 118 | SDRAM_addr_rom = {"warm": 0xa9, 119 | "cmd": 0xaa} 120 | 121 | # node 8 block 1322 122 | SDRAM_control_rom = {"warm": 0xa9} 123 | 124 | # node 7 block 1324 125 | SDRAM_data_rom = {"warm": 0xa9, 126 | "db@": 0xaa, 127 | "db!": 0xb, 128 | "inpt": 0xad} 129 | 130 | # node 105 block 1306 131 | eForth_bitsy_rom = {"warm": 0xa9, 132 | "rp--": 0xaa, 133 | "bs@": 0xac, 134 | "'else": 0xac, 135 | "rp@": 0xb0, 136 | "pshbs": 0xb1, 137 | "'r@": 0xb3, 138 | "@w": 0xb4, 139 | "rfrom": 0xb6, 140 | "popbs": 0xb9, 141 | "pshr": 0xbb, 142 | "rp++": 0xbf, 143 | "ip++": 0xbf, 144 | "tor": 0xc1, 145 | "rp!": 0xc4, 146 | "'con": 0xc7, 147 | "'var": 0xc8, 148 | "'exit": 0xc9, 149 | "bitsy": 0xce, 150 | "xxt": 0xd0, 151 | "'ex": 0xd3, 152 | "'lit": 0xd5, 153 | "'if": 0xd8} 154 | 155 | #node 106 block 1310 156 | eForth_stack_rom= {"warm": 0xa9, 157 | "'c@": 0xaa, 158 | "'@": 0xaa, 159 | "x@": 0xaa, 160 | "sp++": 0xac, 161 | "char+": 0xac, 162 | "cell+": 0xac, 163 | "1+": 0xac, 164 | "popt": 0xae, 165 | "sp--": 0xb0, 166 | "char-": 0xb0, 167 | "cell-": 0xb0, 168 | "1-": 0xb0, 169 | "psht": 0xb2, 170 | "x!": 0xb4, 171 | "'c!": 0xb6, 172 | "'!": 0xb6, 173 | "popts": 0xb7, 174 | "pops": 0xb8, 175 | "pshs": 0xba, 176 | "page@": 0xbc, 177 | "pshw": 0xbe, 178 | "page!": 0xc0, 179 | "sp@": 0xc3, 180 | "sp!": 0xc6, 181 | "'drop": 0xc8, 182 | "'over": 0xc9, 183 | "'dup": 0xca, 184 | "'swap": 0xcb, 185 | "'2/": 0xcd, 186 | "um+": 0xcf, 187 | "'nc": 0xd2, 188 | "'cy": 0xd3, 189 | "zless": 0xd8, 190 | "'or": 0xdb, 191 | "'xor": 0xdc, 192 | "'and": 0xdd, 193 | "negate": 0xde, 194 | "invert": 0xdf, 195 | "zeq": 0xe0, 196 | "'+": 0xe2, 197 | "swap-": 0xe3 } 198 | 199 | # node 107 block 1328 200 | SDRAM_mux_rom = {"warm": 0xa9, 201 | "a2rc": 0xaa, 202 | "row!": 0xaf, 203 | "sd@": 0xbb, 204 | "sd!": 0xc5, #TODO: sd! and poll are not in dumped rom 205 | "poll": 0xcf} 206 | 207 | SDRAM_idle_rom = {"warm": 0xa9, 208 | "noop": 0xaa, 209 | "cmd": 0xac, 210 | "idle": 0xae, 211 | "init": 0xc0} 212 | 213 | node_rom_type = {300: sync_boot_rom, 214 | 708: async_boot_rom, 215 | 705: spi_boot_rom, 216 | 200: one_wire_rom, 217 | 9: SDRAM_addr_rom, 218 | 8: SDRAM_control_rom, 219 | 7: SDRAM_data_rom, 220 | 105: eForth_bitsy_rom, 221 | 106: eForth_stack_rom, 222 | 107: SDRAM_mux_rom, 223 | 108: SDRAM_idle_rom, 224 | 1: serdes_boot_rom, 225 | 107: serdes_boot_rom, 226 | 709: analog_rom, 227 | 713: analog_rom, 228 | 717: analog_rom, 229 | 617: analog_rom, 230 | 117: analog_rom} 231 | -------------------------------------------------------------------------------- /ga_tools/ga_serial.py: -------------------------------------------------------------------------------- 1 | 2 | from serial import Serial 3 | from .bootstream import make_bootstream 4 | 5 | class GA144Serial: 6 | def __init__(self, chips, port, speed): 7 | self.chips = chips 8 | self.serial = Serial(port, speed) 9 | self.serial.reset_input_buffer() 10 | 11 | self.data = [] 12 | self.errors = [] 13 | 14 | def send_bootstream(self, stream): 15 | #if target: 16 | # target.setRTS(0) 17 | # target.setRTS(1) 18 | serial = self.serial 19 | serial.setRTS(0) 20 | serial.setRTS(1) 21 | serial.write(stream) 22 | serial.flush() 23 | 24 | def write_bootstream(self, bootstream_type): 25 | bs = make_bootstream(bootstream_type, self.chips) 26 | self.send_bootstream(bs.stream()) 27 | 28 | def read_n(self, n): 29 | x = [ord(self.serial.read(1)) for _ in range(n)] 30 | x.reverse() 31 | word = 0 32 | for byte in x: 33 | word = (word << 8) | byte 34 | return word 35 | 36 | def listen(self): 37 | while True: 38 | n = self.read_n(1) 39 | if n == 1: 40 | print('[exit]') 41 | return 42 | if n == 0: 43 | n = self.read_n(3) 44 | print(n & 0x3ffff) 45 | else: 46 | raise Exception('unknown serial code: '+str(n)) 47 | 48 | def gather(self): 49 | self.data = [] 50 | self.errors = [] 51 | while True: 52 | n = self.read_n(1) 53 | if n == 1: 54 | return 55 | if n == 0: 56 | n = self.read_n(3) 57 | self.data.append(n & 0x3ffff) 58 | else: 59 | self.errors.append(n) 60 | -------------------------------------------------------------------------------- /ga_tools/parse.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | 4 | from .defs import * 5 | from .word import Ref 6 | 7 | class Token: 8 | def __init__(self, value, line, col, source): 9 | self.value = value 10 | self.line = line 11 | self.col = col - len(value) 12 | #self.filename = intern(filename) 13 | self.source = source 14 | 15 | def __repr__(self): 16 | s = 'NEWLINE' if self.value == '\n' else self.value 17 | return 'token({})'.format(s) 18 | 19 | class Node: 20 | def __init__(self, coord): 21 | self.coord = coord 22 | self.asm = False 23 | self.tokens = [] 24 | self.symbols = [] 25 | self.index = 0 26 | self.next_token = None 27 | 28 | def copy(self, coord): 29 | node = Node(coord) 30 | node.asm = self.asm 31 | node.tokens = self.tokens 32 | node.symbols = self.symbols 33 | return node 34 | 35 | def token_reader(self, node): 36 | return TokenReader(node, self.tokens) 37 | 38 | def finish(self): 39 | pass 40 | 41 | class AforthNode(Node): 42 | def __init__(self, coord): 43 | super(AforthNode, self).__init__(coord) 44 | def add_token(self, token): 45 | v = token.value 46 | if v == '\n': 47 | return 48 | if v == 'asm': 49 | throw_error("'ASM' must come after 'node'") 50 | self.tokens.append(token) 51 | 52 | class AsmNode(Node): 53 | def __init__(self, coord): 54 | super(AsmNode, self).__init__(coord) 55 | self.asm = True 56 | self.line = [] 57 | 58 | def add_token(self, token): 59 | v = token.value 60 | if v == '\n': 61 | if self.line: 62 | self.tokens.append(self.line) 63 | self.line = [] 64 | else: 65 | self.line.append(token) 66 | 67 | def finish(self): 68 | if self.line: 69 | self.tokens.append(self.line) 70 | 71 | class Tokenizer: 72 | def __init__(self, s, source=None): 73 | self.source = source 74 | self.current_line = 1 75 | self.current_column = 1 76 | self.text = None 77 | self.last_word = None 78 | self.unread_word = None 79 | self.index = 0 80 | self.text = tuple(s) 81 | self.last = len(self.text) - 1 82 | 83 | def eof(self): 84 | return self.index > self.last 85 | 86 | def read_char(self): 87 | if self.unread_word == '\n': 88 | self.unread_word = None 89 | return '\n' 90 | c = self.text[self.index] 91 | self.index += 1 92 | self.current_column += 1 93 | if is_newline(c): 94 | self.current_line += 1 95 | self.current_column = 1 96 | return c 97 | 98 | def peak(self): 99 | return self.text[self.index] 100 | 101 | def skip_to(self, char, exclusive=False): 102 | while True: 103 | if self.eof(): 104 | return 105 | c = self.read_char() 106 | if c == char: 107 | if exclusive: 108 | self.index -= 1 109 | return 110 | 111 | def skip(self, fn): 112 | while True: 113 | if self.eof(): 114 | return None 115 | c = self.peak() 116 | if not fn(c): 117 | break 118 | self.read_char() 119 | 120 | def skip_space(self): 121 | self.skip(is_space) 122 | 123 | def skip_whitespace(self): 124 | self.skip(is_whitespace) 125 | 126 | def read_token(self): 127 | self.skip_space() 128 | word = [] 129 | while True: 130 | if self.eof(): 131 | break 132 | if is_newline(self.peak()): 133 | if not word: 134 | self.read_char() 135 | word = ['\n'] 136 | break 137 | else: 138 | char = self.read_char() 139 | if is_space(char): 140 | break 141 | word.append(char) 142 | value = ''.join(word).lower() or None 143 | if value is None: 144 | return None 145 | t = Token(value, self.current_line, 146 | self.current_column, self.source) 147 | self.last_word = t 148 | return t 149 | 150 | def skip_comments(self, val): 151 | if val == '(': 152 | self.skip_to(')') 153 | return True 154 | if val == '\\': 155 | self.skip_to('\n', True) 156 | return True 157 | return False 158 | 159 | def tokens(self): 160 | self.skip_whitespace() 161 | ret = [] 162 | while True: 163 | t = self.read_token() 164 | if t is None: 165 | break 166 | if not self.skip_comments(t.value): 167 | ret.append(t) 168 | self.token_list = ret 169 | return ret 170 | 171 | class Parser: 172 | def __init__(self): 173 | self.chips = {} 174 | self.current_chip = None # dictionary 175 | self.current_node = None # list of tokens 176 | # list of nodes that share the source of current_node 177 | self.other_nodes = [] 178 | self.current_node = None 179 | 180 | self.labels = [] 181 | 182 | self.tokens = [] 183 | self.next_token = None 184 | 185 | def include_string(self, s, source=None): 186 | tokenizer = Tokenizer(s, source) 187 | tokens = tokenizer.tokens() 188 | if self.next_token: 189 | self.tokens.append(self.next_token) 190 | else: 191 | tokens.append(None) 192 | tokens.reverse() 193 | self.next_token = tokens.pop() 194 | self.tokens.extend(tokens) 195 | 196 | def include_file(self, filename=None): 197 | if filename is None: 198 | tok = self.read_name_token() 199 | if tok.source: 200 | base = os.path.abspath(os.path.dirname(tok.source)) 201 | filename = os.path.join(base, tok.value) 202 | else: 203 | filename = tok.value 204 | f = open(filename) 205 | self.include_string(f.read(), filename) 206 | f.close() 207 | 208 | def read_token(self): 209 | t = self.next_token 210 | if t: 211 | self.next_token = self.tokens.pop() 212 | set_current_token(t) 213 | return t 214 | 215 | def eof(self): 216 | return self.next_token is None 217 | 218 | def peak(self): 219 | t = self.next_token 220 | return t.value if t else t 221 | 222 | def to_int(self, s): 223 | try: 224 | return int(s, 0) 225 | except ValueError as e: 226 | throw_error(e) 227 | 228 | def read_name_token(self): 229 | while True: 230 | t = self.read_token() 231 | if t is None: 232 | throw_error('invalid name') 233 | name = t.value 234 | if name == '\n': 235 | continue 236 | return t 237 | 238 | def read_name(self): 239 | t = self.read_name_token() 240 | return t.value if t else None 241 | 242 | def new_chip(self, default=False): 243 | if default: 244 | name = '__default__' 245 | else: 246 | name = self.read_name() 247 | chip = {} 248 | if name in self.chips: 249 | throw_error('repeated chip: ' + str(name)) 250 | self.chips[name] = chip 251 | self.current_chip = chip 252 | return chip 253 | 254 | def finish_node(self): 255 | # handle multiple nodes 256 | if self.current_node is None: 257 | return 258 | node = self.current_node 259 | node.finish() 260 | for coord in self.other_nodes: 261 | self.current_chip[coord] = node.copy(coord) 262 | self.other_nodes = [] 263 | self.current_node = None 264 | 265 | def expand_range(self, a, b): 266 | a, b = self.to_coord(a), self.to_coord(b) 267 | if a//100 == b//100: 268 | inc = 1 269 | else: 270 | inc = 100 271 | if a%100 != b%100: 272 | throw_error('invalid node range') 273 | coord, end = min(a,b), max(a,b) 274 | ret = [coord] 275 | while coord != end: 276 | coord += inc 277 | ret.append(coord) 278 | return ret 279 | 280 | def to_coord(self, s): 281 | n = self.to_int(s) 282 | if not valid_coord(n): 283 | throw_error('invalid node coordinate: ' + str(n)) 284 | return n 285 | 286 | def expand_coords(self, s): 287 | ret = [] 288 | for coord in s.split(','): 289 | crange = coord.split('-') 290 | if len(crange) == 1: 291 | ret.append(self.to_coord(crange[0])) 292 | else: 293 | if len(crange) != 2: 294 | msg = 'invalid node coordinate range: ' +str(crange) 295 | throw_error(msg) 296 | ret.extend(self.expand_range(crange[0], crange[1])) 297 | return ret 298 | 299 | def read_coord(self): 300 | assert not self.other_nodes 301 | coords = self.expand_coords(self.read_name()) 302 | if len(coords) > 1: 303 | self.other_nodes = coords[1:] 304 | return coords[0] 305 | 306 | def read_port_name(self): 307 | name = self.read_name() 308 | if name not in port_names: 309 | throw_error('Invalid port: {}'.format(name)) 310 | return name 311 | 312 | def make_wire(self): 313 | from_port = self.read_port_name() 314 | to_port = self.read_port_name() 315 | # TODO: faster wire code - double unext with preloaded stacks 316 | code = '''boot {} a! {} b! 317 | : loop 0x3ffff for @ !b unext loop'''.format(from_port, to_port) 318 | source = ''.format(from_port, to_port) 319 | self.include_string(code, source) 320 | 321 | def make_node(self, coord): 322 | w = self.peak() 323 | if w == 'asm': 324 | self.read_token() # discard 'asm' 325 | return AsmNode(coord) 326 | else: 327 | return AforthNode(coord) 328 | 329 | def new_node(self): 330 | assert self.current_chip is not None 331 | self.finish_node() 332 | coord = self.read_coord() 333 | if coord in self.current_chip: 334 | throw_error('repeated node: ' + str(coord)) 335 | node = self.make_node(coord) 336 | self.current_node = node 337 | self.current_chip[coord] = node 338 | return node 339 | 340 | def do_colon(self, t): 341 | name = self.read_name_token() 342 | node = self.current_node 343 | if not node: 344 | throw_error('node is unspecified') 345 | if name.value in node.symbols: 346 | throw_error("name '{}' already defined".format(name.value)) 347 | node.symbols.append(name.value) 348 | node.add_token(t) 349 | node.add_token(name) 350 | 351 | def parse(self): 352 | while True: 353 | t = self.read_token() 354 | if t is None: 355 | break 356 | w = t.value 357 | if w == 'chip': 358 | self.new_chip() 359 | continue 360 | if self.current_chip is None: 361 | self.new_chip(True) 362 | if w == 'node': 363 | self.new_node() 364 | continue 365 | if w == 'include': 366 | self.include_file() 367 | continue 368 | if w == 'wire': 369 | self.make_wire() 370 | continue 371 | if w == ':': 372 | self.do_colon(t) 373 | continue 374 | if w == '\n' and not self.current_node: 375 | continue 376 | self.current_node.add_token(t) 377 | self.finish_node() 378 | return self.chips 379 | 380 | class TokenReader: 381 | def __init__(self, node, tokens): 382 | self.tokens = tokens 383 | self.index = 0 384 | self.last = len(tokens) -1 385 | self.node = node 386 | 387 | def peak(self): 388 | return self.read_word(True) 389 | 390 | def read_token(self, peak=False): 391 | if self.index > self.last: 392 | return None 393 | w = self.tokens[self.index] 394 | if not peak: 395 | self.index += 1 396 | set_current_token(w) 397 | return w 398 | 399 | def read_word(self, peak=False): 400 | t = self.read_token(peak) 401 | return t.value if t else None 402 | 403 | def read_int(self): 404 | w = self.read_word() 405 | try: 406 | return int(w, 0) 407 | except ValueError as e: 408 | throw_error(e) 409 | 410 | def read_coord(self): 411 | n = self.read_int() 412 | if not valid_coord(n): 413 | throw_error('invalid node coordinate: ' + str(n)) 414 | return n 415 | 416 | def error(self, msg): 417 | pass 418 | 419 | def is_space(c): 420 | n = ord(c) 421 | return n < 33 and n != 13 and n != 10 422 | 423 | def is_newline(c): 424 | return c == '\n' or c == '\r' 425 | 426 | def is_whitespace(c): 427 | return ord(c) < 33 428 | -------------------------------------------------------------------------------- /ga_tools/word.py: -------------------------------------------------------------------------------- 1 | 2 | from .defs import * 3 | 4 | addr_masks = (0x3ff, 0xff, 0x7) 5 | xor_bits = (0b1010, 0b10101, 0b1010, 0b101) 6 | xor_bits2 = (0b1010, 0b10101, 0b1010, 0b10100) 7 | 8 | INST = 0 9 | CONST = 1 10 | ADDR = 2 11 | 12 | class Word: 13 | def __init__(self, prev=None, addr=None): 14 | self._slots = [None, None, None, None] 15 | if prev: 16 | self.prev.next = self 17 | self.next = None 18 | self.prev = None 19 | self.symbol = None 20 | self._const = None # Ref type set for number literals 21 | self.op_index = 0 # Index of next empty slot 22 | # If word type is ADDR, op_index will be left 23 | # pointing to the last op before the address 24 | self.dest_word = None # forward transfer destination word 25 | self._addr = None # set when word contains an address 26 | # set to True when addr is not yet determined 27 | self.addr_sym = None # Ref 28 | self.addr_slot = None 29 | self.type = INST 30 | self.label = None 31 | self.word_addr = addr # address of this word in RAM 32 | self.extended_arith = 0 33 | self.stream = False 34 | 35 | def empty(self): 36 | #return self.op_index == 0 37 | return self._const is None and self._slots == [None, None, None, None] 38 | 39 | def fill_rest_with_nops(self): 40 | while self.op_index < 4: 41 | self.set_op(NOP) 42 | 43 | def set_op(self, op, final_slot=False): 44 | if self.op_index > 3: 45 | slots = [ops[self._slots[i]] for i in range(4)] 46 | throw_error('slot overflow: {}, {}'.format(slots, op)) 47 | self._slots[self.op_index] = get_op_i(op) 48 | if not final_slot: 49 | self.op_index += 1 50 | 51 | def set_if(self, op): 52 | self.set_op(op, True) 53 | self.type = ADDR 54 | 55 | def set_next(self, op, dest): 56 | self.set_op(op, True) 57 | self.dest_word = dest 58 | self.type = ADDR 59 | 60 | def set_const(self, const, tok=None): 61 | if type(const) == Ref: 62 | self._const = const 63 | else: 64 | self._const = Ref(value=const, tok=tok) 65 | self.type = CONST 66 | 67 | def get_const(self, required=True): 68 | return self._const.resolve(required) 69 | 70 | def set_call(self, op, name): 71 | assert type(name) == Ref 72 | self.set_op(op, True) 73 | self.addr_sym = name 74 | self.type = ADDR 75 | 76 | def set_addr(self, val): 77 | self._addr = val | self.extended_arith 78 | 79 | def move_addr(self, from_word): 80 | # Move address and associated op in word FROM_WORD to self 81 | assert from_word.type == ADDR 82 | assert self.type != ADDR 83 | self.type = ADDR 84 | self.addr_sym = from_word.addr_sym 85 | self.dest_word = from_word.dest_word 86 | self._addr = from_word._addr 87 | self._slots[self.op_index] = from_word._slots[from_word.op_index] 88 | self.extended_arith = from_word.extended_arith 89 | from_word.fill_rest_with_nops() 90 | from_word.type = INST 91 | from_word._addr = None 92 | from_word.addr_sym = None 93 | 94 | def resolve_symbol(self): 95 | if type(self.addr_sym) == int: 96 | self.set_addr(self.addr_sym) 97 | else: 98 | dest = self.addr_sym.resolve() 99 | if dest is None: 100 | m = 'Unresolved symbol: ->{}<-'.format( 101 | self.addr_sym.name) 102 | throw_error(m) 103 | self.set_addr(dest) 104 | 105 | def asm_op(self, slot, shift, xor_bits): 106 | op = self._slots[slot] 107 | if op is None: 108 | return 0 109 | if slot == 3: 110 | op >>= 2 111 | return (op ^ xor_bits) << shift 112 | 113 | def asm(self): 114 | # return assembled word 115 | if self.empty(): 116 | return 0x134a9 # call warm 117 | typ = self.type 118 | if typ == CONST: 119 | w = self.get_const(True) & 0x3ffff 120 | elif typ == INST or typ == ADDR: 121 | f = self.asm_op 122 | w = f(3, 0, 5) | f(2, 3, 10) | f(1, 8, 21) | f(0, 13, 10) 123 | if typ == ADDR: 124 | w |= self._addr & addr_masks[self.op_index] 125 | return w 126 | 127 | def aforth_list(self): 128 | if self.type == CONST: 129 | return self.get_const(True) & 0x3ffff 130 | if self.type == INST: 131 | return [ops[op] if op else '.' for op in self._slots] 132 | if self.type == ADDR: 133 | x = [ops[op] for op in range(self.op_index)] 134 | x.append(self._addr) 135 | return x 136 | 137 | def disasm(self, w): 138 | # disassemble W into this object 139 | # TODO: to properly disassemble addresses need to know P 140 | addr_shift = 0 141 | for x in xor_bits2: 142 | op = ((w & 0x3e000) >> 13) ^ x 143 | w = (w << 5) & 0x3ffff 144 | addr_shift += 5 145 | self._slots[self.op_index] = op 146 | name = ops[op] 147 | if name in address_required: 148 | self._addr = (w >> addr_shift) & 0x1ff 149 | self.type = ADDR 150 | break 151 | self.op_index += 1 152 | if name in ops_using_rest_of_word: 153 | break 154 | 155 | def inst_str(self): 156 | return ' '.join([ops[x] for x in self._slots if x is not None]) 157 | 158 | def __str__(self): 159 | typ = self.type 160 | if typ == INST: 161 | return self.inst_str() 162 | if typ == CONST: 163 | return str(self._const) 164 | if typ == ADDR: 165 | return self.inst_str() + ' ' + hex(self._addr)[2:] 166 | return 'Error unhandled word type' 167 | 168 | class Ref: 169 | def __init__(self, node=None, name=None, value=None, tok=None): 170 | # a reference to the address of NAME in NODE 171 | self.node = node 172 | self.name = name 173 | self.value = value 174 | self.tok = tok 175 | 176 | def resolve(self, required=True): 177 | if self.value is not None: 178 | return self.value 179 | v = self.node.symbol_addr(self.name) 180 | if required and v is None: 181 | throw_error('unresolved reference: ' + self.name, 182 | token=self.tok) 183 | # Don't cache value, it can change as words shift 184 | # self.value = v 185 | return v 186 | def __str__(self): 187 | v = self.resolve() 188 | if v is None: 189 | return "Ref('{}')".format(self.name) 190 | return str(v) 191 | 192 | def disasm_to_str(n): 193 | w = Word() 194 | w.disasm(n) 195 | return str(w) 196 | 197 | def dis_list(lst): 198 | for w in lst: 199 | print(w, disasm_to_str(w)) 200 | -------------------------------------------------------------------------------- /images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 61 | 65 | 72 | 79 | 86 | 93 | 100 | 107 | 114 | 121 | 128 | 135 | 142 | 149 | 156 | 163 | 170 | 177 | 189 | GA* GAGGa* GA 292 | tools 316 | * 329 | 336 | 348 | 349 | 350 | -------------------------------------------------------------------------------- /images/logo_150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mschuldt/ga-tools/2ec8ce36b8bb0b1c635a04ba132e85b0c33d61b3/images/logo_150.png -------------------------------------------------------------------------------- /images/logo_v2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from distutils.core import setup, Extension 4 | import sys 5 | 6 | version = '0.2' 7 | 8 | assert not sys.version_info.major < 3, 'ga-tools requires python3' 9 | 10 | setup(name='ga-tools', 11 | version=version, 12 | description='Tools for the GA144 multi-computer chip', 13 | author='Michael Schuldt', 14 | author_email='mbschuldt@gmail.com', 15 | license='GNU General Public License v3 (GPLv3)', 16 | packages=['ga_tools'], 17 | url='https://github.com/mschuldt/ga-tools', 18 | download_url='https://github.com/mschuldt/ga-tools_wizard/archive/{}.tar.gz'.format(version), 19 | 20 | scripts=['ga'], 21 | install_requires=['pyserial'], 22 | classifiers=[ 23 | 'Environment :: Console', 24 | 'Intended Audience :: Developers', 25 | 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 26 | 'Operating System :: POSIX :: Linux', 27 | 'Operating System :: Microsoft :: Windows', 28 | 'Programming Language :: Python :: 3', 29 | 'Topic :: Software Development :: Libraries :: Python Modules', 30 | 'Topic :: Software Development :: Assemblers', 31 | 'Topic :: Software Development :: Disassemblers', 32 | 'Topic :: System :: Distributed Computing', 33 | ]) 34 | -------------------------------------------------------------------------------- /tests/_test-include.ga: -------------------------------------------------------------------------------- 1 | 2 | \ This is for testing include functionality 3 | 4 | node 5 5 | : one 1 ; 6 | include _test-include2.ga 7 | : three two 1 + ; 8 | 9 | node 6 ASM 10 | : one 11 | @p ; 12 | 1 13 | include _test-include2.ga 14 | : three 15 | call two 16 | @p . + ; 17 | 1 18 | -------------------------------------------------------------------------------- /tests/_test-include2.ga: -------------------------------------------------------------------------------- 1 | 2 | \ This is for testing include functionality 3 | 4 | : two 5 | 2 6 | ; 7 | -------------------------------------------------------------------------------- /tests/test-chip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | 5 | import ga_tools 6 | 7 | parser = argparse.ArgumentParser() 8 | parser.add_argument('--port', 9 | nargs=1, 10 | help='Serial port') 11 | parser.add_argument('--baud', 12 | nargs=1, 13 | default=[460800], 14 | help='Serial port baud rate. Default=460800') 15 | 16 | args = parser.parse_args() 17 | 18 | 19 | ga_tools.set_baud(args.baud[0]) 20 | 21 | program = ''' 22 | node 708 23 | : send 0 _send8 drop _send8 _send8 24 | : _send8 0 _send1 7 for dup _send1 2/ next 1 25 | : _send1 1 and 3 or !b unext_baud for unext ; 26 | : exit 1 _send8 ; 27 | : main coord send 1 4 + send exit 28 | ''' 29 | ga_tools.include_string(program) 30 | ga_tools.do_compile() 31 | 32 | 33 | chip = ga_tools.get_chip() 34 | 35 | 36 | serial = ga_tools.GA144Serial([chip], args.port[0], args.baud[0]) 37 | 38 | serial.write_bootstream('708') 39 | serial.gather() # TODO: timeout 40 | 41 | assert serial.data == [708, 5] 42 | print('ok') 43 | -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import ga_tools 4 | 5 | tests_run = 0 6 | failed_tests = [] 7 | 8 | def run_tests(): 9 | global tests_run 10 | tests_run = 0 11 | failed_tests.clear() 12 | 13 | case('empty', 14 | '''node 1''', 15 | '''node 1 ASM''', 16 | {1: {'ram': []}}) 17 | 18 | case('plus', 19 | '''node 1 20 | + + + . + . +''', 21 | 22 | '''node 1 asm 23 | . + . + 24 | . + . + 25 | . + ''', 26 | {1: {'ram': [180656, 180656, 180658]}}) 27 | 28 | case('literals', 29 | '''node 1 30 | 1 2 3 4 5 ''', 31 | 32 | '''node 1 ASM 33 | @p @p @p @p 34 | 1 35 | 2 36 | 3 37 | 4 38 | @p 39 | 5''', 40 | {1: {'ram': [23831, 1, 2, 3, 4, 18866, 5]}}) 41 | 42 | case('emptySlot4', 43 | '''node 1 44 | @p @p @p @p 45 | over over over over ''', 46 | 47 | '''node 1 ASM 48 | @p @p @p @p 49 | over over over . 50 | over 51 | ''', 52 | {1:{'ram':[23831, 135042, 133554]}}) 53 | 54 | case('..', 55 | '''node 1 56 | dup .. dup dup .. dup dup dup .. dup dup dup dup .. 57 | dup .. .. dup 58 | ''', 59 | None, 60 | {1:{'ram':[149938, 150962, 150930, 150931, 149938, 149938]}} ) 61 | 62 | case('comments', 63 | '''node 1 64 | ( a) dup ( lksdjf) 65 | dup ( lksdjf) 66 | ( ) dup ( a)dup \\ lssd 67 | .. dup \\ 68 | ( ) 69 | \\ 70 | ( x) .. dup 71 | \\ sd''', 72 | 73 | '''node 1 ASM 74 | dup dup dup dup \\ AAA 75 | dup \\ 76 | \\ 77 | \\ BBBB 78 | dup \\ lskd 79 | ''', 80 | {1:{'ram':[150931, 149938, 149938]}}) 81 | 82 | case('if/then', 83 | '''node 1 84 | dup if then 85 | and if . then 86 | over if . . . . . then 87 | ''', 88 | ''' node 1 ASM 89 | dup if if_1 90 | : if_1 91 | and if if_2 92 | . 93 | : if_2 94 | over if if_3 95 | . . . . 96 | . . . . 97 | : if_3 98 | ''', 99 | {1:{'ram':[152321, 258819, 182706, 135942, 182706, 182706]}} ) 100 | 101 | case('nested-if', 102 | '''node 1 103 | if dup if over if + then then then 104 | ''', 105 | 106 | ''' 107 | node 1 ASM 108 | if if_1 109 | dup if if_1 110 | over if if_1 111 | + 112 | : if_1 113 | ''', 114 | {1:{'ram':[98308, 152324, 135940, 248242]}} ) 115 | 116 | case('for/next', 117 | '''node 1 118 | for 119 | for . next 120 | for . . next 121 | for . . . next 122 | for . . . . . next 123 | next 124 | ''', 125 | 126 | '''node 1 ASM 127 | push 128 | : loop_1 129 | push 130 | : loop_2 131 | . next loop_2 132 | push 133 | : loop_3 134 | . . next loop_3 135 | push 136 | : loop_4 137 | . . . . 138 | next loop_4 139 | push 140 | : loop_5 141 | . . . . 142 | . next loop_5 143 | next loop_1 144 | ''', 145 | {1:{'ram':[190898, 190898, 184322, 190898, 182652, 190898, 146 | 182706, 122886, 190898, 182706, 184329, 122881]}} ) 147 | 148 | case('for/unext', 149 | '''node 1 150 | for . unext 151 | for over dup unext 152 | for + !p unext 153 | ''', 154 | 155 | '''node 1 ASM 156 | push 157 | . unext push 158 | over dup unext 159 | push 160 | + !p unext 161 | ''', 162 | {1:{'ram':[190898, 184762, 134514, 190898, 252274]}}) 163 | 164 | 165 | case('aforth+asm', 166 | '''node 100 167 | + 168 | @p 169 | 170 | node 200 ASM 171 | over 172 | dup 173 | 174 | node 300 175 | @ 176 | ! 177 | 178 | node 400 ASM 179 | 4 180 | 5 181 | ''', 182 | None, 183 | {100: {'ram': [180498]}, 184 | 200: {'ram': [133554, 149938]}, 185 | 300: {'ram': [15026]}, 186 | 400: {'ram': [4,5]}}) 187 | 188 | case(':/call', 189 | '''node 1 190 | : zero 191 | dup 192 | : one 193 | : one2 194 | over 195 | : two 196 | zero one one2 dup two dup dup last 197 | : last 198 | ''', 199 | 200 | '''node 1 ASM 201 | : zero 202 | dup 203 | : one 204 | over 205 | : two 206 | call zero 207 | call one 208 | call one 209 | dup call two 210 | dup dup call last 211 | : last 212 | ''', 213 | {1: {'ram': [149938, 133554, 73728, 73729, 214 | 73729, 153090, 150863]}}) 215 | 216 | case('call-return', 217 | '''node 1 218 | : x dup ; 219 | : test test ; 220 | : main test test ; 221 | over 222 | ''', 223 | None, 224 | {1: {'ram': [153010, 65537, 73729, 65537, 133554]}}) 225 | 226 | case('large-address', 227 | '''node 1 228 | push pop if 229 | : test dup ; 230 | : test2 dup ; 231 | . .. . .. . .. . .. . .. 232 | ! !b test test2 233 | dup dup then or 234 | 235 | ''', 236 | None, 237 | {1: {'ram': [191666, 98317, 153010, 153010, 182706, 238 | 182706, 182706, 182706, 182706, 48050, 73730, 239 | 73731, 150962, 231858]}}) 240 | 241 | case('call-before-def', 242 | '''node 1 243 | : x test test2 ; 244 | : test ; 245 | : test2 ; 246 | ''', 247 | 248 | '''node 1 ASM 249 | call test 250 | jump test2 251 | : test 252 | ; 253 | : test2 254 | ; 255 | ''', 256 | {1: {'ram': [73730, 65539, 84402, 84402]}}) 257 | 258 | case('shifted-last-word', 259 | '''node 601 260 | io b! west a! 1 0 261 | : fib over over + dup ! fib''', 262 | None, 263 | {601: {'ram': [19218, 349, 469, 179603, 1, 231858, 264 | 135088, 154290, 73734]}}) 265 | 266 | case(',', 267 | '''node 1 268 | , 1 dup , 2 , 0x3 over , 4 269 | ''', 270 | ''' node 1 ASM 271 | 1 272 | dup over 273 | 2 274 | 3 275 | 4''', 276 | {1: {'ram': [1, 151474, 2, 3, 4]}}) 277 | 278 | case('org', 279 | '''node 1 org 0 280 | over 281 | 282 | node 2 org 5 283 | dup 284 | org 8 285 | over''', 286 | None, 287 | {1: {'ram': [133554]}, 288 | 2: {'ram': [79017, 79017, 79017, 79017, 79017, 149938, 289 | 79017, 79017, 133554]}}) 290 | 291 | case('unext,', 292 | '''node 1 293 | unext, unext, dup dup 294 | !b unext, unext, . 295 | !b !b unext, unext, 296 | ''', 297 | None, 298 | {1: {'ram': [119187, 37234, 39796]}}) 299 | 300 | case('coord', 301 | '''node 1 coord 302 | node 2 coord 303 | node 708 coord 304 | ''', 305 | None, 306 | {1: {'ram':[18866, 1]}, 307 | 2: {'ram': [18866, 2]}, 308 | 708: {'ram': [18866, 708]},}) 309 | 310 | case('multicoord', 311 | '''node 1,2 dup 312 | node 3,4,5-7 + 313 | node 100-300 ! 314 | node 101-101 @ 315 | ''', 316 | None, 317 | {1: {'ram':[149938]}, 318 | 2: {'ram': [149938]}, 319 | 3: {'ram': [180658]}, 320 | 4: {'ram': [180658]}, 321 | 5: {'ram': [180658]}, 322 | 6: {'ram': [180658]}, 323 | 7: {'ram': [180658]}, 324 | 100: {'ram': [43442]}, 325 | 200: {'ram': [43442]}, 326 | 300: {'ram': [43442]}, 327 | 101: {'ram': [10674]}}) 328 | 329 | case('if:/-if:', 330 | '''node 1 331 | : label1 332 | if: label1 333 | -if: label2 334 | @ -if: label2 335 | : label2 336 | : label3 337 | dup 338 | ''', 339 | None, 340 | {1: {'ram': [98304, 106499, 12803, 149938]}}) 341 | 342 | case('while/-while', 343 | '''node 1 344 | 3 for @ -while next dup then 345 | 4 for ! while next ; then 346 | ''', 347 | None, 348 | {1: {'ram': [18610, 3, 12805, 122882, 149938, 18610, 349 | 4, 45834, 122887, 84402]}}) 350 | 351 | 352 | case('include', 353 | '''node 1 dup 354 | include _test-include.ga 355 | node 3 over 356 | ''', 357 | None, 358 | {1: {'ram': [149938]}, 359 | 5: {'ram': [21938, 1, 21938, 2, 73730, 18933, 1]}, 360 | 6: {'ram': [21938, 1, 2, 84402, 73730, 18933, 1]}, 361 | 3: {'ram': [133554]}}) 362 | 363 | case('rom', 364 | '''node 1 warm *.17 poly 365 | node 709 -dac 366 | node 705 18ibits 367 | node 708 18ibits 368 | ''', 369 | ''' node 1 ASM 370 | call warm 371 | call *.17 372 | call poly 373 | node 709 ASM 374 | call -dac 375 | node 705 ASM 376 | call 18ibits 377 | node 708 ASM 378 | call 18ibits''', 379 | {1: {'ram': [73897, 73904, 73898]}, 380 | 709: {'ram': [73916]}, 381 | 705: {'ram': [73945]}, 382 | 708: {'ram': [73931]}}) 383 | 384 | case('ports', 385 | '''node 101 386 | -d-u rdlu ;''', 387 | 388 | '''node 101 ASM 389 | call -d-u 390 | jump rdlu''', 391 | {101: {'ram': [73989, 65957]}}) 392 | 393 | def check_boot_desc(chip): 394 | node = chip.node(1) 395 | ok = cmp_values(node.init_p.resolve(), 0x115, "/p") 396 | ok = ok and cmp_values(node.init_a.resolve(), 0x15d, "/a") 397 | ok = ok and cmp_values(node.init_b.resolve(), 2, "/b") 398 | return ok 399 | 400 | case('boot-descriptors', 401 | '''node 1 402 | /p -d-- /a io /b testword 403 | dup .. dup 404 | : testword ; 405 | ''', 406 | None, 407 | {1: {'ram': [149938, 149938, 84402]}}, 408 | check_boot_desc) 409 | 410 | case(',name', 411 | '''node 1 412 | , 0b101 , io , -d-u , testname 413 | : testname , 3 ; 414 | ''', 415 | None, 416 | {1: {'ram': [5, 349, 261, 4, 3, 84402]}}) 417 | 418 | #case("'", 419 | # '''node 1 420 | # dup 421 | # : testname dup ; 422 | # ' testname ' *.17 lit lit 423 | # ''', 424 | # None, 425 | # {1: {'ram': []}}) 426 | 427 | case("port-references", 428 | '''node 0 , north , east , south , west \ D R U L 429 | node 100 , north , east , south , west \ U R D L 430 | node 1 , north , east , south , west \ D L U R 431 | node 101 , north , east , south , west \ U L D R 432 | ''', 433 | None, 434 | {0: {'ram': [0x115, 0x1D5, 0x145, 0x175]}, 435 | 100: {'ram': [0x145, 0x1D5, 0x115, 0x175]}, 436 | 1: {'ram': [0x115, 0x175, 0x145, 0x1D5]}, 437 | 101: {'ram': [0x145, 0x175, 0x115,0x1D5]}}) 438 | 439 | def error(coord, asm_type, name, msg): 440 | print('Node', coord, asm_type, 441 | "Error: Test '{}' - {}".format(name, msg)) 442 | 443 | def disasm_ram(node, expect): 444 | len_node = len(node) 445 | len_expect = len(expect) 446 | print('____got__________________________expected____________') 447 | for i in range(max(len_node, len_expect)): 448 | print(str(i).ljust(4), end='') 449 | if i < len_node: 450 | print(str(node[i]).ljust(6), '|', 451 | ga_tools.disasm_to_str(node[i]).ljust(20), end='') 452 | else: 453 | print('Nothing'.ljust(29), end='') 454 | if i < len_expect: 455 | print(str(expect[i]).ljust(6), '|', 456 | ga_tools.disasm_to_str(expect[i])) 457 | else: 458 | print('Nothing') 459 | print('ram=', node) 460 | print() 461 | 462 | #print('json:', json) 463 | def cmp_json(asm_type, name, chip, expect): 464 | json = ga_tools.chip_json(chip)['nodes'] 465 | for coord in expect.keys(): 466 | if coord not in json: 467 | msg = "key '{}' not found".format(coord) 468 | error(coord, asm_type, name, msg) 469 | return False 470 | node = json[coord] 471 | expt = expect[coord] 472 | for node_k in expt.keys(): 473 | if node_k not in node: 474 | msg = "node '{}' key '{}' not found".format(coord, node_k) 475 | error(coord, asm_type, name, msg) 476 | return False 477 | node_val = node[node_k] 478 | expt_val = expt[node_k] 479 | if node_val != expt_val: 480 | msg = "value mismatch for '{}'".format(node_k) 481 | error(coord, asm_type, name, msg) 482 | if node_k == 'ram': 483 | disasm_ram(node_val, expt_val) 484 | return False 485 | return True 486 | 487 | def cmp_values(value, expect, test): 488 | if value != expect: 489 | fmt = 'Error: "{}" - value mismatch: {} != {}' 490 | print(fmt.format(test, value, expect)) 491 | return False 492 | return True 493 | 494 | 495 | def case(name, aforth, asm, expect, fn=lambda chip: True): 496 | print('TEST', name) 497 | global tests_run 498 | ga_tools.clear_chips() 499 | if aforth: 500 | ga_tools.include_string('chip test_aforth\n' 501 | + aforth, __file__) 502 | if asm: 503 | ga_tools.include_string('chip test_asm\n' + asm, __file__) 504 | ga_tools.do_compile() 505 | chips = ga_tools.get_chips() 506 | ok = True 507 | if aforth: 508 | chip = chips.get('test_aforth') 509 | ok = cmp_json('Aforth', name, chip, expect) 510 | ok = ok and fn(chip) 511 | if asm: 512 | chip = chips.get('test_asm') 513 | ok = cmp_json('ASM', name, chip, expect) and ok 514 | ok = ok and fn(chip) 515 | tests_run += 1 516 | if not ok: 517 | failed_tests.append(name) 518 | 519 | def test_report(): 520 | if not failed_tests: 521 | print('All {} tests passed.'.format(tests_run)) 522 | else: 523 | print('{}/{} Tests failed:'.format(len(failed_tests), tests_run)) 524 | print(failed_tests) 525 | 526 | if __name__ == '__main__': 527 | run_tests() 528 | test_report() 529 | --------------------------------------------------------------------------------