Blog

  • 32-bit Windows chastehex update

    I made an update to the 32-bit version of chastehex for windows. Variable names have been changed to match the Linux version and the getarg function is now used instead of my old badly maintained code. Having all arguments handled by a separate header file helps keep things very organized.

    Writing this program with the Windows API in Assembly has taking a lot more time than the Linux version did, but this experience has made me appreciate the Linux system call interface a lot more. I also have working Windows versions of my Assembly programs now because I have made the effort. The chastehex program is only a small taste of what will be included in the Windows edition of Assembly Arithmetic Algorithms for Windows. So far, I have 10 chapters out of 15 to 20 planned.

    https://leanpub.com/assemblyarithmeticalgorithms-windows

    main.asm

    format PE console
    entry main
    
    include 'win32a.inc'        ;includes standard Windows 32-bit definitions and macros
    include 'chastelib-w32.asm' ;include standard library functions by Chastity
    include 'getarg-w32.asm'    ;include Chastity's getarg function for command line arguments
    
    main:
    
    mov [radix],16 ; Choose radix for integer output.
    mov [int_width],1
    
    call getarg ;this first call will get the command string
    cmp eax,0   ;did the getarg function return 0?
    jz help     ;if eax was zero, there are no args so we end the program safely after help message
    
    ;optionally display the command string
    ;call putstring
    ;call putline
    
    call getarg ;get next arg as file name
    cmp eax,0   ;did the getarg function return 0?
    jz help     ;if eax was zero, there are no args so we end the program safely after help message
    
    ;print the filename
    call putstring
    call putline
    
    mov [filename], eax ;save the filename to a permanent address
    
    jmp open_sesame
    
    help:
    
    mov eax,help_message
    call putstring
    
    jmp main_end
    
    open_sesame:
    
    ;open a file with the CreateFileA function
    ;https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
    
    ;call CreateFileA with Windows 32-bit calling convention
    
    push 0                  ;hTemplateFile = NULL: We are not using a template file
    push 0x80               ;dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL
    push 3                  ;dwCreationDisposition = OPEN_EXISTING
    push 0                  ;lpSecurityAttributes = NULL: No security attributes
    push 1                  ;dwShareMode = FILE_SHARE_READ (other programs can read this file)
    push 0x10000000         ;dwDesiredAccess = GENERIC_ALL access mode (Read+Write+Execute)
    push [filename]         ;lpFileName
    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 main_end ;end program if the file was not opened
    
    ;this label is jumped to when the file is opened correctly
    file_ok:
    
    mov [fd],eax
    
    ;before we proceed, we also check for more arguments.
    
    ;get next arg (first one after name of program)
    call getarg
    cmp eax,0  ;did the getarg function return 0?
    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 [offset],eax
    
    ;seek to 32-bit 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 [offset]    ;where we are seeking to
    push [fd]        ;seek within this file
    call [SetFilePointer]
    
    
    ;check for more args after the address argument
    call getarg
    cmp eax,0
    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 [buf],al
    
    ;Windows 32-bit WriteFile system call
    ;https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile
    
    
    push 0               ;lpOverlapped = NULL
    push 0               ;lpNumberOfBytesWritten = NULL
    push 1               ;nNumberOfBytesToWrite = 1
    push buf             ;lpBuffer = address of data to write
    push [fd]            ;hFile = [fd] (copied there earlier)
    call [WriteFile]
    
    
    mov eax,[offset]
    inc [offset]
    mov [int_width],8
    call putint_and_space
    
    mov eax,0
    mov al,[buf]
    mov [int_width],2
    call putint_and_line
    
    ;check for more args
    call getarg ;get next arg as potential bytes to write
    cmp eax,0   ;did the getarg function return 0?
    jnz write_bytes
    ;continue write if the args still exist
    ;otherwise end program
    jmp main_end
    
    read_one_byte:
    
    ;read only 1 byte using Win32 ReadFile system call.
    
    
    push 0              ;Optional Overlapped Structure 
    push count     ;Store Number of Bytes Read from this call
    push 1              ;Number of bytes to read
    push buf     ;address to store bytes
    push [fd]  ;handle of the open file
    call [ReadFile]
    
    cmp [count],1 
    jz print_byte ;if less than one bytes read, there is an error
    
    mov eax,[offset]
    mov [int_width],8
    call putint_and_space
    mov eax,end_of_file
    call putstr_and_line
    
    jmp main_end
    
    print_byte:
    mov eax,[offset]
    mov [int_width],8
    call putint_and_space
    
    mov eax,0
    mov al,[buf]
    mov [int_width],2
    call putint_and_line
    
    jmp main_end
    
    hexdump:
    
    ;read bytes using Win32 ReadFile system call.
    push 0              ;Optional Overlapped Structure 
    push count     ;Store Number of Bytes Read from this call
    push 16             ;Number of bytes to read
    push buf     ;address to store bytes
    push [fd]  ;handle of the open file
    call [ReadFile]     ;all the data is in place, do the write thing!
    
    mov eax,[count]
    ;call putint
    ;mov eax,buf
    ;call putstring
    
    cmp eax,0
    jnz read_ok ;if more than zero bytes read, proceed to display
    
    jmp eof_end
    
    read_ok:
    call print_bytes_row
    
    jmp hexdump
    
    print_EOF:
    
    mov eax,[offset]
    mov [int_width],8
    call putint_and_space
    
    mov eax,end_of_file
    call putstr_and_line
    
    jmp main_end
    
    
    eof_end:
    ;before we end the program, let the user know End Of File was reached
    mov eax,end_of_file
    call putstr_and_line
    
    main_end:
    
    ;close the file
    push [fd]
    call [CloseHandle]
    
    ;Exit the process with code 0
    push 0
    call [ExitProcess]
    
    
    
    ;variables for displaying messages
    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 'chastehex by Chastity White Rose',0Ah,0Ah
    db 'hexdump a file:',0Ah,0Ah,9,'chastehex file',0Ah,0Ah
    db 'read a byte:',0Ah,0Ah,9,'chastehex file address',0Ah,0Ah
    db 'write a byte:',0Ah,0Ah,9,'chastehex file address value',0Ah,0Ah
    db 'The file must exist',0Ah,0
    
    ;this function prints a row of hex bytes
    ;each row is 16 bytes
    print_bytes_row:
    mov eax,[offset]
    mov [int_width],8
    call putint_and_space
    
    mov ebx,buf
    mov ecx,[count]
    add [offset],ecx
    next_byte:
    mov eax,0
    mov al,[ebx]
    mov [int_width],2
    call putint_and_space
    
    inc ebx
    dec ecx
    cmp ecx,0
    jnz next_byte
    
    mov ecx,[count]
    pad_spaces:
    cmp ecx,0x10
    jz pad_spaces_end
    mov eax,space_three
    call putstring
    inc ecx
    jmp pad_spaces
    pad_spaces_end:
    
    ;optionally, print chars after hex bytes
    call print_bytes_row_text
    call putline
    
    ret
    
    space_three db '   ',0
    
    print_bytes_row_text:
    mov ebx,buf
    mov ecx,[count]
    next_char:
    mov eax,0
    mov al,[ebx]
    
    ;if char is below '0' or above '9', it is outside the range of these and is not a digit
    cmp al,0x20
    jb not_printable
    cmp al,0x7E
    ja not_printable
    
    printable:
    ;if char is in printable range,copy as is and proceed to next index
    jmp next_index
    
    not_printable:
    mov al,'.' ;otherwise replace with placeholder value
    
    next_index:
    mov [ebx],al
    inc ebx
    dec ecx
    cmp ecx,0
    jnz next_char
    mov [ebx],byte 0 ;make sure string is zero terminated
    
    mov eax,buf
    call putstring
    
    ret
    
    ;variables for managing file IO.
    filename dd ?
    count dd ? ;how many bytes are read with ReadFile operation
    buf db 16 dup ?,0
    fd dd ?
    offset dd ?
    
    ;FASM builds the Import Address Table (IAT) directly in the source file
    section '.idata' import data readable writeable
    
    library kernel32, 'KERNEL32.DLL'
    
    import kernel32,\
     GetStdHandle, 'GetStdHandle',\
     WriteFile, 'WriteFile',\
     ExitProcess, 'ExitProcess',\
     GetCommandLineA, 'GetCommandLineA',\
     CreateFileA, 'CreateFileA',\
     GetLastError, 'GetLastError',\
     SetFilePointer, 'SetFilePointer',\
     ReadFile, 'ReadFile',\
     CloseHandle, 'CloseHandle'
    
    

    chastelib-w32.asm

    ; chastelib assembly header file for 32 bit Windows
    ; This file is where I keep the source of my most important Assembly functions
    ; These are my string and integer output and conversion routines.
    
    ; To simplify documentation. The Accumulator/Arithmetic register
    ; (ax,eax,rax) depending on bit size shall be referred to as register A
    ; for the description of these core functions because the A register
    ; is treated special both by the Intel company and my code;
    
    ; putstring; Prints a zero terminated string from the address pointer to by A register.
    ; intstr;    Converts the number in A into a zero terminated string and points A to that address
    ; putint;    Prints the integer in A by calling intstr and then putstring.
    ; strint;    Converts the zero terminated string into an integer and sets A to that value
       
    ; Now, the source of the functions begins, with comments included for parts that I felt needed explanation.
    
    putstring:         ;print string pointed to by eax register
    
    push eax
    push ebx
    push ecx
    push edx
    
    mov ebx,eax             ;copy eax to ebx to be used as index to the string
    
    putstring_strlen_start: ;this loop finds the length 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 ;subtract start pointer from current pointer to get length of string
    
    ;Windows 32-bit WriteFile system call
    
    push 0               ;lpOverlapped = NULL
    push 0               ;lpNumberOfBytesWritten = NULL
    push ebx             ;nNumberOfBytesToWrite = ebx
    push eax             ;lpBuffer = address of string to write
    push -11             ;STD_OUTPUT_HANDLE = Negative Eleven
    call [GetStdHandle]  ;Get Standard Handle for -11
    push eax             ;hFile = eax (returned from GetStdHandle)
    call [WriteFile]
    
    
    pop edx
    pop ecx
    pop ebx
    pop eax
    
    ret
    
    ; This is the location in memory where digits are written to by the intstr function
    ; The string of bytes and settings such as the radix and width are global variables defined below.
    
    int_string db 32 dup '?' ;reserve bytes for characters string for 32-bit binary integer
    
    int_string_end db 0 ;zero byte terminator for the integer string
    
    radix dd 2     ;radix or base for integer output. 2=binary, 8=octal, 10=decimal, 16=hexadecimal
    int_width dd 8 ;default width of integers. Extra zeros prefixed if more than 1
    
    ;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_string_end-1 ;find address of lowest digit
    mov ecx,1
    
    digits_start:
    
    mov edx,0;
    div dword [radix]
    cmp edx,10
    jb decimal_digit
    jnb 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 ;point eax register to this string for putstring
    
    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
    ;This function is improved with error checking and uses the new strint_error variable
    ;The program can check this value after the call and see how many errors happened.
    
    strint_error db 0 ;declare a byte variable that keeps track of errors
    
    strint:
    
    mov ebx,eax ;copy string address from eax to ebx because eax will be replaced soon!
    mov eax,0
    mov byte[strint_error],0 ;set errors to 0 at the start of this function
    
    read_strint:
    mov ecx,0   ;zero ecx so only lower 8 bits are used
    mov cl,[ebx]
    inc ebx
    cmp cl,0    ;compare this byte 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 decimal digit, but it could be perhaps an alphabet character
    ;which could be a digit in a higher base like hexadecimal
    ;we will check for that possibility next
    
    ;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 with error
    jmp strint_end_error
    
    process_char:
    
    cmp ecx,[radix] ;compare char with radix
    jnb strint_end_error ;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 dword [radix] ;mul eax with radix
    add eax,ecx
    
    jmp read_strint ;jump back and continue the loop if nothing has exited it
    
    strint_end_error:  ;we jump here if there was an error with one of the chars
    inc byte[strint_error] ;increment error counter because char invalid
    
    strint_end: ;we jump here when no errors happened
    
    ret
    
    ;The utility functions below simply print a space or a newline.
    ;these help me save code when printing lots of strings and integers.
    
    space db ' ',0 ;a string containing only a space
    
    putspace:
    push eax
    mov eax,space
    call putstring
    pop eax
    ret
    
    line db 0x0D,0x0A,0 ;a string containing only a newline
    
    ;the next function which pushes eax to the stack
    ;moves the address of the line string and prints it with putstring
    ;then it pops the original value of eax back from the stack before the function returns
    ;this allows me to print a newline anywhere in the code without a single register changing
    
    putline:
    push eax
    mov eax,line
    call putstring
    pop eax
    ret
    
    ;a function for printing a single character that is the value of al
    
    char: db 0,0
    
    putchar:
    push eax
    mov [char],al
    mov eax,char
    call putstring
    pop eax
    ret
    
    ;a small function just for the common operation of
    ;printing an integer followed by a space
    ;this saves a few bytes in the assembled code
    ;by reducing the number of function calls in the main program
    
    putint_and_space:
    call putint
    call putspace
    ret
    
    ;a small function just for the common operation of
    ;printing an integer followed by a line feed
    ;this saves a few bytes in the assembled code
    ;by reducing the number of function calls in the main program
    
    putint_and_line:
    call putint
    call putline
    ret
    
    ;a small function just for the common operation of
    ;printing a string followed by a line feed
    ;this saves a few bytes in the assembled code
    ;by reducing the number of function calls in the main program
    ;it also means we don't need to include a newline in every string!
    
    putstr_and_line:
    call putstring
    call putline
    ret
    
    

    getarg-w32.asm

    ;This is the Windows 32-bit source of the getarg function 
    
    ;The getarg function makes my assembly code for DOS and Windows easier to read
    ;by writing one function to handle all possible cases I will need for my tools.
    ;
    ;The first time it is run, it returns the whole command string or zero if no args are given
    ;Each time after that, it will give you the next argument, which is a substring of the original.
    ;When no more arguments are available, it will always return zero
    ;The program calling this is expected to check for this error and then terminate
    ;or print a message depending on the goals of that program
    
    ;A word of warning, though, this function has multiple return statements and is long
    ;However, it is fully featured in that it can recognize quoted strings as being the same argument
    ;This brings full compatibility for my DOS and Windows programs that use command line arguments.
    
    ;Because all of my programs are ported to DOS and Windows from Linux
    ;I found there to be conflicts in the way these operating systems did not
    ;handle the quoted strings.
    ;
    ;This same function is available in a separate file for DOS Assembly.
    
    getarg:
    
    mov ebx,[arguments_start] ;get the address of start of arguments
    cmp ebx,0 ;is this address zero? (meaning this function was not called before)
    jz get_arg_data ;if it was zero, then get the argument data for the first execution of this function
    
    ;if the start was not zero, then clearly arguments exist and addresses have been saved
    cmp ebx,[arguments_end]  ;is the address of the start and end the same?
    jnz find_next_string  ;if they are not the same, find the next sub string
    mov eax,0 ;otherwise, return eax as zero and check this in the main program
    
    ret
    
    find_next_string:
    
    mov ebx,[arguments_start] ;get address of current arg
    
    skip_spaces:
    
    cmp byte[ebx],' '   ;is this byte a space?
    jnz skip_spaces_end ;if it is not a space, we can end this loop
    inc ebx ;otherwise, go to next byte
    jmp skip_spaces ;and keep looping till we find non-space
    skip_spaces_end:
    mov eax,ebx ;copy this non-space address to eax register
    
    ;we have found a non-space which is the start of a printable string
    ;but we still have to find the next space and terminate it with a zero!
    
    ;however, there is a special case where we want a string to contain spaces. In this case, I have another routine!
    
    ;check for quoted strings
    cmp byte[ebx],0x22 ;is this a double quote -> "
    jz scan_quoted_string
    cmp byte[ebx],0x27 ;is this a single quote -> '
    jz scan_quoted_string
    
    find_space:
    cmp byte [ebx],' ' ;is this a space?
    jz found_space ;if this was a space, end the loop and terminate with zero
    
    ;we must also check to see if we have reached the terminating zero of the arguments string
    cmp byte[ebx],0 ;is this byte a zero?
    jz no_more_args ;if yes this string is already terminated
    
    inc ebx
    jmp find_space ; this char was not space, go to the next char
    found_space:
    mov byte[ebx],0 ;terminate this string
    
    inc ebx ;but go to the next byte
    mov [arguments_start],ebx ;and set the new start address for the next call
    
    ret ;We can return eax safely knowing the string ends in a zero
    
    scan_quoted_string:
    
    mov cl,byte[ebx] ;mov this quote type to cl
    inc ebx ;go to next byte
    mov eax,ebx ;set eax to this address which is assumed to be the start of a quoted string
    
    find_end_quote:
    cmp byte[ebx],cl ;is this the same quote we started with?
    jz found_end_quote ;if it is, end this loop
    
    ;we must also check to see if we have reached the terminating zero of the arguments string
    ;this avoids a crash if I forgot to add the second quotation mark in the arguments
    cmp byte[ebx],0 ;is this byte a zero?
    jz no_more_args ;if yes this string is already terminated
    
    inc ebx
    jmp find_end_quote
    found_end_quote:
    mov byte[ebx],0 ;terminate this string
    
    inc ebx ;but go to the next byte
    mov [arguments_start],ebx ;and set the new start address for the next call
    
    ret
    
    no_more_args:
    
    mov [arguments_start],ebx ;mov the start to where the string ended
    
    ;now that the start and end addresses are the same
    ;this function will always return zero
    ret
    
    ;this will happen first time this function is called to get the argument data
    get_arg_data:
    
    ;get command line argument string with this Windows API call
    ;it returns a pointer in the eax register
    
    call [GetCommandLineA]
    mov [arguments_start],eax ;but copy it to memory too
    
    
    ;the following loop is a modified strlen loop except that we don't
    ;care about how long the string is. We only need the pointer for where it ends
    
    mov ebx,eax  ;copy eax to ebx to be used in loop to find ending zero
    argend_find: ; this loop finds the zero at the end of the argument string
    cmp [ebx],byte 0 ; compare byte at address ebx with 0
    jz argend_found ; if comparison was zero, jump to loop end because we have found the length
    inc ebx
    jmp argend_find
    argend_found:
    mov [arguments_end],ebx ;save ebx address containing the zero byte
    
    mov ebx,eax ;mov the start of the arg string again before the next loop
    
    skip_prog_name:
    cmp byte[ebx],0   ;check for zero first (very important)
    jz no_args        ;if we found a zero before a space, there are no arguments after program name!
    cmp byte[ebx],' ' ;is this byte a space?
    jz skip_prog_name_end ;if it is a space, we can end this loop
    mov byte[ebx],' ' ;turn this byte into a space and therefore delete it
    inc ebx ; go to next byte
    jmp skip_prog_name ;and keep looping till we find space
    skip_prog_name_end:
    mov eax,ebx ;copy this non-space address to eax register
    ;return eax pointing to an address of string with arguments after program name
    ret
    
    no_args:
    mov eax,0
    ;in this case, we return zero because there are no args
    
    ret
    
    ;start and end default to address of zero, which means we have not tested the arguments yet
    arguments_start dd 0
    arguments_end dd 0
    
    
  • Quality is not the Problem

    When people boycott Artificial Intelligence and try to persuade people not to use generative AI, the term AI slop is often used. This is a term I use for many reasons and I like it. My usage of the phrase “AI slop” specifically refers to art (in many possible forms) which is low quality. But not all AI content is low quality nor would I call it all slop.

    In many cases, humans can and do produce art in the form of pictures or text that are much better quality than purely AI generated content. However, art is a subjective experience with humans each having their own biases. My point, however, is that the quality of the art, whether good or bad, should never be used as an argument for or against the use of Artificial Intelligence.

    One of the most controversial areas of using AI is in the making of computer software. Because all AI is really just a computer program, and programs can be used to read and write programs, AI does especially well at writing programs. Just like Chess, this is one of the areas where AI seems to win against most humans. MOST HUMANS, that is.

    The average human is not a very good computer programmer because it takes a special kind of focus and obsession to spend 8 hours straight writing a complicated program that accounts for all kinds of user input and generates correct output. Therefore, these AI models can probably write programs better than humans because unlike humans, machines do not need to eat, drink, sleep, work a job for money, pee, poop, shower, or fight with their spouse about who is going to drive the kids to school. Artificial Intelligence just has more time and can operate faster and with no mathematical mistakes either!

    I am not worried about being replaced as a computer programmer because I only do it completely for fun, but I do follow all the tech news and what the other software developers are saying about how it has affected their jobs and the prices of hardware and software in general.

    Domething went wrong some time ago. CEOs of tech companies decided they could use AI to replace humans and then fire humans from their jobs. On paper it makes sense because AI doesn’t require a salary. It is just a program operating on computers and doesn’t have the same needs as humans do. Those in charge of companies figure out that they can save millions by just not hiring and paying human staff.

    But what happens when it works? Suppose that company X lays off all its staff and just runs AI full time to create and sell some program that is only available by that company for some reason (this never happens in the real world because there are always better Open Source alternatives). Who is going to buy it?

    You see, the other companies had the same idea and got rid of as many human workers as they could. Now you have millions of humans with no jobs who can’t get an interview for another job because there are no jobs left for humans and even when there are, AI is reading the applications and resumes to decide who is qualified to be interviewed by an AI instead of a human.

    Then you have a world full of things to sell but no customers to sell them to. The humans die because they can’t pay their rent or buy food and then everything collapses. Not because AI was less qualified at doing there jobs but because it was, and because to the Presidents, CEOs, and managers of companies, it seemed like a great idea to just not have employees.

    I should know better than anyone how much better computers are than humans when it comes to doing math, because they are just giant calculators, but I don’t like the effect it has on the world when the humans are killed and replaced with machines to do all their work. Computers were made for humans, not humans for the computers. That is why in the final part of my “Assembly Magic” poem, I was writing about how code was no use unless there was at least one human to understand and enjoy it.

    
    But the special way I write my programs  
    Is yet another form of  Creative Writing  
    And because of evil tech companies  
    People like me cannot stop fighting  
    
    They fire people and replace them with AI  
    But that will only work for a short while  
    Because the code is of no use at all  
    Unless it can make a human soul smile
    
    
  • Chapter 16: A Jealous Pastor

    “You embarrassed me out there today in front of everyone. You owe me an apology,” said Pastor Mark.

    “Dad, I’m sorry that I slapped you, but I am also sorry that you can’t accept that there are mistakes in the Bible and that Chad is a better preacher than you,” said Stacy.

    “Chad isn’t even a licensed preacher, pastor, or evangelist! Besides, we must maintain the inerrancy of scripture or people won’t know what to believe!” said Mark.

    “Then you have made an idol out of the Bible. God existed before paper and ink and is still the king of the universe, not you. I am not sure exactly what to believe about the Bible either, but I know Chad cares about doing the right thing and wants the children to know about Jesus,” said Stacy.

    “Since when did you take Chad’s side over your father?” said Mark.

    “There are sides now?! We should be on the same side. Besides, God is my father because we are all God’s children. If you had been listening to what Chad was saying, you would understand that. I do regret yelling at you and slapping you, but only because it was a bad example in front of the children. I don’t know where Chad is going with this new Sunday School mission he started, but I want to be there and support him without getting in the way,” said Stacy.

    “I see what’s going on here! You like Chad!” said Mark.

    “Of course I like Chad. Everyone in Podunk likes Chad because he is a good man who helps people in need, and he loves children and animals,” said Stacy.

    “No, they like Chad because he wins at soccer and he is rich. They will probably believe anything he says because he is the town superstar. Even so, I am glad you finally found a rich guy you can marry,” said Mark.

    “Not that it is any of your business what relationship I have with Chad or anyone else, but you have the wrong idea. I love Chad for who he is, not because of his money, car, fame, or good looks, or his soccer-playing golden retriever dog,” said Stacy.

    “So you DO love Chad!” said Mark.

    Stacy paused for a minute, looking deep in thought, and then finally got up from her chair at the dinner table. She grabbed her purse and walked out the front door of her dad’s house.

    “Hey, where are you going!” yelled Pastor Mark.

    “I am gonna talk to my new boyfriend, Chad!” said Stacy.

    “No, you aren’t. We are not done talking about this!” said Mark.

    “Actually, Pastor Mark, we are. You just don’t like that you are not in charge anymore, and you’re no longer my dad either,” said Stacy.

  • Is This the Real Art?

    People often criticize AI generated pictures or music for taking money away from real artists who should be paid for what they made with their time, blood, sweat, and tears. These concerns are valid, but it raises larger questions. Can humans tell the difference between pictures, music, or videos made by Artificial Intelligence compared to what a human can make?

    From what I have seen, I would argue that it is usually impossible to see the difference between something made from an AI vs a human. AI is getting so good that I can’t tell what is real. What even is real art? All artists take inspiration from other sources and generate remixes based on what they know. The reason AI threatens artists is because it knows everything and has been fed the writings, music, and visual art made by people who were never paid.

    But it goes deeper than that, people are not given the sources used to generate art. The AI bots will never give credit to the people whose art was used to train the AI in the first place. And sometimes, the original creator of the art gets automatically banned for copyright infringement by a robot even though a robot stole it from them.

    So the issue is not only that it potentially prevents people from buying the original work due to not knowing where it came from, but that the original work is deleted from the internet because the artist is banned and accused of stealing what was actually stolen from them.

    Once again, the issue is that the truth is hidden from the public and the AI can take credit for knowing how to make art that seems original because we don’t know which human made it originally. Even if the humans were paid billions of dollars by the AI companies, they would still not receive credit for their creations. But since they are not receiving money either. two crimes are being committed against real humans by machines controlled by the owners who are profiting from someone else’s work.

    Sources:

    AI companies accused of hoarding and destroying millions of books:

    The facts about AI companies destroying books:

  • Autistic Article Against AI

    I had decided not to write a book in favor of Artificial Intelligence or against it. But when I saw it shoved into my face on all social media, and that even Linux users and developers were being led astray, I felt it was right to write this short book.

    I want to stand with those who speak against AI slop and warn believers in Free Software to stay watchful. I could hardly believe that even Linus Torvalds could be deceived enough to share in their suffering and confusion.

    Yet the devil is the god of this world, and where the Natural Intelligence given by God is missing, Satan has found a new vessel of Artificial Intelligence to overpower and deceive both the weak and the strong. May God protect us. Amen.

    Furthermore, I have decided that at no part shall I use Artificial Intelligence in the making of this book. Instead, I want to educate people on the real problems posed by AI that are not being talked about enough.

    My personal issue with AI is not that it is a machine, but that history is being altered. Books long out of print are being scanned into Large Language Models before the books are destroyed. I pray that it is not too late for others to look back in history and see that the people burning books are never the good guys.