- Need to copy notes from notebook into this document.
mygamma <- function(z, n) {
n <- floor(z)
res <- 1
for(i in 1:n) {
res <- res * (1 + 1/n)^z/(1 + z/n)
}
res <- res/z
}
- Assembly language used in the Art of Computer Programming.
- Third edition still contains old MIX code, but the updated versions are available at this page.
- Binary language with a word size of 64bits.
- In the previous version, he used individual binary numbers as bytes. He has abandoned such frivolity in his old age.
We can express 64bit words more comfortably in hexadecimal as follows:
0 = 0000 | 4=0100 | 8=1000 | c=1100 |
1 = 0001 | 5=0101 | 9=1001 | d=1101 |
2 = 0010 | 6=0110 | a=1010 | e=1110 |
3 = 0011 | 7=0111 | b=1011 | f=1111 |
This table makes more sense if read downwards from the left.
A convention used with hexadecimals is to prepend # to the number, as in #9e3779b97f4a7c16.
Uppercase ABCDEF are often used, in order to look better.
ADD x, y, z ;means ADD :x, :y, :z
PREFIX Foo: ;change current prefix to FOO
ADD x, y, z ; ADD :Foo:x, Foo:y, Foo:z
PREFIX Bar: ;prefix now Foo:Bar
ADD :x, y, :z ; ADD :x, Foo:Bar:y, :z
PREFIX : ;prefix reverts to :
- A sequence of 8 bits is often called a byte. English can be represented in one byte using the ASCII encoding.
- ASCII is a seven bit code with #00-#1f as control characters, #20-#7e as printing characters, and a delete character #7f
- It was extended in the 70’s to include European languages, called Latin-1 or ISO 8859-1.
- A 16 bit code that supports all languages (Unicode) became popular in the 1990’s.
- ASCII can be represented in Unicode by appending a zero byte to the beginning of the string
- This document uses the term wyde to describe a 16bit qty like the width of a unicode character (this actually isn’t true anymore, since the expansion of Unicode)
- 2 bytes = 1 wyde (16 bit)
- 2 wydes = 1 tetra (32 bit)
- 2 tetras = 1 octa (64 bit)
type | range |
unisgned byte | 0..255 |
unsigned wyde | 0..65,535 |
unsigned tetra | 0..4,294,967,295 |
unsigned octa | 0..18,446,744,073,709,551,615 |
- integers are often represented in two’s complement notation.
- In this notation:
- the leftmost bit represents the sign
- -1 = #ff (bytes) -2^1
- -1 = #ffff (wyde)
- -1 = #ffffffff (tetrabyte)
- -1 = #ffffffffffffffff (octabyte)
- if the leading bit is 1, we subtract 2^n to get the integer corresponding to an n-bit number in this location (hmmm, don’t understand this)
type | range |
signed byte | -128..127 |
signed wyde | -32,768..32767 |
signed tetra | -2,147,483..4,294,967,295, +2,147,483..4,294,967,294 |
signed octa | -9.233*10^18, +9.233*10^18 |
- 2^64 cells of memory
- 2^8 general registers
- 2^5 special registers
- Cells of memory are called
$M[0], m[1], .. M[2^64 -1]$ - General purpose registers are called $0…,$255
- Memory is grouped into 2^63 wydes, M_2[0] = M_2[1] = M[0]M[1]
- Each wydes consists of two consecutive bytes
- 2^62 terabytes, 2^61 octabytes
- M_2[x], M_4[x], M_8[x], wyde, terra, octa
- For M_n[x] etc we ignore the least lg t significant bits of the number
- Special registers called called rA, rB,….rZ, rBB, rTT, rWW, rXX, rYY, rZZ
- Command/instruction is a terabyte with components OP, X, Y, Z
- OP is operation code/opcode which describes the action
- X, Y, Z are the operands
- Operand bytes are always unsigned
- Each of the opcodes has a symbolic name, #20 = ADD
- #20010203 = ADD $1, $2, $3
- In general, ADD $X, $Y, $Z
- Mostly 3 ops, sometimes 2 and occasionally one
- IF 2, written as OP $X $YZ
- INCL $X, $YZ increases register $X amount $YZ
- If only one operand, then commas are not used (i.e. JMP @+1000000)
- hexadecimal form of previous is #f003d090, because JMP
#f0 and 25000
#03d090
- Most opcodes fall into one of a few patterns
- LDB $X, $Y, $Z (load byte)
LDB $X, $Y, $Z ;
LDW $X, $Y, $Z ;
LDT $X, $Y, $Z ;
LDO $X, $Y, $Z ;
These load bytes, wydes, tetras or octas. They load the sum of the unsigned integers represented by $Y + $Z, ignoring carry that occurs from the left
Next, we have the unsigned register transfer instructions.
LDBU $1, $2, $3 ;
LDWU $1, $2, $3 ;
LDTU $1, $2, $3 ;
LDOU $1, $2, $3 ;
LDHT $X, $Y, $Z ;
Load the tetrabyte M_4[A] into the left half of $X, set the right half to zero. Assuming A=1005, this will result in #89abcdef00000000, taking the highest tetra from the original word, and putting this in place of the lower tetra, padding right with zeros.
LDA $X, $Y, $Z ;
Load a memory address into a register. Essentially the same as the ADDU instructions later.
STB $X, $Y, $Z ;
STW $X, $Y, $Z ;
STT $X, $Y, $Z ;
STO $X, $Y, $Z ;
Store a byte, wyde, tetra or octa from a register to memory.
- Overflow is possible if the signed number in the register lies outside the range of the memory field.
STBU $X, $Y, $Z ;
STWU $X, $Y, $Z ;
STTU $X, $Y, $Z ;
STOU $X, $Y, $Z ;
- Unsigned store instructions.
- No overflow occurs with these instructions.
ADD $X, $Y, $Z ;
SUB $X, $Y, $Z ;
MUL $X, $Y, $Z ;
DIV $X, $Y, $Z ;
Division checks for a zero divisor, and puts the remainder into a special register, rR. This can be retrived with a GET $X, rR.
ADDU $X, $Y, $Z ;
SUBU $X, $Y, $Z ;
MULU $X, $Y, $Z ;
DIVU $X, $Y, $Z ;
These opcodes perform unisgned addition, which never overflows. The MULU operation forms a full 16-byte product, and the upper half goes into the himult register, rH.
2ADDU $X, $Y, $Z ;
4ADDU $X, $Y, $Z ;
6ADDU $X, $Y, $Z ;
8ADDU $X, $Y, $Z ;
These instructions multiply $Y by 2/4/6/8 and add $Z. 2ADDU, $X, $Y, $Y is faster than multiplying by 3 if overflow is not a concern.
NEG $X, $Y, $Z ;
NEGU $X, $Y, $Z ;
Negate a signed or unsigned number.
SL $X, $Y, $Z ;shift left x= y*2u^($Z)
SLU $X, $Y, $Z; shift left unsigned u(y)x2u^$Z
SR $X, $Y, $Z; shift right x=y/2u^$Z
SRU $X, $Y, $Z;shift right unsigned
SL and SR are much faster than MUL or DIV for powers of 2. y << z shifting a binary y to the left by z bits y >> z shifting to the right by z bits
CMP $X, $Y, $Z ;compare
CMPU $X, $Y, $Z;compare unsigned
;returns -1, 0 or 1 depending on whether y is less than, equal to or greater than z
- DO the conditional instructions map to the -1, 0, 1 convention for comparisons?
- Answer: yes they do, as per page 14 of Fasicle 1.
CSN $X, $Y, $Z ;conditional set if negative
CSZ $X, $Y, $Z;conditional set if zero
CSP $X, $Y, $Z ;conditional set if positive
CSOD $X, $Y, $Z;conditional set if odd
CSNN $X, $Y, $Z;conditional set if non-negative
CSNZ $X, $Y, $Z;conditional set if non zero
CSNP $X, $Y, $Z;conditional set if nonpositive
CSEV $X, $Y, $Z;conditional set if even
ZSN $X, $Y, $Z;zero or set if negative
ZSZ $X, $Y, $Z;zero or set if zero
ZSP $X, $Y, $Z;zero or set if positive
ZSOD $X, $Y, $Z;zero or set if odd
ZSNN $X, $Y, $Z;zero or set if non-negative
ZSNZ $X, $Y, $Z;zero or set if non-zero
ZSNP $X, $Y, $Z;zero or set if non-positive
ZSEV $X, $Y, $Z;zero or set if even
If $Y is true, $Z is copied to $X
- A register is only negative if its leading (leftmost) bit is 1.
- A register is odd if its trailing digit is 1.
These are performed on the entire 64bit word (octabyte), independently to each bit.
AND $X, $Y, $Z ;bitwise AND
OR $X, $Y, $Z; bitwise OR
XOR $X, $Y, $Z ;bitwise exclusive OR
ANDN $X, $Y, $Z ;bitwise and-not v($X) = v($Y) AND complement($Z)
ORN $X, $Y, $Z ;;bitwise or-not v($X) = v($Y) OR complement($Z)
NAND $X, $Y, $Z; bitwise not-AND
NOR $X, $Y, $Z ;bitwise not-or
NXOR $X, $Y, $Z; bitwise not-exclusive-or
$v\bar $ is the complement of the vector, obtained by changing 0 to 1 and 1 to 0.
Binary rules exist for or, and and exclusive OR.
- ANDing is the same as multiplying/taking the minimum
- ORing is the same as taking the maximum
- ExOR is the same as adding mod 2.
MUX $X, $Y, $Z ;bitwise multiplex - uses special register rM
SADD $X, $Y, $Z ;count number of bit positions where $Y has a 1 while $Z has an 0
Additionally, we can treat an octabyte as a vector of 8 individual bytes, or 4 wydes or two tetras.
BDIF $X, $Y, $Z ;byte difference
WDIF $X, $Y, $Z ;wyde difference
TDIF $X, $Y, $Z ;tetra difference
ODIF $X, $Y, $Z ;octa difference
- This does a saturated subtraction operator, where the result of y - z = max(0, y-z)
- Apparently these ops are important in text processing
- We can also treat an octabyte as an 8*8 matrix, whrere the rows from top to bottom are the bytes of x from left to right
- We can transpose this matrix
- We can define AND and OR as matrix operators.
MOR $X, $Y, $Z ; multiple OR
MXOR $X, $Y, $Z; multiple exclusive OR
FADD $X, $Y, $Z
FSUB $X, $Y, $Z
FMUL $X, $Y, $Z
FDIV $X, $Y, $Z
FREM $X, $Y, $Z ;floating remainder
FSQRT $X, $Z ;floating sqrt
FINT $X, $Z ;floating integer
FCMP $X, $Y, $Z;floating compare
FEQL $X, $Y, $Z;floating equal to
FUN $X, $Y, $Z;floating un-ordered
FCMPE $X, $Y, $Z;floating compare with respect to epsilon
FEQLE $X, $Y, $Z;floating equivalent, epsilon
FUNE $X, $Y, $Z;floating unordered, epsilon
FIX $X, $Z; convert floating to fixed
FIXU $X, $Z ;convert floating to fixed unsigned
FLOT $X, $Z; convert fixed to floating
FLOTU $X, $Z;fixed to floating unsigned
SFLOT $X, $Z; convert fixed to short float
SFLOTU $X, $Z; convert fixed to short float unsigned
LDSF $X, $Y, $Z;load short float
STSF $X, $Y, $Z;store short float
- Rounding modes are available and used by default.
- There are four available
- ROUND_OFF (1)
- ROUND_UP (2)
- ROUND_DOWN (3)
- ROUND_NEAR (4)
- The $Y field of FSQRT, FINT, FIX, FIXU, FLOT, FLOTU, SFLOT, SFLOTU can be used to specify a rounding method. e.g. FIX $X,ROUND_UP,$Z.
- We often need constant numbers which may be multiples of one another.
- Every instruction so far has a version where $Z is the number Z, and is treated as such.
- The opcode for a variant is one greater than the standard one.
- There are variants called wyde immediate constants
- These range from #0000=0 to #ffff=65535
SETH $X, $XZ ;set high wide x=xz*2^48
SETMH $X, $XZ ;set medium high wyde (X=YZ*2^32)
SETML $X, $YZ ;set medium low wyde YZ*2^16
SETL $X, $XZ; set low wyde YZ
INCH $X, YZ ; increase by high wyde
INCMH $X, YZ ; inc medium high wyde
INCML $X, YZ; inc medium low
INCL $X, YZ; inc low wyde
There are also versions of bitwise OR, AND, and ANT not for each of the variants above (high, medium high, medium low and low).
With only 4 of these instructions, we can get any desired octabyte into a register without loading anything from memory.
SETH $0, #0123;
INCMH $0, #4567
INCML $0, #89ab
INCL $0, #cdef
Sets $0 to be #0123456789abcdef.
- The symbol @ is used to indicate the point at which the program is executing.
- Unless otherwise specified, instructions are carried out sequentially in memory
- Jump and branch instructions are some examples
JMP @+4*2 ;jump 8 bytes ahead
- These offsets can be negative
GO $X, $Y, $Z ;jump to an absolute address, specified by the sum of Y and Z
- Original location is placed in register $X
- Can then be jumped back to
BN $X, RA ;branch negative RA is relative address
BZ $X, RA ;branch if zero
BP $X, RA ;branch if pos
BOD $X, RA ;branch if odd
BNN $X, RA ;branch if non-negative
BNZ $X, RA ;branch if non-zero
BNP $X, RA ;branch if non-positive
BEV $X, RA ;branch if even
- Conditional jump that depends on the relative address
- Only two bytes can be used for the relative address -2^18
BN $X, RA ;branch negative RA is relative address
BZ $X, RA ;branch if zero
BP $X, RA ;branch if pos
BOD $X, RA ;branch if odd
BNN $X, RA ;branch if non-negative
BNZ $X, RA ;branch if non-zero
BNP $X, RA ;branch if non-positive
BEV $X, RA ;branch if even
PBN $X, RA ; probable branch negative RA is relative address
PBZ $X, RA ;probable branch if zero
PBP $X, RA ;probable branch if pos
PBOD $X, RA ;probable branch if odd
PBNN $X, RA ;probable branch if non-negative
PBNZ $X, RA ;probable branch if non-zero
PBNP $X, RA ;probable branch if non-positive
PBEV $X, RA ;probable branch if even
- Modern computers perform best when given information about the likeliness of different branches
- These instructions allow the programmer to hint this to the compiler
PUSHJ $X, RA ;push registers and jump
PUSHGO $X, $Y, $Z; push registers and go
- Save all registers $0 to $X
- Move forward one instruction
POP X, XZ; pop registers and return
SAVE $X, 0; save process state
UNSAVE $Z; restore process state
- Pop restores the previously pushed registers
- Save stores all current registers in memory, and put the address of topmost (what the hell does he mean by topmost) stored octabyte into u($X).
- I think he means that you then start instructions from the next octabyte, thus preserving the saved state.
- Unsave restores this information.
- Two types, trips and traps
- Trips invoke a trip handler, which is part of a user program
- Traps invoke a trap handler run by the operating system
- integer divide check (D)
- integer overflow (V)
- float-to-fix overflow (W)
- invalid floating operation (I)
- floating overflow (O)
- floating underflow (U)
- floating division by zero (Z)
- floating inexact (X)
- These codes are stored in the rightmost 8 bits of the arithmetic status register rA
- Stored in the order DVWIOUZX
- The next 8 bits to the left are called the enable bits
- These are the same order
- When a condition of the appropriate type occurs, these bits are checked
- If the bit is 1, then a trip handler is invoked
- trip to #10 for D, #20 for V etc
- The next two bits hold the current rounding mode mod 4
- The remaining 46 bits should be zero
- Programs can change the setting of rA using
TRIP X, Y, Z
TRIP X, YZ
TRIP XYZ
TRAP X, Y, Z
Trip uses five registers:
- rB bootstrap
- rW where interrupted
- rX execution register
- rY Y operand register
- rZ Z operand register
Trap is similar but uses rBB, rWW, rXX, rYY and rZZ. This can be used for input/output operations.
- TRAP 0 can be used to end a program.
- Wrong
- a) b, d, f; b) a, c, e (lowercase a is 97 in ASCII)
- bots
4), mega 1000kb, giga 1000mb, tera, 1000gb
- If
- Yes, unless over/underflow occurs
- Div by 2^64-1 will overflow
- a) False (because of overlow) b) Dunno what rD does
- CMP $X,2^32
CMP $Y, 2^32
If both of these then overflow has occurred
- Actually get $rA, and then check the highest bit (D)
- Check if result is less than either of the two operands
- Check if result is greater than either of the two operands
- BNP (branch if non-positive) - the numbers may be the same
- SADD gets the count of differences, then sum this?
Now we’re getting into actual programs, huzzah!
j IS $0
m IS $1
kk IS $2
xk IS $3
t IS $255
x0 GREG 0
LOC #100
Maximum SL kk,$0,3
LDO m,x0,kk
JMP DecrK
Loop LDO xk,x0,kk
CMP t,xk,m
PBNP t,DecrK
ChangeM SET m,xk
SR j,kk,3
DecrK SUB kk,kk,8
PBP kk,Loop
POP 2,0
argv IS $1
LOC $100
Main LDOU $255,argv,0;
TRAP 0,Fputs,StdOut
GETA $255,String
TRAP 0,Fputs,StdOut
TRAP 0,Halt,0
String BYTE ", world",#a,0
- There can be no spaces between arguments.
- The whole label thing is pretty important, and you must use tabs to separate these sorts of sections.
- A MMIX program always begins at symbolic location Main.
- Register $0 contains the command line arguments
- Register $1 contains the memory address of the first such argument, which is always the name of the program.
- All arguments are placed into consequtive octabytes, ending with an octabyte of all zeros.
- All arguments represented as a string
He then presents an amazing prime calculation algorithm, which I’m going to implement in python (first, then MMIX).
def primes(num):
primes = [2]
n = 3
j = 1
primes.append(n)
j += 1
while j < n:
n += 2
k = 2
rems = []
for each in primes:
q, r = divmod(n, each)
rems.append(r)
print("q is {} and r is {}".format(q, r))
if any(rems)==0:
n += 2
continue
else:
primes.append(n)
if q<primes[k]:
primes.append(n)
k += 1
return(primes)
Abandoning Python for now, going to use Knuth’s code and then convert.
L IS 1025
t IS $255
n GREG 0
q GREG 0
r GREG 0
jj GREG 0
kk GREG 0
pk GREG 0
mm IS kk
LOC Data_Segment
PRIME1 WYDE 2
LOC PRIME1+2*L
ptop GREG @
j0 GREG PRIME1+2-@
BUF OCTA 0
LOC #100
Main SET n,3
SET jj,j0
2H STWU n,ptop,jj
INCL jj,2
3H BZ jj,2F
4H INCL n,2
5H SET kk,j0
6H LDWU pk,ptop,kk
DIV q,n,pk
GET r,rR
BZ r,4B
7H CMP t,q,pk
PBNP t,2B
8H INCL kk,2
JMP 6B
GREG @
Title BYTE "First Five Hundred Primes"
NewLn BYTE #a,0
Blanks BYTE " ",0
2H LDA t,Title
TRAP 0,Fputs,StdOut
NEG mm,2
3H ADD mm,mm,j0
LDA t,Blanks
TRAP 0,Fputs,StdOut
2H LDWU pk,ptop,mm
0H GREG #2030303030000000
STOU 0B,BUF
LDA t,BUF+4
1H DIV pk,pk,10
GET r,rR
INCL r,'0'
STBU r,t,0
SUB t,t,1
PBNZ pk,1B
LDA t,BUF
TRAP 0,Fputs,StdOut
INCL mm,2*L/10
PBN mm,2B
LDA t,NewLn
TRAP 0,Fputs,StdOut
CMP t,mm,2*(L/10-1)
PBNZ t,3B
TRAP 0,Halt,0
Can’t get this to work, it says that X is not defined for OB,BUF line. Google was of no use (perhaps I have found a mistake?). Nope, the mistake was mine. I used letter-0 instead of digit 0. 0B means to move to the previous 0 instruction in the source.
- Symbols a string of letters, digits beginning with a letter. The underscore character is considdred a letter. Unicode symbols may be used for symbols.
- A constant is either
- A decimal constant (unsigned octabyte in radix 10 notation)
- A hexadecimal constant # followed by one or more hexadecimal digits
- A character constant, consisting of a quote, followed by any character other than newline, followed by another quote
- A string constant: characters surrounded by double quotes
- Each instance of a symbol is either a defined symbol or a future reference
- A defined symbol is one that has occurred on a previous line’s label field
- A future reference is one which has not yet appeared in this way
- Every defined symbol has an equivalent value, either pure (unsigned octabyte), or a register number ($0…$255).
- A primary is either
- A symbol
- A constant
- The character @, denoting the current location
- An expression enclosed in parentheses
- A unary operator followed by a primary
- Unary opertators are
- + (does nothing)
- `-` negation, subtracts from zero
- ~ complemntation, changes all bits to opposite
- $ registerisation, convert pure value to a register number
U+1F3E9
Fopen | 1 |
Fclose | 2 |
Fread | 3 |
Fgets | 4 |
Fgetws | 5 |
Fwrite | 6 |
Fputs | 7 |
Fputws | 8 |
Fseek | 9 |
Ftell | 10 |
The symbols for RW are as follows:
TextRead | 0 |
TextWrite | 1 |
BinaryRead | 2 |
BinaryWrite | 3 |
BinaryReadWrite | 4 |
- a) Move to the previous occurence of label 4 in the source. b) it would (maybe)
- Increment this counter each time the line is run
X0 IS @
N IS 100
x0 GREG X0
j IS $0
m IS $1
kk IS $2
xk IS $3
t IS $255
LOC #100
Maximum SL kk,$0,3
LDO m,x0,kk
JMP DecrK
Loop LDO xk,x0,kk
CMP t,xk,m
PBNP t,DecrK
ChangeM SET m,xk
SR j,kk,3
DecrK SUB kk,kk,8
PBP kk,Loop
POP 2,0
Main GETA t,9F
TRAP 0,Fread,StdIn
SET $0,N<<3
1H SR $2,$0,3
LDO $3,x0,$0
SL $2,$2,3
STO $1,x0,$0
STO $3,x0,$2
SUB $0,$0,1<<3
GETA t,9F
TRAP 0,Fwrite,StdOut
TRAP 0,Halt,0
9H OCTA X0+1<<3,N<<3
- Because words are aligned to minimum 2 (wyde)
- Dunno
- L IS 600 (or any N that fits in a byte)
- I really don’t want to do this
14)
L IS 1025
t IS $255
n GREG 0
q GREG 0
r GREG 0
jj GREG 0
kk GREG 0
pk GREG 0
mm IS kk
fq GREG 0
LOC Data_Segment
PRIME1 WYDE 2
LOC PRIME1+2*L
ptop GREG @
j0 GREG PRIME1+2-@
BUF OCTA 0
LOC #100
Main SET n,3
SET jj,j0
2H STWU n,ptop,jj
INCL jj,2
3H BZ jj,2F
4H INCL n,2
5H SET kk,j0
6H LDWU pk,ptop,kk
FSQRT q,n
FREM r,n,pk
FEQL r,r,fq
BNZ r,4B
7H CMP t,q,pk
PBNP t,2B
8H INCL kk,2
JMP 6B
GREG @
Title BYTE "First Five Hundred Primes"
NewLn BYTE #a,0
Blanks BYTE " ",0
2H LDA t,Title
TRAP 0,Fputs,StdOut
NEG mm,2
3H ADD mm,mm,j0
LDA t,Blanks
TRAP 0,Fputs,StdOut
2H LDWU pk,ptop,mm
0H GREG #2030303030000000
STOU 0B,BUF
LDA t,BUF+4
1H DIV pk,pk,10
GET r,rR
INCL r,'0'
STBU r,t,0
SUB t,t,1
PBNZ pk,1B
LDA t,BUF
TRAP 0,Fputs,StdOut
INCL mm,2*L/10
PBN mm,2B
LDA t,NewLn
TRAP 0,Fputs,StdOut
CMP t,mm,2*(L/10-1)
BNZ t,3B
TRAP 0,Halt,0
L IS 1025
t IS $255
n GREG 0
q GREG 0
r GREG 0
jj GREG 0
kk GREG 0
pk GREG 0
mm IS kk
LOC Data_Segment
PRIME1 WYDE 2
LOC PRIME1+2*L
ptop GREG @
j0 GREG PRIME1+2-@
BUF OCTA 0
LOC #100
Main SET n,3
SET jj,j0
2H STWU n,ptop,jj
INCL jj,2
3H BZ jj,2F
4H INCL n,2
5H SET kk,j0
fn GREG 0
sqrtn GREG 0
FLOT fn,n
FSQRT sqrtn,fn
6H LDWU pk,ptop,kk
FLOT t,pk
FREM r,fn,t
BZ r,4B
7H FCMP t,sqrtn,t
PBNP t,2B
8H INCL kk,2
JMP 6B
GREG @
Title BYTE "First Five Hundred Primes"
NewLn BYTE #a,0
Blanks BYTE " ",0
2H LDA t,Title
TRAP 0,Fputs,StdOut
NEG mm,2
3H ADD mm,mm,j0
LDA t,Blanks
TRAP 0,Fputs,StdOut
2H LDWU pk,ptop,mm
0H GREG #2030303030000000
STOU 0B,BUF
LDA t,BUF+4
1H DIV pk,pk,10
GET r,rR
INCL r,'0'
STBU r,t,0
SUB t,t,1
PBNZ pk,1B
LDA t,BUF
TRAP 0,Fputs,StdOut
INCL mm,2*L/10
PBN mm,2B
LDA t,NewLn
TRAP 0,Fputs,StdOut
CMP t,mm,2*(L/10-1)
PBNZ t,3B
TRAP 0,Halt,0
The FP version does more compute operations (which makes sense, as I suspect FREM has to dedo the division that was already done above). I wonder if that div can be removed.
Attempting to change the q to
- Assume a 9*8 matrix of 1 byte elements
- Find a saddle-point if one exists, returning the location
- Return zero if none
- Saddle point: greatest value in col, smallest in row.
row GREG 0
col GREG 0
cur GREG 0
i GREG 0
j GREG 0
Max LDB cur,A,
def saddle_point(matrix):
for row, col in matrix:
saddle_point <- function(matrix) {
minrow <- apply(matrix, 1, min)
maxcol <- apply(matrix, 2, max)
res <- ifelse(minrow==maxcol, 1, 0)
sum(res)
if(sum(res)==0) {
return(0)
}
else {
return(which(minrow==maxcol))
}
}
x0 GREG 0
y0 GREG 0
x1 GREG 0
y1 GREG n
n GREG 0
temp1 GREG 0
temp2 GREG 0
Y WYDE 2
LOC Y+2*n
X WYDE 2
LOC X+2*n
offset GREG 0
STBU x0,n,0
INCL offset,1,0
INCL y0,1,0
STBU y0,n,offset
INCL offset,1,0
SET temp1,X,2*(n-1)
SET temp2,X,2*(n-2)
SET x,
I need more practice writing MMIX. Let’s do something I’ve done many times, fibonnacci
*first, allocate space for the output
N IS 10
ind GREG 0
first GREG 0
second GREG 1
fib GREG 0
n GREG 0
LOC Data_Segment
res WYDE
LOC res+4*N
done GREG @
LOC #100
Main SET fib,0
SET n,100
1H STT first,res,ind
INCL ind,1
STT second,res,ind
ADD fib,first,second
SET first,second
SET second,fib
SUB n,n,1
PBNZ n,1B
FIB OCTA fib,0
LDA $255,FIB
TRAP 0,Fputs,StdOut
TRAP 0,Halt,0
Excercises 1.31
So, we can express 25x as SR r,x,4; 8ADDU r,x,x for a cost of 2v, as opposed to 10v for multiplication.
I kinda want to do some of these in R first.
get_easter_date <- function(year) {
golden <- (year %% 19) + 1
century <- floor(year/100) + 1
correction_x <- floor(3*century/4) - 12
corr_z <- floor((8 * century + 5)/25) - 5
day <- floor(5*year/4) - correction_x - 10
epact <- (11*golden + 20 + corr_z - correction_x)
if(epact> 24 || (epact>25 & golden > 11)) {
epact <- epact + 1
}
N <- 44 - epact
if(N<21) {
N <- N + 30
}
N <- (N + 7) - ((day + N) %% 7)
if(N>31) {
month <- "April"
}
else {
month <- "March"
}
return(paste0(N, " ", month))
}
This does not yet work. It’s funny that I’m writing this code on easter sunday. It’s also worth mentioning that this same code appears in Numerical Recipes, where it is really obscure. This presentation is extremely clear, however.
LOC Data_Segment
GREG @
MAXP IS #2000
InArg OCTA Buffer,MAXP
Buffer BYTE 0
left GREG '('
right GREG ')'
LOC #100
base IS $0
k IS $1
j IS $2
x IS $4
current IS $5
start IS $6
size IS $7
t IS $8
Main LDA $255,InArg
TRAP 0,Fread,StdIn
SET size,$255
INCL size,MAXP
BNP size,Fail
LDA base,Buffer
ADDU base,base,size
NEG k,size
2H LDBU current,k,base
CMP t,current,#20
CSNP current,t,0
STB current,k,base
CMP t,current,'('
PBNZ t,1F
ORL current,#80
STBU current,k,base
0H ADD k,k,1
LDBU start,k,base
BZ start,0B
1H CMP t,current,')'
PBNZ t,0F
ORL start,#80
STBU start,k,base
0H ADD k,k,1
PBN k,2B
SET j,0
Open NEG k,size
1H LDB x,k,base
PBP x,Go
ADD k,k,1
PBN k,1B
Done BNZ j,0F
STB left,base,0
STB right,base,1
SET j,2
0H SET t,#0a
STB t,base,j
ADD j,j,1
SET t,0
STB t,base,j
ADD j,j,1
SET t,0
SET $255,base
TRAP 0,Fputs,StdOut
SET $255,0
Fail TRAP 0,Halt,0
Go STB left,base,j
ADD j,j,1
STBU x,base,j
ADD j,j,1
SET start,x
Succ ORL x,#80
STBU x,k,base
3H ADD k,k,1
LDBU current,k,base
ANDNL current,#80
PBNZ current,1F
JMP 3B
5H STBU current,base,j
ADD j,j,1
NEG k,size
4H LDBU x,k,base
ANDNL x,#80
CMP t,x,current
BZ t,Succ
1H ADD k,k,1
PBN k,4B
CMP t,start,current
PBNZ t,5B
STBU right,base,j
SUB j,j,2
LDB t,base,j
CMP t,t,'('
BZ t,Open
ADD j,j,3
JMP Open
For reference, the syntax for passing arguments to a program in MMIX is as follows
~/mmix/mmix -fperms.txt -t10 permutations.mmo
LOC Data_Segement
T GREG @-#21
BYTE 0
LOC @+#5F
Z IS $9
GREG @
MAXP IS #2000
InArg OCTA Buffer,MAXP
Buffer BYTE 0
left GREG '('
right GREG ')'
LOC #100
base IS $0
k IS $1
j IS $2
x IS $4
current IS $5
start IS $6
size IS $7
t IS $8
Main LDA $255,InArg
TRAP 0,Fread,StdIn
SET size,$255
INCL size,MAXP
BNP size,Fail
LDA base,Buffer
SET k,#21
0H STB k,T,k
ADD k,k,1
CMP t,k,#80
PBN t,0B
SET k,size
JMP 9F
2H LDB X,base,k
CMP t,X,#20
BNP t,9F
CMP t,X,'('
CSZ X,t,j
CSZ j,Z,X
LDB t,T,X
STB Z,T,X
0H SET Z,t
9H SUB k,k,1
PBNN k,2B
Output ADDU base,base,size
SET j,0
SET k,#21
0H LDB X,T,k
CMP t,X,k
PBZ t,2F
PBN X,2F
STB left,base,j
ADD j,j,1
SET Z,k
1H STB Z,base,j
ADD j,j,1
OR t,X,#80
STBU t,T,Z
SET Z,X
LDB X,T,Z
PBNN X,1B
STB right,base,j
ADD j,j,1
2H ADD k,k,1
CMP t,k,#80
PBN t,0B
Done BNZ j,0F
STB left,base,0
STB right,base,1
SET j,2
0H SET t,#0a
STB t,base,j
ADD j,j,1
SET t,0
STB t,base,j
ADD j,j,1
SET t,0
SET $255,base
TRAP 0,Fputs,StdOut
Hmmm, I can’t get this to work. The X and Y variables have not been defined. We do have small x, which could subsitute except that it is also used in the algorithm. Curiouser and curiouser….
:Invert SUBU x,x,1
SET m,n
NEG j,1
2H LDB i,x,m
BN i,5F
3H STB j,x,m
NEG j,m
SET m,i
LDB i,x,m
4H PBP i,3B
SET i,j
5H NEG i,i
STB i,x,m
6H SUB m,m,1
BP m,2B
:Invert SUBU x,x,1
SET k,n
0H LDB i,x,k
NEG i,i
STB i,x,k
SUB k,k,1
PBP k,0B
SET m,n
2H SET i,m
0H SET j,i
LDB i,x,j
PBP i,0B
NEG i,i
LDB k,x,i
STB k,x,j
STB m,x,i
SUB m,m,1
BP m,2B
- So, in the program above, the first LDB/STB loop (from lines 3-7) negates all of the numbers. It’s essentially a while loop over k.
-
- I really don’t understand all of this.
- I need to take some notes on this stuff to solidify it in my mind
- I do feel like I’m getting a little better at MMIX though, I can almost read the damn stuff now.
dieA <- c(5, 5, 5, 5, 1, 1)
dieB <- c(4,3, 4, 4, 3, 6)
dieC <- c(3, 3, 3, 6, 2, 6)
E(A)= 22/6 = 3.67 E(B) = 24/6 = 4 E(C) = 22/6 = 3.67