├── .gitattributes ├── LICENSE ├── README.md ├── arithmetic.mms ├── data.dat ├── exercise_2.1.7.mms ├── exercise_2.1.8.mms ├── exercise_2.1.9.mms ├── exercise_5.2.2.12.mms ├── inout.mms ├── loadstore.mms ├── program_a_addition_of_polynomials.mms ├── program_b_bubble_sort.mms ├── program_c_comparison_counting.mms ├── program_d_shellsort.mms ├── program_l_list_insertion.mms ├── program_m_maximum.mms ├── program_m_multiple_list_insertion.mms ├── program_s_straight_insertion_sort.mms ├── program_s_straight_selection_sort.mms ├── program_t_topological_sort.mms └── subroutine.mms /.gitattributes: -------------------------------------------------------------------------------- 1 | *.mms linguist-language=MMIX-Assembly 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Zartaj Majeed 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mmixsamples 2 | Sample code in MMIX assembly language for the MMIX computer designed by Donald Knuth for The Art of Computer Programming books 3 | 4 | ## Code 5 | 6 | [**`loadstore.mms:`**](loadstore.mms) examples of load and store instructions - to aid section Loading and storing in 1.3.1', Description of MMIX, from Chapter 1, Basic Concepts, of Fascicle 1, MMIX, by Donald Knuth 7 | 8 | [**`arithmetic.mms:`**](arithmetic.mms) examples of arithmetic operators - to aid section Arithmetic operators in 1.3.1', Description of MMIX, from Chapter 1, Basic Concepts, of Fascicle 1, MMIX, by Donald Knuth 9 | 10 | [**`conditional.mms:`**](conditional.mms) examples of conditional instructions - to aid section Conditional instructions in 1.3.1', Description of MMIX, from Chapter 1, Basic Concepts, of Fascicle 1, MMIX, by Donald Knuth 11 | 12 | **`bitbyte.mms:`** examples of bitwise and bytewise operations - to aid sections Bitwise operations and Bytewise operations in 1.3.1', Description of MMIX, from Chapter 1, Basic Concepts, of Fascicle 1, MMIX, by Donald Knuth 13 | 14 | [**`subroutine.mms:`**](subroutine.mms) examples of subroutine calls - to aid section Subroutine calls in 1.3.1', Description of MMIX, from Chapter 1, Basic Concepts, of Fascicle 1, MMIX, by Donald Knuth 15 | 16 | [**`program_m_maximum.mms:`**](program_m_maximum.mms) annotated Program M in 1.3.1' Description of MMIX, from Chapter 1, Basic Concepts, of Fascicle 1, MMIX, by Donald Knuth 17 | 18 | [**`exercise_2.1.7.mms:`**](exercise_2.1.7.mms) exercise 2.1.7 in The MMIX Supplement 19 | 20 | [**`exercise_2.1.8.mms:`**](exercise_2.1.8.mms) exercise 2.1.8 in The MMIX Supplement 21 | 22 | [**`exercise_2.1.9.mms:`**](exercise_2.1.9.mms) exercise 2.1.9 in The MMIX Supplement 23 | 24 | [**`program_t_topological_sort.mms:`**](program_t_topological_sort.mms) annotated Program T (Topological sort) in 2.2.3 Linked Allocation, The MMIX Supplement, to accompany Chapter 2, Information Structures of TAOCP Volume 1, Fundamental Algorithms, by Donald Knuth 25 | 26 | [**`program_a_addition_of_polynomials.mms:`**](program_a_addition_of_polynomials.mms) annotated Program A (Addition of polynomials) in 2.2.4 Circular Lists, The MMIX Supplement, to accompany Chapter 2, Information Structures of TAOCP Volume 1, Fundamental Algorithms, by Donald Knuth 27 | 28 | [**`program_c_comparison_counting.mms:`**](program_c_comparison_counting.mms) annotated Program C (Comparison counting) in 5.2 Internal sorting, The MMIX Supplement, to accompany Chapter 5, Sorting of TAOCP Volume 3, Sorting and Searching, by Donald Knuth 29 | 30 | [**`program_s_straight_insertion_sort.mms:`**](program_s_straight_insertion_sort.mms) annotated Program S (Straight insertion sort) in 5.2.1 Sorting by Insertion, The MMIX Supplement, to accompany Chapter 5, Sorting of TAOCP Volume 3, Sorting and Searching, by Donald Knuth 31 | 32 | [**`program_d_shellsort.mms:`**](program_d_shellsort.mms) annotated Program D (Shellsort) in 5.2.1 Sorting by Insertion, The MMIX Supplement, to accompany Chapter 5, Sorting of TAOCP Volume 3, Sorting and Searching, by Donald Knuth 33 | 34 | [**`program_l_list_insertion.mms:`**](program_l_list_insertion.mms) annotated Program L (List insertion) in 5.2.1 Sorting by Insertion, The MMIX Supplement, to accompany Chapter 5, Sorting of TAOCP Volume 3, Sorting and Searching, by Donald Knuth 35 | 36 | [**`program_m_multiple_list_insertion.mms:`**](program_m_multiple_list_insertion.mms) annotated Program M (Multiple list insertion) in 5.2.1 Sorting by Insertion, The MMIX Supplement, to accompany Chapter 5, Sorting of TAOCP Volume 3, Sorting and Searching, by Donald Knuth 37 | 38 | [**`program_b_bubble_sort.mms:`**](program_b_bubble_sort.mms) annotated Program B (Bubble sort) in 5.2.2 Sorting by Exchanging, The MMIX Supplement, to accompany Chapter 5, Sorting of TAOCP Volume 3, Sorting and Searching, by Donald Knuth 39 | 40 | [**`exercise_5.2.2.12.mms:`**](exercise_5.2.2.12.mms) annotated solution to exercise 5.2.2.12 implementing Algorithm M (Merge exchange) in 5.2.2 Sorting by Exchanging, The MMIX Supplement, to accompany Chapter 5, Sorting of TAOCP Volume 3, Sorting and Searching, by Donald Knuth 41 | 42 | [**`program_s_straight_selection_sort.mms:`**](program_s_straight_selection_sort.mms) annotated Program S (Straight selection sort) in 5.2.3 Sorting by Selection, The MMIX Supplement, to accompany Chapter 5, Sorting of TAOCP Volume 3, Sorting and Searching, by Donald Knuth 43 | 44 | [**`exercise_5.2.2.12.mms:`**](exercise_5.2.2.12.mms) annotated solution to exercise 5.2.2.12 implementing Algorithm M (Merge exchange) in 5.2.2 Sorting by Exchanging, The MMIX Supplement, to accompany Chapter 5, Sorting of TAOCP Volume 3, Sorting and Searching, by Donald Knuth 45 | 46 | [**`program_s_straight_selection_sort.mms:`**](program_s_straight_selection_sort.mms) annotated Program S (Straight selection sort) in 5.2.3 Sorting by Selection, The MMIX Supplement, to accompany Chapter 5, Sorting of TAOCP Volume 3, Sorting and Searching, by Donald Knuth 47 | 48 | [**`inout.mms:`**](inout.mms) annotated program for decoding problem in 1.4.2' Coroutines, from Chapter 1, Basic Concepts, of Fascicle 1, MMIX, by Donald Knuth 49 | -------------------------------------------------------------------------------- /arithmetic.mms: -------------------------------------------------------------------------------- 1 | // arithmetic.mms 2 | // examples of arithmetic operators 3 | 4 | // to aid section Arithmetic operators in 1.3.1', Description of MMIX 5 | // from Chapter 1, Basic Concepts, of Fascicle 1, MMIX, by Donald Knuth 6 | 7 | // comment line can start with any non-alphannumeric character 8 | // but avoid using semicolon, poundsign or whitespace to start a comment line 9 | // there is no multiline comment block 10 | // trailing part of line after statement is ignored 11 | // so comments may be added following a space after a statement 12 | 13 | // program is loaded at address 0x0 by default 14 | // registers have $ prefix 15 | // hex values have # prefix 16 | // generally everything is case-sensitive 17 | 18 | // place some int32_t in memory 19 | 20 | // address 0x0: 21 | TETRA 3 22 | 23 | // address 0x4: 24 | TETRA 7 25 | 26 | // address 0x8: 27 | // -5 = ffff fffb 28 | TETRA #fffffffb 29 | 30 | // place some int64_t in memory 31 | 32 | // address 0x10: 33 | // largest signed number 2^63 - 1: 7fff ffff ffff ffff 34 | OCTA #7fffffffffffffff 35 | 36 | // address 0x18: 37 | // smallest signed number -2^63: 8000 0000 0000 0000 38 | OCTA #8000000000000000 39 | 40 | // a uint64_t 41 | 42 | // address 0x20: 43 | // largest unsigned number 2^64 - 1: ffff ffff ffff ffff 44 | OCTA #ffffffffffffffff 45 | 46 | // now the code 47 | 48 | // $0 is argc 49 | // $1 is address of argv, 0x4000 0000 0000 0008, second octa of Pool_Segment 50 | // $255 first GREG is numeric code for Main (offset of Main) 51 | // registers $2 $3 ... $254 are zero at start 52 | 53 | // this program can be run in the debugger without a commandline 54 | // $0=argc will be zero in this case 55 | // $1=argv will have an address of an empty array of strings 56 | 57 | // instructions are 4 bytes 58 | // no spaces after commas 59 | 60 | // address 0x20: 61 | // instruction 0: 62 | // symbol Main is required 63 | // $3 = 3 64 | Main LDT $3,$2,#0 65 | 66 | // instruction 1: 67 | // $4 = 7 68 | LDT $4,$2,#4 69 | 70 | // instruction 2: 71 | // $5 = -5 = ffff ffff ffff fffb 72 | LDT $5,$2,#8 73 | 74 | // instruction 3: 75 | // $6 = 2^63 - 1 = 7fff ffff ffff ffff 76 | LDO $6,$2,#10 77 | 78 | // instruction 4: 79 | // $7 = -2^63 = 8000 0000 0000 0000 80 | LDO $7,$2,#18 81 | 82 | // instruction 5: 83 | // $8 = 2^64 - 1 = ffff ffff ffff ffff 84 | LDOU $8,$2,#20 85 | 86 | // arithmetic operators 87 | 88 | // instruction 6: 89 | // $9 = 3 + 7 = 10 90 | ADD $9,$3,$4 91 | 92 | // instruction 7: 93 | // $10 = 3 + -5 = -2 94 | ADD $10,$3,$5 95 | 96 | // instruction 8: 97 | // overflow 98 | // $13 = (2^63 - 1) + 1 = -2^63 99 | // rA = 0x40 100 | ADD $11,$6,1 101 | 102 | // instruction 9: 103 | // clear arithmetic status register rA 104 | PUT rA,0 105 | 106 | // instruction 10: 107 | // immediates are unsigned bytes 108 | // $12 = 3 + 255 = 258 109 | ADD $12,$3,#ff 110 | 111 | // instruction 11: 112 | // $13 = 3 - 7 = -4 113 | SUB $13,$3,$4 114 | 115 | // instruction 12: 116 | // $14 = 3 - 1 = 2 117 | SUB $14,$3,1 118 | 119 | // instruction 13: 120 | // $15 = 3 * -5 = -15 121 | MUL $15,$3,$5 122 | 123 | // instruction 14: 124 | // overflow 125 | // $16 = (2^63 - 1) * 2 = -2 126 | // rA = 0x40 127 | MUL $16,$6,2 128 | 129 | // instruction 15: 130 | // clear arithmetic status register rA 131 | PUT rA,0 132 | 133 | // instruction 16: 134 | // rR remainder register 135 | // $17 = 7 / -5 = floor(-1.4) = -2 136 | // rR = -3 137 | DIV $17,$4,$5 138 | 139 | // instruction 17: 140 | // transfer value from rR to general register 141 | // $18 = -3 142 | GET $18,rR 143 | 144 | // instruction 18: 145 | // divide by zero sets rA integer divide check bit 146 | // $19 = 3 / 0 = 0 147 | // rR = 3 148 | // rA = 0x80 149 | DIV $19,$3,0 150 | 151 | // instruction 19: 152 | // clear arithmetic status register rA 153 | PUT rA,0 154 | 155 | // instruction 20: 156 | // treat -5 as unsigned 157 | // $20 = 7 + ...ffff fffb = 0111 + ...1111 1011 = ...0000 0010 = 2 158 | ADDU $20,$4,$5 159 | 160 | // instruction 21: 161 | // $21 = 3 - 7 = ...ffff fffc 162 | SUBU $21,$3,$4 163 | 164 | // instruction 22: 165 | // treat -5 as unsigned 166 | // sets rH himult register 167 | // $22 = 3 * -5u = low 8 bytes of product 168 | // rH = high 8 bytes of product = 2 169 | MULU $22,$3,$5 170 | 171 | // instruction 23: 172 | // treat -5 as unsigned 173 | // $23 = -5u / 3 = 0x5555 5555 5555 5553 174 | // rR = 2 175 | DIVU $23,$5,$3 176 | 177 | // instruction 24: 178 | // prepare 16-byte dividend from previous himult 179 | // rD = 2 180 | PUT rD,2 181 | 182 | // instruction 25: 183 | // form 16-byte dividend from previous himult 184 | // rD = 2 185 | // $24 = $22 / 3 = -5u 186 | DIVU $24,$22,$3 187 | 188 | // instruction 26: 189 | // clear high dividend register 190 | PUT rD,0 191 | 192 | // TODO 193 | 2ADDU $25,$4,$4 194 | 195 | 4ADDU $26,$3,1 196 | 197 | 8ADDU $27,$3,1 198 | 199 | 16ADDU $28,$3,1 200 | 201 | NEG $29,1,$5 202 | 203 | NEG $30,$5 204 | 205 | NEGU $31,4,$3 206 | 207 | NEGU $32,$3 208 | 209 | SL $33,$4,$3 210 | 211 | SLU $34,$4,$3 212 | 213 | SR $35,$4,$3 214 | 215 | SRU $36,$4,$3 216 | 217 | CMP $37,$3,$4 218 | 219 | CMPU $38,$3,$5 220 | 221 | // program exit 222 | TRAP 0,Halt,0 223 | -------------------------------------------------------------------------------- /data.dat: -------------------------------------------------------------------------------- 1 |  2 |  -------------------------------------------------------------------------------- /exercise_2.1.7.mms: -------------------------------------------------------------------------------- 1 | // exercise_2.1.7.mms 2 | 3 | // solution to exercise 2.1.7 from The MMIX Supplement 4 | 5 | // SUIT field offset is 9 6 | SUIT IS 9 7 | 8 | // LAMBDA or NULL is 0 9 | LAMBDA IS 0 10 | 11 | // temporary register for exercise choice a 12 | ta IS $0 13 | 14 | // temporary register for exercise choice b 15 | tb IS $1 16 | 17 | // temporary register for exercise choice c 18 | tc IS $2 19 | 20 | LOC Data_Segment 21 | // base address for assembler to determine address of TOP 22 | GREG @ 23 | 24 | // address of top of card pile 25 | TOP OCTA @+6*8 26 | 27 | // garbage 28 | OCTA -1 29 | 30 | // card pile as shown in figure (2) in section 2.1 of The MMIX Supplement 31 | // bottom of card pile 32 | // 10 of diamonds, face down 33 | OCTA LAMBDA 34 | BYTE #80,1,10," 10 D" 35 | 36 | // 3 of spades, face up 37 | OCTA @-2*8 38 | BYTE #00,4,3," 3 S" 39 | 40 | // top of card pile 41 | // 2 of diamonds, face up 42 | OCTA @-2*8 43 | BYTE #00,2,2," 2 D" 44 | 45 | LOC #100 46 | 47 | // a) wrong 48 | // LDA loads address of TOP into register ta 49 | // works because address of TOP is known to assembler via GREG 50 | Main LDA ta,TOP 51 | 52 | // ta+SUIT is offset to TOP which is some location right after TOP 53 | // byte is loaded from an address unrelated to card pile 54 | // ta = -1 55 | LDB ta,ta,SUIT 56 | 57 | // b) wrong 58 | // TOP and SUIT are known to assembler so TOP+SUIT is some location after TOP 59 | LDA tb,TOP+SUIT 60 | 61 | // again byte is loaded from address unrelated to card pile 62 | // tb = -1 63 | LDB tb,tb,0 64 | 65 | // c) right 66 | // octabyte at address of TOP i.e. CONTENTS(TOP) is loaded into register tc 67 | // this octabyte is the address of the top card 68 | LDOU tc,TOP 69 | 70 | // byte is loaded from tc+SUIT that is the value of the SUIT field 71 | // tc = 2 72 | LDB tc,tc,SUIT 73 | 74 | TRAP 0,Halt,0 75 | 76 | -------------------------------------------------------------------------------- /exercise_2.1.8.mms: -------------------------------------------------------------------------------- 1 | // exercise_2.1.8.mms 2 | 3 | // solution to exercise 2.1.8 from The MMIX Supplement 4 | 5 | // NEXT field offset is 0 6 | NEXT IS 0 7 | 8 | // LAMBDA or NULL is 0 9 | LAMBDA IS 0 10 | 11 | // register for card count variable N 12 | n IS $0 13 | 14 | // register for link variable X pointing to next card 15 | x IS $1 16 | 17 | LOC Data_Segment 18 | // base address for assembler to use for data symbols 19 | GREG @ 20 | 21 | // final answer 22 | N OCTA 0 23 | 24 | // address of top of card pile 25 | TOP OCTA @+6*8 26 | 27 | // garbage 28 | OCTA -1 29 | 30 | // card pile as shown in figure (2) in section 2.1 of The MMIX Supplement 31 | // bottom of card pile 32 | // 10 of diamonds, face down 33 | OCTA LAMBDA 34 | BYTE #80,1,10," 10 D" 35 | 36 | // 3 of spades, face up 37 | OCTA @-2*8 38 | BYTE #00,4,3," 3 S" 39 | 40 | // top of card pile 41 | // 2 of diamonds, face up 42 | OCTA @-2*8 43 | BYTE #00,2,2," 2 D" 44 | 45 | LOC #100 46 | 47 | // initialize card count 48 | Main SET n,0 B1: N <- 0 49 | 50 | // start with top of card pile 51 | LDOU x,TOP B1: X <- TOP 52 | 53 | // check for bottom of card pile 54 | 0H BZ x,Stop B2: Stop if X = LAMBDA 55 | 56 | // increment card count 57 | INCL n,1 B3: N <- N + 1 58 | 59 | // update link variable X 60 | LDOU x,x,NEXT B3: X <- NEXT(X) 61 | 62 | // loop back 63 | JMP 0B B3: To B2 64 | 65 | // write card pile size 66 | Stop STOU n,N N is number of cards in pile 67 | 68 | TRAP 0,Halt,0 69 | 70 | -------------------------------------------------------------------------------- /exercise_2.1.9.mms: -------------------------------------------------------------------------------- 1 | // exercise_2.1.9.mms 2 | 3 | // solution to exercise 2.1.9 from The MMIX Supplement 4 | 5 | // NEXT field offset is 0 6 | NEXT IS 0 7 | TAG IS 8 8 | TITLE IS 11 9 | 10 | // LAMBDA or NULL is 0 11 | LAMBDA IS 0 12 | TITLE_LEN IS 5 13 | 14 | // card 15 | x IS $0 16 | tag IS $1 17 | 18 | // temp 19 | t IS $255 20 | 21 | LOC Data_Segment 22 | // base address for data symbols 23 | GREG @ 24 | 25 | NL BYTE #0a,0 26 | LP BYTE '(',0 27 | RP BYTE ')',0 28 | 29 | Arg OCTA 0,TITLE_LEN 30 | // address of top of card pile 31 | TOP OCTA @+5*8 32 | 33 | // card pile as shown in figure (2) in section 2.1 of The MMIX Supplement 34 | // bottom of card pile 35 | // 10 of diamonds, face down 36 | OCTA LAMBDA 37 | BYTE #80,1,10," 10 D" 38 | 39 | // 3 of spades, face up 40 | OCTA @-2*8 41 | BYTE #00,4,3," 3 S" 42 | 43 | // top of card pile 44 | // 2 of diamonds, face up 45 | OCTA @-2*8 46 | BYTE #00,2,2," 2 D" 47 | 48 | LOC #100 49 | 50 | 51 | // PrintCards subroutine 52 | // check valid address of card 53 | PrintCards BNZ x,0F 54 | 55 | // return if null 56 | POP 0,0 57 | 58 | // get tag field 59 | 0H LDB tag,x,TAG 60 | 61 | // tag = 0 means face up 62 | BZ tag,1F 63 | // print leftparen for face down 64 | LDA t,LP 65 | TRAP 0,Fputs,StdOut 66 | 67 | // print title 68 | 1H LDA t,x,TITLE 69 | STOU t,Arg 70 | LDA t,Arg 71 | TRAP 0,Fwrite,StdOut 72 | 73 | // tag = 0 is face up 74 | BZ tag,2F 75 | // print rightparen for face down 76 | LDA t,RP 77 | TRAP 0,Fputs,StdOut 78 | 79 | // print newline 80 | 2H LDA t,NL 81 | TRAP 0,Fputs,StdOut 82 | 83 | // get next card 84 | LDOU x,x,NEXT 85 | // loop back 86 | JMP PrintCards 87 | 88 | // main 89 | // get top card 90 | Main LDOU $1,TOP 91 | 92 | // pass top card in register 1 to PrintCards 93 | PUSHJ 0,PrintCards 94 | 95 | TRAP 0,Halt,0 96 | 97 | -------------------------------------------------------------------------------- /exercise_5.2.2.12.mms: -------------------------------------------------------------------------------- 1 | // exercise_5.2.2.12.mms 2 | 3 | // Based on: 5.2.2/batcher.mms, 5.2.2/batcher.tst 4 | // Copyright: This file is part of the MMIX Supplement package 5 | // (c) Martin Ruckert 2014 6 | // Author: M.Ruckert, 16.5.2012 7 | 8 | // MMIX implementation of Algorithm M (Merge exchange sort) 9 | // 5.2.2 Sorting by Exchanging 10 | 11 | // algorithm has two phases, sort phase then merge phase 12 | // array is d-ordered for various d as powers of 2 in sort phase 13 | // eg array of 16 elements will be 8-ordered, 4-ordered and 2-ordered 14 | // at the end of sort phase 15 | // then all the d-ordered sequences are merged incrementally into 16 | // completely sorted array 17 | 18 | key IS $0 Array of Records (OCTAs) 19 | n IS $1 Number of Records 20 | 21 | // p is a power of 2 22 | p IS $2 23 | 24 | // q is also a power of 2 25 | q IS $3 26 | 27 | // i is scaled offset key in array 28 | i IS $4 29 | 30 | // k is lower key to possibly exchange 31 | k IS $5 32 | 33 | // kd is higher key compared for possible exchange with lower key 34 | kd IS $6 35 | 36 | // d is distance between pairs of key to compare 37 | d IS $7 38 | 39 | // keyn is address of end of array 40 | keyn IS $8 41 | 42 | // temporary register 43 | t IS $9 44 | 45 | // r is used to select ranges of keys to possibly exchange in one pass 46 | r IS $10 47 | 48 | // temporary register 49 | c IS $11 50 | 51 | // M1 [Initialize p] Set p <- 2^{t-1}, where t = ceil(lg N) is the least 52 | // integer such that 2^t >= N 53 | 54 | // 64-bit ieee 754 is sign_bit 11_exponent_E_bits 52_fraction_F_bits 55 | // number is 2^(E - 1023) * (1 + F / 2^52) 56 | // in other words 1023 is added to real exponent to get biased exponent E 57 | // example bits: 17 = sign: 0, E: _100 0000 0011, F: 0001 0000 ... 58 | // so E = 1027 and 2^(1027 - 1023) = 2^4 59 | // 2^4 * F / 2^52 = F / 2^48 = ... 0001 = 1 60 | // so number is 2^4 + 1 = 17 61 | 62 | // use floating point representation to get exponent of closest power of 2 63 | // t $9 is float n rounded up 64 | :Sort FLOTU t,ROUND_UP,n 01: M1 Initialize p 65 | 66 | // c $11 is ff f0, set high 3 bytes or 12 bits 67 | SETH c,#FFF0 02: 68 | 69 | // c $11 is 00 0f ff ff ff ff ff ff, set low 52 bits 70 | NOR c,c,c 03: 71 | 72 | // verify: add all 1's to fraction to allow any carry to bump exponent up 1 73 | ADDU t,t,c 04: Round N up to 2^t 74 | 75 | // shift right t $9 52 bits to remove fraction and only leave exponent in t 76 | SRU t,t,52 05: Extract t 77 | 78 | // low wyde of t $9 holds 11-bit exponent 79 | // 0x400 = 2^10 = 1024 = _100 0000 0000 80 | // ~0x400 = _011 1111 1111 81 | // t & ~0x400 clears bit 11 of t 82 | // following only works for numbers between 1024 and 2047 which is the 83 | // case for array size N > 1 84 | // clearing bit 11 means subtracting 1024 = 1023 + 1 85 | // which is good because we actually need to start with p = 2^(t - 1) below 86 | ANDNL t,#400 06: t <- ceil(lg N) - 1 87 | 88 | // keyn $8 is address of end of array 89 | 8ADDU keyn,n,key 07: keyn <- LOC(K_{N+1}) 90 | 91 | // initialize p $2 scaled by octabyte 92 | SET p,8 08: p <- 1 93 | 94 | // p $2 is 2^t scaled by octabyte 95 | SLU p,p,t 09: p <- p * 2^t 96 | 97 | // triple nested loop 98 | 99 | // outer loop on p $2 decreasing powers of 2 to 1 100 | 101 | // set scaling factor for q $3 102 | M2 SET q,8 10: M2 Initialize q, r, d 103 | 104 | // q $3 also initialized to 2^t same as p $2 105 | SL q,q,t 11: q <- 2^t 106 | 107 | // initialize r $10 to 0 for first pass 108 | SET r,0 12: r <- 0 109 | 110 | // initialize d $7 to address of entry at offset p $2 111 | ADDU d,p,key 13: d <- p 112 | 113 | // start first iteration of loop 114 | JMP M3 14: 115 | 116 | // middle loop on q $3 117 | 118 | // set d $7 to address of highest key to compare 119 | M5 ADDU d,key,d 15: M5 Loop on q 120 | 121 | // decrease q $3 to smaller power of 2 122 | SR q,q,1 16: q <- q / 2 123 | 124 | ANDNL q,7 17: q <- 8 * floor(q / 8) 125 | 126 | // set r $10 to p $2 127 | SET r,p 18: r <- p 128 | 129 | // initialize i $4 to offset of highest key to compare pairs of keys that 130 | // are d-apart 131 | M3 SUB i,keyn,d 19: M3 Loop on i, i <- N + 1 - d 132 | 133 | // start first iteration of loop over key pairs to compare in reverse 134 | JMP 0F 20: 135 | 136 | // inner loop over all pairs of keys that are d-apart in reverse 137 | 138 | // c $11 is i $4 & p $2 139 | 1H AND c,i,p 21: 140 | 141 | // compare c $11 to r $10, result 1, 0, -1 back in c $11 142 | CMP c,c,r 22: If i & p = r, 143 | 144 | // unpredictable branch to skip comparison of key pair if equality test fails 145 | BNZ c,0F 23: go to M4 146 | 147 | // fallthrough to compare pair of keys when equality test passes 148 | 149 | // k $5 has first (lower) key of pair to compare 150 | LDO k,key,i 24: M4 Compare/exchange 151 | 152 | // kd $6 has second (higher) key of pair to compare at distance d 153 | LDO kd,d,i 25: R_{i + 1} : R_{i + d + 1} 154 | 155 | // c $11 has result of comparing k $5 to kd $6: 1, 0, or -1 156 | CMP c,k,kd 26: 157 | 158 | // likely branch to skip exchange if lower key is not bigger 159 | PBNP c,0F 27: If K_{i + 1} > K_{i + d + 1}, 160 | 161 | // fallthrough to exchange pair of keys since lower key is bigger 162 | 163 | // write k $5 to key at i + d 164 | STO k,d,i 28: interchange R_{i + d + 1} 165 | 166 | // write kd $6 to to key at i 167 | STO kd,key,i 29: and R_{i + 1} 168 | 169 | // decrement i $4 to check next key 170 | 0H SUB i,i,8 30: i <- i - 1 171 | 172 | // likely branch to continue loop over keys to compare 173 | PBNN i,1B 31: 0 <= i < N - d 174 | 175 | // update d 176 | SUB d,q,p 32: M5 Loop on q, d <- q - p 177 | 178 | PBNZ d,M5 33: 179 | 180 | SR p,p,1 34: M6 Loop on p, p <- p / 2 181 | 182 | ANDNL p,7 35: p <- 8 * floor(p / 8) 183 | 184 | // likely branch to loop on p 185 | PBP p,M2 36: 186 | 187 | POP 0,0 37: 188 | 189 | 190 | // main program 191 | 192 | LOC Data_Segment 193 | 194 | GREG @ 195 | 196 | //Data OCTA 5,3,2,5,7,11,-3,2,99,5,0,2,2,2,3,3,4 197 | Data OCTA 503,87,512,61,908,170,897,275 198 | OCTA 653,426,154,509,612,677,765,703 199 | 200 | //Size IS 17 201 | Size IS 16 202 | 203 | //Sorted OCTA -3,0,2,2,2,2,2,3,3,3,4,5,5,5,7,11,99 204 | Sorted OCTA 61,87,154,170,275,426,503,509 205 | OCTA 512,612,653,677,703,765,897,908 206 | 207 | LOC #100 208 | 209 | Main LDA $1,Data 210 | SET $2,Size 211 | PUSHJ 0,Sort 212 | 213 | SET $255,0 214 | TRAP 0,Halt,0 215 | 216 | -------------------------------------------------------------------------------- /inout.mms: -------------------------------------------------------------------------------- 1 | // inout.mms 2 | 3 | // Example from 1.4.2' Coroutines, Fascicle 1, MMIX, by Donald Knuth 4 | // Based on https://github.com/theartofcomputerprogramming/mmixware/blob/githubmaster/inout.mms 5 | // Reformatted with comments from Fascicle 1 6 | // sample input on stdin: a2b5e3426fg0zyw3210pq89r. 7 | // sample output on stdout: abb bee eee e44 446 66f gzy w22 220 0pq 999 999 999 r. 8 | 9 | // * Coroutine example for 1.4.2 10 | 11 | // 01: * An example of coroutines 12 | // $255 is already available as the first global register 13 | t IS $255 02: Temporary data of short duration 14 | 15 | // in is $254 16 | in GREG // 03: Address for resuming the first coroutine 17 | 18 | // out is $253 19 | out GREG // 04: Address for resuming the second coroutine 20 | 21 | // 05: * Input and output buffers 22 | LOC Data_Segment 06: 23 | // $252 holds base address for output and input buffer symbols 24 | GREG @ 07: Base address 25 | // OutBuf is output buffer of tetras initialized to 15 spaces, newline, terminating null 26 | // each character of the string is separate tetra 27 | // tetra is used for convenient compact initialization 28 | // because output is groups of 3 characters separated by space 29 | // so plan is for each tetra to hold one group 30 | // the three output characters will go into high bytes 0, 1, 2 of tetra 31 | OutBuf TETRA " ",#a,0 08: (see exercise 3) 32 | 33 | Period BYTE '.' 09: 34 | // two arguments for Fgets, input destination buffer, bytes to read 35 | InArgs OCTA InBuf,1000 10: 36 | // InBuf is input buffer filled by Fgets 37 | // InBuf is address of this location which is two octas past OutBuf and period 38 | // note this location is still in data segment but the next assembler directive 39 | // changes the location to 0x100 which is where code usually starts out in the text segment 40 | // InBuf is last address used in data segment so there's more than enough bytes 41 | // available for 1000-byte buffer 42 | InBuf LOC #100 11: 43 | 44 | // 12: * Subroutine for character input 45 | // inptr is $251 46 | inptr GREG // 13: (the current input position) 47 | 1H LDA t,InArgs 14: Fill the input buffer 48 | TRAP 0,Fgets,StdIn 15: 49 | LDA inptr,InBuf 16: Start at beginning of buffer 50 | // $250 is period character 51 | // why not Period IS '.'? because CSN does not take constants? 52 | 0H GREG Period 17: 53 | 54 | // set input buffer pointer to address of Period constant if Fgets returned error 55 | // period signals end of input 56 | // so NextChar always returns valid character 57 | CSN inptr,t,0B 18: If error occurred, read a '.' 58 | 59 | // NextChar subroutine called with PUSHJ, has own local registers on register stack 60 | // $0 has new byte 61 | NextChar LDBU $0,inptr,0 19: Fetch the next character 62 | // increment position in input buffer 63 | INCL inptr,1 20: 64 | // terminating null 65 | // refill buffer 66 | // works first time because all memory is zeroed out at startup except for assembled program 67 | BZ $0,1B 21: Branch if at end of buffer 68 | CMPU t,$0,' ' 22: 69 | // skip whitespace 70 | BNP t,NextChar 23: Branch if character is whitespace 71 | // return one character in $0 72 | POP 1,0 24: Return to caller 73 | 74 | // 25: * First coroutine 75 | // In1 is the input parsing coroutine 76 | // count is $249 77 | count GREG // 26: (the repetition counter) 78 | 79 | 1H GO in,out,0 27: Send a character to the Out coroutine 80 | 81 | In1 PUSHJ $0,NextChar 28: Get a new character 82 | CMPU t,$0,'9' 29: 83 | // nondigit 84 | PBP t,1B 30: Branch if it exceeds '9' 85 | SUB count,$0,'0' 31: 86 | // nondigithttps://research.swtch.com/duff 87 | BN count,1B 32: Branch if it is less than '0' 88 | 89 | // now count $249 has digit 90 | // next character must be repeated 91 | // call NextChar subroutine, saving no registers, return value will be in $0 92 | PUSHJ $0,NextChar 33: Get another character 93 | 94 | // switch to return location in Out1 coroutine, address of next instruction to return to is placed in in $254 95 | // return value character is in $0 96 | 1H GO in,out,0 34: Send it to Out 97 | 98 | // In2 99 | SUB count,count,1 35: Decrease the repetition counter 100 | PBNN count,1B 36: Repeat if necessary 101 | // loop back to repeatedly pass character to Out1 coroutine 102 | JMP In1 37: Otherwise begin a new cycle 103 | 104 | // 38: * Second coroutine 105 | // Out1 is the output formatting coroutine 106 | // outptr $248 is current output position 107 | outptr GREG // 39: (the current output position) 108 | 109 | 1H LDA t,OutBuf 40: Empty the output buffer 110 | TRAP 0,Fputs,StdOut 41: 111 | 112 | Out1 LDA outptr,OutBuf 42: Start at beginning of buffer 113 | 114 | // switch to return location in In1 coroutine kept in in $254 115 | // address of next instruction to return to here is placed in out $253 116 | 2H GO out,in,0 43: Get a new character from In 117 | 118 | // Out2 119 | // $0 has character returned by In1 120 | STBU $0,outptr,0 44: Store it as the first of three 121 | CMP t,$0,'.' 45: 122 | 123 | BZ t,1F 46: Branch if it was '.' 124 | 125 | GO out,in,0 47: Otherwise get another character 126 | 127 | // Out3 128 | STBU $0,outptr,1 48: Store it as the second of three 129 | CMP t,$0,'.' 49: Branch if it was '.' 130 | BZ t,2F 50: Otherwise get another character 131 | 132 | GO out,in,0 51: Store it as the third of three 133 | 134 | // Out4 135 | STBU $0,outptr,2 52: 136 | CMP t,$0,'.' 53: Branch if it was '.' 137 | BZ t,3F 54: 138 | INCL outptr,4 55: Otherwise advance to next group 139 | // $247 140 | 0H GREG OutBuf+4*16 56: 141 | CMP t,outptr,0B 57: 142 | PBNZ t,2B 58: Branch if fewer than 16 groups 143 | 144 | JMP 1B 59: Otherwise finish the line 145 | 146 | 3H INCL outptr,1 60: Move past a stored character 147 | 2H INCL outptr,1 61: Move past a stored character 148 | // $246 is newline 149 | 0H GREG #a 62: (newline character) 150 | 1H STBU 0B,outptr,1 63: Store newline after period 151 | // $245 is null 152 | 0H GREG 0 64: (null character) 153 | STBU 0B,outptr,2 65: Store null after newline 154 | 155 | LDA t,OutBuf 66: 156 | TRAP 0,Fputs,StdOut 67: Output the final line 157 | TRAP 0,Halt,0 68: Terminate the program 158 | 159 | // 69: * Initialization 160 | Main LDA inptr,InBuf 70: Initialize NextChar 161 | GETA in,In1 71: Initialize In 162 | JMP Out1 72: Start with Out (see exercise 2) 163 | -------------------------------------------------------------------------------- /loadstore.mms: -------------------------------------------------------------------------------- 1 | // loadstore.mms 2 | // examples of load and store instructions 3 | 4 | // to aid section Loading and storing in 1.3.1', Description of MMIX, from Chapter 1, Basic Concepts, of Fascicle 1, MMIX, by Donald Knuth 5 | 6 | // comment line can start with any non-alphanumeric character 7 | // but avoid using semicolon, poundsign or whitespace to start a comment line 8 | // there is no multiline comment block 9 | // trailing part of line after statement is ignored 10 | // so comments may be added following a space after a statement 11 | 12 | // program is loaded at address 0x0 by default 13 | // registers have $ prefix 14 | // hex values have # prefix 15 | // generally everything is case-sensitive 16 | 17 | // assembler storage directives 18 | // tell assembler to place this data in memory 19 | 20 | // address 0x0: 21 | BYTE #12 22 | 23 | // address 0x1: 24 | // high bit set 25 | BYTE #83 26 | 27 | // address 0x2: 28 | // high bit set 29 | BYTE #a7 30 | 31 | // address 0x4: 32 | // address 0x3 skipped for 2-byte alignment 33 | WYDE #8765 34 | 35 | // address 0x8: 87 65 43 21 36 | // addresses 0x6 - 0x7 skipped for 4-byte alignment 37 | TETRA #87654321 38 | 39 | // address 0x10: fe dc ba 98 76 54 32 10 40 | // addresses 0xc - 0xf skipped for 8-byte alignment 41 | OCTA #fedcba9876543210 42 | 43 | // Data_Segment address needed to demonstrate store instructions 44 | // address 0x18: 0x2000 0000 0000 0000 45 | OCTA #2000000000000000 46 | 47 | // now the code 48 | 49 | // $0 is argc 50 | // $1 is address of argv, 0x4000 0000 0000 0008, second octa of Pool_Segment 51 | // $255 first GREG is numeric code for Main (offset of Main) 52 | // registers $2 $3 ... $254 are zero at start 53 | 54 | // this program can be run in the debugger without a commandline 55 | // $0=argc will be zero in this case 56 | // $1=argv will have an address of an empty array of strings 57 | 58 | // instructions are 4 bytes 59 | // no spaces after commas 60 | 61 | // load bytes 62 | 63 | // address 0x20: 64 | // instruction 0: 65 | // use $2 as base address, $3 as offset 66 | // symbol Main is required 67 | Main LDB $4,$2,$3 trailing comment: program starts at Main 68 | 69 | // address 0x24: 70 | // instruction 1: 71 | // third operand is immediate instead of register 72 | // loads same byte as instruction 0 73 | LDB $5,$2,#0 74 | 75 | // instruction 2: 76 | // sign extension 77 | LDB $6,$2,#1 78 | 79 | // instruction 3: 80 | // unsigned load, no sign extension 81 | LDBU $7,$2,#2 82 | 83 | // load wydes 84 | 85 | // instruction 4: 86 | LDW $8,$2,#0 87 | 88 | // instruction 5: 89 | // different address but loads same wyde as previous instruction 90 | LDW $9,$2,#1 91 | 92 | // instruction 6: 93 | // sign extension 94 | LDW $10,$2,#2 95 | 96 | // instruction 7: 97 | // different address into same wyde as above 98 | // no sign extension for unsigned load 99 | LDWU $11,$2,#3 100 | 101 | // load tetras 102 | 103 | // instruction 8: 104 | // sign extension 105 | LDT $12,$2,#8 106 | 107 | // instruction 9: 108 | // different address into same tetra as before, also sign extension 109 | LDT $13,$2,#a 110 | 111 | // instruction 10: 112 | // unsigned load of same tetra as before, no sign extension 113 | LDTU $14,$2,#b 114 | 115 | // instruction 11: 116 | // load high tetra, loads tetra into upper half of register 117 | // useful to detect overflow in arithmetic with tetras 118 | LDHT $15,$2,#8 119 | 120 | // load octas 121 | 122 | // instruction 12: 123 | // no sign extension possible when loading octa 124 | LDO $16,$2,#10 125 | 126 | // instruction 13: 127 | // different address into same octa as above 128 | LDO $17,$2,#17 129 | 130 | // instruction 14: 131 | // unsigned load of same octa addressed in middle 132 | LDOU $18,$2,#14 133 | 134 | // instruction 15: 135 | // load Data_Segment address where data can be written: 0x2000 0000 0000 0000 136 | LDOU $3,$2,#18 137 | 138 | // instruction 16: 139 | // form new address from base and offset 140 | LDA $19,$3,#20 141 | 142 | // use $3 as base address for store instructions 143 | // writable data storage starts at Data_Segment 0x2000 0000 0000 0000 144 | // now store values to memory that were previously loaded into registers 145 | 146 | // store bytes 147 | 148 | // instruction 17: 149 | STB $4,$3,#0 150 | 151 | // instruction 18: 152 | STB $5,$3,#1 153 | 154 | // instruction 19: 155 | // no integer overflow, $6 is 0x...ff83 = -125 is in byte range -128..127 156 | STB $6,$3,#2 157 | 158 | // instruction 20: 159 | // this causes overflow because $9 is 0x1283, obviously greater than 127 160 | // sets rA overflow bit 0x40 161 | STB $9,$3,#3 162 | 163 | // instruction 21: 164 | // clear arithmetic status register rA 165 | PUT rA,#0 166 | 167 | // instruction 22: 168 | // unsigned store never overflows 169 | STBU $12,$3,#4 170 | 171 | // instruction 23: 172 | // sets rA overflow bit again 173 | STB $10,$3,#5 174 | 175 | // instruction 24: 176 | // clear arithmetic status register rA 177 | PUT rA,#0 178 | 179 | // store wydes 180 | 181 | // instruction 25: 182 | // no overflow, $6 has 0x...ff83 = -125 in wyde range -32768..32767 183 | STW $6,$3,#6 184 | 185 | // instruction 26: 186 | // overflow 187 | STW $12,$3,#9 188 | 189 | // instruction 27: 190 | // clear arithmetic status register rA 191 | PUT rA,#0 192 | 193 | // instruction 28: 194 | // unsigned store, no overflow 195 | STWU $13,$3,#b 196 | 197 | // store tetras 198 | 199 | // instruction 29: 200 | // overflow 201 | STT $15,$3,#c 202 | 203 | // instruction 30: 204 | // clear arithmetic status register rA 205 | PUT rA,#0 206 | 207 | // instruction 31: 208 | // unsigned tetra store, no overflow 209 | STTU $16,$3,#10 210 | 211 | // store octas 212 | 213 | // instruction 32: 214 | // no overflow for signed or unsigned octa stores 215 | STO $16,$19,#0 216 | 217 | // instruction 33: 218 | // no overflow for signed or unsigned octa stores 219 | STOU $16,$19,#a 220 | 221 | // instruction 34: 222 | // store 1-byte constant into octa 223 | STCO #ff,$19,#11 224 | 225 | // instruction 35: 226 | // store high tetra into octa 227 | STHT $15,$19,#18 228 | 229 | // program exit 230 | TRAP 0,Halt,0 231 | -------------------------------------------------------------------------------- /program_a_addition_of_polynomials.mms: -------------------------------------------------------------------------------- 1 | // program_a_addition_of_polynomials.mms 2 | 3 | // Program A (addition of polynomials), 2.2.4 Circular Lists 4 | // The MMIX Supplement, Martin Ruckert 5 | 6 | // Copyright: This file is part of the MMIX Supplement package 7 | // (c) Martin Ruckert 2014 8 | 9 | // node layout little different from p.25 10 | // 3 octas = 3*8 = 24 bytes 11 | // S is sign bit, A, B, C, D are exponents of x, y, z, w 12 | // 7 6 5 4 3 2 1 0 13 | // --- --- --- --- --- --- --- --- 14 | // | LINK | 15 | // --- --- --- --- --- --- --- --- 16 | // |S| A | B | C | D | 17 | // --- --- --- --- --- --- --- --- 18 | // | COEF | 19 | // --- --- --- --- --- --- --- --- 20 | 21 | LOC Data_Segment 22 | // base address register 23 | GREG @ 24 | 25 | // address: 0x2000 0000 0000 0000 26 | // polynomial P: 5xy + 7x + 7yz + 20 27 | // 1F is address of local symbol 1H in forward direction 28 | // P points to special node at end of polynomial with ABC = -1 29 | P OCTA 1F,-1,0 30 | 31 | // first term of polynomial: 5xy 32 | 1H OCTA 1F; WYDE 1,1,0,0; OCTA 5 33 | 34 | // second term of polynomial: 7x 35 | 1H OCTA 1F; WYDE 1,0,0,0; OCTA 7 36 | 37 | // third term of polynomial: 7yz 38 | 1H OCTA 1F; WYDE 0,1,1,0; OCTA 7 39 | 40 | // fourth and last term of polynomial: 20 41 | // links to special node 42 | 1H OCTA P; WYDE 0,0,0,0; OCTA 20 43 | 44 | // address: 0x2000 0000 0000 0078 45 | // polynomial Q: 3xy - 7yz + 3y - 20 46 | Q OCTA 1F,-1,0 47 | 1H OCTA 1F; WYDE 1,1,0,0; OCTA 3 48 | 1H OCTA 1F; WYDE 0,1,1,0; OCTA -7 49 | 1H OCTA 1F; WYDE 0,1,0,0; OCTA 3 50 | 1H OCTA Q; WYDE 0,0,0,0; OCTA -20 51 | 52 | // address: 0x2000 0000 0000 00f0 53 | // result verification polynomial S: 8xy + 7x + 3y 54 | S OCTA 1F,-1,0 55 | 1H OCTA 1F; WYDE 1,1,0,0; OCTA 8 56 | 1H OCTA 1F; WYDE 1,0,0,0; OCTA 7 57 | 1H OCTA S; WYDE 0,1,0,0; OCTA 3 58 | 59 | // base address register 60 | GREG @ 61 | 62 | // address: 0x2000 0000 0000 0150 63 | // storage pool nodes 64 | Free OCTA 1F,0,0 65 | 1H OCTA 1F,0,0 66 | 1H OCTA 1F,0,0 67 | 1H OCTA 1F,0,0 68 | 1H OCTA 1F,0,0 69 | 1H OCTA 1F,0,0 70 | 1H OCTA 1F,0,0 71 | 1H OCTA 1F,0,0 72 | 1H OCTA 1F,0,0 73 | 1H OCTA 1F,0,0 74 | 1H OCTA 1F,0,0 75 | 1H OCTA 1F,0,0 76 | 1H OCTA 1F,0,0 77 | 1H OCTA 1F,0,0 78 | 1H OCTA 1F,0,0 79 | 1H OCTA 1F,0,0 80 | 1H OCTA 1F,0,0 81 | 1H OCTA 1F,0,0 82 | 1H OCTA 1F,0,0 83 | 1H OCTA 1F,0,0 84 | 1H OCTA 1F,0,0 85 | 1H OCTA 1F,0,0 86 | 1H OCTA 1F,0,0 87 | 1H OCTA 1F,0,0 88 | 1H OCTA 1F,0,0 89 | 1H OCTA 1F,0,0 90 | 1H OCTA 1F,0,0 91 | 1H OCTA 1F,0,0 92 | 1H OCTA 0,0,0 93 | 94 | // for global storage pool base address 95 | avail GREG 0 96 | 97 | 98 | PREFIX :Add: 99 | 100 | COEF IS 16 Definition of coefficient field 101 | ABC IS 8 Definition of ABC exponent field 102 | LINK IS 0 Definition of link field 103 | 104 | p IS $0 105 | q IS $1 106 | 107 | q1 IS $2 Q1 108 | q2 IS $3 109 | abcp IS $4 ABC(P) 110 | coefp IS $5 coefficient P 111 | coefq IS $6 coefficient Q 112 | t IS $7 temporary variable t 113 | 114 | LOC #100 115 | 116 | // add polynomials P and Q passed in registers $0 and $1 117 | // Q is modified in place to contain sum 118 | :Add SET q1,q 01: A1 Initialize Q1 <- Q 119 | 120 | LDOU q,q,LINK 02: Q <- LINK(Q) 121 | 0H LDOU p,p,LINK 03: P <- LINK(P) 122 | 123 | LDO coefp,p,COEF 04: coefp <- COEF(P) 124 | 125 | LDO abcp,p,ABC 05: A2 ABC(P): ABC(Q) 126 | 127 | // main loop 128 | 2H LDO t,q,ABC 06: t <- ABC(Q) 129 | 130 | CMP t,abcp,t 07: Compare ABC(P) and ABC(Q) 131 | 132 | // zero means terms must be summed 133 | BZ t,A3 08: If equal, go to A3 134 | 135 | // P term bigger than Q term means term must be inserted 136 | BP t,A5 09: If greater, go to A5 137 | 138 | // P term less than Q term means go to next term of Q 139 | SET q1,q 10: If less, set Q1 <- Q 140 | 141 | LDOU q,q,LINK 11: Q <- LINK(Q) 142 | 143 | JMP 2B 12: Repeat 144 | 145 | // negative exponent means at end of P and done 146 | A3 BN abcp,6F 13: A3 Add coefficients 147 | 148 | // add two terms 149 | LDO coefq,q,COEF 14: coefq <- COEF(Q) 150 | 151 | ADD coefq,coefq,coefp 15: coefq <- coefq + coefp 152 | 153 | STO coefq,q,COEF 16: COEF(Q) <- COEF(Q) + COEF(P) 154 | 155 | PBNZ coefq,:Add 17: Jump if nonzero 156 | 157 | // sum is zero 158 | SET q2,q 18: A4 Delete zero term Q2 <- Q 159 | 160 | LDOU q,q,LINK 19: Q <- LINK(Q) 161 | 162 | STOU q,q1,LINK 20: LINK(Q1) <- Q 163 | 164 | // return node to avail pool 165 | STOU :avail,q2,LINK 21: 166 | 167 | SET :avail,q2 22: AVAIL <= Q2 168 | 169 | // go to next term of P 170 | JMP 0B 23: Go to advance P 171 | 172 | // insert new term 173 | A5 SET q2,:avail 24: A5 Insert new term 174 | 175 | // get new node from avail pool 176 | LDOU :avail,:avail,LINK 25: Q2 <= AVAIL 177 | 178 | STO coefp,q2,COEF 26: COEF(Q2) <- COEF(P) 179 | 180 | STOU abcp,q2,ABC 27: ABC(Q2) <- ABC(P) 181 | 182 | STOU q,q2,LINK 28: LINK(Q2) <- Q 183 | 184 | STOU q2,q1,LINK 29: LINK(Q1) <- Q2 185 | 186 | SET q1,q2 30: Q1 <- Q2 187 | 188 | // go to next term of P 189 | JMP 0B 31: Go to advance P 190 | 191 | 6H POP 0,0 32: Return from subroutine 192 | 193 | PREFIX : 194 | 195 | 196 | // initialize storage pool 197 | Main LDA avail,Free 198 | LDA $1,P 199 | LDA $2,Q 200 | PUSHJ $0,Add 201 | 202 | LDA $1,S 203 | LDA $2,Q 204 | PUSHJ $0,Compare 205 | BNZ $0,Fail 206 | 207 | SET $255,0 208 | TRAP 0,Halt,0 209 | 210 | Fail SET $255,1 211 | 212 | // halt 213 | TRAP 0,Halt,0 214 | 215 | PREFIX :CompPoly 216 | COEF IS 16 Definition of coefficient field 217 | ABC IS 8 Definition of ABC exponent field 218 | LINK IS 0 Definition of link field 219 | 220 | S IS $0 221 | Q IS $1 222 | xS IS $2 223 | xQ IS $3 224 | cS IS $4 225 | cQ IS $5 226 | t IS $6 227 | 228 | :Compare LDOU S,S,LINK 229 | LDOU Q,Q,LINK 230 | LDO xS,S,ABC 231 | LDO xQ,Q,ABC 232 | CMP t,xS,xQ 233 | BNZ t,Fail 234 | LDO cS,S,COEF 235 | LDO cQ,Q,COEF 236 | CMP t,cS,cQ 237 | BNZ t,Fail 238 | BNN xQ,:Compare 239 | POP 0,0 240 | 241 | Fail SET $0,1 242 | POP 1,0 243 | 244 | PREFIX : 245 | 246 | -------------------------------------------------------------------------------- /program_b_bubble_sort.mms: -------------------------------------------------------------------------------- 1 | // program_b_bubble_sort.mms 2 | 3 | // Based on: 5.2.2/bubble.mms, 5.2.2/bubble.tst 4 | // Copyright: This file is part of the MMIX Supplement package 5 | // (c) Martin Ruckert 2014 6 | // Author: M.Ruckert, 26.3.2012 7 | 8 | // idea is to keep bubbling a bigger key up till it cannot go higher 9 | // so biggest key will reach the top in first pass and other keys will 10 | // move above smaller adjacent keys 11 | // after each pass we track the point above which all the biggest keys 12 | // are in order - this point is called BOUND 13 | // then bubbling needs to take place only upto BOUND 14 | // initialize BOUND to the last index of the array 15 | // the upper half of the array grows to contain the biggest keys in order 16 | 17 | 18 | key IS $0 Array of Records (OCTAs) 19 | n IS $1 Number of Records 20 | 21 | keyb IS $1 Reusing register for n 22 | j IS $2 scaled 23 | kj IS $3 24 | kjj IS $4 25 | t IS $5 scaled 26 | c IS $6 27 | 28 | // Sort params 29 | // key $0 address of data array 30 | // n $1 size of array 31 | 32 | // get index of last key 33 | :Sort SUB n,n,1 01: B1 Initialize BOUND 34 | 35 | // set keyb $1 to address of last key 36 | // 8ADDU is 8*n + key 37 | 8ADDU keyb,n,key 02: BOUND <- N 38 | 39 | // start first iteration of nested loop 40 | JMP B2 03: 41 | 42 | // nested loop 43 | 44 | // outer loop till no bubbling occurs in inner loop 45 | // inner loop on j $2 to bubble keys from start of array upto BOUND 46 | 47 | // j $2 is negative scaled offset from address of BOUND entry 48 | 49 | // kj $3 is new key to bubble 50 | B3 LDO kj,keyb,j 04: B3 Compare/exchange R_j : R_{j+1} 51 | 52 | // increment j $2 to use as offset to adjacent higher index 53 | B3A ADD j,j,8 05: j <- j + 1 54 | 55 | // kjj $4 is adjacent key to compare bubble key to 56 | LDO kjj,keyb,j 06: kjj <- K_{j+1} 57 | 58 | // compare bubble key kj $3 to adjacent key kjj $4: result 1, 0, -1 in c $6 59 | CMP c,kj,kjj 07: K_j > K_{j+1} 60 | 61 | // unpredictable branch for bubble key kj $3 at most adjacent key kjj $4 62 | BNP c,0F 08: If K_j > K_{j+1}, 63 | 64 | // bubble key kj $3 is bigger than adjacent key kjj $4, swap them 65 | // write bubble key to higher index 66 | STO kj,keyb,j 09: interchange R_j <-> R_{j+1} 67 | 68 | // decrement j $2 to get lower index t $5 69 | SUB t,j,8 10: t <- j 70 | 71 | // write adjacent key to lower index 72 | STO kjj,keyb,t 11: K_j <- K_{j+1} 73 | 74 | // likely branch to inner loop over remaining keys upto BOUND index 75 | PBN j,B3A 12: 76 | 77 | // reached BOUND index, continue with another pass over first half of array 78 | JMP 1F 13: To B4 (but skip test for termination) 79 | 80 | // bubble key was smaller 81 | // make adjacent key the bubble key 82 | 0H SET kj,kjj 14: kj <- K_j 83 | 84 | // likely branch to inner loop to continue comparing new bubble key 85 | // j $2 negative means it has not yet reached BOUND 86 | PBN j,B3A 15: 87 | 88 | // fallthrough on completion of one full inner loop when j $2 reaches BOUND 89 | 90 | // t $5 zero means no key bubbled up 91 | // branch to terminate nested loop 92 | B4 BZ t,9F 16: B4 Any exchanges? 93 | 94 | // fallthrough on nonzero t $5 so some key bubbled up 95 | // update BOUND keyb $1 to last index used for bubbling swap 96 | 1H ADD keyb,keyb,t 17: BOUND <- t 97 | 98 | // initialize last bubble swap index tracker to 0 99 | B2 SET t,0 18: B2. Loop on j, t <- 0 100 | 101 | // initialize j $2 to negative offset from BOUND entry keyb $1 to 102 | // start of array 103 | SUB j,key,keyb 19: j <- 1 104 | 105 | // likely branch to inner loop over initial portion of array to begin 106 | // bubbling 107 | PBN j,B3 20: 1 <= j < BOUND 108 | 109 | // return, no return value 110 | 9H POP 0,0 21: 111 | 112 | 113 | // data segment 114 | LOC Data_Segment 115 | 116 | // global base address register $254 for address of data array 117 | GREG @ 118 | Data OCTA 503,87,512,61,908,170,897,275 119 | OCTA 653,426,154,509,612,677,765,703 120 | 121 | Size IS 16 122 | 123 | // global base address register $253 for address of sorted array 124 | GREG @ 125 | Sorted OCTA 61,87,154,170,275,426,503,509 126 | OCTA 512,612,653,677,703,765,897,908 127 | 128 | // code segment 129 | LOC #100 130 | 131 | // main program 132 | 133 | // pass address of array of unsorted keys to $0 134 | Main LDA $1,Data 135 | 136 | // pass size of array to $1 137 | SET $2,Size 138 | 139 | // call Sort with 2 parameters, no saved registers 140 | PUSHJ 0,Sort 141 | 142 | SET $255,0 143 | 144 | TRAP 0,Halt,0 145 | 146 | -------------------------------------------------------------------------------- /program_c_comparison_counting.mms: -------------------------------------------------------------------------------- 1 | // program_c_comparison_counting.mms 2 | 3 | // Copyright: This file is part of the MMIX Supplement package 4 | // (c) Martin Ruckert 2014 5 | // Authors: Martin Ruckert, Kenneth Laskoski 6 | 7 | PREFIX :Sort: 8 | 9 | k IS $0 Parameter 10 | 11 | // count register holds COUNT[i] 12 | count IS $1 13 | n IS $2 14 | 15 | i IS $3 Local variables 16 | j IS $4 17 | ki IS $5 18 | kj IS $6 19 | cj IS $7 20 | ci IS $8 21 | t IS $9 22 | 23 | // Sort function parameters 24 | // $0: address of array to sort 25 | // $1: address of array of counts 26 | // $2: number of elements to sort 27 | 28 | // loop to initialize COUNT array to 0 29 | // initialize i in $3 to n in $2 scaled to octa for reverse iteration 30 | :Sort SL i,n,3 01: C1 Clear COUNTs 31 | 32 | // start first iteration of loop 33 | JMP 0F 02: 34 | 35 | // store constant 0 36 | 1H STCO 0,count,i 03: COUNT[i] <- 0 37 | 38 | // decrement i in $3 scaled to octa 39 | 0H SUB i,i,8 04: 40 | 41 | // branch likely taken because linear scan 42 | PBNN i,1B 05: N > i >= 0 43 | 44 | // sort nested loop 45 | // initialize i in $3 to n in $2 scaled to octa 46 | SL i,n,3 06: C2 Loop on i 47 | 48 | // start first iteration of loop 49 | JMP 1F 07: 50 | 51 | // outer loop on i 52 | // get COUNT[i] into register $8 53 | 2H LDO ci,count,i 08: 54 | 55 | // get K[i] into register $5 56 | LDO ki,k,i 09: 57 | 58 | // inner loop on j 59 | // get K[j] into register $6 60 | 3H LDO kj,k,j 10: 61 | 62 | // compare K[i] and K[j] with result in $9 63 | CMP t,ki,kj 11: C4 Compare K_i:K_j 64 | 65 | // branch if $9 >= 0 66 | PBNN t,4F 12: Jump if K_i >= K_j 67 | 68 | // increment COUNT[j] because K[j] > K[i] 69 | // get COUNT[j] into register $7 70 | LDO cj,count,j 13: COUNT[j] 71 | 72 | // increment $7 scaled to octa 73 | ADD cj,cj,8 14: + 1 74 | 75 | // write incremented value to COUNT[j] 76 | STO cj,count,j 15: -> COUNT[j] 77 | 78 | // continue loop 79 | JMP 5F 16: 80 | 81 | // increment COUNT[i] because K[i] >= K[j] 82 | 4H ADD ci,ci,8 17: COUNT[i] <- COUNT[i] + 1 83 | 84 | // decrement j in $4 scaled by octa 85 | 5H SUB j,j,8 18: C3 Loop on j 86 | 87 | // back to start of inner loop 88 | // branch likely taken as array traversed sequentially 89 | PBNN j,3B 19: 90 | 91 | STO ci,count,i 20: 92 | 93 | // decrement i in $3 scaled to octa 94 | 1H SUB i,i,8 21: 95 | 96 | // initialize j in $4 to i-1 scaled to octa for reverse iteration 97 | SUB j,i,8 22: N > i > j >= 0 98 | 99 | // back to start of outer loop 100 | // branch likely taken as array traversed sequentially 101 | PBNN j,2B 23: 102 | 103 | // no return value 104 | POP 0,0 105 | 106 | PREFIX : 107 | 108 | LOC Data_Segment 109 | // base address register for input array 110 | GREG @ 111 | 112 | Data OCTA 5,-2,-3,6 113 | 114 | Size IS 4 115 | 116 | // base address register 117 | GREG @ 118 | Count OCTA 0 119 | 120 | // reserve memory for COUNT array 121 | LOC Count+Size*8 122 | 123 | // expected counts array for verification 124 | GREG @ 125 | Solution OCTA 8*2,8*1,8*0,8*3 126 | 127 | GREG @ 128 | // expected sorted array for verification 129 | Sorted OCTA -3,-2,5,6 130 | 131 | // code segment 132 | LOC #100 133 | 134 | // main program 135 | // set parameters to pass to Sort function 136 | Main LDA $1,Data 137 | LDA $2,Count 138 | SET $3,Size 139 | 140 | // call Sort, no registers are saved 141 | PUSHJ 0,Sort 142 | 143 | SET $255,0 144 | 145 | TRAP 0,Halt,0 146 | 147 | -------------------------------------------------------------------------------- /program_d_shellsort.mms: -------------------------------------------------------------------------------- 1 | // program_d_shellsort.mms 2 | 3 | // Based on: 5.2.1/shell.mms, 5.2.1/shell.tst 4 | // Copyright: This file is part of the MMIX Supplement package 5 | // (c) Martin Ruckert 2014 6 | // Authors: M. Ruckert, 27.4.2012, Blake Hegerle 7 | 8 | PREFIX :Sort: 9 | 10 | // base address of input array ie first key 11 | key IS $0 Parameter 12 | 13 | // size of input array 14 | n IS $1 15 | 16 | // base address of increments array 17 | inc IS $2 18 | 19 | // size of increments array 20 | t IS $3 21 | 22 | s IS $4 Local variables 23 | j IS $5 24 | i IS $6 25 | 26 | k IS $7 27 | ki IS $8 28 | 29 | keyh IS $9 30 | keyn IS $10 31 | 32 | d IS $11 33 | h IS $12 34 | c IS $13 35 | 36 | // Sort params 37 | // key $0 address of first key 38 | // n $1 number of keys 39 | // inc $2 address of increments array 40 | // t $3 number of increments 41 | 42 | // keyn $10 is address of end of array 43 | :Sort 8ADDU keyn,n,key 01: keyn <- LOC(K_{N+1}) 44 | 45 | // s $4 is number of increments scaled by octa to use as offset 46 | SL s,t,3 02: s <- t-1 47 | 48 | // start first iteration of loop 49 | JMP D1 03: 50 | 51 | // set increment for nested loop in h $12 52 | D2 LDO h,inc,s 04: D2 Loop on j, h <- h_s 53 | 54 | // scale increment by octa to use as offset into data array 55 | SL h,h,3 05: 56 | 57 | // keyh $9 is address of first unsorted key at increment offset from the start 58 | // keyh is used as base address for moving keys up by the increment 59 | ADDU keyh,key,h 06: keyh <- LOC(K_{h+1}) 60 | 61 | // d $11 is distance from unsorted key to end of array in bytes 62 | SUBU d,keyn,keyh 07: d <- N - h 63 | 64 | // initialize outer loop counter j $2 to negative distance 65 | // j $5 is negative offset of unsorted key from end of array that counts up 66 | SUBU j,keyh,keyn 08: j <- h+1 67 | 68 | // start first iteration of nested loop 69 | JMP 0F 09: 70 | 71 | // nested loop 72 | 73 | // outer loop over unsorted keys 74 | 75 | // initialize inner loop counter i $6 76 | D3 ADD i,d,j 10: D3 Set up j, K, R, i <- j-h 77 | 78 | // get current unsorted key into k $7 79 | LDO k,keyn,j 11: 80 | 81 | // inner loop on i $6 over sorted keys in reverse 82 | 83 | // get current sorted key into ki $8 84 | D4 LDO ki,key,i 12: D4 Compare K : K_i 85 | 86 | // compare k to ki with result in c $13: 1, 0, or -1 87 | CMP c,k,ki 13: 88 | 89 | // less likely branch with higher disorder 90 | BNN c,D6 14: To D6 if K >= K_i 91 | 92 | // unsorted new key is smaller, keep going 93 | // move sorted key one increment up in memory 94 | STO ki,keyh,i 15: D5 Move R_i, decrease i 95 | 96 | // decrement inner counter i $6 by one increment 97 | SUB i,i,h 16: i <- i-h 98 | 99 | // likely branch with higher disorder to get next sorted key 100 | PBNN i,D4 17: To D4 if i >= 0 101 | 102 | // unsorted new key is bigger, stop 103 | // found correct position of unsorted key, write to memory 104 | D6 STO k,keyh,i 18: D6 R into R_{i+1} 105 | 106 | // increment outer counter j $5 by octa 107 | ADD j,j,8 19: j <- j + 1 108 | 109 | // likely branch for sequential counter 110 | 0H PBN j,D3 20: To D3 if j < N 111 | 112 | // decrement offset into increments array 113 | D1 SUB s,s,8 21: D1 Loop on s 114 | 115 | // likely branch 116 | PBNN s,D2 22: 0 <= s < t 117 | 118 | // return with no return value 119 | POP 0,0 120 | 121 | // global namespace 122 | PREFIX : 123 | 124 | // place input data in memory 125 | LOC Data_Segment 126 | 127 | // base address $254 for Data array 128 | GREG @ 129 | 130 | // data to sort 131 | Data OCTA 503,87,512,61,908,170,897,275 132 | OCTA 653,426,154,509,612,677,765,703 133 | 134 | // size of data array 135 | Size IS 16 136 | 137 | // base address $253 for Sorted array 138 | GREG @ 139 | 140 | // sorted array for verification 141 | Sorted OCTA 61,87,154,170,275,426,503,509 142 | OCTA 512,612,653,677,703,765,897,908 143 | 144 | // base address $252 for H array 145 | GREG @ 146 | 147 | // increments array 148 | H OCTA 1,3,5,9,17,33,65,129,257,513 149 | 150 | // size of increments array 151 | t IS 4 152 | 153 | // code segment 154 | LOC #100 155 | 156 | // main program 157 | 158 | // prep params to Sort 159 | // pass address of data array to $0 160 | Main LDA $1,Data 161 | 162 | // pass array size to $1 163 | SET $2,Size 164 | 165 | // pass address of increments array to $2 166 | LDA $3,H 167 | 168 | // pass size of increments array to $3 169 | SET $4,t 170 | 171 | // call Sort, no registers saved 172 | PUSHJ 0,Sort 173 | 174 | // set exit status 0 175 | SET $255,0 176 | 177 | // halt program 178 | TRAP 0,Halt,0 179 | 180 | -------------------------------------------------------------------------------- /program_l_list_insertion.mms: -------------------------------------------------------------------------------- 1 | // program_l_list_insertion.mms 2 | 3 | // Based on: 5.2.1/listi.mms, 5.2.1/listi.tst 4 | // Copyright: This file is part of the MMIX Supplement package 5 | // (c) Martin Ruckert 2014 6 | // Authors: B. Hegerle , Martin Ruckert, 2005-11-22 21:41 7 | 8 | // idea is to set link field in each node to point to next node in 9 | // increasing sorted order 10 | // special head node at start of array is the beginning of the sorted 11 | // linked list 12 | 13 | PREFIX :Sort: 14 | // base address register for LINK fields 15 | link IS $0 Parameter 16 | 17 | // size of array of nodes 18 | n IS $1 N 19 | 20 | // base address register for KEY fields 21 | key IS $2 22 | 23 | // offset of current unsorted node to sort 24 | j IS $3 Local variables 25 | 26 | // address of current sorted node to compare 27 | p IS $4 28 | 29 | // trails p, offset of node pointing to current sorted node to compare 30 | q IS $5 31 | 32 | // current unsorted key 33 | k IS $6 34 | 35 | // current sorted key 36 | kp IS $7 37 | 38 | // temporary 39 | t IS $9 40 | 41 | // offset of LINK field in node 42 | // LINK contains offset of next sorted node 43 | LINK IS 0 44 | 45 | // offset of KEY field in node 46 | KEY IS 8 47 | 48 | // Sort params 49 | // link $0 address of data array 50 | // n $1 number of elements 51 | 52 | // set key $2 as base address for KEY field offsets 53 | :Sort ADDU key,link,KEY 01: L1 Loop on j 54 | 55 | // initialize j $3 as offset to last node scaled by node size 56 | SL j,n,4 02: j <- N 57 | 58 | // initialize head node to point to last node 59 | STOU j,link,0 03: L_0 <- N 60 | 61 | // initialize last node to point to head node 62 | STCO 0,link,j 04: L_N <- 0 63 | 64 | // start first iteration of nested loop 65 | JMP 0F 05: Go to decrease j 66 | 67 | // nested loop 68 | 69 | // outer loop over array of unsorted nodes in reverse 70 | 71 | // initialize p $4 to offset of smallest sorted key 72 | L2 LDOU p,link,0 06: L2 Set up p, q, K. p <- L_0 73 | 74 | // initialize q $5 to point to node pointing to p, so q trails p 75 | SET q,0 07: q <- 0 76 | 77 | // set k $6 as new unsorted key for entire inner loop 78 | LDO k,key,j 08: K <- K_j 79 | 80 | // inner loop over linked list of nodes sorted in increasing order 81 | 82 | // kp $7 is current sorted key 83 | L3 LDO kp,key,p 09: L3 Compare K:K_p 84 | 85 | // compare unsorted key k $6 to sorted key kp $7: result 1, 0, -1 in $9 86 | CMP t,k,kp 10: 87 | 88 | // less likely branch if unsorted key is at most sorted key 89 | BNP t,L5 11: To L5 if K <= K_p 90 | 91 | // unsorted key is bigger, keep going 92 | // update q $5 to trail p $4 93 | SET q,p 12: L4 Bump p, q. q <- p 94 | 95 | // set p $4 to point to next sorted node 96 | LDOU p,link,q 13: p <- L_q 97 | 98 | // likely branch for p != 0 continue traversing sorted list in inner loop 99 | PBNZ p,L3 14: To L3 if p != 0 100 | 101 | // unsorted key is smaller or at end of sorted list 102 | // found correct position of unsorted node 103 | // "insert" unsorted node before current sorted node 104 | // this is where trailing pointer q is needed 105 | 106 | // update node of trailing pointer q $5 to point to unsorted node j $3 107 | L5 STOU j,link,q 15: L5 Insert into list. L_q <- j 108 | 109 | // update unsorted node to point to current sorted node p $4 110 | STOU p,link,j 16: L_j <- p 111 | 112 | // decrement outer counter j $3 by one node to next unsorted node 113 | 0H SUB j,j,16 17: j <- j - 1 114 | 115 | // continue outer loop over unsorted nodes 116 | PBP j,L2 18: N > j >= 1 117 | 118 | // return, no return value 119 | POP 0,0 120 | 121 | PREFIX : 122 | 123 | 124 | // data segment 125 | LOC Data_Segment 126 | 127 | // base address register $254 128 | GREG @ 129 | // array of unsorted data with link field for sorting 130 | Head OCTA 0,0 the artificial R_0 record with its link field 131 | 132 | Data OCTA 0,5,0,3,0,2,0,5,0,7,0,11,0,-3,0,2,0,99,0,5 133 | 134 | Size IS 10 135 | 136 | // base address register $253 137 | GREG @ 138 | 139 | Sorted OCTA -3,2,2,3,5,5,5,7,11,99 140 | 141 | // code segment 142 | LOC #100 143 | 144 | // main program 145 | 146 | // pass address of array of unsorted data to $0 147 | Main LDA $1,Head 148 | 149 | // pass array size to $1 150 | SET $2,Size 151 | 152 | // call Sort, no registers saved 153 | PUSHJ 0,Sort 154 | 155 | // code block to verify sorted linked data 156 | // traverses links in Head array and compares key in node from Head 157 | // to key in Sorted array 158 | // exit 0 for success, i.e. linked nodes of Head are sorted 159 | // exit 1 for failure 160 | PREFIX :LEqual: 161 | 162 | p IS $2 163 | kp IS $3 164 | k IS $5 165 | t IS $6 166 | j IS $7 167 | S IS $8 168 | n IS $9 169 | K IS $10 170 | L IS $11 171 | 172 | LDA L,:Head 173 | LDA K,:Head+8 174 | LDOU p,L,0 175 | SET j,0 176 | LDA S,:Sorted 177 | 178 | SET n,:Size 179 | 180 | SL n,n,3 181 | JMP 0F 182 | 183 | 1H LDO kp,K,p 184 | LDO k,S,j 185 | CMP t,k,kp 186 | BZ t,2F 187 | 188 | // error exit status 1 189 | 3H SET $255,1 190 | TRAP 0,:Halt,0 191 | 192 | // get next key from Head to verify 193 | 2H LDOU p,L,p 194 | 195 | // increment index to next entry of expected result array Sorted 196 | ADD j,j,8 197 | 0H CMP t,j,n 198 | 199 | // fall through if end of Sorted array or of sorted links is reached 200 | BN t,1B 201 | BNZ p,3B 202 | 203 | PREFIX : 204 | 205 | // exit status 0 206 | SET $255,0 207 | TRAP 0,Halt,0 208 | 209 | 210 | -------------------------------------------------------------------------------- /program_m_maximum.mms: -------------------------------------------------------------------------------- 1 | // program_m_maximum.mms 2 | 3 | // usage: mmix program_m_find_the_maximum m] - [X[k] < m] 35 | 36 | // address 0x114: 5c ff 00 03 37 | PBNP t,DecrK 12: To M5 if X[k] <= m 38 | 39 | // address 0x118: c1 01 03 00 40 | ChangeM SET m,xk 13: M4 [Change m] m <- X[k] 41 | 42 | // address 0x11c: 3d 00 02 03 43 | SR j,kk,3 14: j <- k 44 | 45 | // address 0x120: 25 02 02 08 46 | DecrK SUB kk,kk,8 15: M5 [Decrease k] k <- k - 1 47 | 48 | // address 0x124: 55 00 ff fa 49 | PBP kk,Loop 16: M2 [All tested?] To M3 if k > 0 50 | 51 | // address 0x128: f8 02 00 00 52 | POP 2,0 17: Return to main program 53 | 54 | 55 | Main GETA t,8F 56 | // fill array using Fread parameters at 8H 57 | TRAP 0,Fread,StdIn 58 | 59 | // $0 is bytes offset of end of array 60 | SET $0,N<<3 61 | // $1 is array index 62 | SR $1,$0,3 63 | // call Maximum 64 | // $0 is hole 65 | // $1 renamed $0 for Maximum 66 | PUSHJ 0,Maximum 67 | 68 | // hole $0 has first return value - the maximum - from Maximum 69 | // index of max returned in $1 70 | STO $1,x0,1<<3 71 | STO $0,x0,2<<3 72 | 73 | // Fwrite parameters at 9H: source buffer address followed by size bytes 74 | GETA t,9F 75 | TRAP 0,Fwrite,StdOut 76 | 77 | // exit 78 | TRAP 0,Halt,0 79 | 80 | // parameters for Fread 81 | // destination buffer X0 + 8*1, size 8*N bytes 82 | 8H OCTA X0+1<<3,N<<3 83 | 84 | // parameters for Fwrite 85 | // source buffer X0 + 8*1, size 8*2 bytes 86 | 9H OCTA X0+1<<3,2<<3 87 | 88 | -------------------------------------------------------------------------------- /program_m_multiple_list_insertion.mms: -------------------------------------------------------------------------------- 1 | // program_m_multiple_list_insertion.mms 2 | 3 | // Based on: 5.2.1/mlisti.mms, 5.2.1/mlisti.tst 4 | // Copyright: This file is part of the MMIX Supplement package 5 | // (c) Martin Ruckert 2014 6 | // Author: Martin Ruckert 7 | 8 | // basic idea is to modify Algorithm L (List insertion) and replace the 9 | // head node for the single list of sorted nodes with multiple head 10 | // nodes of separate lists through the array of records 11 | // each head node/linked list corresponds to a bucket or range of keys 12 | // the range is of non-negative numbers upto a power of 2 that fits all keys 13 | // for example the range could be 0-1024 14 | // and a number of buckets is chosen to partition the range, say 4 15 | 16 | // the only extra space needed is for the extra head nodes compared to 17 | // Algorithm L 18 | 19 | // the only difference in processing is to compute the head node to use 20 | // for a particular key when trying to insert it into a list of sorted keys 21 | // otherwise this works the same as Algorithm L 22 | 23 | PREFIX : 24 | 25 | // data segment 26 | LOC Data_Segment 27 | 28 | // number of distribution linked lists (buckets) 29 | M IS 4 30 | 31 | // keys must be in the range [0, 2^e) 32 | e IS 10 33 | 34 | // number of keys to sort 35 | Size IS 16 36 | 37 | // base address register $254 for data array 38 | GREG @ 39 | 40 | // array of keys to sort 41 | // still need extra first record because 0 index is special just as in 42 | // program_l_list_insertion.mms, even though this first record is no longer 43 | // used as the head of a linked list 44 | R0 OCTA 0,0 artificial first record 45 | 46 | Data OCTA 0,503,0,87,0,512,0,61,0,908,0,170,0,897,0,275 47 | OCTA 0,653,0,426,0,154,0,509,0,612,0,677,0,765,0,703 48 | 49 | 50 | // base address register $253 for array of heads for linked lists 51 | GREG @ 52 | 53 | // array of heads of sorted linked lists 54 | Heads OCTA 0 55 | 56 | // reserve space for Heads array 57 | LOC Heads+8*M 58 | 59 | // base address register $252 for expected sorted keys in Sorted array 60 | GREG @ 61 | 62 | // sorted array for verification 63 | Sorted OCTA 61,87,154,170,275,426,503,509 64 | OCTA 512,612,653,677,703,765,897,908 65 | 66 | // code segment 67 | LOC #100 68 | 69 | // main program 70 | 71 | // address of data array passed to $0 72 | Main LDA $1,R0 73 | 74 | // size of data array passed to $1 75 | SET $2,Size 76 | 77 | // address of list heads array passed to $2 78 | LDA $3,Heads 79 | 80 | // size of list heads array passed to $3 81 | SET $4,M 82 | 83 | // call Sort, no registers saved 84 | PUSHJ 0,Sort 85 | 86 | LDA $1,:Heads 87 | SET $2,:M 88 | LDA $3,:Sorted 89 | SET $4,:Size 90 | 91 | PUSHJ $0,:List:Equal 92 | 93 | SET $255,0 94 | TRAP 0,Halt,0 95 | 96 | // multiple list insertion 97 | 98 | PREFIX :MListSort: 99 | 100 | // base address register for LINK fields 101 | link IS $0 Parameter 102 | 103 | n IS $1 104 | 105 | // base address register for array of linked list heads 106 | head IS $2 107 | 108 | // number of linked lists 109 | m IS $3 110 | 111 | // keys must be in the range [0..2^e) 112 | e IS :e 113 | 114 | key IS $4 115 | 116 | j IS $5 scaled Local variables 117 | 118 | p IS $6 119 | q IS $7 120 | k IS $8 121 | kp IS $9 122 | 123 | i IS $10 scaled 124 | t IS $11 125 | 126 | // offset of LINK field of node 127 | // LINK contains offset of next sorted node 128 | LINK IS 0 129 | 130 | // offset of KEY field of node 131 | KEY IS 8 132 | 133 | 134 | // Sort params 135 | // link $0 is address of data array 136 | // n $1 is size of data array 137 | // head $2 is address of array of heads of linked lists 138 | // m $3 is size of array of heads 139 | 140 | // i $10 is size of array of linked list heads scaled by octa 141 | :Sort SL i,m,3 01: i <- M 142 | 143 | // start first iteration of loop to initialize array of heads 144 | JMP 1F 02: 145 | 146 | // loop to initialize array of heads 147 | // set each head to 0 the null link 148 | 0H STCO 0,head,i 03: Clear heads 149 | 150 | // decrement i $10 by octa 151 | 1H SUB i,i,8 04: i <- i - 1 152 | 153 | // likely branch to sequential loop over heads array 154 | PBNN i,0B 05: 155 | 156 | // turns head $2 into offset from link 157 | // so each head can be treated like a LINK field 158 | SUBU head,head,link 06: Make head a relative address 159 | 160 | // key $4 is base address for KEY fields 161 | ADDU key,link,KEY 07: Loop on j 162 | 163 | // initialize j $5 to size of data array scaled by 2 octas 164 | SL j,n,4 08: j <- N 165 | 166 | // start first iteration of nested loop 167 | JMP 0F 09: 168 | 169 | // nested loop 170 | 171 | // outer loop on j $5 over unsorted keys of data array in reverse 172 | 173 | // get new unsorted key into k $8 174 | M2 LDO k,key,j 10: Set up p, q, K. K <- K_j 175 | 176 | // there are M buckets, each bucket size is 2^e / M 177 | // so a key K belongs to bucket index K / (2^e / M) or M * K / 2^e 178 | 179 | // i $10 will be bucket index scaled by octa 180 | MUL i,m,k 11: i <- M * K_j 181 | 182 | // divide i $10 by 2^e scaled by octa which is 2^(e-3) 183 | // okay for i to not be multiple of octa since mmix ignores unaligned bits 184 | // when loading an octa from an address 185 | SRU i,i,e-3 12: i <- floor(M * K_j / 2^e) 186 | 187 | // initialize q $7 to bucket index scaled by octa 188 | ADDU q,head,i 13: q <- relative address of H_i 189 | 190 | // start first iteration of loop over associated sorted list 191 | JMP 4F 14: Jump to load and test p 192 | 193 | // inner loop over sorted sublist 194 | 195 | // get next sorted key into kp $9 196 | M3 LDO kp,key,p 15: Compare K:K_p 197 | 198 | // compare unsorted new key $8 to sorted key kp $9: result 1, 0, -1 in $11 199 | CMP t,k,kp 16: 200 | 201 | // less likely branch for unsorted new key at most sorted key 202 | BNP t,M5 17: To L5 if K <= K_p 203 | 204 | // unsorted key is bigger, keep going through list of sorted keys 205 | // update q $7 to trail p $6 206 | SET q,p 18: Bump p, q. q <- p 207 | 208 | // p $6 points to next sorted node 209 | 4H LDOU p,link,q 19: p <- L_q 210 | 211 | // likely branch on p != 0 to continue scanning linked list of sorted keys 212 | PBNZ p,M3 20: To L3 if p != 0 213 | 214 | // unsorted key is smaller or at end of sorted list 215 | 216 | // update node of trailing pointer q $7 to point to unsorted node j $5 217 | M5 STOU j,link,q 21: Insert into list. L_q <- j 218 | 219 | // update unsorted node to point to current sorted node p 220 | STOU p,link,j 22: L_j <- p 221 | 222 | // decrement outer counter j $5 by 2 octas to process next unsorted node 223 | SUB j,j,16 23: 224 | 225 | // likely branch to continue outer loop over unsorted nodes 226 | 0H PBP j,M2 24: N > j >=1 227 | 228 | // return, no return value 229 | POP 0,0 230 | 231 | 232 | PREFIX :List: 233 | H IS $0 Heads Parameter 234 | m IS $1 Number of heads 235 | S IS $2 Sorted keys 236 | n IS $3 Number of keys 237 | kp IS $4 Local variables 238 | p IS $5 239 | k IS $6 240 | t IS $7 241 | j IS $8 242 | key IS $9 243 | link IS $10 244 | 245 | Equal SET j,0 246 | SL n,n,3 247 | SET p,0 248 | LDA link,:R0 249 | LDA key,:R0+8 250 | JMP 0F 251 | 252 | 253 | 1H BNZ p,4F 254 | LDO p,H,0 255 | SUB m,m,1 256 | BN m,3F 257 | ADD H,H,8 258 | JMP 1B 259 | 260 | 4H LDO kp,key,p 261 | LDO k,S,j 262 | CMP t,k,kp 263 | BZ t,2F 264 | 265 | 3H SET $255,1 266 | TRAP 0,:Halt,0 267 | 2H LDOU p,link,p 268 | ADD j,j,8 269 | 0H CMP t,j,n 270 | BN t,1B 271 | BNZ p,3B 272 | 273 | 274 | PREFIX : 275 | 276 | -------------------------------------------------------------------------------- /program_s_straight_insertion_sort.mms: -------------------------------------------------------------------------------- 1 | // program_s_straight_insertion_sort.mms 2 | 3 | // Based on: 5.2.1/insert3.mms, 5.2.1/insert3.tst 4 | // Copyright: This file is part of the MMIX Supplement package 5 | // (c) Martin Ruckert 2014 6 | // Author: M.Ruckert, 26.3.2012 7 | 8 | // base address of input array ie first key 9 | key IS $0 Parameter 10 | 11 | // size of input array 12 | n IS $1 13 | 14 | j IS $2 Local variables 15 | i IS $3 16 | k IS $4 17 | 18 | ki IS $5 19 | key1 IS $6 20 | keyn IS $7 21 | 22 | d IS $8 23 | c IS $9 24 | 25 | // Sort params: key $0 address of first key, n $1 number of keys 26 | // initialize key1 $6 to address of second key 27 | :Sort ADD key1,key,8 01: 28 | 29 | // initialize keyn $7 to address of end of array 30 | 8ADDU keyn,n,key 02: 31 | 32 | // d $8 is distance from second key to end of array in bytes 33 | SUBU d,keyn,key1 03: 34 | 35 | // initialize outer loop counter j $2 to negative distance 36 | // j is negative offset from end of array that counts up 37 | SUBU j,key1,keyn 04: j <- 1 38 | 39 | // start first iteration of nested loop 40 | JMP S1 05: 41 | 42 | // nested loop 43 | 44 | // outer loop on j $2 over unsorted keys 45 | // get current unsorted key into register k $4 46 | S2 LDO k,keyn,j 06: S2 Set up j, K, R 47 | 48 | // initialize inner loop counter i $3 to offset of node before unsorted key 49 | // i is offset from array start that counts down 50 | ADD i,d,j 07: i <- j-1 51 | 52 | // inner loop on i $3 53 | // get current sorted key into ki $5 54 | S3 LDO ki,key,i 08: S3 Compare K : K_i 55 | 56 | // compare unsorted key k to sorted key ki with result in c $9: 1, 0, or -1 57 | CMP c,k,ki 09: 58 | 59 | // less likely branch with higher disorder 60 | BNN c,S5 10: To S5 if K >= K_i 61 | 62 | // sorted key is bigger, keep going 63 | // move sorted key one up in memory, remember key1 $6 is address of second key 64 | STO ki,key1,i 11: S4 Move R_i, decrease i 65 | 66 | // decrement inner counter i $3 by octa 67 | SUB i,i,8 12: i <- i-1 68 | 69 | // likely branch with higher disorder 70 | PBNN i,S3 13: To S3 if i >= 0 71 | 72 | // unsorted key is bigger or at start of array 73 | // found correct position of unsorted key, write to memory 74 | S5 STO k,key1,i 14: S5 R into R_{i+1} 75 | 76 | // increment outer counter j $2 by octa to offset of next unsorted key 77 | ADD j,j,8 15: j <- j + 1 78 | 79 | // likely branch for linear scan 80 | S1 PBN j,S2 16: S1 Loop on j, 1 <= j <= N 81 | 82 | // return with no return value 83 | POP 0,0 17: 84 | 85 | // place data in data segment 86 | LOC Data_Segment 87 | 88 | // base address register $254 for addresses of data symbols 89 | GREG @ 90 | 91 | // input array to sort 92 | Data OCTA 5,3,2,5,7,11,-3,2,99,5 93 | 94 | // array size 95 | Size IS 10 96 | 97 | // sorted array for verification 98 | Sorted OCTA -3,2,2,3,5,5,5,7,11,99 99 | 100 | // code segment 101 | LOC #100 102 | 103 | // main program 104 | 105 | // prep params to Sort 106 | // pass address of input array to $0 107 | Main LDA $1,Data 108 | 109 | // pass array size to $1 110 | SET $2,Size 111 | 112 | // call Sort, no registers saved 113 | PUSHJ 0,Sort 114 | 115 | // set exit status 0 116 | SET $255,0 117 | 118 | // halt 119 | TRAP 0,Halt,0 120 | 121 | -------------------------------------------------------------------------------- /program_s_straight_selection_sort.mms: -------------------------------------------------------------------------------- 1 | // program_s_straight_selection_sort.mms 2 | 3 | // Copyright: This file is part of the MMIX Supplement package 4 | // (c) Martin Ruckert 2014 5 | // Based on: 5.2.3/straightselectionsort.mms 5.2.3/straightselectionsort.tst 6 | // Author: Michael Unverzart 7 | 8 | // set location to data segment 9 | LOC Data_Segment 10 | 11 | // global base address register $254 for input array 12 | GREG @ 13 | 14 | // unsorted input array 15 | Data OCTA 503,87,512,61,908,170,897,275 16 | OCTA 653,426,154,509,612,677,765,703 17 | 18 | // number of keys to sort 19 | Size IS 16 20 | 21 | // global base address register $253 for expected output 22 | GREG @ 23 | // expected sorted result for verification 24 | Sorted OCTA 61,87,154,170,275,426,503,509 25 | OCTA 512,612,653,677,703,765,897,908 26 | 27 | // code segment 28 | LOC #100 29 | 30 | // main program 31 | 32 | // pass address of input array to callee $0 33 | Main LDA $1,Data 34 | // pass size of array to callee $1 35 | SET $2,Size 36 | 37 | // call Sort subroutine, no saved registers 38 | PUSHJ 0,Sort 39 | 40 | // set exit status 41 | SET $255,$0 42 | // halt 43 | TRAP 0,Halt,0 44 | 45 | 46 | 47 | // global base address register $252 48 | GREG @ 49 | 50 | // algorithm s straight selection sort 51 | PREFIX :sort: 52 | 53 | // parameter 54 | key IS $0 base address 55 | n IS $1 number of elements 56 | 57 | // local variables 58 | j IS $2 scaled index (offset) S1: loop on j 59 | k IS $3 scaled index (offset) S2: find max 60 | 61 | kk IS $4 element at index k (Kk) 62 | i IS $5 scaled current maximum offset 63 | 64 | max IS $6 current maximum element (Ki) 65 | t IS $7 66 | 67 | // Sort params 68 | // key $0 address of array of unsorted keys 69 | // n $1 number of keys to sort 70 | 71 | // j $2 initialized to offset of end of array 72 | :Sort SL j,n,3 01: S1 Loop on j, j <- N 73 | 74 | // start first iteration of loop 75 | JMP 1F 02: 76 | 77 | // nested loop 78 | 79 | // outer loop on j $2 backwards from end of array 80 | // initialize k $3 to j $2 81 | 2H SET k,j 03: S2 Find max(K_1,..., K_j) 82 | 83 | // initialize i $5 index of max key to j $2 84 | SET i,j 04: i <- j 85 | 86 | // initialize max $6 to key at offset i $5 87 | // break here to see the sorted position that will be filled 88 | LDO max,key,i 05: max <- K_i 89 | 90 | // inner loop on k $3 forward from j $2 to find maximal key 91 | 3H SUB k,k,8 06: Loop on k 92 | 93 | // get key at offset k $3 into kk $4 94 | LDO kk,key,k 07: kk <- K_k 95 | 96 | // compare max $6 to new key kk $4: result 1, 0, -1 in t $7 97 | CMP t,max,kk 08: Compare max : K_k 98 | 99 | // likely branch to continue without updating max if comparison is nonnegative 100 | PBNN t,0F 09: If max < K_k 101 | 102 | // fallthrough to update max $6 and its index i $5 103 | // since max is smaller than new key 104 | SET i,k 10: i <- k and 105 | // break here to see candidate max $6 get updated 106 | SET max,kk 11: max <- K_k 107 | 108 | // continue forward scan of array checking for max 109 | 0H PBP k,3B 12: Repeat if k > 0 110 | 111 | // reached end of array, max key and index has been found 112 | // swap key at offset j $2 with key at offset i $5 113 | LDO t,key,j 13: S3 Exchange with R_j 114 | // break here to see max key placed in correct sorted position 115 | STO max,key,j 14: 116 | 117 | STO t,key,i 15: 118 | 119 | // decrement j $2 to continue backward loop over array 120 | 1H SUB j,j,8 16: Decrement j 121 | 122 | PBP j,2B 17: N > j > 0 123 | 124 | // reached start of array, sorting is complete 125 | // return, no return value 126 | POP 0,0 127 | 128 | PREFIX : 129 | 130 | -------------------------------------------------------------------------------- /program_t_topological_sort.mms: -------------------------------------------------------------------------------- 1 | // program_t_topological_sort.mms 2 | 3 | // Program T (Topological sort), 2.2.3 Linked Allocation, 4 | // The MMIX Supplement, Martin Ruckert 5 | 6 | 7 | // usage: mmix program_t_topological_sort in.dat out.dat 8 | // reads sequence of pairs of uint32_t values from in.dat 9 | // each pair is a dependency relation between objects 10 | // first value is predecessor, second is successor 11 | // first pair is special: first value is 0, second is objects count 12 | // last pair is special: both values are 0 13 | 14 | PREFIX :TSort: 15 | 16 | LOC :Data_Segment 17 | 18 | // base address register for i/o buffer 19 | // address: 0x2000 0000 0000 0000 20 | GREG @ 21 | 22 | // file descriptors 23 | Fin IS 3 24 | Fout IS 4 25 | 26 | InName BYTE 0 /* we fake input and output */ 27 | BYTE 0 28 | OutName BYTE 0 29 | BYTE 0 30 | 31 | // open input filename stored in InName, in binary read mode 32 | // address: 0x2000 0000 0000 0008 33 | InOpen OCTA InName,:BinaryRead 34 | 35 | // open output filename stored in OutName, in binary write mode 36 | // address: 0x2000 0000 0000 0018 37 | OutOpen OCTA OutName,:BinaryWrite 38 | 39 | // buffer capacity in bytes 40 | // each pair is 2 tetras 41 | // capacity is 256 pairs = 2^11 bytes = 0x800 bytes 42 | SIZE IS 256*2*4 43 | 44 | // input buffer reused for output too 45 | // address: 0x2000 0000 0000 0028 46 | Buffer TETRA 0,9,9,2,3,7,7,5,5,8,8,6,4,6,1,3,7,4,9,5,2,8,0,0 47 | 48 | //Buffer TETRA 0,3,1,2,1,3,0,0 49 | //Sorted TETRA 1,3,2,0 50 | 51 | //Buffer TETRA 0,9,9,2,3,7,7,5,5,8,8,6,4,6,1,3,7,4,9,5,2,8,0,0 52 | //Sorted TETRA 9,1,2,3,7,4,5,8,6,0 53 | 54 | // reserve SIZE bytes for Buffer 55 | LOC Buffer+SIZE 56 | 57 | // base address register 58 | // address: 0x2000 0000 0000 0828 59 | GREG @ 60 | 61 | // address: 0x2000 0000 0000 0828 62 | Sentinel OCTA 0 Terminates input buffer 63 | 64 | // io parameters to fill input Buffer with SIZE bytes 65 | // address: 0x2000 0000 0000 0830 66 | IOArgs OCTA Buffer,SIZE 67 | 68 | // start of avail memory allocation pool 69 | // address: 0x2000 0000 0000 0840 70 | Base OCTA 0 Last OCTA in data segment. 71 | 72 | // n octas for objects array 73 | // 3 octas mean add 0x18 -> address 0x2000 0000 0000 0858 74 | // 9 octas mean add 0x48 -> address 0x2000 0000 0000 0888 75 | 76 | // code segment 77 | LOC #100 78 | 79 | // the 3 lists 80 | 81 | // sequentially allocated fixed list (array) of objects 82 | 83 | // offset of COUNT field, holds number of predecessors 84 | COUNT IS 0 85 | // offset of TOP field, link to list of successors, stack? 86 | TOP IS 4 87 | 88 | // linked list of successor objects 89 | 90 | // offset of SUC field, holds successor value 91 | SUC IS 0 92 | // offset of NEXT field, link to next successor 93 | NEXT IS 4 94 | 95 | // output queue of objects reusing original array of objects 96 | 97 | // offset of QLINK field (reused COUNT field), link to next entry in queue 98 | QLINK IS 0 99 | 100 | // register holds number of objects 101 | n IS $0 102 | 103 | // offset of next available node from allocation pool 104 | :avail IS $1 105 | 106 | // base address registers for COUNT and TOP fields 107 | count IS $2 108 | top IS $3 109 | 110 | // base address registers for SUC and NEXT fields of successors list node 111 | suc IS count 112 | next IS top 113 | 114 | // base address register for QLINK field of output queue 115 | qlink IS count 116 | 117 | // counter registers 118 | i IS $4 119 | j IS $5 120 | k IS $6 121 | 122 | // address of left tetra of pair 123 | left IS $7 124 | // address of right tetra of pair 125 | right IS $8 126 | 127 | // offset of new node obtained from avail pool 128 | p IS $9 129 | 130 | // rear of queue is link index into array of objects 131 | r IS $10 132 | 133 | // register to hold successor value 134 | s IS $12 135 | 136 | // front of queue is link index into array of objects 137 | f IS $13 138 | 139 | // buffer capacity 140 | size IS $14 141 | 142 | // temporary register 143 | t IS $15 144 | 145 | 146 | // read input data 147 | :TSort LDA $255,InOpen 01: T1 Initialize 148 | TRAP 0,:Fopen,Fin 02: Open input file 149 | 150 | LDA $255,IOArgs 03: 151 | TRAP 0,:Fread,Fin 04: Read first input buffer 152 | 153 | // prep to process input pairs 154 | SET size,SIZE 05: Load buffer size 155 | 156 | // initialize left and right to end of buffer to use negative indexing 157 | LDA left,Buffer+SIZE 06: Point left to the buffer end 158 | ADDU right,left,4 07: Point right to next TETRA 159 | 160 | // i input pairs counter scaled to octabytes 161 | // initialize i to -size use as negative offset from end of buffer 162 | NEG i,size 08: i <- 0 163 | 164 | // right of first pair is number of objects 165 | LDT n,right,i 09: First pair is (0,n), n <- n 166 | 167 | // increment i by size of 1 pair 168 | ADD i,i,8 10: i <- i+1 169 | 170 | // initialize avail memory allocation pool 171 | // node size 8 bytes, two tetras for COUNT and TOP fields 172 | SET :avail,8 11: Allocate QLINK[0] 173 | 174 | // avail = 8*n + 8 = 8 * (n+1) 175 | 8ADDU :avail,n,:avail 12: Allocate n COUNT and TOP fields 176 | 177 | // count is base address of COUNT field of object 0 178 | LDA count,Base+COUNT 13: count <- LOC(COUNT[0]) 179 | 180 | // top is base address of TOP field of object 0 181 | LDA top,Base+TOP 14: top <- LOC(TOP[0]) 182 | 183 | // initialize COUNT and TOP to 0 for all nodes 184 | // k is current node index scaled to byte offset 185 | // initialize k to n for reverse iteration 186 | SL k,n,3 15: k <- n 187 | 188 | // initialize both COUNT and TOP fields with one 0 octabyte 189 | 1H STCO 0,k,count 16: Set (COUNT[k], TOP[k]) <- (0,0) 190 | 191 | // count k down to zero 192 | SUB k,k,8 17: for 0 <= k <= n 193 | 194 | // branch likely taken because k runs sequentially from n to 0 195 | PBNN k,1B 18: Anticipate QLINK[0] <- 0 (step T4) 196 | 197 | // begin first iteration of loop to process input pairs 198 | JMP T2 19: 199 | 200 | // process one pair from input 201 | // k has value of right of pair i.e. successor 202 | T3 SL k,k,3 20: T3 Record the relation 203 | 204 | // update COUNT field of successor object 205 | LDT t,k,count 21: Increase COUNT[k] by one 206 | ADD t,t,1 22: 207 | STT t,k,count 23: 208 | 209 | // simplified AVAIL list 210 | // note avail offset starts at Base taking into account entire objects array 211 | // p is new node from avail pool 212 | SET p,:avail 24: P <= AVAIL 213 | 214 | // update avail to sequentially next node in pool 215 | ADD :avail,:avail,8 25: 216 | 217 | // suc reuses same base address as count since SUC field is at same 218 | // offset in successor node as COUNT field in object 219 | STT k,suc,p 26: SUC(P) <- k 220 | 221 | // scale j index to octabytes 222 | SL j,j,3 27: 223 | 224 | LDTU t,top,j 28: NEXT(P) <- TOP[j] 225 | 226 | // next reuses same base address as top since NEXT field is at same 227 | // offset in successor node as TOP field in object 228 | STTU t,next,p 29: 229 | 230 | STTU p,top,j 30: TOP[j] <- P 231 | 232 | // update left, right addresses to next pair 233 | T2 LDT j,left,i 31: T2 Next relation 234 | LDT k,right,i 32: 235 | 236 | // count up to zero bytes 237 | ADD i,i,8 33: i <- i+1 238 | 239 | // branch likely taken because input buffer is traversed sequentially 240 | // pair left zero means end of input or sentinel past buffer 241 | PBNZ j,T3 34: End of input or buffer? 242 | 243 | // branch taken if input does not fill buffer 244 | 1H BNP i,T4 35: End of input? 245 | 246 | // refill buffer with more input data 247 | TRAP 0,:Fread,Fin 36: Read next buffer 248 | 249 | // reinitialize i to -size for negative indexing from end of buffer 250 | NEG i,size 37: i <- 0 251 | JMP T2 38: 252 | 253 | T4 TRAP 0,:Fclose,Fin 39: T4 Scan for zeros 254 | 255 | // end of input phase 256 | 257 | // begin topological sort 258 | 259 | // fill queue of objects with zero predecessors 260 | // initialize rear of queue to zero 261 | SET r,0 40: R <- 0 262 | 263 | // k current node index scaled to octabytes 264 | // initialize k to n for reverse iteration over array of objects 265 | SL k,n,3 41: k <- n 266 | 267 | // check object predecessor count 268 | 1H LDT t,k,count 42: Examine COUNT[k], 269 | 270 | // branch likely taken 271 | PBNZ t,0F 43: and if it is zero, 272 | 273 | // object has no predecessors 274 | // insert object value at rear of queue 275 | // update rear to object index 276 | STT k,qlink,r 44: set QLINK[R] <- k, 277 | SET r,k 45: and R <- k 278 | 279 | // decrement k by 1 octabyte toward zero 280 | 0H SUB k,k,8 46: 281 | PBP k,1B 47: For n >= k > 0 282 | 283 | // f is front of queue 284 | LDT f,qlink,0 48: F <- QLINK[0] 285 | 286 | // prepare to output sorted list 287 | LDA $255,OutOpen 49: Open output file 288 | TRAP 0,:Fopen,Fout 50: 289 | 290 | // initialize i to negative size to offset from buffer end 291 | NEG i,size 51: Point i to the buffer start 292 | 293 | JMP T5 52: 294 | 295 | // branch likely taken because buffer has some capacity 296 | T5B PBN i,0F 53: Jump if buffer is not full 297 | 298 | LDA $255,IOArgs 54: 299 | TRAP 0,:Fwrite,Fout 55: Flush output buffer 300 | 301 | // reinitialize i for negative indexing from end of buffer 302 | NEG i,size 56: Point i to the buffer start 303 | 304 | // count down number of objects sorted so far 305 | 0H SUB n,n,1 57: n <- n-1 306 | 307 | LDTU p,top,f 58: P <- TOP[F] 308 | 309 | // end of queue means no more objects in topological order 310 | BZ p,T7 59: If P = Lambda go to T7 311 | 312 | // decrement predecessors count for a successor 313 | T6 LDT s,suc,p 60: T6 Erase relations 314 | 315 | LDT t,s,count 61: Decrease COUNT[SUC(P)] 316 | SUB t,t,1 62: 317 | STT t,s,count 63: 318 | 319 | // branch likely taken because object will often have predecessors 320 | PBNZ t,0F 64: If zero, 321 | 322 | // add object with no predecessors to output queue 323 | STT s,qlink,r 65: set QLINK[R] <- SUC(P), 324 | SET r,s 66: and R <- SUC(P) 325 | 0H LDT p,next,p 67: P <- NEXT(P) 326 | 327 | // branch likely taken because object will often have successors 328 | PBNZ p,T6 68: If P = Lambda go to T7 329 | 330 | // pop front of output queue 331 | T7 LDT f,qlink,f 69: T7 Remove from queue 332 | 333 | T5 SR t,f,3 70: T5 Output front of queue 334 | 335 | // write output values back into reused input buffer 336 | STT t,left,i 71: Output the value of F 337 | 338 | // increment i by 1 tetra 339 | ADD i,i,4 72: 340 | 341 | // branch likely taken because queue won't be empty 342 | PBNZ f,T5B 73: If F = 0 go to T8 343 | 344 | T8 LDA $255,IOArgs 74: T8 End of process 345 | TRAP 0,:Fwrite,Fout 75: Flush output buffer 346 | TRAP 0,:Fclose,Fout 76: Close output file 347 | 348 | POP 1,0 77: Return n 349 | 350 | PREFIX : 351 | 352 | 353 | Main PUSHJ $0,TSort 354 | 355 | // set exit status to TSort return value in $0 356 | SET $255,$0 357 | // halt 358 | TRAP 0,Halt,0 359 | 360 | -------------------------------------------------------------------------------- /subroutine.mms: -------------------------------------------------------------------------------- 1 | // subroutine.mms 2 | // examples of subroutine calls with PUSHJ and POP 3 | 4 | // to aid section Subroutine calls in 1.3.1', Description of MMIX, 5 | // from Chapter 1, Basic Concepts, of Fascicle 1, MMIX, by Donald Knuth 6 | 7 | LOC Data_Segment 8 | // an array at the beginning of the data segment 9 | buf GREG @ 10 | 11 | // switch to code segment 12 | LOC #100 13 | 14 | // subroutine func expects 5 byte parameters in registers $0-$4 15 | // it immediately writes the parameters to array in memory 16 | func STB $0,buf 17 | STB $1,buf,1 18 | STB $2,buf,2 19 | STB $3,buf,3 20 | STB $4,buf,4 21 | 22 | // func returns 6 byte values in registers $0-$5 23 | // $5 or highest numbered return register gets first return value 24 | // register $5 is the hole 25 | SET $5,'r' 26 | 27 | // extra return values are placed in registers $0 upward 28 | SET $0,'e' 29 | SET $1,'t' 30 | SET $2,'u' 31 | SET $3,'r' 32 | SET $4,'n' 33 | 34 | // func returns 6 registers $0-$5 to caller 35 | // func returns to offset 0 to address in register rJ 36 | POP 6,0 37 | 38 | 39 | // set local registers $0-$3 40 | // Main intends to retain these values during function call 41 | Main SET $0,'m' 42 | SET $1,'a' 43 | SET $2,'i' 44 | SET $3,'n' 45 | 46 | // register $4 is hole 47 | // $4 will have the first return value from subroutine 48 | // set $4 to some throwaway value 49 | SET $4,'X' 50 | 51 | // prepare 5 byte parameters to pass to func in registers $5-$9 52 | SET $5,'p' 53 | SET $6,'a' 54 | SET $7,'r' 55 | SET $8,'a' 56 | SET $9,'m' 57 | 58 | // save the four local registers $0-$3 and call func 59 | // register $4 is overwritten with the number of saved registers i.e. 4 60 | // register $4 is the hole, it will be inaccessible to func 61 | // register $4 later will have the first return value 62 | // registers $5, $6 and higher will be available to func as $0, $1 etc 63 | // return address register rJ is set to @+4 i.e. next instruction 64 | PUSHJ 4,func 65 | 66 | // get 6 return values from func in registers $4-$9 67 | // write the values to array in memory 68 | STB $4,buf,5 69 | STB $5,buf,6 70 | STB $6,buf,7 71 | STB $7,buf,8 72 | STB $8,buf,9 73 | STB $9,buf,10 74 | 75 | // write values saved in local registers $0-$3 before function call to memory 76 | STB $0,buf,11 77 | STB $1,buf,12 78 | STB $2,buf,13 79 | STB $3,buf,14 80 | 81 | // halt 82 | TRAP 0,Halt,0 83 | 84 | --------------------------------------------------------------------------------