Category: Uncategorized

  • Rust Programming Language: putint function

    I started learning the Rust programming language and so far I find it more difficult than assembly. However, I finally got a working prototype of my putint function that I have written using both C and Assembly before.

    fn main()
    {
     println!("This is a test of the putint function I wrote for the Rust programming language.");
     let mut i:i32=0;
     while i<256
     {
      putint(i,2,8);
      print!(" ");
      putint(i,16,2);
      print!(" ");
      putint(i,10,3);
      println!();
      i+=1;
     }
    }
    
    /*
     This is the putint function for printing an integer in any base from 2 to 36.
     It is the same function I wrote in C and Assembly but with some key differences for Rust.
    
     Rust doesn't allow global variables in "safe" mode. Therefore, the radix and the int_width must be passed to the function each time.
     I find this inefficient because usually I am just choosing one radix for the duration of the entire program.
     The width also typically stays the same unless I am doing something fancy, such as I did in chastehex.
    
     The first loop stores the correct ASCII numbers as unsigned bytes in an array by repeatedly dividing by the radix and converting the remainder of division into u8 (unsigned 8 bit integer) after adding the correct numbers based on the ASCII table.
    
     The second loop converts these ASCII numbers into the Rust (char) type and prints them in the reverse order of how they were stored.
    
     Most of the code in this function was required only because Rust imposes limitations on what I can do because strings are not simply mutable arrays of bytes like they are in C or Assembly. Additionally the char type is not the same as the char type in C. In C, chars are the same as 1 byte but in Rust they are actually unicode characters that are 4 bytes each.
    
    There is probably a better way to write this function in Rust but this is the first that has worked for me. The code is nearly twice the size of the C version of this function, but it will allow me to print my integers in any radix I want as I continue to learn the Rust programming language and see if it is worth the trouble of learning.
    
    */
    
    fn putint(mut i:i32,radix:i32,int_width:usize)
    {
     let mut a: [u8;32]=[0;32]; //create array of max size needed for 32 bit integer
     let mut width=0; //keeps track of current width of integer (how many digits in the chosen radix)
     let mut r:i32; //used to store the remainder of division
    
     while i!=0 || width<int_width
     {
      r=i%radix;
      i/=radix;
      if r<10 { r+=0x30 }
      else {r+=0x37}
      a[width]=r as u8;
      width+=1;
     }
    
     while width>0
     {
      width-=1;
      print!("{}", a[width] as char );
     }
    }
    

    The output of this program is the following. As you can see, it allows me to customize the radix/base and the width so that everything is lined up neatly in the autistic way I require.

    This is a test of the putint function I wrote for the Rust programming language.
    00000000 00 000
    00000001 01 001
    00000010 02 002
    00000011 03 003
    00000100 04 004
    00000101 05 005
    00000110 06 006
    00000111 07 007
    00001000 08 008
    00001001 09 009
    00001010 0A 010
    00001011 0B 011
    00001100 0C 012
    00001101 0D 013
    00001110 0E 014
    00001111 0F 015
    00010000 10 016
    00010001 11 017
    00010010 12 018
    00010011 13 019
    00010100 14 020
    00010101 15 021
    00010110 16 022
    00010111 17 023
    00011000 18 024
    00011001 19 025
    00011010 1A 026
    00011011 1B 027
    00011100 1C 028
    00011101 1D 029
    00011110 1E 030
    00011111 1F 031
    00100000 20 032
    00100001 21 033
    00100010 22 034
    00100011 23 035
    00100100 24 036
    00100101 25 037
    00100110 26 038
    00100111 27 039
    00101000 28 040
    00101001 29 041
    00101010 2A 042
    00101011 2B 043
    00101100 2C 044
    00101101 2D 045
    00101110 2E 046
    00101111 2F 047
    00110000 30 048
    00110001 31 049
    00110010 32 050
    00110011 33 051
    00110100 34 052
    00110101 35 053
    00110110 36 054
    00110111 37 055
    00111000 38 056
    00111001 39 057
    00111010 3A 058
    00111011 3B 059
    00111100 3C 060
    00111101 3D 061
    00111110 3E 062
    00111111 3F 063
    01000000 40 064
    01000001 41 065
    01000010 42 066
    01000011 43 067
    01000100 44 068
    01000101 45 069
    01000110 46 070
    01000111 47 071
    01001000 48 072
    01001001 49 073
    01001010 4A 074
    01001011 4B 075
    01001100 4C 076
    01001101 4D 077
    01001110 4E 078
    01001111 4F 079
    01010000 50 080
    01010001 51 081
    01010010 52 082
    01010011 53 083
    01010100 54 084
    01010101 55 085
    01010110 56 086
    01010111 57 087
    01011000 58 088
    01011001 59 089
    01011010 5A 090
    01011011 5B 091
    01011100 5C 092
    01011101 5D 093
    01011110 5E 094
    01011111 5F 095
    01100000 60 096
    01100001 61 097
    01100010 62 098
    01100011 63 099
    01100100 64 100
    01100101 65 101
    01100110 66 102
    01100111 67 103
    01101000 68 104
    01101001 69 105
    01101010 6A 106
    01101011 6B 107
    01101100 6C 108
    01101101 6D 109
    01101110 6E 110
    01101111 6F 111
    01110000 70 112
    01110001 71 113
    01110010 72 114
    01110011 73 115
    01110100 74 116
    01110101 75 117
    01110110 76 118
    01110111 77 119
    01111000 78 120
    01111001 79 121
    01111010 7A 122
    01111011 7B 123
    01111100 7C 124
    01111101 7D 125
    01111110 7E 126
    01111111 7F 127
    10000000 80 128
    10000001 81 129
    10000010 82 130
    10000011 83 131
    10000100 84 132
    10000101 85 133
    10000110 86 134
    10000111 87 135
    10001000 88 136
    10001001 89 137
    10001010 8A 138
    10001011 8B 139
    10001100 8C 140
    10001101 8D 141
    10001110 8E 142
    10001111 8F 143
    10010000 90 144
    10010001 91 145
    10010010 92 146
    10010011 93 147
    10010100 94 148
    10010101 95 149
    10010110 96 150
    10010111 97 151
    10011000 98 152
    10011001 99 153
    10011010 9A 154
    10011011 9B 155
    10011100 9C 156
    10011101 9D 157
    10011110 9E 158
    10011111 9F 159
    10100000 A0 160
    10100001 A1 161
    10100010 A2 162
    10100011 A3 163
    10100100 A4 164
    10100101 A5 165
    10100110 A6 166
    10100111 A7 167
    10101000 A8 168
    10101001 A9 169
    10101010 AA 170
    10101011 AB 171
    10101100 AC 172
    10101101 AD 173
    10101110 AE 174
    10101111 AF 175
    10110000 B0 176
    10110001 B1 177
    10110010 B2 178
    10110011 B3 179
    10110100 B4 180
    10110101 B5 181
    10110110 B6 182
    10110111 B7 183
    10111000 B8 184
    10111001 B9 185
    10111010 BA 186
    10111011 BB 187
    10111100 BC 188
    10111101 BD 189
    10111110 BE 190
    10111111 BF 191
    11000000 C0 192
    11000001 C1 193
    11000010 C2 194
    11000011 C3 195
    11000100 C4 196
    11000101 C5 197
    11000110 C6 198
    11000111 C7 199
    11001000 C8 200
    11001001 C9 201
    11001010 CA 202
    11001011 CB 203
    11001100 CC 204
    11001101 CD 205
    11001110 CE 206
    11001111 CF 207
    11010000 D0 208
    11010001 D1 209
    11010010 D2 210
    11010011 D3 211
    11010100 D4 212
    11010101 D5 213
    11010110 D6 214
    11010111 D7 215
    11011000 D8 216
    11011001 D9 217
    11011010 DA 218
    11011011 DB 219
    11011100 DC 220
    11011101 DD 221
    11011110 DE 222
    11011111 DF 223
    11100000 E0 224
    11100001 E1 225
    11100010 E2 226
    11100011 E3 227
    11100100 E4 228
    11100101 E5 229
    11100110 E6 230
    11100111 E7 231
    11101000 E8 232
    11101001 E9 233
    11101010 EA 234
    11101011 EB 235
    11101100 EC 236
    11101101 ED 237
    11101110 EE 238
    11101111 EF 239
    11110000 F0 240
    11110001 F1 241
    11110010 F2 242
    11110011 F3 243
    11110100 F4 244
    11110101 F5 245
    11110110 F6 246
    11110111 F7 247
    11111000 F8 248
    11111001 F9 249
    11111010 FA 250
    11111011 FB 251
    11111100 FC 252
    11111101 FD 253
    11111110 FE 254
    11111111 FF 255
    
    
  • Book Review: Learn Multiplatform Assembly Programming with ChibiAkumas!

    I purchased volume 1, and I was blown away by how much information on the basics of hardware, registers, memory, and number systems was contained. In this review, I will explain how this book helped me and what I used it for.

    I am a hobbyist Assembly programmer who has mostly done Intel/8086 Assembly on Linux and DOS operating systems. However, I have started trying to learn about other types of CPUs and their Assembly languages.

    I learn Assembly because I believe learning Assembly can make someone a better programmer in other languages as well. But beyond that, I like learning about all the different machines there are and their differences. Most importantly, which are easier to learn than others? This book series by ChibiAkumas / Keith ‘Akuyou’ mentions the exact information I need so that I can decide which CPU I want to learn next and what tools to start with.

    I was very pleased with the Kindle edition of volume 1, and so I purchased paperbacks of volumes 1 and 2. My ONLY disappointment with the Kindle edition specifically was that I cannot read it on the Kindle cloud reader, https://read.amazon.com/, on my PC. Because I use Linux, installing a Kindle app on a PC is not an option. I would advise the author to see if there is a way to make it readable in a web browser, like every other Kindle book in my library is. However, at least I will have the paperback soon for an easier reading experience. Reading on a tiny iPhone screen is hard on the eyes.

    https://www.amazon.com/Learn-Multiplatform-Assembly-Programming-ChibiAkumas-ebook/dp/B0BLM31GX1

  • hexdump for MIPS emulator: mars

    I translated the RISC-V assembly hex dump into MIPS assembly. Because of the fact that my RISC-V simulator, rars, is a fork of the MIPS simulator, mars, I just had to do some slight changes in register names and system calls because they are mostly compatible. In fact the main thing is that the MIPS registers must all be prefixed with $. It took a few hours to do.

    Theoretically, it also could be made to be compatible with spim, however I haven’t learned how command line arguments are passed using spim.

    #hexdump for MIPS emulator: mars
    .data
    title: .asciiz "hexdump program in MIPS assembly language\n\n"
    
    # test string of integer for input
    test_int: .asciiz "10011101001110011110011"
    hex_message: .asciiz "Hex Dump of File: "
    file_message_yes: .asciiz "The file is open.\n"
    file_message_no: .asciiz "The file could not be opened.\n"
    file_data: .byte '?':16
               .byte 0
    space_three: .asciiz "   "
    
    #this is the location in memory where digits are written to by the putint function
    int_string: .byte '?':32
    int_newline: .byte 10,0
    radix: .byte 2
    int_width: .byte 4
    
    argc: .word 0
    argv: .word 0
    
    .text
    main:
    
    # at the beginning of the program a0 has the number of arguments
    # so we will save it in the argc variable
    la $t1,argc
    sw $a0,0($t1)
    
    # at the beginning of the program a1 has a pointer to the argument strings
    # so we save it because we may need a1 for system calls
    la $t1,argv
    sw $a1,0($t1)
    
    #Now that the argument data is stored away, we can access it even if it is overwritten.
    #For example, the putstring function uses a0 for system call number 4, which prints a string
    
    la $s0,title
    jal putstring
    
    li $t0,16    #change radix
    la $t1,radix
    sb $t0,0($t1)
    
    li $t0,1    #change width
    la $t1,int_width
    sb $t0,0($t1)
    
    
    # next, we load argc from the memory so we can display the number of arguments
    la $t1,argc
    lw $s0,0($t1)
    #jal putint
    
    beq $s0,$zero,exit # if the number of arguments is zero, exit the program because nothing else to print
    
    # this section processes the filename and opens the file from the first argument
    
    jal next_argument
    #jal putstring
    move $s7,$s0 #save the filename in register s7 so we can use it any time
    
    li $v0,13 # open file call number
    move $a0,$s7  # copy filename for the open call
    li $a1,0    # read only access for the file we will open (rars does not support read+write mode)
    syscall
    
    move $s0,$v0
    #jal putspace
    #jal putint
    
    blt $s0,$zero,file_error # branch if argc is not equal to zero
    
    move $s6,$s0 # save the file handle in register s6
    la $s0,file_message_yes
    #jal putstring
    
    jal hexdump
    
    j exit
    
    file_error:
    
    la $s0,file_message_no
    jal putstring
    
    
    j exit
    
    exit:
    li $v0, 10     # exit syscall
    syscall
    
    
    
    
    
    
    
    
    # this is the hexdump function
    
    hexdump:
    addi $sp,$sp,-4
    sw $ra,0($sp)
    
    la $s0,hex_message
    jal putstring
    move $s0,$s7
    jal putstring
    jal putline
    
    
    li $t0,0    #disable automatic newlines after putint
    la $t1,int_newline
    sb $t0,0($t1)
    
    li, $s5,0 # we will use s5 register as current offset
    
    
    
    hex_read_row:
    li $v0,14        # read system call
    move $a0,$s6        # file handle
    la $a1,file_data # where to store data
    li $a2,16        # how many bytes to read
    syscall           # a0 will have number of bytes read after this call
    
    move $s3,$v0 #save v0 to s3 to keep count of how many bytes read
    move $s2,$v0 #save v0 to s2 to keep count of how many bytes read
    
    beq $v0,$zero,hexdump_end
    
    li $s0,8    #change width
    la $s1,int_width
    sb $s0,0($s1)
    
    move $s0,$s5
    add $s5,$s5,$s3
    jal putint
    jal putspace
    
    li $s0,2    #change width to 2 for the bytes printed this row
    la $s1,int_width
    sb $s0,0($s1)
    
    la $s1,file_data
    hex_row_print:
    lb $s0,0($s1)
    jal putint
    jal putspace
    addi $s1,$s1,1
    
    addi $s2,$s2,-1
    bne $s2,$zero,hex_row_print
    
    #pad the row with extra spaces
    
    move $t2,$s3
    li $t3,16
    extra_row_space:
    beq $t2,$t3,extra_row_space_complete
    la $s0,space_three
    jal putstring
    addi $t2,$t2,1
    j extra_row_space
    extra_row_space_complete:
    
    #now the hex form of the bytes are printed
    #we will filter the text form and also print it
    
    li $s2,0
    la $s1,file_data
    char_filter:
    lb $s0,0($s1)
    
    #if char is below 0x20 or above 0x7E, it is outside the range of printable characters
    
    li $t5,0x20
    blt $s0,$t5,not_printable
    li $t5,0x7E
    bgt $s0,$t5,not_printable
    
    j next_char_index
    
    not_printable:
    li $s0,'.'
    sb $s0,0($s1)
    
    next_char_index:
    addi $s1,$s1,1
    addi $s2,$s2,1
    blt $s2,$s3,char_filter
    
    li $s0,0
    sb $s0,0($s1)   #terminate string with a zero
    
    la $s0,file_data
    jal putstring
    
    
    jal putline
    
    j hex_read_row
    
    hexdump_end:
    lw $ra,0($sp)
    addi $sp,$sp,4
    jr $ra
    
    
    
    
    
    
    
    
    # this function gets the next command line argument and returns it in s0
    # it also decrements the argc variable so that it can be checked for 0 to exit the program if needed by the main program
    
    next_argument:
    
    la $t1,argv
    lw $t0,0($t1) #load the string pointer located in argv into t0 register
    lw $s0,0($t0) #load the data being pointed to by t0 into s0 for displaying the string
    addi $t0,$t0,4 #add 4 to the pointer
    sw $t0,0($t1)  #store the pointer so it will be loaded at the next string if the loop continues
    
    # load the number of arguments from memory, subtract 1, store back to memory
    # then use to compare and loop if nonzero
    la $t1,argc
    lw $t0,0($t1)
    
    addi $t0,$t0,-1
    sw $t0,0($t1)
    
    jr $ra
    
    
    
    putline:
    li $v0,11
    li $a0,10
    syscall
    jr $ra
    
    putspace:
    li $v0,11
    li $a0,' '
    syscall
    jr $ra
    
    
    
    
    
    putstring:
    li $v0,4      # load immediate, v0 = 4 (4 is print string system call)
    move $a0,$s0  # load address of string to print into a0
    syscall
    jr $ra
    
    
    #this is the intstr function, the ultimate integer to string conversion function
    #just like the Intel Assembly version, it can convert an integer into a string
    #radixes 2 to 36 are supported. Digits higher than 9 will be capital letters
    
    intstr:
    
    la $t1,int_newline # load target index address of lowest digit
    addi $t1,$t1,-1
    
    lb $t2,radix     # load value of radix into $t2
    lb $t4,int_width # load value of int_width into $t4
    li $t3,1         # load current number of digits, always 1
    
    digits_start:
    
    divu $s0,$s0,$t2 # $s0=$s0/$t2 (divide s0 by the radix value in $t2)
    mfhi $t0        # $t0=remainder of the previous division
    blt $t0,10,decimal_digit
    bge $t0,10,hexadecimal_digit
    
    decimal_digit: # we go here if it is only a digit 0 to 9
    addi $t0,$t0,'0'
    j save_digit
    
    hexadecimal_digit:
    addi $t0,$t0,-10
    addi $t0,$t0,'A'
    
    save_digit:
    sb $t0,($t1) # store byte from $t0 at address $t1
    beq $s0,$0,intstr_end
    addi $t1,$t1,-1
    addi $t3,$t3,1
    j digits_start
    
    intstr_end:
    
    li $t0,'0'
    prefix_zeros:
    bge $t3,$t4,end_zeros
    addi $t1,$t1,-1
    sb $t0,($t1) # store byte from $t0 at address $t1
    addi $t3,$t3,1
    j prefix_zeros
    end_zeros:
    
    move $s0,$t1
    
    jr $ra
    
    #this function calls intstr to convert the $s0 register into a string
    #then it uses a system call to print the string
    #it also uses the stack to save the value of $s0 and $ra (return address)
    
    putint:
    addi $sp,$sp,-8
    sw $ra,0($sp)
    sw $s0,4($sp)
    jal intstr
    #print string
    li $v0,4      # load immediate, v0 = 4 (4 is print string system call)
    move $a0,$s0  # load address of string to print into a0
    syscall
    lw $ra,0($sp)
    lw $s0,4($sp)
    addi $sp,$sp,8
    jr $ra
    
    
    
    
    strint:
    
    move $t1,$s0 # copy string address from $s0 to $t1
    li $s0,0
    
    lb $t2,radix     # load value of radix into $t2
    
    read_strint:
    lb $t0,($t1)
    addi $t1,$t1,1
    beq $t0,0,strint_end
    
    #if char is below '0' or above '9', it is outside the range of these and is not a digit
    blt $t0,'0',not_digit
    bgt $t0,'9',not_digit
    
    #but if it is a digit, then correct and process the character
    is_digit:
    and $t0,$t0,0xF
    j process_char
    
    not_digit:
    #it isn't a digit, but it could be perhaps and alphabet character
    #which is a digit in a higher base
    
    #if char is below 'A' or above 'Z', it is outside the range of these and is not capital letter
    blt $t0,'A',not_upper
    bgt $t0,'Z',not_upper
    
    is_upper:
    
    sub $t0,$t0,'A'
    addi $t0,$t0,10
    j process_char
    
    not_upper:
    
    #if char is below 'a' or above 'z', it is outside the range of these and is not lowercase letter
    blt $t0,'a',not_lower
    bgt $t0,'z',not_lower
    
    is_lower:
    
    sub $t0,$t0,'a'
    addi $t0,$t0,10
    j process_char
    
    not_lower:
    
    #if we have reached this point, result invalid and end function
    #this is only reached if the byte was not a valid digit or alphabet character
    j strint_end
    
    process_char:
    
    bgt $t0,$t2 strint_end #;if this value is above or equal to radix, it is too high despite being a valid digit/alpha
    
    mul $s0,$s0,$t2 # multiply $s0 by the radix
    add $s0,$s0,$t0     # add the correct value of this digit
    
    j read_strint # jump back and continue the loop if nothing has exited it
    
    strint_end:
    
    jr $ra
    
    

    To assemble and run this source, this command will do:

    java -jar ~/Mars4_5.jar main.s pa testfile

  • Hacking Minecraft Java Edition with chastehex

    The save files on my Linux machine are here:

    /home/chastity/.minecraft/saves/

    I decided to see if my chastehex program would be any use at editing save files of Minecraft.

    The first step is to use chastecmp (another program I wrote) to compare two files of slight differences.

    The first target was the level.dat file. I had created a new world, collected 3 oak logs from a tree, and saved.

    Debian Linux detected that the level.dat file was actually a gzip archive. Therefore, to get the true data, these commands are required. The first copies it so that it has a “.gz” extension. The second decompresses it.

    cp level.dat file0.gz
    gzip -d file0.gz
    

    Now there is a file named file0 which has the raw data uncompressed. The next step is to load up the game, make a small change and then resave the file. I threw away one oak log so that I had 2 instead of 3. Then I saved the game and ran these commands to get the uncompressed data for the second file.

    cp level.dat file1.gz
    gzip -d file1.gz
    

    I compared the two files with chastecmp:

    chastecmp file0 file1

    And the result was:

    file0 Opened OK
    file1 Opened OK
    00000052 48 CF 
    000003E9 48 CF 
    000007FE 48 CF 
    000009F0 03 02 
    000011D2 69 68 
    000011D3 0E 87 
    000011E5 FB 74 
    0000124B 72 81 
    0000124C EE C7 
    0000124D 99 BA 
    file0 has reached EOF
    

    It appears that address 9F0 contains the amount of oak logs in the first item slot in the game.

    I ran this command to change that byte to 20 hex/32 decimal

    chastehex file0 9F0 20

    The next step is to recompress the data into a level.dat file that the game expects to load.

    gzip -k file0
    cp file0.gz level.dat
    

    Amazingly, when I loaded the game, I did in fact have 32 oak logs. Obviously this process requires multiple steps and is painfully slow, however it proves that my command line tools can hack Minecraft because there is compression but no encryption in the files.

    The level.dat file seems to contain the player’s inventory and other important data. I remember this from experiments with it years ago.

    Warning

    The addresses and what they mean can change wildly as new data is added to the file. For example, I found a village and there were 5 iron ingots in a chest. I repeated the steps above to create two uncompressed files.

    The difference between the files in this case were that I threw 3 of the ingots on the ground and so the number had changed from 5 to 2 in the chastecmp file0 file1 output

    00000F5F 05 02

    So then I ran the command to change the count to 64 (40 hex)

    chastehex file0 F5F 40

    Upon recompressing the file and putting it back in the game folder, I did have 64 ingots. I had previously tried numbers higher than 64 but unfortunately the results were not good. I ended up with only one ingot instead. Therefore, it is good to stick within the limits of what the game expects for the cheating to be successful.

    But because the addresses change as new data gets added to the game, cheating by hex editing is painfully slow on Minecraft. This is best done on a brand new world, both because the data is small and also so that you don’t corrupt worlds you have been playing on longer.

    But I did it as a proof of concept just to see if the programs I wrote can be used to hack Minecraft. The answer is yes, with a little help from gzip for decompression and recompression.

    But what if I told you there was an easier way to cheat at Minecraft Java Edition? You see, the secret still lies with the level.dat file. You don’t actually need to use chastehex, chastecmp, or compression. This is because the file contains the player inventory but not the items they have stored in chests in the world! This allows for item duplication.

    So place the items you want to duplicate in your inventory. Then save the game and backup the file.

    cp level.dat backup.dat

    Then place those items in a chest then save again. Restore the file you backed up earlier.

    cp backup.dat level.dat

    When you reload the game, the files will still be in the chest but your player will also be holding them. This means you can infinitely duplicate any item you can obtain in the game normally by just copying save files repeatedly.

    Why then, did I go through the process of showing how to cheat with chastehex? Because the point is not so much about hacking the game, or what the game is, but it is about testing the programs I wrote. I care more about my C and Assembly programming skills than I do the outcome of a game.

    The point is not whether you win or lose the game. The point is that the game was made by humans and can be broken by humans. Sometimes, as in Castle of the Winds or Cave Story, hacking with a hex editor is the most reliable method. In a game like Minecraft, sometimes cheating is easier because of player and world data being in completely separate files.

  • MIPS Assembly chastelib

    I have been learning the Assembly language of MIPS recently, just for fun. I have been learning from this book:

    I learned enough to translate my Intel functions into the language of MIPS. My code runs in both the spim and mars simulators.

    Also, this reference was helpful for understanding the division instructions which were not covered by the book written by Robert Winkler:

    I don’t expect to have much use for these functions, but theoretically, I could write nearly anything with them if I put enough time with them. Most of the time I will stick with the Intel Assembly because it runs natively on my computer. I am very glad these simulators exist and were so easy to use. Unfortunately I haven’t found the equivalent simulators I was hoping to for 6502.

    # This is the source of the MIPS version of chastelib
    # The four basic functions have been translated from Intel x86 Assembly
    # They are as follows
    #
    # putstring (prints string pointed to by $s0 register)
    # intstr (converts integer in $s0 register to a string)
    # putint (prints integer in $s0 register by use of the previous two functions)
    # strint (converts a string pointed to by $s0 register into an integer in $s0)
    #
    # Most importantly, the intstr and strint functions depend on a global variable (radix)
    # In fact, these two functions are the foundation of everything.
    # They can convert to and from any radix from 2 to 36
    
    .data
    title: .asciiz "A test of Chastity's integer and string conversion functions.\n"
    
    # test string of integer for input
    test_int: .asciiz "10011101001110011110011"
    
    #this is the location in memory where digits are written to by the putint function
    int_string: .byte '?':32
    int_newline: .byte 10,0
    radix: .byte 2
    int_width: .byte 4
    
    .text
    main:
    
    la $s0,title
    jal putstring
    
    li $s0,0 #we will load the $s0 register with the number we want to convert to string
    
    loop:
    jal putint
    addi $s0,$s0,1
    blt $s0,16,loop
    
    la $s0,test_int # convert string to integer
    jal strint
    
    li $t0,10    #change radix
    sb $t0,radix
    
    li $t0,8    #change width
    sb $t0,int_width
    
    jal putint
    
    li   $v0, 10     # exit syscall
    syscall
    
    
    putstring:
    li $v0,4      # load immediate, v0 = 4 (4 is print string system call)
    move $a0,$s0  # load address of string to print into a0
    syscall
    jr $ra
    
    
    #this is the intstr function, the ultimate integer to string conversion function
    #just like the Intel Assembly version, it can convert an integer into a string
    #radixes 2 to 36 are supported. Digits higher than 9 will be capital letters
    
    intstr:
    
    la $t1,int_newline # load target index address of lowest digit
    addi $t1,$t1,-1
    
    lb $t2,radix     # load value of radix into $t2
    lb $t4,int_width # load value of int_width into $t4
    li $t3,1         # load current number of digits, always 1
    
    digits_start:
    
    divu $s0,$s0,$t2 # $s0=$s0/$t2 (divide s0 by the radix value in $t2)
    mfhi $t0        # $t0=remainder of the previous division
    blt $t0,10,decimal_digit
    bge $t0,10,hexadecimal_digit
    
    decimal_digit: # we go here if it is only a digit 0 to 9
    addi $t0,$t0,'0'
    j save_digit
    
    hexadecimal_digit:
    addi $t0,$t0,-10
    addi $t0,$t0,'A'
    
    save_digit:
    sb $t0,($t1) # store byte from $t0 at address $t1
    beq $s0,$0,intstr_end
    addi $t1,$t1,-1
    addi $t3,$t3,1
    j digits_start
    
    intstr_end:
    
    li $t0,'0'
    prefix_zeros:
    bge $t3,$t4,end_zeros
    addi $t1,$t1,-1
    sb $t0,($t1) # store byte from $t0 at address $t1
    addi $t3,$t3,1
    j prefix_zeros
    end_zeros:
    
    move $s0,$t1
    
    jr $ra
    
    #this function calls intstr to convert the $s0 register into a string
    #then it uses a system call to print the string
    #it also uses the stack to save the value of $s0 and $ra (return address)
    
    putint:
    sw $ra,0($sp)
    sw $s0,4($sp)
    jal intstr
    #print string
    li $v0,4      # load immediate, v0 = 4 (4 is print string system call)
    move $a0,$s0  # load address of string to print into a0
    syscall
    lw $ra,0($sp)
    lw $s0,4($sp)
    jr $ra
    
    
    
    
    
    
    
    
    
    
    
    strint:
    
    move $t1,$s0 # copy string address from $s0 to $t1
    li $s0,0
    
    lb $t2,radix     # load value of radix into $t2
    
    read_strint:
    lb $t0,($t1)
    addi $t1,$t1,1
    beq $t0,0,strint_end
    
    #if char is below '0' or above '9', it is outside the range of these and is not a digit
    blt $t0,'0',not_digit
    bgt $t0,'9',not_digit
    
    #but if it is a digit, then correct and process the character
    is_digit:
    and $t0,$t0,0xF
    j process_char
    
    not_digit:
    #it isn't a digit, but it could be perhaps and alphabet character
    #which is a digit in a higher base
    
    #if char is below 'A' or above 'Z', it is outside the range of these and is not capital letter
    blt $t0,'A',not_upper
    bgt $t0,'Z',not_upper
    
    is_upper:
    
    sub $t0,$t0,'A'
    addi $t0,$t0,10
    j process_char
    
    not_upper:
    
    #if char is below 'a' or above 'z', it is outside the range of these and is not lowercase letter
    blt $t0,'a',not_lower
    bgt $t0,'z',not_lower
    
    is_lower:
    
    sub $t0,$t0,'a'
    addi $t0,$t0,10
    j process_char
    
    not_lower:
    
    #if we have reached this point, result invalid and end function
    #this is only reached if the byte was not a valid digit or alphabet character
    j strint_end
    
    process_char:
    
    bgt $t0,$t2 strint_end #;if this value is above or equal to radix, it is too high despite being a valid digit/alpha
    
    mul $s0,$s0,$t2 # multiply $s0 by the radix
    add $s0,$s0,$t0     # add the correct value of this digit
    
    j read_strint # jump back and continue the loop if nothing has exited it
    
    strint_end:
    
    jr $ra