Blog

  • Windows: Assembly Arithmetic Algorithms

    32 and 64 bit Windows Edition

    Preface

    This book is the Windows edition of Assembly Arithmetic Algorithms. The first book was for 16-bit DOS programming using Assembly. The second book was for 32-bit Linux programming using the same assembly language for Intel machines. But this book is very different than those because it is for Windows users who don’t know anything about DOS or Linux.

    I suspect most people fall into this category because Windows comes preinstalled on almost any PC you would buy in a store. Although I am old enough to have experienced DOS, and autistic enough use Linux for everything since my teenage days, I am aware that most people will never both trying other operating systems.

    Although I use Linux for most things, I had to buy a laptop with Windows on it to use specific software required by Full Sail University when I was an online student. Since I have it, I decided I might as well try out some assembly language on it and learn how it works so I can pass the knowledge on to other peeople who are not ready to leave Windows but ARE ready to try learning assembly language.

    At the time of this writing, most Windows systems use the x86-64 Intel architecture which can run 32-bit or 64-bit code. Because of this, I have decided to include code samples for both modes and explain the differences between them.

    I highly suspect people don’t even know what it means for something to be 32 or 64 bits. Understanding this requires knowing that a bit is a BInary digiT and explaining the binary numeral system.

    If you are someone who likes to learn the math behind how computers work, but still cannot or don’t want to switch to Linux, this book will act as a bridge to test the waters of Assembly language and the control it offers you as a programmer. Programming in Assembly language is not a task for complete computer programming beginners. I do recommend having some C or C++ experience before jumping into this book, but I have tried my best not to assume knowledge of any prior languages when writing my explanations.

    Introduction

    In this short book, I plan to teach you the basics of Assembly language for Intel Central Processing Units and you will learn how to make small programs that run on the Windows operating system. Theoretically, these programs should be compatible with Windows version 7, 8, 10, and 11. My only OS to test with is Windows 11 which is on the laptop I am writing this on.

    There is one myth that I need to break before I can teach you how to get started programming on Windows. This book will not use an IDE (Interactive Development Environment). I consider IDEs to be evil because they hide the details of how things work. You WILL be entering commands at a terminal which is called the “Command Prompt” or the executable file at:

    "C:\WINDOWS\system32\cmd.exe"
    

    This program is the modern descendant of the original command.com from DOS. Windows may no longer be compatible with DOS but MS-DOS was a Microsoft product and Windows originally started as a program that can in DOS. Therefore, common commands such as “dir”, "mkdir, “copy”, “del”, “rename”, “type” and “exit” still work the same as they did on DOS.

    Because the Assembler I will be using is FASM, which includes an IDE, you don’t technically have to use the command line the way I will teach you, but you are cheating yourself if you don’t become comfortable with basic commands in a terminal/console.

    There is a common lie that Windows is point and click whereas Linux requires running commands at a terminal. Technically neither of these are true. The actualy truth is that a PROGRAMMER must know how to use the command line on ANY operating system to achieve full power in controlling their own operating system or the building of their own programs.

    But don’t worry, you don’t need to have been born in 1987 or grow up reading MS-DOS manuals to learn these commands. I will give you all the commands you need and you will still be pointing and clicking your way through the Windows file explorer a lot when going to your specific folder or directory (these two words mean the exact same thing in this context).

    The best part is that you can use any text editor you like. However, I recommend either the default Notepad so you don’t have to install an extra tool, or perhaps installing Notepad++ to benefit from syntax highlighting.

    Chapter 1: The First Program

    Before you can write Windows programs in Assembly language, you will need the FASM Assembler. Be sure to download the Windows version from here:

    https://flatassembler.net/

    The file will probably be named something similar to “fasmw17335.zip”

    You will need to extract the files in the zip archive and place them somewhere convenient for you. I placed them in my root C drive directory.

    C:\fasm
    

    Here is an easy way to test and see if the files are correctly located.

    Using the command “dir c:\fasm” should return the results of the following files:

     Volume in drive C is Windows-SSD
     Volume Serial Number is D43F-B788
    
     Directory of c:\fasm
    
    08/20/2026  04:00 AM    <DIR>          .
    08/20/2026  04:00 AM    <DIR>          EXAMPLES
    08/20/2026  04:00 AM           118,272 FASM.EXE
    08/20/2026  04:00 AM           529,038 FASM.PDF
    08/20/2026  04:00 AM           161,280 FASMW.EXE
    08/20/2026  04:00 AM    <DIR>          INCLUDE
    08/20/2026  04:00 AM             1,820 LICENSE.TXT
    08/20/2026  04:00 AM    <DIR>          SOURCE
    08/20/2026  04:00 AM    <DIR>          TOOLS
    08/20/2026  04:00 AM            17,640 WHATSNEW.TXT
                   5 File(s)        828,050 bytes
                   5 Dir(s)   5,106,724,864 bytes free
    

    For this book, we will mostly be concerned with FASM.EXE and the INCLUDE directory. I also recommend reading the FASM.PDF file because it is where I learned how to use the FASM Assembler.

    The next step is to (temporarily) set your path variables so that you can assemble your source files no matter which folder/directory you happen to be in. Once you have chosen you location to begin coding, you will want to run two commands to set the “path” and “include” variables. I usually place them in a short batch file named fasmpath.bat for convenience.

    fasmpath.bat

    set path=C:\fasm
    set include=C:\fasm\INCLUDE
    

    Whether you type those two commands or just place them in a batch file and enter “fasmpath” to execute the script, either way, your paths will be set until you close your console/terminal window. Then all changes will revert to whatever your system defaults were.

    There is a GUI setting to permanently change the variables but I DO NOT recommend this because making a mistake can make your system completely unusable. I will explain more about this later.

    Anyway, once you have a source file of a valid program, you can assemble it like this.

    fasm main.asm
    

    The file does not have to specifically be named “main.asm”. It could just as well be “fartbutt.asm” or even “count-dracula.txt”. You can choose whatever seems like a good name to you and adjust the commands accordingly.

    To get started, I will provide the first example program that can be assembled and run under the Windows operating system. This was tested on my laptop with Windows 11 but should theoretically work on older versions as well as long as you followed my instructions so far.

    Behold,the “Hello World” source file for a Windows console program.

    Hello World for 32-bit Windows

    format PE console
    entry main
    
    include 'win32ax.inc'       ;includes standard Windows 32-bit definitions and macros
    
    main:
    
    mov eax,main_string
    call putstring
    
    
    push 0             ;exit code for operating system
    call [ExitProcess] ;Exit the process with code 0
    
    ;A string to test if output works
    main_string db 'Hello World',0x0D,0x0A,0
    
    write_count dd 0        ;variable to store how many bytes were written
    
    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
    
    ;Write string using Win32 WriteFile system call.
    push 0              ;Optional Overlapped Structure
    push write_count    ;address to store how many bytes are 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
    
    ;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'
    

    You might wonder why it took nearly 70 lines to print a simple message. That is because unlike in C, Pascal, or BASIC, there are no printf, write, or print statements. The included putstring function is one I had to write and is not normally available unless someone like me builds it.

    It does however make use of the WriteFile Windows API call. My function calculates the length of the string by finding where the zero is and then subtracting the address of the beginning from the end. Then once the length is known, the arguments to the function are pushed to the stack in the order that Microsoft wanted them to be before calling the WriteFile function.

    I have no idea where the source code for this API call is because it is proprietary information and Windows is not an Open Source operating system. However, using a Windows API call like this is an extremely fast operation and it is the start of everything else this book will cover.

    However, this is only the 32 bit version of the program. A 64 bit version looks more like the following.

    Hello World for 64-bit Windows

    format PE64 console
    entry main
    
    include 'win64ax.inc'       ;includes standard Windows 64-bit definitions and macros
    
    main:
    
    mov rax,main_string
    call putstring
    
    sub rsp,40         ;align stack (required in windows 64-bit)
    mov rcx,0          ;exit code for operating system
    call [ExitProcess] ;Exit the process with code 0
    
    ;A string to test if output works
    main_string db 'Hello World',0x0D,0x0A,0
    
    write_count dq 0        ;variable to store how many bytes were written
    
    putstring:              ;print string pointed to by rax register
    
    push rax
    push rbx
    push rcx
    push rdx
    
    mov rbx,rax             ;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 [rbx],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 rbx
    jmp putstring_strlen_start
    
    putstring_strlen_end:
    sub rbx,rax ;subtract start pointer from current pointer to get length of string
    
    sub rsp,40  ;align stack before Win API functions(required in windows 64-bit)
    
    mov rdx,rax ;pointer to message
    
    mov rcx, -11        ; STD_OUTPUT_HANDLE
    call [GetStdHandle] ; Get Standard Output Handle
    mov rcx,rax         ; copy handle to ecx
    
    mov r8,rbx          ;message length
    mov r9,write_count  ;address to store how many bytes are written
    
    mov qword [rsp + 32], 0 ; Parameter 5: Must be placed on the stack
    call [WriteFile]
    
    add rsp,40  ;restore stack now that WinAPI calls are done
    
    pop rdx
    pop rcx
    pop rbx
    pop rax
    
    ret ;this is the end of the putstring function return to calling location
    
    ;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'
    

    Because in both cases, the programs are identical, you might wonder which is better or the correct thing to use. Actually, they are exactly the same but using a different calling convention.

    You may also notice that at the bottom of the source files there is an “idata” section which includes data from the Windows kernel which is KERNEL32.DLL. Regardless of whether your code using 32 or 64 bit registers, the exact same functions from the kernel are being dynamically linked and loaded so that your program can do basic tasks.

    First 3 Windows API calls

    These three functions are required for even a simple Hello World program like both of those above.

    • GetStdHandle
    • WriteFile
    • ExitProcess

    The documentation for these functions can be found on Microsoft’s website but it is not very helpful because it is written for C and C++ programming.

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

    https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile

    https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-exitprocess

    Despite the fact that most of the web pages don’t tell us what we need for assembly, some of it is helpful. For example the following table for the 3 standard handles on the GetStdHandle page is copied below.

    GetStdHandle function table

    Value Meaning
    -10 STD_INPUT_HANDLE
    -11 STD_OUTPUT_HANDLE
    -12 STD_ERROR_HANDLE

    Because negative 11 is how the standard output handle is obtained, that is why the 32 bit putstring has these 3 lines

    push -11            ;STD_OUTPUT_HANDLE = Negative Eleven
    call [GetStdHandle] ;use the above handle
    push eax            ;eax is return value of previous function
    

    and the 64 bit putstring has these lines

    mov rcx, -11        ; STD_OUTPUT_HANDLE
    call [GetStdHandle] ; Get Standard Output Handle
    mov rcx,rax         ; copy handle to ecx
    

    In both cases, the argument -11 is passed to the GetStdHandle function. In 32 bit mode, it is pushed to the stack before the call and in 64 bit mode is is loaded into the rcx register before the call.

    This may seem silly but it highlights the importance of a calling convention. If you read my DOS or Linux editions of Assembly Arithmetic Algorithms, you will see that they use a purely register based convention for all system calls.

    Windows is harder because it uses a hybrid approach of sometimes using registers for function arguments and other times using specific locations on the stack relative to the stack pointer.

    But you are probably asking at this point: “What is a stack?”, “What is a register?”, and “What is a bit?”.

    I will attempt to answer all these questions but it will take time. But before I end this chapter, I will give brief definitions.

    Register

    A variable with a fixed name that is always available to use. These come in different sizes such as “EAX” for 32-bit and “RAX” for 64-bit.

    Bit

    A bit is a BInary digiT. It is a number that can be 0 or 1. These are the only two numbers a bit can be but by combining multiple bits as a group, any number can be represented. Just as the decimal systems humans use only uses digits 0,1,2,3,4,5,6,7,8,9 but can represent any possible number, binary can also represent any number once you learn how it works. Explaining the Binary Numeral System will be a central feature of this book because no programmer can be successful without it.

    Stack

    A stack can be many things. It can be a stack of plates, a stack of pancakes on top of plates that you are going to eat, or it can be a stack of numbers where we temporarily place numbers that are in registers and free them up to be used for other tasks. Assembly programming requires basic understanding of the stack, but Windows specifically requires using the stack in the way Microsoft wants you do. Admittedly this is less fun and restrictive but there are clever ways to break the convention.

    This is the point where most people will give up. There are so many terms to learn and it takes a lot of information to even get a small program working to display a message like “Hello World”.

    But despite being difficult to get started, it gets easier as you proceed. It is like playing a new game which you don’t know the controls for or where your character is supposed to go next. Yes Assembly is hard, but not as hard as playing the Legend of Zelda: Ocarina of Time. Seriously, that game way more stressful than any programming language I have have used (except for Rust).

    A funny example I suppose, but programming really is like playing a game where you get to create your own rules. Perhaps Minecraft would be an even better example because you start with nothing and slowly create your own tools to progress faster.

    To be continued

  • 64-bit Windows API test suite for chastelib

    I published a new gist on github of my working test suite for my chastelib set of functions using the 64-bit API. The putstring function finally works now that I fixed the stack alignment issue I was having because I am new to 64-bit Windows programming. The other functions like intstr and strint were copied from the Linux version because they are independent of which operating system is being used as long as it is a 64-bit capable Intel machine.

    test suite for 64 bit Windows Assembly version of chastelib

    The original 32-bit Windows test suite is available as well. It behaves in the exact same way as the 64-bit but uses the 32 bit registers and stack based calling convention for Windows. This was easier because it doesn’t required the weird stack alignment that 64-bit does.

    test suite for 32 bit Windows Assembly version of chastelib

    There is still a lot I don’t understand about Windows API programming but I am keeping it simple and sticking only to making console based programs. There may eventually be a book that I write on this subject.

  • Chad Chapter 13: Spirit is Thicker than Blood

    After the children and their parents ate watermelon, Chad wanted to explain the purpose behind the Bible verses he and his friends were sharing.

    “In the first chapter of Luke, we discovered that the mother of Jesus was a virgin named Mary. In case anyone is still wondering about the virgin part. It is clear that Mary became pregnant by the power of the holy Spirit instead of through the regular way all of you were created by your parents.”

    “But how did our parents create us?” asked Simon.

    “I wish I could tell you, but if I were to try, your parents would probably get angry at me for telling you something they should have told you themselves already. For now, just keep in mind that you are biologically related to your parents and look like a combination of them because you received half your DNA from your mother and father,” said Chad.

    “What is a DNA?” asked Sally.

    "Honestly, Sally, I ain’t got a clue. It is one of those fancy things biologists talk about, but I don’t know how it works. But more importantly, I would like Matthew to begin reading from the first chapter of the book of Matthew so we can compare it to the story in Luke.

    Matt then began to read from the book of Matthew:

    Matthew Chapter 1 from NET Bible


    Matthew 1:1 This is the record of the genealogy of Jesus Christ, the son of David, the son of Abraham.

    2 Abraham was the father of Isaac, Isaac the father of Jacob, Jacob the father of Judah and his brothers, 3 Judah the father of Perez and Zerah (by Tamar), Perez the father of Hezron, Hezron the father of Ram, 4 Ram the father of Amminadab, Amminadab the father of Nahshon, Nahshon the father of Salmon, 5 Salmon the father of Boaz (by Rahab), Boaz the father of Obed (by Ruth), Obed the father of Jesse, 6 and Jesse the father of David the king.

    David was the father of Solomon (by the wife of Uriah), 7 Solomon the father of Rehoboam, Rehoboam the father of Abijah, Abijah the father of Asa, 8 Asa the father of Jehoshaphat, Jehoshaphat the father of Joram, Joram the father of Uzziah, 9 Uzziah the father of Jotham, Jotham the father of Ahaz, Ahaz the father of Hezekiah, 10 Hezekiah the father of Manasseh, Manasseh the father of Amon, Amon the father of Josiah, 11 and Josiah the father of Jeconiah and his brothers, at the time of the deportation to Babylon.

    12 After the deportation to Babylon, Jeconiah became the father of Shealtiel, Shealtiel the father of Zerubbabel, 13 Zerubbabel the father of Abiud, Abiud the father of Eliakim, Eliakim the father of Azor, 14 Azor the father of Zadok, Zadok the father of Achim, Achim the father of Eliud, 15 Eliud the father of Eleazar, Eleazar the father of Matthan, Matthan the father of Jacob, 16 and Jacob the father of Joseph, the husband of Mary, by whom Jesus was born, who is called Christ.

    17 So all the generations from Abraham to David are fourteen generations, and from David to the deportation to Babylon, fourteen generations, and from the deportation to Babylon to Christ, fourteen generations.

    18 Now the birth of Jesus Christ happened this way. While his mother Mary was engaged to Joseph, but before they came together, she was found to be pregnant through the Holy Spirit. 19 Because Joseph, her husband to be, was a righteous man, and because he did not want to disgrace her, he intended to divorce her privately. 20 When he had contemplated this, an angel of the Lord appeared to him in a dream and said, “Joseph, son of David, do not be afraid to take Mary as your wife because the child conceived in her is from the Holy Spirit. 21 She will give birth to a son and you will name him Jesus because he will save his people from their sins.” 22 This all happened so that what was spoken by the Lord through the prophet would be fulfilled: 23 “Look! The virgin will conceive and give birth to a son, and they will name him Emmanuel,” which means “God with us.” 24 When Joseph awoke from sleep he did what the angel of the Lord told him. He took his wife, 25 but did not have marital relations with her until she gave birth to a son, whom he named Jesus.


    Before Matt had finished reading from chapter 1 of the book of Matthew, some children were so bored they fell asleep.

    “This doesn’t make any sense! Why should we care who was the father of some guy who was the father of another dude who was the father of some other brother from another mother?” asked Trevor.

    "Glad you asked, Trevor! I don’t care any more than you do. I don’t know the names of my ancestors from thousands of years ago, and I don’t care. I am who I am regardless of how I came to this world. Similarly, I don’t care about the people mentioned in these verses because who Jesus was had nothing to do with these people. Also, the genealogy was clearly an error because it was the ancestors of Joseph, who was not the biological father of Jesus. God was the father of Jesus, and Mary was his mother. Jesus was conceived by the holy Spirit and not through the bloodline of the 42 generations of ancestors mentioned in the book of Matthew.

    “Why were these people included in the book if they don’t matter?” asked Trevor.

    Whoever was writing these books died thousands of years ago, and so we can’t ask them, but if I had to guess, they saw Jesus as being the legal son of Joseph because Mary became his wife, and in those days, women were seen more as property than people. You may have noticed that the genealogy lists mostly men and only a few women, despite the fact that every one of them had a mother. The writer probably didn’t like women or perhaps excluded them so he didn’t have to write so many of those long names," said Chad.

    “Come on, Chad, that is just guessing. You have no clue what you are talking about,” said Luke.

    “Yes, I did say I was guessing, but it doesn’t miss the larger point that the Holy Spirit was the reason that Mary became pregnant with Jesus without needing a man. I guess you could say that the Spirit is thicker than blood in this case,” said Chad.

    At this point, Stacy decided to speak, despite generally being shy in crowds.

    “Chad, sorry to interrupt, but I think I understand. The writers tried to frame everything in terms of their culture and ancestry because it was important to them, but it was not important to God. The conception and birth of Jesus completely contradict everything else in the society. Patriarchy and a man’s permission were ignored because God sent the angel directly to Mary before Joseph knew anything. It also opposed the genealogy because Jesus was not a biological descendant of Joseph’s bloodline,” said Stacy.

    “Thanks for your input, Stacy! Yes, I do think that the writers tried to include extra things that were not important to understanding the life of Jesus. It is one of the mistakes in the New Testament, but we can still learn a lot from the gospels about who Jesus is and what he taught,” said Chad.

    “Excuse me, Chad, but did you really say there are mistakes in the Bible?” said Pastor Mark.

    “Of course, Mark. All writings have mistakes due to human error and personal or political bias. Seeing which parts are true and which are just the opinions of humans is the hardest part,” said Chad.

    “But the Bible is the infallible word of God!” I can’t let you speak such heresy to these children," said Mark.

    “And I won’t let you lie to them and say that there are no mistakes in the Bible when clearly there are contradictions between the intentions of the writers and what God was intending to teach people through the life of Jesus,” said Chad.

    “Chad, you’re fired. No one talks back to me. I am the pastor of this church, and I say what happens here,” said Mark

    SLAP

    Stacy slapped her father hard in the face and interrupted him.

    “Shut up, Dad. You think you are the boss, but you are not God, and you are not a man of God either. You care only about your reputation and money rather than whether these kids hear the truth. I think Chad has a point here, and I want the kids to hear it,” said Stacy.

    "Thanks, Stacy. We are almost done here for today. Pastor, we will talk about this later, but for the record, you can’t fire me because I am a volunteer!

    Pastor Mark did not say anything else because his daughter, Stacy, might slap him again. She was in a mood today that he had never seen before.

  • chastdin for FreeBASIC

    I have rewritten my chastdin program (the stack based calculator that reads keyboard input from standard input) into the FreeBASIC programming language. I did it as an exercise to prepare myself for a future book on the BASIC programming language which was my first programming language. FreeBASIC is compatible with QBASIC which is what I first started on. Luckily, BASIC is not so different from C but I had to spend a lot of time on the documentation to refresh my memory in how I used to do things with it.

    Eventually I would like to make a GUI version of this command line calculator. I am trying to take baby steps in working my way into programs that the average person would use. However, command line utilities are still the easiest to build and that is an acceptable place to start.

    main.bas

    #include "chastelib.bi"
    #include "chastdin.bi"
    
    dim shared as integer chastack(256)
    dim shared as integer csi=0 'Chastity's Stack Index
    
    radix=10
    
    dim as integer a,b
    dim shared as string s
    
    sub help()
    ?  "chastdin is a stack based interactive calculator"
    ?  "Numbers are pushed on the stack and commands can do math."
    ?  "It is a fork of chastack that reads from stdin instead of arguments."
    ?  "Each line can contain multiple numbers or commands."
    ?
    ?  "Math commands are add,sub,mul,div,rem"
    ?  "And use the top two stack numbers for their operations"
    ?
    ?  "The setradix command uses the top of stack as the new radix"
    ?  "The exit command ends the program"
    ?  "The ? command prints the entire stack"
    ?
    end sub
    
    sub stack_check()
     if csi>0 then
      chastack(csi+1)=0 /'erase old top of stack because command was successful'/
     else
      print "Error: two numbers required for command: ";s
      csi+=1 /'increment the pointer to what it was before the failed command'/
     end if
    end sub
    
    help():
    
    while s<>"exit"
    
    s=""
    
     s=getstr() 'read and ignore empty strings
    
    'print entire stack
    if s="?" or s="print" then
     b=csi
     while csi>0
      print intstr(chastack(csi))
      csi-=1
     wend
     csi=b
    
    elseif s="exit" then
    exit while
    
    elseif s="help" then
    help()
    
    elseif s="setradix" then
     if csi>0 then
     radix=chastack(csi)
     chastack(csi)=0
     csi-=1
     else
      print "Error: need one number on stack for command: ";s
     end if
    
    elseif s="add" then
    b=chastack(csi)
    csi-=1
    a=chastack(csi)
    a+=b
    chastack(csi)=a
    stack_check()
    
    elseif s="sub" then
    b=chastack(csi)
    csi-=1
    a=chastack(csi)
    a-=b
    chastack(csi)=a
    stack_check()
    
    elseif s="mul" then
    b=chastack(csi)
    csi-=1
    a=chastack(csi)
    a*=b
    chastack(csi)=a
    stack_check()
    
    elseif s="div" then
    b=chastack(csi)
    csi-=1
    a=chastack(csi)
    a\=b
    chastack(csi)=a
    stack_check()
    
    elseif s="rem" then
    b=chastack(csi)
    csi-=1
    a=chastack(csi)
    a=a mod b
    chastack(csi)=a
    stack_check()
    
    else
    
    'try to interpret string as a number if not empty
     a=strint(s)
     if strint_errors<>0 or len(s)=0 then
     'print s;" cannot be added to the stack because it is not a valid number"
     else
     csi+=1
     chastack(csi)=a
     print intstr(a);" was added to the stack"
     end if
    
    end if
    
    wend
    
    /'
     This is a FreeBASIC program.
    
     compile and run as:
    
     fbc main.bas && ./main
    '/
    
    

    chastelib.bi

    /'
     global variables to define radix and formatting
     for the intstr function
    '/
    dim shared as integer radix=2
    dim shared as integer int_width=1
    
    /'
     translation of intstr function for FreeBASIC
     by original C programmer Chastity White Rose
    '/
    function intstr(i as uinteger) as string
     dim as string s=""
     dim as integer w=0
     dim as byte c
    
     while i<>0 or w<int_width 
    
      c=i mod radix                  
      i\=radix                     
    
      if c<10 then 
      c+=48
      else
      c+=55
      end if
    
      s=chr(c)+s
    
      w+=1                     
     wend
    
    return s
    end function
    
    /'
     global variable for error detection in strint function
     this variable will be zero if last string was a number
    '/
    dim shared as integer strint_errors=0
    
    /'
     translation of strint function for FreeBASIC
     by original C programmer Chastity White Rose
    '/
    function strint(s as string) as uinteger
    dim as uinteger i=0
    dim as integer x=0,y=len(s)
    dim as byte c
    
    strint_errors = 0 /' clear errors '/
    
    while x<y
    
     /' read digit from string '/
     c=s[x]
    
     /' 0 to 9 '/
     if c >= 48 and c <= 57 then
     c-=48
     /' A to Z '/
     elseif c >= 65 and c <= 90 then
     c-=65
     c+=10
     /' a to z '/
     elseif c >= 97 and c <= 122 then
     c-=97
     c+=10
     /' whitespace '/
     elseif c >= 0 and c <= 32 then
      exit while /' exit correctly at string end '/
     else
      strint_errors+=1
      print "Error: ";chr(s[x]);" is not an alphanumeric character!"
      exit while /' exit at invalid character '/
     end if
    
     if c>=radix then
      strint_errors+=1
      print "Error: ";chr(s[x]);" is not a valid character for radix ";radix
      exit while /' exit at digit wrong for radix '/
     end if
    
     /'multiply by radix then add digit'/
     i*=radix
     i+=c
    
    x+=1
    wend
    
    return i
    end function
    

    chastdin.bi

    dim shared as string stdin_buf
    dim shared as integer stdin_buf_index
    dim shared as integer stdin_buf_length=0
    
    function getstr() as string
    dim as string s=""         'create empty string
    dim as byte c              'temporary byte/char variable
    
    /'
    this section gets a line of text
    if the length of the string/buffer is 0
    '/
    
    if stdin_buf_length=0 then      'check if there are characters in the buf
    input "-> ",stdin_buf           'if not, read a line of text
    stdin_buf_index=0               'set index to zero
    stdin_buf_length=len(stdin_buf) 'set the length
    end if
    
    /'
    regardless of whether input was added above
    or if it still had bytes from the last input
    we then extract characters one at a time into the
    substring s to be returned from the function
    '/
    
    while stdin_buf_index<stdin_buf_length
    c=stdin_buf[stdin_buf_index]
    stdin_buf_index+=1
    if(c>=33) and (c<=126) then
    s=s+chr(c)
    else
    exit while
    endif
    wend
    
    /'
    if the index matches the length of buffer
    set length to zero so that more will be read
    next time this function is called
    '/
    
    if stdin_buf_index=stdin_buf_length then
    stdin_buf_length=0
    end if
    
    return s
    end function
    
    /'
    the getline function always gets an entire line of text
    I don't really need it but it is here as a reminder of
    how to use the input statement in FreeBASIC
    '/
    
    function getline() as string
    dim as string s=""
    input "-> ",stdin_buf
    s=stdin_buf
    return s
    end function
    

  • chastelib for Pascal Programming Language

    I managed to hack my four functions from chastelib into the Pascal programming language. This program includes the functions and the test suite which works just like the C version. The code is a bit more complex than the C version because strings and characters are handled very differently than they are in the C programming language. The strint function was the hardest to write but it seems to be working according to the standards I require.

    I am doing this for education and possibly a future book on old programming languages. Pascal is nice but I will also be studying BASIC again.

    program chastelib;
    
    const
     string0='Official test suite for the Pascal version of chastelib.'#10;
    
    var //this is the global variable section
     a:integer;
     b:integer;
     
     radix:integer;       //current radix being used
     int_width:integer=1; //global integer width
     strint_errors:integer=0; //error result for strint function
    
    (*
    A function to print a string using Pascal's write function.
    *)
    procedure putstr(s:string);
    begin
     write(s);
    end;
    
    (*
     a function to return a string form of an integer
     using the global radix variable
    *)
    function intstr(i:integer):string;
    var
     s:string=''; //string that will be built and returned from this function
     width:integer=0; //the current width
     c:integer;
     ch:char;
    begin
     while (i>0) or (width<int_width) do
     begin
    
      c:=i mod radix; //get integer division modulus or remainder
      i:=i div radix; //get integer division quotient
    
      (*turn remainder c into character ch for digit in this radix*)
      if c<10 then
      begin
       ch:=chr(c+48);
      end
      else
      begin
       ch:=chr(c+55);
      end;
    
       s:=ch+s; //prefix the string with this character
       width+=1;
    
     end;
    
     intstr:=s; //return this string from the function
    
    end;
    
    (*use both putstr and intstr to print an integer*)
    procedure putint(i:integer);
    begin
     putstr(intstr(i));
    end;
    
    (*
    Because characters and integers are separate types in Pascal,
    it is required to get the ASCII value of characters in the string
    for the strint function so I can do the math the same way
    as I did in the C version of the function.
    *)
    
    function strint(s:string):integer;
    var
     i:integer=0; //integer that will be built and returned from this function
     x:integer=1; //index used to scan forward through the string
     c:integer=0;
    begin
     strint_errors := 0; (*set zero errors before we parse the string*)
     if (radix<2) or (radix>36 ) then
     begin
      strint_errors+=1;
      writeln('Error: radix ',radix,' is out of range!');
     end;
     while(x<=length(s)) do
     begin
      c:=ord(s[x]);
      if (c>=ord('0')) and (c<=ord('9')) then 
      begin
       c-=ord('0')
      end
      else if (c>=ord('A')) and (c<=ord('Z')) then
      begin
       c-=ord('A');
       c+=10;
      end
      else if (c>=ord('a')) and (c<=ord('z')) then
      begin
       c-=ord('a');
       c+=10;
      end
      
      else if (c < $21 ) then
      begin
       break; (*end loop because we have found whitespace*)
      end
      
      else
      begin
       strint_errors+=1;
       writeln('Error: ',s[x],' is not an alphanumeric character!');break;
      end;
      
      if(c>=radix) then
      begin
       strint_errors+=1;
       writeln('Error: ',s[x],' is not a valid character for radix ',radix);
       break;
      end;
      
      i*=radix; //multiply by the radix
      i+=c;     //add the digit from the character processed
    
      x:=x+1;
     end;
     strint:=i;
    end;
    
    
    
    
    begin
     radix:=16; //set the radix used by both intstr and strint functions
    
     a:=0;
     b:=strint('100');
    
     putstr(string0);
    
     while a<b do
     begin
      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>=$20) and (a<=$7E) then
      begin
       putstr(' ');
       putstr(chr(a));
      end;
    
      putstr(#10);
      a+=1;
     end;
     
     putstr(string0);
    
    end.
    
    (*
     fpc main.pas && ./main
    *)