Tag: coding

  • chastelib SDL2 extension

    I wrote an extension to chastelib which is really an entire font library on its own. The idea is to emulate a Linux terminal and write text to it except that everything is really done using SDL. The characters printed are from the custom font I used in Chaste Tris. This font is perfect because it is pixelated and predictable.

    Besides the fact that this library allows me to use my favorite bitmap font, it also allows complete control over input. Normally I would have to use ncurses to control a terminal in the same way, but since this is not a real terminal, I can do anything I want.

    Now imagine a game that was based entirely on typing text but was done in SDL. This would allow a cross platform nerd environment for people who like DOS and Linux terminals but would be completely cross platform. It wouldn’t matter if you are on Windows, Mac, or Linux because it would operate the same. I don’t know the details of what this game would have but it might be fun if I ever figure it out.

    full source code of this program

    main.c

    /*
     main.c source file for an SDL2 project by Chastity White Rose
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <SDL.h>
    #include "chastelib.h"
    
    int width=1280,height=720;
    int loop=1;
    SDL_Window *window;
    SDL_Surface *surface;
    SDL_Event e;
    
    /*
    This header file must be included after the above global variables
    because it depends on them.
    */
    #include "chastelib_font_sdl.h"
    #include "chastelib_demo_sdl.h"
    
    int main(int argc, char **argv)
    {
     int x; /*variable to use for whatever I feel like*/
    
     if(SDL_Init(SDL_INIT_VIDEO))
     {
      printf( "SDL could not initialize! SDL_Error: %s\n",SDL_GetError());return -1;
     }
     window=SDL_CreateWindow("SDL2 Program",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,width,height,SDL_WINDOW_SHOWN );
     if(window==NULL){printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );return -1;}
     surface = SDL_GetWindowSurface( window ); /*get surface for this window*/
     SDL_FillRect(surface,NULL,0xFF00FF);
     SDL_UpdateWindowSurface(window);
     printf("SDL Program Compiled Correctly\n");
     
     /*load the font from a file*/
     main_font=chaste_font_load("./font/FreeBASIC Font 8.bmp");
     
     /*change the scale of each character*/
     main_font.char_scale=4; 
     
     /*change the putstr function to the SDL version*/
     putstr=sdl_putstring;
     
     /*or use the version that automatically wraps words of text*/
     putstr=sdl_putstring_wrapped;
    
     /*
     below is an eight line test program to check if everything is correct!
     */
    
     if(0)
     {
      sdl_clear();  /*clear the screen before we begin writing*/
      x=putstr("Hello World\n"); /*draw a string of text to the surface*/
      putstr("string length = ");
      radix=10;
      putint(x);
      putstr("\nPress Esc to continue.\n");
      SDL_UpdateWindowSurface(window); /*update window to show the results*/
      sdl_wait_escape(); /*wait till escape key pressed*/
     }
    
     /*now call a demo function I wrote*/
     sdl_chastelib_test_suite();
    
     if(0)
     {
      sdl_clear();  /*clear the screen before we begin writing*/
      putstr("This program has ended\nPress Esc to close this window.\n");
      SDL_UpdateWindowSurface(window); /*update window to show the results*/
      sdl_wait_escape(); /*wait till escape key pressed*/
     }
     
     SDL_DestroyWindow(window);
     SDL_Quit();
     return 0;
    }
    
    /*
     This source file is an example to be included in the Chastity's Code Cookbook repository.
     This example follows the SDL version 2 which works differently than
     the most up to date version (version 3 at this time).
    
    main-sdl2:
    	gcc -Wall -ansi -pedantic main.c -o main `sdl2-config --cflags --libs` -lm && ./main
    
    */
    

    chastelib_font_sdl.h

    /*
    chastity font SDL2 surface version
    
    SDL surfaces are easy to work with and this was the original way I implemented my own text writing library.
    There is an incomplete version that uses an SDL renderer but offers no advantages over this one.
    */
    
    
    /*
    chastelib font structure
    
    In is version of my SDL2 font extension, a surface is used as an image which contains the printable characters.
    The data in it will be loaded by another function from an image file.
    */
    struct chaste_font
    {
     int char_width; /*width of a character*/
     int char_height; /*height of a character*/
     int char_scale; /*multiplier of original character size used in relevant functions*/
     SDL_Surface *surface; /*the surface of the image of loaded font*/
    };
    
    /*global font that will be reused many times*/
    struct chaste_font main_font;
    
    /*function to load a font and return a structure with the needed data to draw later*/
    struct chaste_font chaste_font_load(char *s)
    {
     struct chaste_font new_font;
     SDL_Surface *temp_surface;
     printf("Loading font: %s\n",s);
    
     /*load bitmap to temporary surface*/
     temp_surface=SDL_LoadBMP(s);
    
     /*convert to same surface as screen for faster blitting*/
     new_font.surface=SDL_ConvertSurface(temp_surface, surface->format, 0);
     
     /*free the temp surface*/
     SDL_FreeSurface(temp_surface); 
    
     if(new_font.surface==NULL){printf( "SDL could not load image! SDL_Error: %s\n",SDL_GetError());return new_font;}
    
     /*
      by default,font height is detected by original image height
      but the font width is the width of the image divided by 95
      because there are exactly 95 characters in the font format that I created.
     */
     new_font.char_width=new_font.surface->w/95; /*there are 95 characters in my font files*/
     new_font.char_height=new_font.surface->h;
    
     if(new_font.char_height==0)
     {
      printf("Something went horribly wrong loading the font from file:\n%s\n",s);
     }
     else
     {
      /*printf("Font loaded correctly\n");*/
      printf("Size of each character in loaded font is %d,%d\n",new_font.char_width,new_font.char_height);
      new_font.char_scale=1;
      printf("Character scale initialized to %d\n\n",new_font.char_scale);
     }
    
     return new_font;
    }
    
    /*global variables to control the cursor in the putchar function*/
    int cursor_x=0,cursor_y=0;
    int line_spacing_pixels=1; /*optionally space lines of text by this many pixels*/
    
    /*
    This function is designed to print a single character to the current surface of the main window
    This means that it can be called repeatedly to write entire strings of text
    */
    
    int sdl_putchar(char c)
    {
     int x,y; /*used as coordinates for source image to blit from*/
     int error=0; /*used only for error checking*/
     SDL_Rect rect_source,rect_dest;
    
      /*
      in the special case of a newline, the cursor is updated to the next line
      but no character is printed.
      */
      if(c=='\n')
      {
       cursor_x=0;
       cursor_y+=main_font.char_height*main_font.char_scale;
       cursor_y+=line_spacing_pixels; /*add space between lines for readability*/
      }
      else
      {
       x=(c-' ')*main_font.char_width; /*the x position of where this char is stored in the font source bitmap*/
       y=0*main_font.char_height;      /*the y position of where this char is stored in the font source bitmap*/
    
       rect_source.x=x;
       rect_source.y=y;
       rect_source.w=main_font.char_width;
       rect_source.h=main_font.char_height;
    
       rect_dest.x=cursor_x;
       rect_dest.y=cursor_y;
       rect_dest.w=main_font.char_width*main_font.char_scale;
       rect_dest.h=main_font.char_height*main_font.char_scale;
    
       /*copy the character to the screen (including scale of character)*/
       error=SDL_BlitScaled(main_font.surface,&rect_source,surface,&rect_dest);
       if(error){printf("Error: %s\n",SDL_GetError());}
       
       /*
       copy the character directly but ignore scale
       this will result in the tiny character from the source font
       and is only intended as a joke
       */
       /*error=SDL_BlitSurface(main_font.surface,&rect_source,surface,&rect_dest);
       if(error){printf("Error: %s\n",SDL_GetError());}*/
    
       cursor_x+=main_font.char_width*main_font.char_scale;
      }
    
     return c;
    }
    
    /*
     This function is the SDL equivalent of my putstring function.
     Besides writing the text to an SDL window, it still writes it to the terminal
     This way I can always read it from the terminal and debug if necessary.
    */
    
    int sdl_putstring(const char *s)
    {
     int count=0;                    /*used to calcular how many bytes will be written*/
     const char *p=s;                /*pointer used to find terminating zero of string*/
     while(*p)
     {
      sdl_putchar(*p); /*print this character to the SDL window using a function I wrote*/
      p++;             /*increment the pointer*/
     } 
     count=p-s;                      /*count is the difference of pointers p and s*/
     fwrite(s,1,count,stdout);       /*https://cppreference.com/w/c/io/fwrite.html*/
     return count;                   /*return how many bytes were written*/
    }
    
    /*
     This function writes a string but wraps the text to always fit the screen.
    */
    
    int sdl_putstring_wrapped(const char *s)
    {
     int count=0;     /*used to calcular how many bytes will be written*/
     const char *p=s; /*pointer used to find terminating zero of string*/
     const char *w;   /*pointer used to check length of string for wrapping text*/
     int wx;          /*x position used to see if we need to wrap words*/
     while(*p)
     {
      w=p; /*the wrap pointer is used in a loop to determine if the "word" will fit on the current line*/
      wx=cursor_x; 
      while(*w>=0x21 && *w<=0x7E) /*while the chars in current word are not special character*/
      {
       wx+=main_font.char_width*main_font.char_scale;
       w++;
      }
      /*if the previous loop goes off the right edge of window, wrap to next line*/
      if(wx>=width)
      {
       cursor_x=0;
       cursor_y+=main_font.char_height*main_font.char_scale;
       cursor_y+=line_spacing_pixels; /*add space between lines for readability*/
       putchar('\n'); /*insert newline to terminal*/
      }
      sdl_putchar(*p); /*print this character to the SDL window using a function I wrote*/
      putchar(*p);     /*print to stdout with libc putchar*/
      p++;             /*increment the pointer*/
     } 
     count=p-s;                      /*count is the difference of pointers p and s*/
     return count;                   /*return how many bytes were written*/
    }
    
    /*
    A function to clear the screen and reset the cursor to the top left
    This makes sense because the Linux clear command does the same thing
    */
    
    void sdl_clear()
    {
     cursor_x=0;cursor_y=0;
     SDL_FillRect(surface,NULL,0x000000);
     
     /*
     these next lines use escape sequences to also clear the terminal
     and reset the terminal cursor so it matches the SDL cursor by this library
    */
     putstring("\x1B[2J"); /*clear the terminal with an escape sequence*/
     putstring("\x1B[H"); /*reset terminal cursor to home*/
    }
    
    /*
     a function with a loop which will only end if we click the X or press escape
     This function serve as a useful way to keep the SDL Window on the screen
     so I can see the text of I have drawn to it.
     It is also something I can copy paste into larger input loops.
    */
    
    void sdl_wait_escape()
    {
     int loop=1;
     while(loop)
     {
      while(SDL_PollEvent(&e))
      {
       if(e.type == SDL_QUIT){loop=0;}
       if(e.type == SDL_KEYUP)
       {
        if(e.key.keysym.sym==SDLK_ESCAPE){loop=0;}
       }
      }
     }
    }
    

    chastelib_demo_sdl.h

    /* chastelib_demo_sdl.h */
    
    int sdl_chastelib_test_suite()
    {
     /*variables required by SDL*/
     int loop=1;
     int key=1;
     SDL_Event e;
    
     int a=0,b,c,d; /*variables for this test program*/
    
     line_spacing_pixels=1; /*empty space in pixels between lines*/
    
     radix=16;
     int_width=1;
    
     /*
      I use strint to set the variables by strings rather than immediate values directly
      Doing it this way looks silly, but it is for the purpose of testing the strint function
     */
     b=strint("10"); /*will always be radix*/
     c=b; /*save what the radix was at the beginning. This will be used later.*/
     d=strint("100"); /*will always be radix squared*/
    
     /*a loop which will only end if we click the X or press escape*/
     while(loop)
     {
      /*start of game loop*/
    
    
    
     if(key) /*start of update on input section*/
     {
      
      sdl_clear();  /*clear the screen before we begin writing*/
    
      main_font.char_scale=3;
      putstr("Official test suite for the C version of chastelib.\nThis version uses SDL2.\n\n");
    
      main_font.char_scale=4; 
    
      /*the actual loop that shows the data for 16 numbers at a time*/
      a=b-c;
      while(a<b)
      {
       radix=2;
       int_width=8;
       putint(a);
       putstr(" ");
       radix=16;
       int_width=2;
       putint(a);
       putstr(" ");
       radix=10;
       int_width=3;
       putint(a);
    
       if(a>=0x20 && a<=0x7E)
       {
        putstr(" ");
        putchar(a);
        sdl_putchar(a);
       }
    
       putstr("\n");
       a+=1;
      }
    
      SDL_UpdateWindowSurface(window); /*update window to show the results*/
     
    } /*end of update on input section*/
    
     key=0; /*key of zero means no input right now*/
    
      /*loop to capture and process input that happens*/
      while(SDL_PollEvent(&e))
      {
       if(e.type == SDL_QUIT){loop=0;}
    
       /*use Escape as a key that can also end this loop*/
       if(e.type == SDL_KEYUP)
       {
        if(e.key.keysym.sym==SDLK_ESCAPE){loop=0;}
       }
    
       if(e.type == SDL_KEYDOWN /*&& e.key.repeat==0*/)
       {
        key=e.key.keysym.sym;
        switch(key)
        {
         /*use q as a key that can also end this loop*/
         case SDLK_q:
          loop=0;
         break;
       
         /*the main 4 directions*/
         case SDLK_UP:
          if(b>c){b--;}
         break;
         case SDLK_DOWN:
          if(b<d){b++;}
         break;
         case SDLK_LEFT:
          if(b>=c+c){b-=c;}
         break;
         case SDLK_RIGHT:
          if(b<=d-c){b+=c;}
         break;
        }
    
    
    
        
       } /*end of SDL_KEYDOWN section*/
    
    
      }
    
      /*end of game loop*/
     }
     
     return 0;
    }
    

    chastelib.h

    /*
     This file is a C library of functions written by Chastity White Rose. The functions are for converting strings into integers and integers into strings.
     I did it partly for future programming plans and also because it helped me learn a lot in the process about how pointers work
     as well as which features the standard library provides, and which things I need to write my own functions for.
    
     As it turns out, the integer output routines for C are too limited for my tastes. This library corrects this problem.
     Using the global variables and functions in this file, integers can be output in bases/radixes 2 to 36.
     
     Although this code is commented, I have also written a readme.md file designed to explain the usage of these functions and the philosophy behind them.
    */
    
    /*
     These following lines define a static array with a size big enough to store the digits of an integer, including padding it with extra zeroes.
     The integer conversion function (intstr) always references a pointer to this global string, and this allows other C standard library functions
     such as printf to display the integers to standard output or even possibly to files.
     This string can be repurposed for absolutely anything I desire.
    */
    
    
    #define usl 0x100 /*usl stands for Unsigned or Universal String Length.*/
    char int_string[usl+1]; /*global string which will be used to store string of integers. Size is usl+1 for terminating zero*/
    
    /*radix or base for integer output. 2=binary, 8=octal, 10=decimal, 16=hexadecimal*/
    int radix=2;
    /*default minimum digits for printing integers*/
    int int_width=1;
    
    /*
    The intstr function is one that I wrote because the standard library can display integers as decimal, octal, or hexadecimal, but not any other bases(including binary, which is my favorite).
    
    My function corrects this, and in my opinion, such a function should have been part of the standard library, but I'm not complaining because now I have my own, which I can use forever!
    More importantly, it can be adapted for any programming language in the world if I learn the basics of that language. That being said, C is the best language and I will use it forever.
    */
    
    char *intstr(unsigned int i)    /*Chastity's supreme integer to string conversion function*/
    {
     int width=0;                   /*the width or how many digits including prefixed zeros are printed*/
     char *s=int_string+usl;        /*a pointer starting to the place where we will end the string with zero*/
     *s=0;                          /*set the zero that terminates the string in the C language*/
     while(i!=0 || width<int_width) /*loop to fill the string with every required digit plus prefixed zeros*/
     {
      s--;                          /*decrement the pointer to go left for corrent digit placing*/
      *s=i%radix;                   /*get the remainder of division by the radix or base*/
      i/=radix;                     /*divide the input by radix*/
      if(*s<10){*s+='0';}           /*fconvert digits 0 to 9 to the ASCII character for that digit*/
      else{*s=*s+'A'-10;}           /*for digits higher than 9, convert to letters starting at A*/
      width++;                      /*increment the width so we know when enough digits are saved*/
     }
     return s;                      /*return this string to be used by putstr,printf,std::cout or whatever*/
    }
    
    /*
    The strint_errors variable is used to keep track of how many errors happened in the strint function.
    The following errors can occur:
    
    Radix is not in range 2 to 36
    Character is not a number 0 to 9 or alphabet A to Z (in either case)
    Character is alphanumeric but is not valid for current radix
    
    If any of these errors happen, error messages are printed to let the programmer or user know what went wrong in the string that was passed to the function.
    If getting input from the keyboard, the strint_errors variable can be used in a conditional statement to tell them to try again and recall the code that grabs user input.
    */
    
    int strint_errors = 0; 
    
    /*
     The strint function is my own replacement for the strtol function from the C standard library.
     I didn't technically need to make this function because the functions from stdlib.h can already convert strings from bases 2 to 36 into integers.
     However, my function is simpler because it only requires 2 arguments instead of three, and it also does not handle negative numbers.
    I have never needed negative integers, but if I ever do, I can use the standard functions or write my own in the future.
    */
    
    int strint(const char *s)
    {
     int i=0;
     char c;
     strint_errors = 0; /*set zero errors before we parse the string*/
     if( radix<2 || radix>36 ){ strint_errors++; printf("Error: radix %i is out of range!\n",radix);}
     while( *s == ' ' || *s == '\n' || *s == '\t' ){s++;} /*skip whitespace at beginning*/
     while(*s!=0)
     {
      c=*s;
      if( c >= '0' && c <= '9' ){c-='0';}
      else if( c >= 'A' && c <= 'Z' ){c-='A';c+=10;}
      else if( c >= 'a' && c <= 'z' ){c-='a';c+=10;}
      else if( c == ' ' || c == '\n' || c == '\t' ){break;}
      else{ strint_errors++; printf("Error: %c is not an alphanumeric character!\n",c);break;}
      if(c>=radix){ strint_errors++; printf("Error: %c is not a valid character for radix %i\n",*s,radix);break;}
      i*=radix;
      i+=c;
      s++;
     }
     return i;
    }
    
    /*
     This function prints a string using fwrite.
     This algorithm is the best C representation of how my Assembly programs also work.
     Its true purpose is to be used in the putint function for conveniently printing integers, 
     but it can print any valid string.
    */
    
    int putstring(const char *s)
    {
     int count=0;              /*used to calcular how many bytes will be written*/
     const char *p=s;          /*pointer used to find terminating zero of string*/
     while(*p){p++;}           /*loop until zero found and immediately exit*/
     count=p-s;                /*count is the difference of pointers p and s*/
     fwrite(s,1,count,stdout); /*https://cppreference.com/w/c/io/fwrite.html*/
     return count;             /*return how many bytes were written*/
    }
    
    /*
     A function pointer named putstr which is a shorter name for calling putstring
     But this doesn't exist just to save bytes of source files. Otherwise I wouldn't have these huge comments!
     This exists so that all strings can be redirected to another function for output.
     For example, if the strings were written to a log file during a game which didn't use a terminal.
     
     But the most common use case is "putstr=addstr" when using the ncurses library to manage
     terminal control functions for a text based game. Having the putstr pointer allows me to 
     include this same source file and use it for ncurses based projects.
    */
    int (*putstr)(const char *)=putstring;
    
    /*
     This function uses both intstr and putstring to print an integer in the currently selected radix and width.
    */
    
    void putint(unsigned int i)
    {
     putstr(intstr(i));
    }
    
    /*
     Those four functions above are the core of chastelib.
     While there may be extensions written for specific programs, these functions are essential for absolutely every program I write.
     
     The only reason you would not need them is if you only output numbers in decimal or hexadecimal, because printf in C can do all that just fine.
     However, the reason my core functions are superior to printf is that printf and its family of functions require the user to memorize all the arcane symbols for format specifiers.
     
     The core functions are primarily concerned with standard output and the conversion of strings and integers. They do not deal with input from the keyboard or files. A separate extension will be written for my programs that need these features.
    */
    
    

  • C++ Version of chastehex!

    Hey, remember that chastehex program I wrote in C and several types of Assembly language? Well I finally made a C++ edition of it!

    What is the different between the C and C++ edition? Nothing at all as far as the function of it. However, I made an effort to replace all C library components with their C++ equivalents from the Standard Template library. This basically means the the console and the file input/output use only the iostream and fstream libraries.

    I am in a Programming 1 class with Full Sail University right now and I still don’t know much what I am doing with C++ but I have a good reference for C++ that I use a lot. It has all the functions from the C and C++ libraries sorted by categories.

    https://cppreference.com/index.html

    main.cpp

    #include <iostream>
    #include <fstream>
    using namespace std;
    #include "chastelib.hpp"
    
    std::fstream f;
    char bytes[17]; /*the byte buffer for hex and text dumping*/
    int count=1; /*keeps track of how many bytes were read during each row*/
    
    /*outputs the ASCII text to the right of the hex field*/
    void textdump()
    {
     int a,x=0;
    
     x=count;
     while(x<0x10)
     {
      putstring("   ");
      x++;
     }
    
     x=0;
     while(x<count)
     {
      a=bytes[x];
      if( a < 0x20 || a > 0x7E ){a='.';bytes[x]=a;}
      x++;
     }
     bytes[x]=0;
    
     putstring(bytes);
    }
    
    /*outputs up to 16 bytes on each row in hexadecimal*/
    void hexdump()
    {
     int x,address=0;
     x=0;
     f.read(bytes,16);
     count=f.gcount();
     while(count)
     {
      int_width=8;
      putint(address);
      putstring(" ");
    
      int_width=2;
      x=0;
      while(x<count)
      {
       putint(bytes[x]&0xFF);
       putstring(" ");
       x++;
      }
      textdump();
      putstring("\n");
    
      address+=count;
      f.read(bytes,16);
      count=f.gcount();
     }
    }
    
    int main(int argc, char *argv[])
    {
     int argx,x;
       
     radix=0x10; /*set radix for integer output*/
     int_width=1; /*set default integer width*/
    
     if(argc==1)
     {
      putstring
      (
       "Welcome to chastehex! The tool for reading and writing bytes of a file!\n\n"
       "To hexdump an entire file:\n\n\tchastehex file\n\n"
       "To read a single byte at an address:\n\n\tchastehex file address\n\n"
       "To write a single byte at an address:\n\n\tchastehex file address value\n\n"
      );
      return 0;
     }
    
     if(argc>1)
     {
      f.open(argv[1],ios::in|ios::out|ios::binary);
      if(!f.is_open())
      {
       printf("File \"%s\" cannot be opened.\n",argv[1]);
       return 1;
      }
      else
      {
       putstring(argv[1]);
       putstring("\n");
      }
     }
    
     if(argc==2)
     {
      hexdump(); /*hex dump only if filename given*/
     }
    
     if(argc>2)
     {
      x=strint(argv[2]);
      f.seekp(x);
     }
    
    
    
     /*read a byte at address of second arg*/
     if(argc==3)
     {
      f.read(&bytes[0],1);
      count=f.gcount();
      int_width=8;
      putint(x);
      putstring(" ");
      if(count==0){putstring("EOF");}
      else
      {
       int_width=2;
       putint(bytes[0]&0xFF);
      }
      putstring("\n");
     }
    
     /*any arguments past the address are hex bytes to be written*/
     if(argc>3)
     {
      argx=3;
      while(argx<argc)
      {
       bytes[0]=strint(argv[argx]);
       int_width=8;
       putint(x);
       putstring(" ");
       int_width=2;
       putint(bytes[0]&0xFF);
       putstring("\n");
       f.write(bytes,1);
       x++;
       argx++;
      }
     }
     
     f.close();
     return 0;
    }
    

    chastelib.hpp

    /*
     This file is a library of functions written by Chastity White Rose. The functions are for converting strings into integers and integers into strings.
     I did it partly for future programming plans and also because it helped me learn a lot in the process about how pointers work
     as well as which features the standard library provides and which things I need to write my own functions for.
    
     As it turns out, the integer output routines for C are too limited for my tastes. This library corrects this problem.
     Using the global variables and functions in this file, integers can be output in bases/radixes 2 to 36
    */
    
    /*
     These two lines define a static array with a size big enough to store the digits of an integer including padding it with extra zeroes.
     The function which follows always returns a pointer to this global string and this allows other standard library functions
     such as printf to display the integers to standard output or even possibly to files.
    */
    
    #define usl 32 /*usl stands for Unsigned String Length*/
    char int_string[usl+1]; /*global string which will be used to store string of integers. Size is usl+1 for terminating zero*/
    
     /*radix or base for integer output. 2=binary, 8=octal, 10=decimal, 16=hexadecimal*/
    int radix=2;
    /*default minimum digits for printing integers*/
    int int_width=1;
    
    /*
    This function is one that I wrote because the standard library can display integers as decimai, octai, or hexadecimal but not any other bases(including binary which is my favorite).
    My function corrects this and in my opinion such a function should have been part of the standard library but I'm not complaining because now I have my own which I can use forever!
    */
    
    char* intstr(unsigned int i)
    {
     int width=0;
     char *s=int_string+usl;
     *s=0;
     while(i!=0 || width<int_width)
     {
      s--;
      *s=i%radix;
      i/=radix;
      if(*s<10){*s+='0';}else{*s=*s+'A'-10;}
      width++;
     }
    
     return s;
    }
    
    /*
     This function prints a string using cout instead of fwrite.
     This is the best C++ representation of how my Assembly programs also work/
     It's true purpose is to be used in the putint function for conveniently printing integers, 
     but it can print any valid string.
    
     In the original C version, the putstring function was implemented with some pointer math to
     get the length of the string and then fwrite was used:
        fwrite(s,1,c,stdout);
    
     Technically this entire function could have been summed up in one statement:
        cout<<s;
    
     But where is the fun in that? I already had the logic for determining the length of the string.
     I also think that the new way of using << to write to cout is confusing because it is the left shift operator from C.
     Therefore, I wrote the function the following way to rebel against this common practice.
    */
    
    void putstring(const char *s)
    {
     int c=0;
     const char *p=s;
     while(*p++){c++;} 
     cout.write(s,c);
    }
    
    /*
     This function uses both intstr and putstring to print an integer in the currently selected radix and width
    */
    void putint(unsigned int i)
    {
     putstring(intstr(i));
    }
    
    /*
     This function is my own replacement for the strtol function from the C standard library.
     I didn't technically need to make this function because the functions from stdlib.h can already convert strings from bases 2 to 36 into integers.
     However my function is simpler because it only requires 2 arguments instead of three and it also does not handle negative numbers.
     Never have I needed negative integers but if I ever do I can use the standard functions or write my own in the future.
    */
    
    int strint(const char *s)
    {
     int i=0;
     char c;
     if( radix<2 || radix>36 ){ cout << "Error: radix " << i << " is out of range!\n"; return i;}
     while( *s == ' ' || *s == '\n' || *s == '\t' ){s++;} /*skip whitespace at beginning*/
     while(*s!=0)
     {
      c=*s;
      if( c >= '0' && c <= '9' ){c-='0';}
      else if( c >= 'A' && c <= 'Z' ){c-='A';c+=10;}
      else if( c >= 'a' && c <= 'z' ){c-='a';c+=10;}
      else if( c == ' ' || c == '\n' || c == '\t' ){return i;}
      else{ cout << "Error: " << c << " is not an alphanumeric character!\n";return i;}
      if(c>=radix){ cout << "Error: " << c << " is not a valid character for radix " << radix; return i;}
      i*=radix;
      i+=c;
      s++;
     }
     return i;
    }
    
    /*
     Those four functions above are pretty much the entirety of chastelib.
     While there may be extensions written for specific programs, these functions are essential for absolutely every program.
     The only reason you would not need them is if you only output numbers in decimal or hexadecimal, because printf in C can do all that just fine.
    */
    
  • 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.

  • 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;
    }
    
  • 3 SDL Examples

    This post isn’t Chess related but it is still very nerdy. I have made 3 different SDL programs that all do the exact same thing. However they are written using different SDL versions over time as the API has changed. Therefore, SDL version 1,2, and 3 source code is provided as well as the commands to compile them!

    SDL Version 1

    #include <stdio.h>
    #include <SDL.h>
    int width=1280,height=720;
    int loop=1;
    SDL_Surface *surface;
    SDL_Event e;
    int main(int argc, char **argv)
    {
     if(SDL_Init(SDL_INIT_EVERYTHING)){return 1;}
    
     SDL_putenv("SDL_VIDEO_WINDOW_POS=center");
     SDL_WM_SetCaption("SDL1 Program",NULL);
     surface=SDL_SetVideoMode(width,height,32,SDL_SWSURFACE);
     if(surface==NULL){return 1;}
    
     SDL_FillRect(surface,NULL,0xFF00FF);
    
     while(loop)
     {
      while(SDL_PollEvent(&e))
      {
       if(e.type==SDL_QUIT){loop=0;}
       if(e.type==SDL_KEYUP){if(e.key.keysym.sym==SDLK_ESCAPE){loop=0;}}
      }
      SDL_Flip(surface);
     }
    
     SDL_Quit();
     return 0;    
    }
    
    /*
    SDL1 program that creates a window but nothing else.
    Closes when user presses Esc or clicks the X of the window.
    This is to test whether SDL1 programs can be compiled.
    
     With the sdl-config script:
     gcc -Wall -ansi -pedantic sdl1-test.c -o sdl1-test `sdl-config --cflags --libs` && ./sdl1-test
    
     Without the sdl-config script:
     gcc -Wall -ansi -pedantic sdl1-test.c -o sdl1-test -I/usr/include/SDL -D_GNU_SOURCE=1 -L/usr/lib/x86_64-linux-gnu -lSDL && ./sdl1-test
    */
    

    SDL Version 2

    #include <stdio.h>
    #include <SDL.h>
    int width=1280,height=720;
    int loop=1;
    SDL_Window *window;
    SDL_Surface *surface;
    SDL_Event e;
    int main(int argc, char **argv)
    {
     if(SDL_Init(SDL_INIT_VIDEO))
     {
      printf( "SDL could not initialize! SDL_Error: %s\n",SDL_GetError());return -1;
     }
     window=SDL_CreateWindow("SDL2 Program",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,width,height,SDL_WINDOW_SHOWN );
     if(window==NULL){printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );return -1;}
     surface = SDL_GetWindowSurface( window ); /*get surface for this window*/
     SDL_FillRect(surface,NULL,0xFF00FF);
     SDL_UpdateWindowSurface(window);
     printf("SDL Program Compiled Correctly\n");
     while(loop)
     {
      while(SDL_PollEvent(&e))
      {
       if(e.type == SDL_QUIT){loop=0;}
       if(e.type == SDL_KEYUP)
       {
        if(e.key.keysym.sym==SDLK_ESCAPE){loop=0;}
       }
      }
     }
     SDL_DestroyWindow(window);
     SDL_Quit();
     return 0;
    }
    
    /*
     This source file is an example to be included in Chastity's Code Cookbook. This example follows the SDL version 2 which works differently than the most up to date version (version 3 at this time). There is also an updated version in the same repository that works with version 3.
    
     Linux Compile Command:
    
     With the sdl2-config script:
     gcc -Wall -ansi -pedantic sdl2-test.c -o sdl2-test `sdl2-config --cflags --libs` && ./sdl2-test
    
     Without the sdl2-config script:
     gcc -Wall -ansi -pedantic sdl2-test.c -o sdl2-test -I/usr/include/SDL2 -lSDL2 && ./sdl2-test
    */
    

    SDL Version 3

    #include <stdio.h>
    #include <SDL.h>
    int width=1280,height=720;
    int loop=1;
    SDL_Window *window;
    SDL_Surface *surface;
    SDL_Event e;
    int main(int argc, char **argv)
    {
     if(!SDL_Init(SDL_INIT_VIDEO))
     {
      printf( "SDL could not initialize! SDL_Error: %s\n",SDL_GetError());return -1;
     }
     window=SDL_CreateWindow("SDL3 Program",width,height,0);
     if(window==NULL){printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );return -1;}
     surface = SDL_GetWindowSurface( window ); /*get surface for this window*/
     SDL_FillSurfaceRect(surface,NULL,0xFF00FF);
     SDL_UpdateWindowSurface(window);
     printf("SDL Program Compiled Correctly\n");
     while(loop)
     {
      while(SDL_PollEvent(&e))
      {
       if(e.type == SDL_EVENT_QUIT){loop=0;}
       if(e.type == SDL_EVENT_KEY_UP)
       {
        if(e.key.key==SDLK_ESCAPE){loop=0;}
       }
      }
     }
     SDL_DestroyWindow(window);
     SDL_Quit();
     return 0;
    }
    
    /*
     This source file is an example to be included in Chastity's Code Cookbook. By following the migration guide, I converted the SDL2-test program to the changes in SDL3.
    
     https://wiki.libsdl.org/SDL3/README-migration
    
     Windows Compile Command:
    
     gcc -Wall -ansi -pedantic sdl3-test.c -o sdl3-test -IC:/w64devkit/include/SDL3 -lSDL3 && sdl3-test
    
     Linux Compile Command:
    
     gcc -Wall -ansi -pedantic sdl3-test.c -o sdl3-test -I/usr/include/SDL3 -lSDL3 && ./sdl3-test
    
    */
    

    No matter which of those I compile and run, I get the same result:

    All the programs do is create a window and fill the whole thing with Magenta. It’s not that exciting, but it is pretty, and it shows that all 3 versions of SDL work on my Debian Linux machine.