# adder-exp.m4 # Expanded (unrolled) form of adder code # Doesn't work quite right yet... # But I don't care to fix it right now. # Since self-modifying code is sooooo 1337 it's not even funny. # Go use adder-mod.m4 instead. # Use { and } instead of ` and ' to quote changequote({, }) # === Control structure definitions =========================================== # These definiions help clean up repetitive portions of the code # Usage: for(var, init_value, final_value, expansion) # Sets 'var' equal to 'init_value' and repeatedly increments or decrements # 'var'. One copy of 'expansion' is emitted during each iteration. Iteration # terminates when 'var' equals 'final_value', and 'expansion' is not iterated # for this final case. define(for, {pushdef({$1}, {$2})_for({$1}, {$2}, {$3}, {$4}, ifelse(eval({$2} < {$3}), 1, 1, -1))popdef({$1})}) # Internal helper macro define(_for, {ifelse($1, {$3}, , {$4{}define({$1}, eval($1 + $5))_for({$1}, {$2}, {$3}, {$4}, {$5})})}) # === Pseudoinstruction definitions =========================================== # These definitions make it a little easier to determine the intent of the code # Usage: jr(nreg) # Branches to the address indicated by 'nreg'. define(jr, add($pc, {$1}, $zero)) # Usage: ji(imm16) # Branches to the address indicated by 'imm16'. define(ji, addi($pc, $zero, {$1})) # Usage: jri(imm16) # Branches to the address indicated by 'nreg' + 'imm16'. define(jri, addi($pc, {$1}, {$2})) # Usage: clear(nreg) # Sets 'nreg' equal to 0x00000000. define(clear, add({$1}, $zero, $zero)) # === Miscellaneous definitions =============================================== # More helpful stuff... # Usage: array(name, width) # Reserves 'width' words for an array named 'name'. define(array, pushdef({i})$1:{for}({i}, 1, $2, 0xdeadbeef;) 0xdeadbeef{}popdef({i})) # === Initial values ========================================================== # Set boot-time initial values for memory and registers # These two values help to flush out bugs in the code; 0xdeadbeef shows up in a # hurry in the vm register and memory dumps .nreg: 0xdeadbeef; # Initial value for normal registers .breg: 0xf; # Initial value for BCD registers # Now set some registers to have *useful* values .zero: 0; # $zero = 0, duh .xw: 0; # $xw = 0 (sets BCD x = 0) .yw: 0; # $yw = 0 (sets BCD y = 0) .cf: 0; # $cf = 0 (no prior add to carry from) .pc: @entry; # $pc = entry point # === Entry point ============================================================= # Get and process user input # ----------------------------------------------------------------------------- entry: # Call on the virtual machine to get user input read($r0, $r1, @ascii_in); # Get command beqi($r0, 2, @do_add); # If command is ADD ARRAYS, branch # === Command: IN ARRAY k numeric=string ====================================== # Convert array k from ASCII to BCD # ----------------------------------------------------------------------------- do_in: # Determine how many ASCII words to convert cshri($r2, $r1, 3); # $r2 = num of word pairs, rounded up shli($r2, $r2, 6); # $r2 = num of instructions to skip # Determine which BCD array to load beqi($r0, 1, @use_y); # If using BCD array y, branch # Prepare to initialize BCD array x cshri($xw, $r1, 3); # $xw = number of BCD words in x addi($r3, $zero, @bcd_x); # $r3 = address of BCD x jri($r2, @to_bcd_0); # Start converting # ----------------------------------------------------------------------------- use_y: # Prepare to initialize BCD array y cshri($yw, $r1, 3); # $yw = number of BCD words in y addi($r3, $zero, @bcd_y); # $r3 = address of BCD y jri($r2, @to_bcd_0); # Start converting # ----------------------------------------------------------------------------- # Convert to BCD; include code for all possible array lengths for(i, 0, 17, { to_bcd_{}i{}: for(j, 0, i, { la($uh, $zero, @ascii_in + 32 - (i - j) * 2); # Load more-significant word la($ul, $zero, @ascii_in + 33 - (i - j) * 2); # Load less-significant word sb($u, $r3, i - j - 1); # Store word }) ji(@entry); # Go get more input for(j, 0, eval(63 - 3 * i), {0xdeadbeef;}) # Pad to 64 words per case }) # === Command: ADD ARRAYS ===================================================== # Add the two arrays and display the result # ----------------------------------------------------------------------------- do_add: # Set $r0 to the number of instructions to skip to reach the correct # block of symmetric code min($r0, $xw, $yw); # $r0 = number of symmetric words # Set $r1 to the number of instructions to skip into the asymmetric code add($r1, $r0, $zero); # $r1 = number of symmetric words adds($r1, $r0, 1); # $r1 = symmetric words * 3 adds($r1, $r0, 3); # $r1 = symmetic words * 11 # Finish setting up $r0 shli($r0, $r0, 6); # $r0 = symmetric block to jump to # Determine what kind of symmetry is present bgt($yw, $xw, @case_2); # Branch to @case_2 if $yw > $xw # ----------------------------------------------------------------------------- # Case 1: BCD x has at least as many words as BCD y case_1: # Determine how far to jump to reach the correct asymmetric code block add($zw, $xw, $zero); # $zw = resultant number of words shli($r2, $zw, 6); # $r2 = asymmetric block to jump to add($r2, $r2, $r1); # $r2 = asymmetric instruction to go to addi($r3, $zero, @bcd_x); # $r3 = address of larger BCD array (x) jri($r0, @sym_add_0); # Let's hope this works... # ----------------------------------------------------------------------------- # Case 2: BCD y has more words than BCD x case_2: # Determine how far to jump to reach the correct asymmetric code block add($zw, $yw, $zero); # $zw = resultant number of words shli($r2, $zw, 6); # $r2 = asymmetric block to jump to add($r2, $r2, $r1); # $r2 = asymmetric instruction to go to addi($r3, $zero, @bcd_x); # $r3 = address of larger BCD array (y) jri($r0, @sym_add_0); # Time for addin' # ----------------------------------------------------------------------------- # Symmetric addition of all digits in BCD x and BCD y # Add each word; replicate the code for all possible symmetric word counts for(i, 0, 17, { sym_add_{}i{}: for(j, 0, i, { lb($u, $zero, @bcd_x + j); # Load a word from BCD array x lb($v, $zero, @bcd_y + j); # Load a word from BCD array y for(k, 0, 8, { format({badd($u%d, $u%d, $v%d);}, k, k, k) # Add a pair of digits }) sb($u, $zero, @bcd_z + j); # Store the result in BCD z }) jri($r2, @asy_add_0); # Jump into asymmetric code for(j, 0, eval(63 - 11 * i), {0xdeadbeef;}) # Pad to 64 words per case }) # ----------------------------------------------------------------------------- # Asymmetric addition (number with base $r3 has more digits than the other) # Propagate carry; duplicate the code for all possible word counts for(i, 0, 17, { asy_add_{}i{}: for(j, 0, i, { beqi($cf, 0, @copy_add_{}i + j * 2); # Stop when carry runs out lb($u, $r3, i); # Get word from larger BCD array for(k, 0, 8, { format({baddi($u%d, $u%d, 0);}, k, k) # Propagate any carry }) sb($u, $zero, @bcd_z + j); # Store the result in BCD z }) disp($zw, $zero, @bcd_z); # Display result for(j, 0, eval(63 - 11 * i), {0xdeadbeef;}) # Pad to 64 words per case }) # ----------------------------------------------------------------------------- # Asymmetric addition with no carry (simply copy digits from $r3 to BCD z) # Just copy; repeat code for all possible word counts for(i, 0, 17, { copy_add_{}i{}: for(j, 0, i, { lb($u, $r3, j); # Get word from larger BCD array sb($u, $zero, @bcd_z + j); # Store the result in BCD z }) disp($zw, $zero, @bcd_z); # Display result }) # === Data ==================================================================== # Array values # Name and reserve blocks of memory array(bcd_x, 16); # Input BCD array x array(bcd_y, 16); # Input BCD array y array(bcd_z, 16); # Output BCD array z array(ascii_in, 32); # Input ASCII array # ----------------------------------------------------------------------------- # Nothing to see here... move along now... eof: # vim:syntax=sh # EOF