Tag: coding

  • 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
    
    
  • arithmetic by bitwise operators

    I have started a new programming library in C which simulates arithmetic using only bitwise operators. This is purely for computer science purposes and to prove that it can be done. The hardest part was getting the division function working correctly. I can prove that these functions work with another program I wrote, but I really want to record a video sometime to show the process of how these work.

    For now, here is the source of the 4 arithmetic functions and some global variables used in the division function.

    /*
     bitlib.h
    
     This library simulates the four arithmetic functions: ( addition, subtraction, multiplication, and division )
     Using only bitwise operations: ( AND, OR, XOR, SHL, SHR )
     
     Most of the time I would not need to do this, however, there exist applications where this information may prove useful.
     
     - Programming ARM CPUs which don't have a division instruction.
     - Arbitary Precision Arithmetic involving thousands of digits.
    
    */
    
    int add(int di,int si)
    {
     while(si!=0)
     {
      int ax=di;
      di^=si;
      si&=ax;
      si<<=1;
     }
     return di;
    }
    
    
    int sub(int di,int si)
    {
     while(si!=0)
     {
      di^=si;
      si&=di;
      si<<=1;
     }
     return di;
    }
    
    
    
    int mul(int di,int si)
    {
     int ax=0;
     while(si!=0)
     {
      if((si&1)!=0){ax=add(ax,di);}
      di<<=1;
      si>>=1;
     }
     return ax;
    }
    
    /*
    this division function returns the quotient, but also stores the remainder of division in a global variable
    */
    
    int sign_bit=1<<((sizeof(int)<<3)-1); /*used to extract the most significant bit during division function*/
    
    int mod=0; /*to store the modulus/remainder of the division function*/
    
    int bitdiv(int di,int si)
    {
     int ax=0,bx=0,cx=1;
     if(si==0){return 0;} /*division by zero is invalid*/
    
     while(cx!=0)
     {
      ax<<=1;
      bx<<=1;
      if(di&sign_bit){bx|=1;}
      di<<=1;
      
      if(bx>=si)
      {
       bx=sub(bx,si);
       ax|=1;
      }
     
      cx<<=1;
     }
    
     mod=bx;
     return ax;
    }
    
  • Chastity’s Source for ELF 32-bit executable creation

    I wrote an example program using a custom made ELF-32 header using data declaration statements. I wrote many comments which reference the official specification. This was an exercise both in programming and also Technical Reading and Writing. I had to read enough of the specification PDF file to understand what I was doing. I then tried to write descriptive comments that at the very least I would understand when I need to remind myself how this format is created.

    ;Chastity's Source for ELF 32-bit executable creation
    ;
    ;All data as defined in this file is based off of the specification of the ELF file format.
    ;I first looked at the type of file created by FASM's "format ELF executable" directive.
    ;It is great that FASM can create an executable file automatically. (Thanks Tomasz Grysztar, you are a true warrior!)
    ;However, I wanted to understand the format for theoretical use in other assemblers like NASM.
    
    ;The Github repository with the spec I used is here.
    ;<https://github.com/xinuos/gabi>
    ;And this is the wikipedia article which linked me to the specification document
    ;<https://en.wikipedia.org/wiki/Executable_and_Linkable_Format>
    
    ;This file contains a raw binary ELF32 header created using db,dw,dd commands.
    ;After that, it proceeds to assemble a real "Hello World!" program
    
    ;Header for 32 bit ELF executable (with comments based on specification)
    
    db 0x7F,"ELF" ;ELFMAGIC: 4 bytes that identify this as an ELF file. The magic numbers you could say.
    db 1          ;EI_CLASS: 1=32-bit 2=64-bit
    db 1          ;EI_DATA: The endianness of the data. 1=ELFDATA2LSB 2=ELFDATA2MSB For Intel x86 this is always 1 as far as I know.
    db 1          ;EI_VERSION: 1=EV_CURRENT (ELF identity version 1) (which is current at time of specification Version 4.2 I was using)
    db 9 dup 0    ;padding zeros to bring us to address 0x10
    dw 2          ;e_type: 2=ET_EXEC (executable instead of object file)
    dw 3          ;e_machine : 3=EM_386 (Intel 80386)
    dd 1          ;e_version: 1=EV_CURRENT (ELF object file version.)
    
    p_vaddr=0x8048000
    e_entry=0x8048054 ;we will be reusing this constant later 
    
    dd e_entry    ;e_entry: the virtual address at which the program starts
    dd 0x34       ;e_phoff: where in the file the program header offset is
    db 8 dup 0    ;e_shoff and e_flags are unused in this example,therefore all zeros
    dw 0x34       ;e_ehsize: size of the ELF header
    dw 0x20       ;e_phentsize: size of program header which happens after ELF header
    dw 1          ;e_phnum: How many program headers. Only 1 in this case
    dw 0x28       ;e_shentsize: Size of a section header
    dw 0          ;e_shnum number of section headers
    dw 0          ;e_shstrndx: section header string index (not used here)
    
    ;That is the end of the 0x34 byte (52 bytes decimal) ELF header. Sadly, this is not the end and a program header is also required (what drunk person made this format?)
    
    dd 1          ;p_type: 1=PT_LOAD
    dd 0          ;p_offset: Base address from file (zero)
    dd p_vaddr    ;p_vaddr: Virtual address in memory where the file will be.
    dd p_vaddr    ;p_paddr: Physical address. Same as previous
    
    image_size=0x1000 ;Chosen size for file and memory size. At minimum this must be as big as the actual binary file (code after header included)
                      ;By choosing a default size of 0x1000, I am assuming all assembly programs I write will be less than 4 kilobytes
    
    dd image_size  ;p_filesz: Size of file image of the segment. Must be equal to the file size or greater
    dd image_size  ;p_memsz: Size of memory image of the segment, which may be equal to or greater than file image.
    
    dd 7           ;p_flags: permission flags: 7=4(Read)+2(Write)+1(Execute)
    dd 0           ;p_align; Alignment (none)
    
    ;important FASM directives
    use32          ;tell assembler that 32 bit code is being used
    org e_entry    ;origin of new code begins at the entry point
    
    ;now, the actual hello world program
    mov eax,4      ;invoke SYS_WRITE (kernel opcode 4 on 32 bit systems)
    mov ebx,1      ;write to the STDOUT file
    mov ecx,msg    ;pointer/address of string to write
    mov edx,13     ;number of bytes to write
    int 80h
    
    mov eax,1 ;function SYS_EXIT (kernel opcode 1 on 32 bit systems)
    mov ebx,0 ;return 0 status on exit - 'No Errors'
    int 80h   ;call Linux kernel with interrupt
    
    msg db 'Hello World!',0Ah
    
    ;This is the makefile I use when assembling and running this program
    
    ;main-fasm:
    ;	fasm ELF-32-hello.asm
    ;	chmod +x ELF-32-hello.bin
    ;	./ELF-32-hello.bin
    
    

    I made a repository for examples like this. Others may want to understand the header used on Linux systems.

    https://github.com/chastitywhiterose/ELF

  • Windows chastehex

    This is the source code of the Windows Assembly version of my program I invented called chastehex. I still need to record a video tutorial for how to use it, but it also displays a help message that is self-explanatory. However, most people are do not know how to assemble this from source. The main purpose of posting it on my blog is for my own backup. There is also an executable available on Github for those who want to try it out.

    https://github.com/chastitywhiterose/chastehex/tree/main/asm/windows

    It can arbitrarily read to or write from any address of a file with the correct arguments. It was designed to be part of a script to modify sections of files without requiring the installation of a hex editor. The best part is that it is free and open source because I wrote it and I say so. If you read the Github repository, you will also see that I released it under the GNU GENERAL PUBLIC LICENSE Version 3.

    main.asm

    format PE console
    include 'win32ax.inc'
    include 'chastelibw32.asm'
    
    main:
    
    mov [radix],16 ; Choose radix for integer output.
    mov [int_width],1
    
    ;get command line argument string
    call [GetCommandLineA]
    
    mov [arg_start],eax ;store start of arg string
    
    ;short routine to find the length of the string
    ;and whether arguments are present
    mov ebx,eax
    find_arg_length:
    cmp [ebx], byte 0
    jz found_arg_length
    inc ebx
    jmp find_arg_length
    found_arg_length:
    ;at this point, ebx has the address of last byte in string which contains a zero
    ;we will subtract to get and store the length of the string
    mov [arg_end],ebx
    sub ebx,eax
    mov eax,ebx
    mov [arg_length],eax
    
    ;display the arg string to make sure it is working correctly
    ;mov eax,[arg_start]
    ;call putstring
    ;call putline
    
    ;print the length in bytes of the arg string
    ;mov eax,[arg_length]
    ;call putint
    
    ;this loop will filter the string, replacing all spaces with zero
    mov ebx,[arg_start]
    arg_filter:
    cmp byte [ebx],' '
    ja notspace ; if char is above space, leave it alone
    mov byte [ebx],0 ;otherwise it counts as a space, change it to a zero
    notspace:
    inc ebx
    cmp ebx,[arg_end]
    jnz arg_filter
    
    arg_filter_end:
    
    ;optionally print first arg (name of program)
    ;mov eax,[arg_start]
    ;call putstring
    ;call putline
    
    ;get next arg (first one after name of program)
    call get_next_arg
    cmp eax,[arg_end]
    jz help
    
    mov [file_name],eax
    mov eax,file_open_message
    call putstring
    mov eax,[file_name]
    call putstring
    call putline
    
    jmp open_sesame
    
    help:
    
    mov eax,help_message
    call putstring
    
    jmp args_none
    
    open_sesame:
    
    ;open a file with the CreateFileA function
    ;https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
    
    push 0           ;NULL: We are not using a template file
    push 0x80        ;FILE_ATTRIBUTE_NORMAL
    push 3           ;OPEN_EXISTING
    push 0           ;NULL: No security attributes
    push 0           ;NULL: Share mode irrelevant. Only this program reads the file.
    push 0x10000000  ;GENERIC_ALL access mode (Read+Write)
    push [file_name] ;
    call [CreateFileA]
    
    ;check eax for file handle or error code
    ;call putint
    cmp eax,-1
    jnz file_ok
    
    mov eax,file_error_message
    call putstring
    call [GetLastError]
    call putint
    jmp args_none ;end program if the file was not opened
    
    ;this label is jumped to when the file is opened correctly
    file_ok:
    
    mov [file_handle],eax
    
    mov [int_newline],0 ;disable automatic printing of newlines after putint
    ;we will be manually printing spaces or newlines depending on context
    
    ;before we proceed, we also check for more arguments.
    
    ;get next arg (first one after name of program)
    call get_next_arg
    cmp eax,[arg_end]
    jz hexdump ;proceed to normal hex dump if no more args
    
    ;otherwise interpret the arg as a hex address to seek to
    
    call strint
    mov [file_offset],eax
    mov eax,file_seek_message
    call putstring
    mov eax,[file_offset]
    call putint
    call putline
    
    ;seek to address of file with SetFilePointer function
    ;https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfilepointer
    push 0             ;seek from beginning of file (SEEK_SET)
    push 0             ;NULL: We are not using a 64 bit address
    push [file_offset] ;where we are seeking to
    push [file_handle] ;seek within this file
    call [SetFilePointer]
    
    ;check for more args
    call get_next_arg
    cmp eax,[arg_end]
    jz read_one_byte ;proceed to read one byte mode
    
    ;otherwise, write the rest of the arguments as bytes to the file!
    write_bytes:
    call strint
    mov [byte_array],al
    
    ;write only 1 byte using Win32 WriteFile system call.
    push 0              ;Optional Overlapped Structure 
    push 0              ;Optionally Store Number of Bytes Written
    push 1              ;Number of bytes to write
    push byte_array     ;address to store bytes
    push [file_handle]  ;handle of the open file
    call [WriteFile]
    
    mov eax,[file_offset]
    inc [file_offset]
    mov [int_width],8
    call putint
    call putspace
    
    mov eax,0
    mov al,[byte_array]
    mov [int_width],2
    call putint
    call putline
    
    ;check for more args
    call get_next_arg
    cmp eax,[arg_end]
    jnz write_bytes
    ;continue write if the args still exist
    ;otherwise end program
    jmp args_none
    
    read_one_byte:
    
    ;read only 1 byte using Win32 ReadFile system call.
    push 0              ;Optional Overlapped Structure 
    push bytes_read     ;Store Number of Bytes Read from this call
    push 1              ;Number of bytes to read
    push byte_array     ;address to store bytes
    push [file_handle]  ;handle of the open file
    call [ReadFile]
    
    cmp [bytes_read],1 
    jb print_EOF ;if less than one bytes read, there is an error
    
    mov eax,[file_offset]
    mov [int_width],8
    call putint
    call putspace
    
    mov eax,0
    mov al,[byte_array]
    mov [int_width],2
    call putint
    call putline
    
    jmp args_none
    
    hexdump:
    
    ;read bytes using Win32 ReadFile system call.
    push 0              ;Optional Overlapped Structure 
    push bytes_read     ;Store Number of Bytes Read from this call
    push 16             ;Number of bytes to read
    push byte_array     ;address to store bytes
    push [file_handle]  ;handle of the open file
    call [ReadFile]     ;all the data is in place, do the write thing!
    
    mov eax,[bytes_read]
    ;call putint
    ;mov eax,byte_array
    ;call putstring
    
    cmp [bytes_read],1 
    jb print_EOF ;if less than one bytes read, there is an error
    
    call print_bytes_row
    
    jmp hexdump
    
    print_EOF:
    
    mov eax,[file_offset]
    mov [int_width],8
    call putint
    call putspace
    
    mov eax,end_of_file
    call putstring
    call putline
    
    jmp args_none
    
    
    
    
    ;this loop is very safe because it only prints arguments if they are valid
    ;if the end of the args are reached by comparison of eax with [arg_end]
    ;then it will jump to args_none and proceed from there
    args_list:
    call get_next_arg
    cmp eax,[arg_end]
    jz args_none
    call putstring
    call putline
    jmp args_list
    args_none:
    
    ;Exit the process with code 0
    push 0
    call [ExitProcess]
    
    .end main
    
    arg_start  dd 0 ;start of arg string
    arg_end    dd 0 ;address of the end of the arg string
    arg_length dd 0 ;length of arg string
    arg_spaces dd 0 ;how many spaces exist in the arg command line
    
    ;variables for managing file IO.
    file_name dd 0
    bytes_read dd 0 ;how many bytes are read with ReadFile operation
    byte_array db 16 dup '?',0
    file_handle dd 0
    file_offset dd 0
    
    ;variables for displaying messages
    file_open_message db 'opening: ',0
    file_seek_message db 'seek: ',0
    file_error_message db 'error: ',0
    end_of_file db 'EOF',0
    read_error_message db 'Failure during reading of file. Error number: ',0
    
    help_message db 'Welcome to chastehex! The tool for reading and writing bytes of a file!',0Ah,0Ah
    db 'To hexdump an entire file:',0Ah,0Ah,9,'chastehex file',0Ah,0Ah
    db 'To read a single byte at an address:',0Ah,0Ah,9,'chastehex file address',0Ah,0Ah
    db 'To write a single byte at an address:',0Ah,0Ah,9,'chastehex file address value',0Ah,0Ah
    db 'The file must exist before you launch the program.',0Ah
    db 'This design was to prevent accidentally opening a mistyped filename.',0Ah,0
    
    ;function to move ahead to the next art
    ;only works after the filter has been applied to turn all spaces into zeroes
    get_next_arg:
    mov ebx,[arg_start]
    find_zero:
    cmp byte [ebx],0
    jz found_zero
    inc ebx
    jmp find_zero ; this char is not zero, go to the next char
    found_zero:
    
    find_non_zero:
    cmp ebx,[arg_end]
    jz arg_finish ;if ebx is already at end, nothing left to find
    cmp byte [ebx],0
    jnz arg_finish ;if this char is not zero we have found the next string!
    inc ebx
    jmp find_non_zero ;otherwise, keep looking
    
    arg_finish:
    mov [arg_start],ebx ; save this index to variable
    mov eax,ebx ;but also save it to ax register for use
    ret
    ;we can know that there are no more arguments when
    ;the either [arg_start] or eax are equal to [arg_end]
    
    
    
    ;this function prints a row of bytes
    ;each row is 16 bytes
    print_bytes_row:
    mov eax,[file_offset]
    mov [int_width],8
    call putint
    call putspace
    
    mov ebx,byte_array
    mov ecx,[bytes_read]
    add [file_offset],ecx
    next_byte:
    mov eax,0
    mov al,[ebx]
    mov [int_width],2
    call putint
    call putspace
    
    inc ebx
    dec ecx
    cmp ecx,0
    jnz next_byte
    
    call putline
    
    ret
    

    chastelibw32.asm

    ; This file is where I keep my function definitions.
    ; These are usually my string and integer output routines.
    
    ; function to print zero terminated string pointed to by register eax
    
    stdout dd 1 ; variable for standard output so that it can theoretically be redirected
    
    putstring:
    
    push eax
    push ebx
    push ecx
    push edx
    
    mov ebx,eax ; copy eax to ebx as well. Now both registers have the address of the main_string
    
    putstring_strlen_start: ; this loop finds the lenge of the string as part of the putstring function
    
    cmp [ebx],byte 0 ; compare byte at address ebx with 0
    jz putstring_strlen_end ; if comparison was zero, jump to loop end because we have found the length
    inc ebx
    jmp putstring_strlen_start
    
    putstring_strlen_end:
    sub ebx,eax ;ebx will now have correct number of bytes
    
    ;Write String using Win32 WriteFile system call.
    push 0              ;Optional Overlapped Structure 
    push 0              ;Optionally Store Number of Bytes Written
    push ebx            ;Number of bytes to write
    push eax            ;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 [WriteFile]    ;all the data is in place, do the write thing!
    
    pop edx
    pop ecx
    pop ebx
    pop eax
    
    ret ; this is the end of the putstring function return to calling location
    
    ;this is the location in memory where digits are written to by the putint function
    int_string     db 32 dup '?' ;enough bytes to hold maximum size 32-bit binary integer
    ; this is the end of the integer string optional line feed and terminating zero
    ; clever use of this label can change the ending to be a different character when needed 
    int_newline db 0Ah,0
    
    radix dd 2 ;radix or base for integer output. 2=binary, 8=octal, 10=decimal, 16=hexadecimal
    int_width dd 8
    
    ;this function creates a string of the integer in eax
    ;it uses the above radix variable to determine base from 2 to 36
    ;it then loads eax with the address of the string
    ;this means that it can be used with the putstring function
    
    intstr:
    
    mov ebx,int_newline-1 ;find address of lowest digit(just before the newline 0Ah)
    mov ecx,1
    
    digits_start:
    
    mov edx,0;
    div dword [radix]
    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 [ebx],dl
    cmp eax,0
    jz intstr_end
    dec ebx
    inc ecx
    jmp digits_start
    
    intstr_end:
    
    prefix_zeros:
    cmp ecx,[int_width]
    jnb end_zeros
    dec ebx
    mov [ebx],byte '0'
    inc ecx
    jmp prefix_zeros
    end_zeros:
    
    mov eax,ebx ; now that the digits have been written to the string, display it!
    
    ret
    
    
    ; function to print string form of whatever integer is in eax
    ; The radix determines which number base the string form takes.
    ; Anything from 2 to 36 is a valid radix
    ; in practice though, only bases 2,8,10,and 16 will make sense to other programmers
    ; this function does not process anything by itself but calls the combination of my other
    ; functions in the order I intended them to be used.
    
    putint: 
    
    push eax
    push ebx
    push ecx
    push edx
    
    call intstr
    
    call putstring
    
    pop edx
    pop ecx
    pop ebx
    pop eax
    
    ret
    
    ;this function converts a string pointed to by eax into an integer returned in eax instead
    ;it is a little complicated because it has to account for whether the character in
    ;a string is a decimal digit 0 to 9, or an alphabet character for bases higher than ten
    ;it also checks for both uppercase and lowercase letters for bases 11 to 36
    ;finally, it checks if that letter makes sense for the base.
    ;For example, G to Z cannot be used in hexadecimal, only A to F can
    ;The purpose of writing this function was to be able to accept user input as integers
    
    strint:
    
    mov ebx,eax ;copy string address from eax to ebx because eax will be replaced soon!
    mov eax,0
    
    read_strint:
    mov ecx,0 ; zero ecx so only lower 8 bits are used
    mov cl,[ebx]
    inc ebx
    cmp cl,0 ; compare byte at address edx with 0
    jz strint_end ; if comparison was zero, this is the end of string
    
    ;if char is below '0' or above '9', it is outside the range of these and is not a digit
    cmp cl,'0'
    jb not_digit
    cmp cl,'9'
    ja not_digit
    
    ;but if it is a digit, then correct and process the character
    is_digit:
    sub cl,'0'
    jmp 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
    cmp cl,'A'
    jb not_upper
    cmp cl,'Z'
    ja not_upper
    
    is_upper:
    sub cl,'A'
    add cl,10
    jmp process_char
    
    not_upper:
    
    ;if char is below 'a' or above 'z', it is outside the range of these and is not lowercase letter
    cmp cl,'a'
    jb not_lower
    cmp cl,'z'
    ja not_lower
    
    is_lower:
    sub cl,'a'
    add cl,10
    jmp process_char
    
    not_lower:
    
    ;if we have reached this point, result invalid and end function
    jmp strint_end
    
    process_char:
    
    cmp ecx,[radix] ;compare char with radix
    jae strint_end ;if this value is above or equal to radix, it is too high despite being a valid digit/alpha
    
    mov edx,0 ;zero edx because it is used in mul sometimes
    mul [radix]    ;mul eax with radix
    add eax,ecx
    
    jmp read_strint ;jump back and continue the loop if nothing has exited it
    
    strint_end:
    
    ret
    
    
    
    ;the next utility functions simply print a space or a newline
    ;these help me save code when printing lots of things for debugging
    
    space db ' ',0
    line db 0Dh,0Ah,0
    
    putspace:
    push eax
    mov eax,space
    call putstring
    pop eax
    ret
    
    putline:
    push eax
    mov eax,line
    call putstring
    pop eax
    ret
    
  • The Bitwise Operations

    There are 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 Englist 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.

    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.

    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, it is commonly used to invert a bit. For example, if you go back to the source of my graphics programs in Chapter 2, you will see that most of those programs contain the statement:

    index^=1;

    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. In the context of those programs, the index variable is meant to be 0 to represent black and 1 to represent white. The XOR operation is the quickest way to achieve this bit inversion. In fact, in all my years of programming, that’s pretty much the only thing I have used it for!

    Bitwise Left and Right Shift Operations

    Consider the case of the following 8 bit 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 : after left shift
    00000100 ==  4 : after right shift
    

    Left and right shift operations allow us to multiply or divide a number by 2 by taking advantage of the base 2 system. These shifts are essential in graphics programming because sometimes to need to extract the red, green, or blue values separately out of their 24 bit representation. For example, consider this code:

       pixel=p[x+y*width];
       r=(pixel&0xFF0000)>>16;
       g=(pixel&0x00FF00)>>8;
       b=(pixel&0x0000FF);
    

    The first statement gets the pixel out of an array of data which is indexed by x and y geometric coordinates. This will be a 24 bit value, or in some cases 32 bit with the highest 8 bits representing the alpha or transparency level.

    variables r,g,b represent red, green, and blue. With clever use of bitwise AND operations and right shifting by the correct number of bits, it is possible to extract just that color component to be modified. Without the ability to do this, my graphics animations and my Tetris game would never have been possible. The colors had to be exactly sent to the drawing functions. This is true not just for SDL but using any graphical system involving colors.

    Learning More

    I know I covered a lot in this chapter but I encourage you to learn about the binary numeral system and its close cousin the hexadecimal system. If you do an online search, you will find courses, tutorials, and videos by millions of people who can probably explain these same concepts in a way that you understand better if you are still confused after reading this chapter!