Skip to content

Instantly share code, notes, and snippets.

@jac18281828
Last active December 28, 2025 01:42
Show Gist options
  • Select an option

  • Save jac18281828/d9e51c7a47d693d9fbb6f4ef1d0a5139 to your computer and use it in GitHub Desktop.

Select an option

Save jac18281828/d9e51c7a47d693d9fbb6f4ef1d0a5139 to your computer and use it in GitHub Desktop.
Advent of Code 2025 Day 1 Example MMIX
DIAL_INCREMENTS IS 100
LOC #100
% Entry point
GREG @
InputPtr GREG 0 % global register for input pointer
Main LDA InputPtr,MyInput % initialize pointer
SETI $10,50 % dial setting - current dial pointer (starts at 50)
SETI $11,0 % count number of operations handled
SETI $12,0 % hit count - how many times the dial got set to 0
TokenLoop
LDBI $1,InputPtr,0 % check if at end BEFORE parsing
BZ $1,Done % null terminator, done
PUSHJ $0,ParseRotations
PUSHJ $0,HandleRotation
ADDUI $11,$11,1 % increment operations processed
JMP TokenLoop
% ----------------------------------------------------
% HandleRotation
% ----------------------------------------------------
HandleRotation
% operation = direction * magnitude
MUL $5,$2,$3 % apply direction to value
% temp = dial + operation
ADD $0,$10,$5 % move dial in direction of vector
% temp = temp.mod_euclid(DIAL_INCREMENTS)
SETI $1,DIAL_INCREMENTS
PUSHJ $4,RemEuclid
% dial = temp
SET $10,$0
% if dial == 0?
BNZ $10,SkipCount
% if dial is zero increment the number of hits
ADDI $12,$12,1
SkipCount
POP 0,0
% ------------------------------------------------------------
% RemEuclid: $0 := ($0 rem_euclid $1), with $1 > 0
% Input: $0 = dividend, $1 = divisor (must be > 0)
% Output: $0 = remainder in range [0, $1)
% Uses: $2, $3
% ------------------------------------------------------------
RemEuclid DIV $2, $0, $1 % q = trunc(a/m)
MUL $3, $2, $1 % q*m
SUB $0, $0, $3 % r = a - q*m (can be negative)
BNN $0, RemDone % if r >= 0, we're done
ADD $0, $0, $1 % r += m
RemDone POP 1,0
% ----------------------------------------------------
% ParseRotations - Parse ONE rotation from input string
% Input: InputPtr = pointer to current position in string (global)
% Returns: $2 = direction (-1 or +1), $3 = value, InputPtr = updated pointer
% Uses: $1 = current char, $5/$6 = digit scratch
ParseRotations
LDBI $1,InputPtr,0 % load first char
BZ $1,ParseEnd % null terminator
CMPI $2,$1,'L' % check if 'L'
BZ $2,ParseL
CMPI $2,$1,'R'
BZ $2,ParseR
ADDUI InputPtr,InputPtr,1 % skip unknown char
POP 0,0 % return early
ParseL SETI $2,-1 % direction = -1 for left
JMP ParseNumber
ParseR SETI $2,1 % direction = +1 for right
ParseNumber
ADDUI InputPtr,InputPtr,1 % skip L/R char
SETI $3,0 % value accumulator
DigitLoop
LDBI $1,InputPtr,0 % load next char
SUBI $5,$1,'0' % convert to digit
BN $5,EndNumber % < '0'
CMPI $6,$5,10
BNN $6,EndNumber % >= 10
MULI $3,$3,10 % value *= 10
ADD $3,$3,$5 % value += digit
ADDUI InputPtr,InputPtr,1
JMP DigitLoop
EndNumber
ADDUI InputPtr,InputPtr,1 % skip newline/delimiter
ParseEnd
POP 0,0
Done TRAP 0,Halt,0
LOC Data_Segment
MyInput BYTE "L68", '\n', "L30", '\n', "R48", '\n'
BYTE "L5", '\n', "R60", '\n'
BYTE "L55", '\n', "L1", '\n', "L99", '\n'
BYTE "R14", '\n', "L82", '\n', 0
DIAL_INCREMENTS IS 100
LOC #100
% Entry point
GREG @
InputPtr GREG 0 % global register for input pointer
Main LDA InputPtr,MyInput % initialize pointer
SETI $10,50 % dial setting - current dial pointer (starts at 50)
SETI $11,0 % count number of operations handled
SETI $12,0 % hit count - how many times the dial got set to 0
SETI $13,0 % number of zero crossings
TokenLoop
LDBI $1,InputPtr,0 % check if at end BEFORE parsing
BZ $1,Done % null terminator, done
PUSHJ $0,ParseRotations
PUSHJ $0,HandleRotation
ADDUI $11,$11,1 % increment operations processed
JMP TokenLoop
% ----------------------------------------------------
% HandleRotation
% ----------------------------------------------------
HandleRotation
SET $20,$10 % old dial
% turns from raw magnitude
DIVI $21,$3,DIAL_INCREMENTS
ADDU $13,$13,$21
% k = mag rem_euclid 100
SET $0,$3
SETI $1,DIAL_INCREMENTS
PUSHJ $4,RemEuclid
SET $22,$0 % k
% dial update using k
MUL $5,$2,$22
ADD $0,$10,$5
SETI $1,DIAL_INCREMENTS
PUSHJ $4,RemEuclid
SET $10,$0
% partial wrap using k (and signed left)
BNN $2,CrossAbove
BN $2,CrossBelow
JMP CrossyEnd
CrossAbove
BZ $22,CrossyEnd
ADDU $254,$20,$22
CMPI $0,$254,DIAL_INCREMENTS
BN $0,CrossyEnd
ADDI $13,$13,1
JMP CrossyEnd
CrossBelow
BZ $22,CrossyEnd
SUB $254,$20,$22
BNN $254,CrossyEnd
ADDI $13,$13,1
CrossyEnd
BNZ $10,SkipCount
ADDI $12,$12,1
SkipCount
POP 0,0
% ------------------------------------------------------------
% RemEuclid: $0 := ($0 rem_euclid $1), with $1 > 0
% Input: $0 = dividend, $1 = divisor (must be > 0)
% Output: $0 = remainder in range [0, $1)
% Uses: $2, $3
% ------------------------------------------------------------
RemEuclid DIV $2, $0, $1 % q = trunc(a/m)
MUL $3, $2, $1 % q*m
SUB $0, $0, $3 % r = a - q*m (can be negative)
BNN $0, RemDone % if r >= 0, we're done
ADD $0, $0, $1 % r += m
RemDone POP 1,0
% ----------------------------------------------------
% ParseRotations - Parse ONE rotation from input string
% Input: InputPtr = pointer to current position in string (global)
% Returns: $2 = direction (-1 or +1), $3 = value, InputPtr = updated pointer
% Uses: $1 = current char, $5/$6 = digit scratch
ParseRotations
LDBI $1,InputPtr,0 % load first char
BZ $1,ParseEnd % null terminator
CMPI $2,$1,'L' % check if 'L'
BZ $2,ParseL
CMPI $2,$1,'R'
BZ $2,ParseR
ADDUI InputPtr,InputPtr,1 % skip unknown char
POP 0,0 % return early
ParseL SETI $2,-1 % direction = -1 for left
JMP ParseNumber
ParseR SETI $2,1 % direction = +1 for right
ParseNumber
ADDUI InputPtr,InputPtr,1 % skip L/R char
SETI $3,0 % value accumulator
DigitLoop
LDBI $1,InputPtr,0 % load next char
SUBI $5,$1,'0' % convert to digit
BN $5,EndNumber % < '0'
CMPI $6,$5,10
BNN $6,EndNumber % >= 10
MULI $3,$3,10 % value *= 10
ADD $3,$3,$5 % value += digit
ADDUI InputPtr,InputPtr,1
JMP DigitLoop
EndNumber
ADDUI InputPtr,InputPtr,1 % skip newline/delimiter
ParseEnd
POP 0,0
Done TRAP 0,Halt,0
LOC Data_Segment
MyInput BYTE "L68", '\n', "L30", '\n', "R48", '\n'
BYTE "L5", '\n', "R60", '\n'
BYTE "L55", '\n', "L1", '\n', "L99", '\n'
BYTE "R14", '\n', "L82", '\n', 0
#
# cheater - python implementation to confirm the correct answer
# MMIX is correct! ✅
MOD = 100
dial = 50
cross = 0
with open("/mnt/data/input.txt") as f:
for s in map(str.strip, f):
if not s:
continue
dir_ = 1 if s[0] == "R" else -1
mag = int(s[1:])
cross += mag // MOD
k = mag % MOD
old = dial
dial = (dial + dir_ * k) % MOD
if k:
if dir_ > 0 and old + k >= MOD:
cross += 1
if dir_ < 0 and old - k < 0:
cross += 1
print(cross, dial)
# -> 6580 25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment