Category: chess

  • RISC-V hexdump program

    I wrote a small program in Risc-V that does a hex dump similarly to the one in chastehex. This is not the full chastehex program but it at least mimics the hexdump feature perfectly. Here is a screenshot of it working.

    Source Code

    #hexdump for RISC-V emulator: rars
    .data
    title: .asciz "hexdump program in RISC-V assembly language\n\n"
    
    # test string of integer for input
    test_int: .asciz "10011101001110011110011"
    hex_message: .asciz "Hex Dump of File: "
    file_message_yes: "The file is open.\n"
    file_message_no: "The file could not be opened.\n"
    file_data: .byte '?':16
               .byte 0
    space_three: .asciz "   "
    
    #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
    mv s11,s0 #save the filename in register s11 so we can use it any time
    
    li a7,1024 # open file call number
    mv a0,s11  # 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)
    ecall
    
    mv s0,a0
    #jal putint
    
    blt s0,zero,file_error # branch if argc is not equal to zero
    
    mv s9,s0 # save the find handle in register s9
    la s0,file_message_yes
    #jal putstring
    jal hexdump
    
    j exit
    
    file_error:
    
    la s0,file_message_no
    jal putstring
    
    
    j exit
    
    exit:
    li a7, 10     # exit syscall
    ecall
    
    # this is the hexdump function
    
    hexdump:
    addi sp,sp,-4
    sw ra,0(sp)
    
    la s0,hex_message
    jal putstring
    mv s0,s11
    jal putstring
    jal putline
    
    li t0,0    #disable automatic newlines after putint
    la t1,int_newline
    sb t0,0(t1)
    
    li, s10,0 # we will use s10 register as current offset
    
    hex_read_row:
    li a7,63        # read system call
    mv a0,s9        # file handle
    la a1,file_data # where to store data
    li a2,16        # how many bytes to read
    ecall           # a0 will have number of bytes read after this call
    
    mv s3,a0 #save a0 to s3 to keep count of how many bytes read
    mv s2,a0 #save a0 to s2 to keep count of how many bytes read
    
    beq a0,zero,hexdump_end
    
    li s0,8    #change width
    la s1,int_width
    sb s0,0(s1)
    
    mv s0,s10
    add s10,s10,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
    
    mv 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
    #add s1,s1,s3
    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 a7,11
    li a0,10
    ecall
    jr ra
    
    putspace:
    li a7,11
    li a0,' '
    ecall
    jr ra
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    putstring:
    li a7,4      # load immediate, v0 = 4 (4 is print string system call)
    mv a0,s0  # load address of string to print into a0
    ecall
    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:
    
    remu t0,s0,t2 # t0=remainder of the previous division
    divu s0,s0,t2 # s0=s0/t2 (divide s0 by the radix value in t2)
    
    li t5,10 # load t5 with 10 because RISC-V does not allow constants for branches
    blt t0,t5,decimal_digit
    bge t0,t5,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,zero,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:
    
    mv 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 a7,4      # load immediate, v0 = 4 (4 is print string system call)
    mv a0,s0  # load address of string to print into a0
    ecall
    lw ra,0(sp)
    lw s0,4(sp)
    addi sp,sp,8
    jr ra
    
    
    
    
    # RISC-V does not allow constants for branches
    # Because of this fact, the RISC-V version of strint
    # requires a lot more code than the MIPS version
    # Whatever value I wanted to compare in the branch statement
    # was placed in the t5 register on the line before the conditional branch
    # Even though it is completely stupid, it has proven to work
    
    strint:
    
    mv 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,zero,strint_end
    
    #if char is below '0' or above '9', it is outside the range of these and is not a digit
    li t5,'0'
    blt t0,t5,not_digit
    li t5,'9'
    bgt t0,t5,not_digit
    
    #but if it is a digit, then correct and process the character
    is_digit:
    andi 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
    li t5,'A'
    blt t0,t5,not_upper
    li t5,'Z'
    bgt t0,t5,not_upper
    
    is_upper:
    li t5,'A'
    sub t0,t0,t5
    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
    li t5,'a'
    blt t0,t5,not_lower
    li t5,'z'
    bgt t0,t5,not_lower
    
    is_lower:
    li t5,'a'
    sub t0,t0,t5
    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
    

    I don’t yet know a good place for sharing this code. I need a good RISC-V community. Leave me a comment if you have suggestions. In the meantime, I am using this blog, and my github account to back up all these amazing assembly programs I have been writing. RISC-V seems very weird but I am starting to understand it better.

  • RISC-V Assembly chastelib

    I have translated the MIPS version of my chastelib library into RISC-V. The source includes some relevant changes between the two architectures. Both RISC-V and MIPS are very different from Intel Assembly. However, now that the translation is complete, I can run them in simulators and create almost any program that I would have the skill to do in Intel assembly.

    I learned this language from this book by Robert Winkler.
    https://leanpub.com/riscvassemblyprogramming

    # This is the source of the RISC-V 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
    #
    # There is also a MIPS assembly source of this library.
    # The primary differences between the two assembly languages from my experience are:
    # RISC-V requires registers for all branch comparisons
    # RISC-V uses ecall but MIPS uses syscall
    # RISC-V selects system call with a7 but MIPS uses $v0
    # RISC-V does not use dollar signs ($) in the name of registers but MIPS does 
    
    .data
    title: .asciz "A test of Chastity's integer and string conversion functions.\n"
    
    # test string of integer for input
    test_int: .asciz "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
    li s1,16
    
    loop:
    jal putint
    addi s0,s0,1
    blt s0,s1,loop
    
    la s0,test_int # convert string to integer
    jal strint
    
    li t0,10    #change radix
    la t1,radix
    sb t0,0(t1)
    
    li t0,8    #change width
    la t1,int_width
    sb t0,0(t1)
    
    jal putint
    
    li   a7, 10     # exit syscall
    ecall
    
    putstring:
    li a7,4      # load immediate, v0 = 4 (4 is print string system call)
    mv a0,s0  # load address of string to print into a0
    ecall
    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:
    
    remu t0,s0,t2 # t0=remainder of the previous division
    divu s0,s0,t2 # s0=s0/t2 (divide s0 by the radix value in t2)
    
    li t5,10 # load t5 with 10 because RISC-V does not allow constants for branches
    blt t0,t5,decimal_digit
    bge t0,t5,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,zero,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:
    
    mv 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 a7,4      # load immediate, v0 = 4 (4 is print string system call)
    mv a0,s0  # load address of string to print into a0
    ecall
    lw ra,0(sp)
    lw s0,4(sp)
    jr ra
    
    
    
    
    # RISC-V does not allow constants for branches
    # Because of this fact, the RISC-V version of strint
    # requires a lot more code than the MIPS version
    # Whatever value I wanted to compare in the branch statement
    # was placed in the t5 register on the line before the conditional branch
    # Even though it is completely stupid, it has proven to work
    
    strint:
    
    mv 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,zero,strint_end
    
    #if char is below '0' or above '9', it is outside the range of these and is not a digit
    li t5,'0'
    blt t0,t5,not_digit
    li t5,'9'
    bgt t0,t5,not_digit
    
    #but if it is a digit, then correct and process the character
    is_digit:
    andi 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
    li t5,'A'
    blt t0,t5,not_upper
    li t5,'Z'
    bgt t0,t5,not_upper
    
    is_upper:
    li t5,'A'
    sub t0,t0,t5
    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
    li t5,'a'
    blt t0,t5,not_lower
    li t5,'z'
    bgt t0,t5,not_lower
    
    is_lower:
    li t5,'a'
    sub t0,t0,t5
    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
    
  • Chapter 9: Bitwise Operations for Advanced Nerds

    Today’s post is a preview of chapter 9 in my upcoming book on programming in DOS. If anything, this chapter is more of a joke or a meme than actually useful for writing most programs. I was feeling naughty and decided to show how far down the coding rabbit hole I have traveled!

    This chapter contains information which will assist you in understanding more about how computers work, but that in general is not required for MOST programming unless you are trying to operate on individual bits.

    To start out, I will describe 5 essential bitwise operations independently of any specific programming language. This is because these operations exist in every programming language I know of, including Assembly and C.

    After I have explained what the bitwise operations do, I will give examples of how this can be used in Assembly language to substitute for addition and subtraction! You might wonder why you would do this, the fact is that you don’t need to but it is a fun trick that only advanced nerds like me do for a special challenge.

    The Bitwise Operations

    This chapter explains 5 bitwise operations which operate on the bits of data in a computer. For the purpose of demonstration, it doesn’t matter which number the bits represent at the moment. This is because the bits don’t have to represent numbers at all but can represent anything described in two states. Bits are commonly used to represent statements that are true or false. For the purposes of this section, the words AND, OR, XOR are in capital letters because their meaning is only loosely related to the English words they get their name from.

    Bitwise AND Operation

    0 AND 0 == 0
    0 AND 1 == 0	
    1 AND 0 == 0
    1 AND 1 == 1
    

    Think of the bitwise AND operation as multiplication of single bits. 1 times 1 is always 1 but 0 times anything is always 0. That’s how I personally think of it. I guess you could say that something is true only if two conditions are true. For example, if I go to Walmart AND do my job then it is true that I get paid.

    I like to think of the AND operation as the “prefer 0” operation. It will always choose a 0 if either of the two bits is a 0, otherwise, if no 0 is available, it will choose 1.

    Bitwise OR Operation

    0 OR 0 == 0
    0 OR 1 == 1	
    1 OR 0 == 1
    1 OR 1 == 1
    

    The bitwise OR operation can be thought of as something that is true if one or two conditions are true. For example, it is true that playing in the street will result in you dying because you got run over by a car. It is also true that if you live long enough, something else will kill you. Therefore, the bit of your impending death is always 1.

    I like to think of the OR operation as the “prefer 1” operation. It will always choose a 1 if one of the two bits is a 1, otherwise, if no 1 is available, it will choose 0.

    Bitwise XOR Operation

    0 XOR 0 == 0
    0 XOR 1 == 1	
    1 XOR 0 == 1
    1 XOR 1 == 0
    

    The bitwise XOR operation is different because it isn’t really used much for evaluating true or false. Instead, this operation returns 1 if the bits compared are different or 0 if they are the same. This means that any bit, or group of bits, XORed with itself, will always result in 0.

    If you look at my XOR chart above, you will see that using XOR of any bit with a 1 causes the result to be the opposite of the original bit.

    The XOR operation is the quickest way to achieve this bit inversion. If you have a setting that you want to switch on or off, you can toggle it by XORing that bit with 1.

    While the AND, OR, XOR operations can work in the context of individual bits, or groups of them, the next operations, the bit shifts, only make sense in the context of a group of bits. At minimum, you will be operating on 8 bits at a time because a byte is the lowest addressable size of memory.

    Bitwise Left and Right Shift Operations

    Consider the case of the following 8 bit binary value:

    00001000

    This would of course represent the number 8 because a 1 is in the 8’s place value. We can left shift or right shift.

    00001000 ==  8 : is the original byte
    
    00010000 == 16 : original left shift 1
    00000100 ==  4 : original right shift 1
    

    That is really all there is to shifts. They can be used to multiply or divide by a power of two. In some cases, this can be faster than using the mul and div instructions described in chapter 4.

    Example 0: Fake Add

    The following example shows how it is possible to write an addition routine using a combination of the AND,XOR,SHL operations. In this case, the numbers are shown in decimal to be easier for most people to see that the addition is correct.

    org 100h
    
    main:
    
    mov word [radix],10 ; choose radix for integer input/output
    mov word [int_width],1
    
    mov di,1987
    mov si,38
    
    mov ax,di
    call putint
    mov ax,si
    call putint
    call putline
    
    fake_add:
    mov ax,di
    xor di,si
    and si,ax
    shl si,1
    jnz fake_add
    
    mov ax,di
    call putint
    mov ax,si
    call putint
    
    mov ax,4C00h
    int 21h
    
    include 'chastelib16.asm'
    

    If you run it, you will see that the correct result of 2025 which is 1987+38. These are the values we set the di and si registers to before simulating addition with these fancy bitwise operations that make even seasoned programmers run scared.

    But how does this monstrosity of a program work? You see the AND operation keeps track of whether both bits in each place value are 1 or not. If they both are, this means that we have to “carry” those bits as we would do in an ordinary binary division. We store the carry in the si register and then left shift it once each time in the loop. The loop continues until si equals zero and there are no more bits to invert with XOR.

    The fact that it works is easy to work out in my head but I don’t blame you if you can’t visualize it. However, this shows the power of what bit operations can do, even though you will probably never need to do this.

    Example 1: Fake Sub

    In case the fake addition example above wasn’t enough for you, here is a slightly modified example that does a fake subtraction operation using the same operations. Try it out and you will see that it subtracts 38 from 2025 and gets the original 1987.

    org 100h
    
    main:
    
    mov word [radix],10 ; choose radix for integer input/output
    mov word [int_width],1
    
    mov di,2025
    mov si,38
    
    mov ax,di
    call putint
    mov ax,si
    call putint
    call putline
    
    fake_sub:
    xor di,si
    and si,di
    shl si,1
    jnz fake_sub
    
    mov ax,di
    call putint
    mov ax,si
    call putint
    
    mov ax,4C00h
    int 21h
    
    include 'chastelib16.asm'
    

    I will try to explain how this works. You see, we first XOR the di register with the si register. Then, we AND si with the new value of di. This means that the bits in the current place value will only both be 1 if those bits were 0 in di and then were inverted to 1 by the XOR with si. This means that at the start of the loop, destination bit=0 and source bit=1. 0 minus 1 means that we need to “borrow” (I hate that term because it is really stealing because we never give it back). We left shift si as usual and then we keep XORing the new borrow in si until it is zero.

    Also, you may have noticed that I never used the “cmp” instruction to compare si with zero in this examples. This is because the zero flag is automatically updated with most operations. In fact there are places in my standard library of functions (chastelib) where it wasn’t strictly required to compare with “cmp” but I added it for clarity so I could read my code and more easily remember what I was doing.

    But let’s face it, the examples in this chapter are purely for showing off how advanced my knowledge of the binary numeral system and manipulating bits in ways no reasonable person should ever attempt. I must admit, it would be great for an obfuscated code contest to make a program with code that is unreadable to most humans.

  • Chapter 10: Software Licenses

    This blog has turned into most of my rants about computer programming recently, but I still play and teach Chess in case you are interested. But tonight, I spent some time writing another chapter of my programming book, Chastity’s Code Cookbook.

    Chapter 10: Software Licenses

    Perhaps it could be said that once you have written a program, what you do with it is even more important. For most of my life, I never considered the concept of copyright or ownership of the toy programs I wrote. I figured that unless I made a great game or operating system that I would not need to consider writing the terms and conditions about what people can or should do with my work.

    And even now, I don’t think my programs have enough of an impact for anyone to care about software licenses. However, I have found some software licenses that are compatible with my personal philosophy for how software should be shared and distributed.

    Generally, I only recommend software that is considered “Free Software” by the definitions of the Free Software Foundation.

    I have been an advocate of Free Software as it is directly tied to Freedom of speech. I am well aware that Software Freedom and Open source are usually, but not always, the same thing. This page by the FSF on the GNU project links to licenses where you can read the full text. However, I will also provide my summaries based on my understanding.

    https://www.gnu.org/licenses/license-list.html

    GNU GENERAL PUBLIC LICENSE Version 3

    https://www.gnu.org/licenses/gpl-3.0.html

    This section is the abridged and simplified version by Chastity White Rose. In case of confusion, see the original text.

    The General Public License guarantees your Freedom to change a program licensed under it and to share it with others. However, when you share it with others, they must have the same Freedom you do. Therefore, you must give others access to your source code if you choose to distribute your own modified version.

    Part of this Freedom is to include the source code when you distribute it. Source code is the preferred form of the program that makes it possible and/or easy to modify, provided you know the programming language being used. Examples include source files for languages such as C, C++, Assembly, Pascal, or Java. Also included in this definition are build scripts written in Bash, Windows Batch, GNU Make, or any similar system.

    In my opinion, the benefit of the GPL3, as well as past and future versions of it, is that it declares the author does not intend it to be used in proprietary programs. As a programmer and author, I would not want a big tech company to come along and use my code for evil purposes or to restrict others from accessing what I intended to be free.

    At the time of this writing, I don’t think anything I have written is in danger of being misused. Still, I hope that people use my programs if they find them useful, modify them to make them more useful to their purposes, and share their work with others who go on to do the same.

    The reason for restricting proprietary use is so that another person or company can’t take my code, claim to be the original owner, and turn it into something opposed to what I intended. More importantly, I never want someone to charge money for my software. Specifically in the case of software written by me, Chastity White Rose, Free Software is free as in Free Speech and Free Price.

    Considering all this, I place all my code in this book under the GPL3 license so that the Free Software Foundation and entire world of Open Source and Free Software nerds can take action on my behalf if someone ever tries to restrict my work after my death. These words are my statement that you can’t steal what was made to be free.

    GNU Lesser General Public License Version 3

    https://www.gnu.org/licenses/lgpl-3.0.html

    The Lesser GPL is very much like the regular GPL with an important exception. It is to allow proprietary programs to use a library. At first, I didn’t see the point of this; however, when I read the following article, I came to understand better.

    https://www.gnu.org/licenses/why-not-lgpl.html

    The reason it was to the advantage of the Free Software community to allow the GNU C library to be linked by proprietary programs is that it prevents the need for those developers to rely on other proprietary software.

    When I think about it, would I really want someone to have to rely on a C compiler or library made by Microsoft or Apple because they couldn’t use GNU Libc for the proprietary game they made? No, I wouldn’t want that. I will explain my reasons for this.

    Basically, complying with the normal GPL3 prevents someone from profiting from their work. If you are required to provide the source code, then anyone smart enough to compile it on their system can copy and modify your game infinitely without paying you.

    The Lesser GPL3 allows someone, for example, to use a free library to implement the graphics, sound, etc., for their game or utility program without providing the source code to their own program, which uses these libraries, but does not copy code from it.

    In short, if you want to make money, you probably want the Lesser GPL, but if you just want to be a nice person and make your code free for the education and entertainment of all people without promise of reward, the normal GPL serves the purpose best in my opinion.

    There are hundreds of other software licenses to be considered if you have written a program and want to decide which terms to release it under. You can, of course, create your own from scratch, but it might be worth reading about those that already exist to see if they match your ideals.

    Free vs. Proprietary

    I am not against proprietary programs when they are video games for entertainment only. I also believe the programmers should be paid for their work, like any other job, to help them survive.

    But my personal ideals are different from those of most programmers. My goal is to write books to share my code and to teach people how to do things, but I don’t plan to profit off the code because I value individual Freedom more than I do money. I hope that even after my death, others will still learn the joy of computer programming and use it to make great things.

    Writing computer software isn’t just a hobby or a business; it is a ministry. Think about all the software that has been written to create the internet and allow people to write books and share them with the world. Think about the programmers who made video and audio recording, encoding, and formatting possible.

    This book is about computer programming, not religion, but I must say, if you had a message that would save the lives or the souls of others, would you really want to be restricted in what manner you use to share that information? Therefore, I propose that Free Software is a necessity in light of Digital Rights Management and companies like Amazon removing ebooks from people’s devices that they have already paid for.

    Traditional books are dying, and bookstores are closing. If we don’t work together to stop digital book burning, then we lose the final method of sharing words of eternal value.

  • 6502 Assembly monochrome demo

    As if learning Intel x86 Assembly wasn’t weird enough, I have decided to learn some basic 6502 Assembly. This CPU was used in the NES and the Apple 2 computers. I found an Easy 6502 emulator that runs entirely within a web page. The only form of output is the pixels in a window that represent memory addresses on a fictional computer. I made a very cool demo. Below is my source code.

    ;this part draws vertical stripes
    lda #$0
    tax
    tay
    loop_stripe_vertical:
    sta $200,x
    inx
    eor #$1
    iny
    cpy #$0
    bne loop_stripe_vertical
    
    ;this part makes a checkerboard
    lda #$0
    tax
    tay
    loop_checkerboard:
    sta $380,x
    inx
    eor #$1
    
    iny
    cpy #$20
    bne color_keep_checker
    
    pha ;push A to the stack
    lda #$0 ;load A with zero
    tay ;transfer A to Y
    pla ;pull original A back from stack
    eor #$1
    
    color_keep_checker:
    cpx #$0
    bne loop_checkerboard
    
    ;this part draws horizontal stripes
    lda #$0
    tax
    tay
    loop_stripe_horizontal:
    sta $500,x
    inx
    ;eor #$1
    
    iny
    cpy #$20
    bne color_keep
    
    pha ;push A to the stack
    lda #$0 ;load A with zero
    tay ;transfer A to Y
    pla ;pull original A back from stack
    eor #$1
    
    color_keep:
    cpx #$0
    bne loop_stripe_horizontal
    

    When assembled and run, the result looks like this. Small routines that draw stripes and a checkerboard based on the way this specific emulator works.

    This example, while extremely cool, is limited in its usefulness compared to a program that runs on a PC with a way of outputting text to a terminal. If I find the right kind of emulator, I would like to figure out how to rewrite some of my Intel Assembly programs into 6502 Assembly. Honestly, the languages are different but the math is still the same once I find the correct name of the instruction to do what I want. I am following this reference.

    http://www.6502.org/tutorials/6502opcodes.html

  • Chastity Windows Reverse Engineering Notes

    The Windows version of FASM includes header files for the Windows API. It also includes some examples, but not a single one of them were a simple “Hello World” console program.

    Fortunately, I was able to find one that actually assembled and ran on the FASM forum. Below is the source code.

    format PE console
    include 'win32ax.inc'
    .code
    start:
    invoke  WriteConsole, <invoke GetStdHandle,STD_OUTPUT_HANDLE>,"Hello World!",12,0
    invoke  ExitProcess,0
    .end start
    

    To get it working required me to keep the include files in a location I remembered and set the include environment variable. I found this out from the FASM Windows documentation.

    set include=C:\fasm\INCLUDE

    However, there was another problem. The example that I found online and got working uses macros, most specifically, one called “invoke”. While this works if you include the headers, it hides the details of what is actually happening. Therefore, I decided to reverse engineer the process by using NOP instructions to sandwich the bytes of machine code.

    90 hex is the byte for NOP (No OPeration). So to extract the macro call that exits the program, I use this.

    db 10h dup 90h
    invoke  ExitProcess,0
    db 10h dup 90h
    

    Then I disassemble the executable and find the actual instructions given.

    ndisasm main.exe -b 32 > disasm.txt

    As simple as this method is, it actually works. For example, this output is given as part of the output.

    0000022D  90                nop
    0000022E  90                nop
    0000022F  90                nop
    00000230  90                nop
    00000231  90                nop
    00000232  90                nop
    00000233  90                nop
    00000234  90                nop
    00000235  90                nop
    00000236  90                nop
    00000237  90                nop
    00000238  90                nop
    00000239  90                nop
    0000023A  90                nop
    0000023B  90                nop
    0000023C  90                nop
    0000023D  6A00              push dword 0x0
    0000023F  FF1548204000      call dword near [0x402048]
    00000245  90                nop
    00000246  90                nop
    00000247  90                nop
    00000248  90                nop
    00000249  90                nop
    0000024A  90                nop
    0000024B  90                nop
    0000024C  90                nop
    0000024D  90                nop
    0000024E  90                nop
    0000024F  90                nop
    00000250  90                nop
    00000251  90                nop
    00000252  90                nop
    00000253  90                nop
    00000254  90                nop
    

    There can be no mistake that it is that location between the NOPs where the relevant code is. Therefore, I replaced the macro that exits the program with this.

    ;Exit the process with code 0
     push 0
     call [ExitProcess]
    

    What I learned

    As I repeated the same process for the other macros, I found that the way system calls in Windows work is that the numbers are pushed onto the stack in the reverse order they are needed. I was able to decode the macros and get a working program without the use of “invoke”. Here is the full source!

    format PE console
    include 'win32ax.inc'
    
    main:
    
    ;Write 13 bytes from a string to standard output
    push 0              ;this must be zero. I have no idea why!  
    push 13             ;number of bytes to write
    push main_string    ;address of string to print
    push -11            ;STD_OUTPUT_HANDLE = Negative Eleven
    call [GetStdHandle] ;use the above handle
    push eax            ;eax is return value of previous function
    call [WriteConsole] ;all the data is in place, do the write thing!
    
    ;Exit the process with code 0
    push 0
    call [ExitProcess]
    
    .end main
    
    main_string db 'Hello World!',0Ah
    

    I don’t know much about the Windows API, but I did discover some helpful information when I searched the names of these functions that were part of the original macros.

    https://learn.microsoft.com/en-us/windows/console/getstdhandle
    https://learn.microsoft.com/en-us/windows/console/writeconsole

    Why I did this

    You might wonder why I even bothered to get a working Windows API program in Assembly Language. After all, I am a Linux user to the extreme. However, since Windows is the most used operating system for the average person, I figured that if I write any useful programs in Assembly for 32-bit Linux, I can probably port them over to Windows by changing just a few things.

    Since my toy programs are designed to write text to the console anyway and I don’t do GUI stuff unless I am programming a game in C with SDL, I now have enough information from this small Hello World example to theoretically write anything to the console that I might want to in an official Windows executable.

    Obviously I need to learn a lot more for bigger programs but this is the first Assembly program I have ever gotten working for Windows, despite my great success with DOS and Linux, which are easier because they are better documented and ARE TAUGHT BETTER by others. People programming Assembly in Windows have been ruined by macros which hide the actual instructions being used. As I learn how these things work, I will be sure to pass on the information to others!

  • chastehex-DOS-32-bit

    This is a video showing some very long source code of a DOS program I wrote using Assembly Language.

    This program is technically a 16 bit DOS .com program and yet it can access 32 bit addresses of the file it operates on because the LSEEK DOS call uses CX:DX as a 32 bit address by using two 16 bit registers. By modifying several parts of the program, I improved upon it greatly. It should theoretically be able to operate on any file less than 2 gigabytes even though the program itself never accesses more than 16 bytes at one time.

    I may have just invented the world’s smallest and fastest DOS hex dumper and editor. The official gitlab repository has the source code seen in this video as well as the Linux 32 bit Assembly and the original C version I wrote first when I invented this program.

    https://gitlab.com/chastitywhiterose/chastehex.git

    I will have to make more videos showing examples of how it can be used, but I have a readme file in the repository that explains what it does and why I made it.

    ——–D-2142——————————-
    INT 21 – DOS 2+ – “LSEEK” – SET CURRENT FILE POSITION
    AH = 42h
    AL = origin of move
    00h start of file
    01h current file position
    02h end of file
    BX = file handle
    CX:DX = (signed) offset from origin of new file position
    Return: CF clear if successful
    DX:AX = new file position in bytes from start of file
    CF set on error
    AX = error code (01h,06h) (see #01680 at AH=59h/BX=0000h)
    Notes: for origins 01h and 02h, the pointer may be positioned before the
    start of the file; no error is returned in that case (except under
    Windows NT), but subsequent attempts at I/O will produce errors
    if the new position is beyond the current end of file, the file will
    be extended by the next write (see AH=40h); for FAT32 drives, the
    file must have been opened with AX=6C00h with the “extended size”
    flag in order to expand the file beyond 2GB
    BUG: using this method to grow a file from zero bytes to a very large size
    can corrupt the FAT in some versions of DOS; the file should first
    be grown from zero to one byte and then to the desired large size
    SeeAlso: AH=24h,INT 2F/AX=1228h

  • Chastity’s Hex Compare Tool

    Welcome to Chastity’s Hex Compare program also known as “chastecmp”. Enter two filenames as command line arguments such as:

    ./chastecmp file1.txt file2.txt

    It works for any binary files too, not just text. In fact for text comparison you want entirely different tools. This tool can be used to find the tiny differences between files in hexadecimal. It shows only those bytes which are different. I wrote it as a solution to a reddit user who asked how to compare two files in hexadecimal.

    It is an improvement over the Linux “cmp” tool which displays the offsets in decimal and the bytes in octal. Aside from using two different bases in the data, it falls short of usefulness because there are more hex editors than octal editors.

    Here are also some graphical tools I can recommend if you are looking for a GUI instead of my command line program.

    vbindiff
    wxHexeditor

    Below is the full source code:

    #include <stdio.h>
    #include <stdlib.h>
     
    int main(int argc, char *argv[])
    {
     int argx,x;
     FILE* fp[3]; /*file pointers*/
     int c1,c2;
     long flength[3]; /*length of the file opened*/
       
     /*printf("argc=%i\n",argc);*/
    
     if(argc<3)
     {
      printf("Welcome to Chastity's Hex Compare program also known as \"chastecmp\".\n\n");
      printf("Enter two filenames as command line arguments such as:\n");
      printf("%s file1.txt file2.txt\n",argv[0]);
      return 0;
     }
    
     argx=1;
     while(argx<3)
     {
       fp[argx] = fopen(argv[argx], "rb"); /*Try to open the file.*/
       if(!fp[argx]) /*If the pointer is NULL then this becomes true and the file open has failed!*/
       {
        printf("Error: Cannot open file \"%s\": ",argv[argx]);
        printf("No such file or directory\n");
        return 1;
       }
      /*printf("File \"%s\": opened.\n",argv[argx]);*/
    
      printf("fp[%X] = fopen(%s, \"rb\");\n",argx,argv[argx]);
      argx++;
     }
    
     printf("Comparing files %s and %s\n",argv[1],argv[2]);
    
     argx=1;
     while(argx<3)
     {
      fseek(fp[argx],0,SEEK_END); /*go to end of file*/
      flength[argx]=ftell(fp[argx]); /*get position of the file*/
      printf("length of file fp[%X]=%lX\n",argx,flength[argx]);
      fseek(fp[argx],0,SEEK_SET); /*go back to the beginning*/
      argx++;
     }
    
     x=0;
     while(x<flength[1])
     {
      c1 = fgetc(fp[1]);
      c2 = fgetc(fp[2]);
      if(c1!=c2)
      {
       printf("%08X: %02X %02X\n",x,c1,c2);
      }
      x++;  
     }
    
     argx=1;
     while(argx<3)
     {
      fclose(fp[argx]);
      printf("fclose(fp[%X]);\n",argx);
      argx++;
     }
    
     return 0;
    }
    
  • Assembly Chaste Lib

    I have created two functions in x86 Assembly Language that allow me to output any zero terminated string or output any integer in any base from 2 to 36. I will show the full source so people can play with it. This uses system calls on Linux so it won’t work on Windows.

    first, the header file “chaste-lib.asm”

    ; This file is where I keep my function definitions.
    ; These are usually my string and integer output routines.
    
    putstring: ; function to print zero terminated string pointed to by register eax
    
    mov edx,eax ; copy eax to edx as well. Now both registers have the address of the main_string
    
    strlen_start: ; this loop finds the lenge of the string as part of the putstring function
    
    cmp [edx],byte 0 ; compare byte at address edx with 0
    jz strlen_end ; if comparison was zero, jump to loop end because we have found the length
    inc edx
    jmp strlen_start
    
    strlen_end:
    sub edx,eax ; edx will now have correct number of bytes when we use it for the system write call
    
    mov ecx,eax ; copy eax to ecx which must contain address of string to write
    mov eax, 4  ; invoke SYS_WRITE (kernel opcode 4)
    ;mov ebx, 1  ; ebx=1 means write to the STDOUT file
    int 80h     ; system call to write the message
    
    ret ; this is the end of the putstring function return to calling location
    
    
    
    
    
    
    radix dd 16 ;radix or base for integer output. 2=binary, 16=hexadecimal, 10=decimal
    
    putint: ; function to output decimal form of whatever integer is in eax
    
    push eax ;save eax on the stack to restore later
    
    mov ebp,int_string+31 ;address of start digits
    
    digits_start:
    
    mov edx,0;
    mov esi,[radix] ;radix is from memory location just before this function
    div esi
    cmp edx,10
    jb decimal_digit
    jge hexadecimal_digit
    
    decimal_digit: ;we go here if it is only a digit 0 to 9
    add edx,'0'
    jmp save_digit
    
    hexadecimal_digit:
    sub edx,10
    add edx,'A'
    
    
    save_digit:
    
    mov [ebp],dl
    cmp eax,0
    jz digits_end
    dec ebp
    jmp digits_start
    
    digits_end:
    
    mov eax,ebp ; now that the digits have been written to the string, display it!
    call putstring
    
    pop eax  ;load eax from the stack so it will be as it was before this function was called
    ret
    

    next, the main source that includes the header: “main.asm”

    format ELF executable
    entry main
    
    include 'chaste-lib.asm'
    
    main: ; the main function of our assembly function, just as if I were writing C.
    
    ; I can load any string address into eax and print it!
    
    mov ebx, 1 ;ebx must be 1 to write to standard output
    
    mov eax,msg
    call putstring
    mov eax,main_string ; move the address of main_string into eax register
    call putstring
    
    mov [radix],2 ; can choose radix for integer output!
    
    mov eax,0
    loop1:
    call putint
    inc eax
    cmp eax,100h;
    jnz loop1
    
    mov eax, 1  ; invoke SYS_EXIT (kernel opcode 1)
    mov ebx, 0  ; return 0 status on exit - 'No Errors'
    int 80h
    
    ; this is where I keep my string variables
    
    msg db 'Hello World!', 0Ah,0     ; assign msg variable with your message string
    main_string db "This is Chastity's Assembly Language counting program!",0Ah,0
    int_string db 32 dup '?',0Ah,0
    
    
    
    ; This Assembly source file has been formatted for the FASM assembler.
    ; The following 3 commands assemble, give executable permissions, and run the program
    ;
    ;	fasm main.asm
    ;	chmod +x main
    ;	./main
    
    

    If you have the fasm compiler, you can assemble this source on any Linux distribution running on an intel CPU. Assembly is platform specific but it runs really fast and the process of programming with it is enjoyable for me.

  • Assembly Language Counting Program

    I have been learning x86 assembly language. I know a little bit from years ago but now it is coming back to me. I formerly did 16 bit DOS hobby programs. This is 32 bit Linux programming in assembly using FASM as my assembler.

    format ELF executable
    entry main
    
    main: ; the main function of our assembly function, just as if I were writing C.
    
    ; I can load any string address into eax and print it!
    
    mov eax,msg
    call putstring
    mov eax,main_string ; move the address of main_string into eax register
    call putstring
    
    
    mov eax,0
    loop1:
    call putint
    inc eax
    cmp eax,16;
    jnz loop1
    
    mov eax, 1  ; invoke SYS_EXIT (kernel opcode 1)
    mov ebx, 0  ; return 0 status on exit - 'No Errors'
    int 80h
    
    ; this is where I keep my string variables
    
    msg: db 'Hello World!', 0Ah,0     ; assign msg variable with your message string
    main_string db 'This is the assembly counting program!',0Ah,0
    int_string db 32 dup '?',0Ah,0
    
    ; this is where I keep my function definitions
    
    putstring: ; function to print zero terminated string pointed to by register eax
    
    mov edx,eax ; copy eax to edx as well. Now both registers have the address of the main_string
    
    strlen_start: ; this loop finds the lenge of the string as part of the putstring function
    
    cmp [edx],byte 0 ; compare byte at address edx with 0
    jz strlen_end ; if comparison was zero, jump to loop end because we have found the length
    inc edx
    jmp strlen_start
    
    strlen_end:
    sub edx,eax ; edx will now have correct number of bytes when we use it for the system write call
    
    mov ecx,eax ; copy eax to ecx which must contain address of string to write
    mov eax, 4  ; invoke SYS_WRITE (kernel opcode 4)
    mov ebx, 1  ; write to the STDOUT file
    int 80h     ; system call to write the message
    
    ret ; this is the end of the putstring function return to calling location
    
    putint: ; function to output decimal form of whatever integer is in eax
    
    push eax ;save eax on the stack to restore later
    
    mov ebx,int_string+31 ;address of start digits
    
    digits_start:
    
    mov edx,0;
    mov esi,10
    div esi
    add edx,'0'
    mov [ebx],dl
    cmp eax,0
    jz digits_end
    dec ebx
    jmp digits_start
    
    digits_end:
    
    mov eax,ebx ; now that the digits have been written to the string, display it!
    call putstring
    
    pop eax  ;load eax from the stack so it will be as it was before this function was called
    ret
    
    ; This Assembly source file has been formatted for the FASM assembler.
    ; The following 3 commands assemble, give executable permissions, and run the program
    ;
    ;	fasm main.asm
    ;	chmod +x main
    ;	./main
    

    This program only works on computers with Intel x86 CPUs and running a Linux distribution. the “int 80h” instructions mean interrupt 128. For some reason, this calls the Linux kernel. I don’t know why it works this way but I do know enough assembly to write my own string and integer routines to replace printf since it is not available in pure assembly like I could in the C programming language.