Our server costs ~$56 per month to run. Please consider donating or becoming a Patron to help keep the site running. Help us gain new members by following us on Twitter and liking our page on Facebook!
Current time: March 29, 2024, 3:59 am

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Arithmetic Expression Compiler
#31
RE: Arithmetic Expression Compiler
Yesterday, I implemented the "ElseIf" directive into my programming language. Here is an example of how to use it:
Code:
AsmStart
    debug=0
    macro pushIntToStack x
    {
        sub esp,4
        fld dword [x]
        fistp dword [esp]
    }
    macro pushPointerToStack x
    {
        sub esp,4
        lea ebx,[x]
        mov [esp],ebx
    }
    macro pushStringToStack x
    {
        sub esp,4
        mov dword [esp],x
    }
    format PE console
    entry start

    include 'win32a.inc'

    section '.text' code executable
    start:
    jmp enterNumber$
        enterNumber db "Enter the ordinal number of the month.",10,0
    enterNumber$:
    pushStringToStack enterNumber
    call [printf]
    pushPointerToStack month
    jmp floatSign$
        floatSign db "%f",0
    floatSign$:
    pushStringToStack floatSign
    call [scanf]
AsmEnd
If month=1
    days:=31
ElseIf month=2
    AsmStart
        jmp enterTheYearString$
        enterTheYearString:
            db "Enter the year:",10,0
        enterTheYearString$:
        invoke printf,enterTheYearString
        pushPointerToStack year
        pushStringToStack floatSign
        call [scanf]
    AsmEnd
    If mod(year,4)=0 & not(mod(year,400)=0)
        days:=29
    Else
        days:=28
    EndIf
ElseIf month=3
    days:=31
ElseIf month=4
    days:=30
ElseIf month=5
    days:=31
ElseIf month=6
    days:=30
ElseIf month=7 | month=8
    days:=31
ElseIf month=9
    days:=30
ElseIf month=10
    days:=31
ElseIf month=11
    days:=30
ElseIf month=12
    days:=31
Else
    AsmStart
        jmp invalidDateString$
        invalidDateString:
            db "Next time you open this program, please enter a natural number between 1 and 12.",10,0
        invalidDateString$:
        invoke printf,invalidDateString
        invoke system,_pause
        invoke exit,1
    AsmEnd
EndIf
AsmStart
    pushIntToStack days
    jmp numberOfDaysString$
    numberOfDaysString:
        db "The month with that ordinal number has %d days.",10,0
    numberOfDaysString$:
    invoke printf,numberOfDaysString
    invoke system,_pause
    invoke exit,0

_pause db "PAUSE",0

section '.rdata' readable writable
result dd ?
month dd ?
days dd ?
year dd ?

section '.idata' data readable import
library msvcrt,'msvcrt.dll'
import msvcrt,printf,'printf',system,'system',exit,'exit',scanf,'scanf'
AsmEnd
The 32-bit Windows executable is available as the "months.exe" file in this ZIP-archive.
#32
RE: Arithmetic Expression Compiler
I've written another example program in AEC, this time it's printing the Pascal's Traingle:
Code:
;Pascal's triangle
AsmStart ;Inline assembly in AEC starts with "AsmStart" and ends with "AsmEnd".
    macro pushIntegerToTheSystemStack decimalNumber ;This is why I've chosen FlatAssembler for the back-end of my compiler: powerful and easy-to-use preprocessor.
    {
        sub esp,4 ;"esp" is the CPU register which points right below the data at the top of the system stack.
        fld dword [decimalNumber]
        fistp dword [esp] ;"fistp" is the x86 assembly language directive for converting decimal numbers to integers.
    }
    macro pushPointerToTheSystemStack pointer
    {
        sub esp,4
        lea ebx,[pointer]
        mov [esp],ebx
    }
    macro pushStringToTheSystemStack string
    {
        sub esp,4
        mov dword [esp],string
    }
    format PE console ;"PE" means 32-bit Windows executable.
    entry start

    include 'win32a.inc' ;FlatAssembler macros for importing functions from DLLs.
    section '.text' code executable
    
    start:
    jmp howManyRowsString$
    howManyRowsString:
        db "How many rows of Pascal's triangle do you want to be printed?",10,0 ;10 is '\n', and 0 is '\0'.
    howManyRowsString$:
    pushStringToTheSystemStack howManyRowsString
    call [printf] ;printf(howManyRowsString)
    jmp theFloatSymbol$
    theFloatSymbol:
        db "%f",0
    theFloatSymbol$:
    pushPointerToTheSystemStack numberOfRows
    pushStringToTheSystemStack theFloatSymbol
    call [scanf] ;scanf(theFloatSymbol,&numberOfRows)
AsmEnd
currentRow := 0
While currentRow < numberOfRows | currentRow = numberOfRows
    AsmStart
        jmp currentRowString$
        currentRowString:
            db "Row #%d:",9,0 ;9 is '\t' (the tabulator).
        currentRowString$:
        pushIntegerToTheSystemStack currentRow
        pushStringToTheSystemStack currentRowString
        call [printf] ;printf(currentRowString,currentRow)
    AsmEnd
    currentColumn:=0
    While currentColumn < currentRow | currentColumn = currentRow
        If currentColumn = 0
            array (currentRow * numberOfRows + currentColumn) := 1 ;When I haven't programmed the compiler to deal with 2-dimensional arrays...
        ElseIf currentColumn = currentRow
            array (currentRow * numberOfRows + currentColumn) := 1  
        Else
            numberImmediatelyAbove := array ( (currentRow - 1) * numberOfRows + currentColumn)
            numberBeforeTheImmediatelyAboveOne := array ( (currentRow - 1) * numberOfRows + currentColumn - 1)
            array (currentRow * numberOfRows + currentColumn) := numberBeforeTheImmediatelyAboveOne + numberImmediatelyAbove
        EndIf
        numberToBePrinted := array (currentRow * numberOfRows + currentColumn)
        AsmStart
            jmp integerSignWithTabulator$
            integerSignWithTabulator:
                db "%.0f",9,0 ;"%.0f\t", "%.0f" means for "printf" to round the decimal number to the nearest integer.
            integerSignWithTabulator$:
            fld dword [numberToBePrinted]
            fstp qword [esp] ;"qword" means "double", because "printf" from "MSVCRT.DLL" can't print "float" which hasn't been converted to "double". When writing in Assembly, you need to deal with that kind of annoying stuff.
            pushStringToTheSystemStack integerSignWithTabulator
            call [printf] ;printf(integerSignWithTabulator,numberToBePrinted)
        AsmEnd
        currentColumn := currentColumn + 1
    EndWhile
    AsmStart
        jmp newLineString$
        newLineString:
            db 10,0 ;"\n"
        newLineString$:
        pushStringToTheSystemStack newLineString
        call [printf] ;printf(newLineString)
    AsmEnd
    currentRow := currentRow + 1
EndWhile
AsmStart
pushStringToTheSystemStack pauseString
call [system] ;system(pauseString), the "Press any key to continue..." message so that the console window doesn't immediately close.
invoke exit,0 ;exit(0)

pauseString db "PAUSE",0

section '.rdata' readable writable
    result dd ? ;A variable used internally by the AEC compiler.
    numberOfRows dd ?
    currentRow dd ?
    currentColumn dd ?
    numberBeforeTheImmediatelyAboveOne dd ?
    numberImmediatelyAbove dd ?
    numberToBePrinted dd ?
    array dd 30000 DUP(?)

section '.idata' data readable import
    library msvcrt,'msvcrt.dll' ;"Microsoft Visual C Runtime Library", available as "C:\Windows\System32\msvcrt.dll" on Windows 98 and newer.
        import msvcrt,printf,'printf',system,'system',exit,'exit',scanf,'scanf',clock,'clock'
AsmEnd
Now there are 7 example programs in the ZIP archive. It's kind of a symbolic number, I think I'll stop there for some time. I've also slightly refactored the "permutations" example to make it easier to read.
#33
RE: Arithmetic Expression Compiler
Some "dragitz" on GitHub will collaborate with me on that compiler, he claims to be able to implement matrix operations into it. I am a bit skeptical this will end up well (primarily because they will need to edit the parser code, which is rather dirty), but it's good to have a company.
https://github.com/dragitz/ArithmeticExp...ler/pull/1
#34
RE: Arithmetic Expression Compiler
Anyway, I've updated the AEC.
First, and perhaps most important, I didn't realize before that JavaScript had a standard way (using ArrayBuffer) to convert decimal numbers to IEEE754 hexadecimals. Not only in modern browsers, it works all the way back to Internet Explorer 10-era browsers. That opens a door towards making an AEC compiler running in NodeJS as powerful (if not more) than the one that runs on Duktape. It also opens a door to targetting GNU Assembler as well as FlatAssembler. In case you didn't know, GNU Assembler has been supporting Intel Syntax for quite some time now (though it's not as feature-full as FlatAssembler is).
Second, today, I implemented the conditional "?:" operator in my compiler. You can also see that in the browser. It can really make the code shorter sometimes. On the university, we are taught not to use it, but I strongly disagree with that.
I also changed the UI of the web-based version of the compiler, though I am not sure I made it better.
#35
RE: Arithmetic Expression Compiler
Anyway, here is an example of how conditional operators and string constants (things I've added to AEC recently) can make the code significantly shorter:
Code:
AsmStart
   debug=0
    macro pushIntToStack x
    {
        sub esp,4
        fld dword [x]
        fistp dword [esp]
    }
    macro pushPointerToStack x
    {
        sub esp,4
        lea ebx,[x]
        mov [esp],ebx
    }
    macro pushStringToStack x
    {
        sub esp,4
        mov dword [esp],x
    }
    format PE console
    entry start

    include 'win32a.inc'

section '.text' code executable
    start:
    jmp enterNumber$
        enterNumber db "Enter the ordinal number of the month.",10,0
    enterNumber$:
    pushStringToStack enterNumber
    call [printf]
    pushPointerToStack month
    jmp floatSign$
        floatSign db "%f",0
    floatSign$:
    pushStringToStack floatSign
    call [scanf]
AsmEnd
If month=2
    enterTheYearString<="Enter the year:",10,0
    AsmStart
        invoke printf,enterTheYearString
        pushPointerToStack year
        pushStringToStack floatSign
        call [scanf]
    AsmEnd
    If mod(year,4)=0 & not(mod(year,400)=0)
        days:=29
    Else
        days:=28
    EndIf
ElseIf mod(month,1)=0 & month>0 & month<13
    days:= month=1? 31 : month=3? 31 : month=4? 30 : month=5? 31 : month=6? 30 : (month=7 | month=8)? 31 : month=9? 30 : month=10? 31 : month=11? 30 : 31
Else
    invalidDateString<="Next time you open this program, please enter a natural number between 1 and 12.",10,0
    AsmStart
        invoke printf,invalidDateString
        invoke system,_pause
        invoke exit,1
    AsmEnd
EndIf
numberOfDaysString<="The month with that ordinal number has %d days.",10,0
AsmStart
    pushIntToStack days
    invoke printf,numberOfDaysString
    invoke system,_pause
    invoke exit,0

    _pause db "PAUSE",0

section '.rdata' readable writable
    result dd ?
    month dd ?
    days dd ?
    year dd ?

section '.idata' data readable import
    library msvcrt,'msvcrt.dll'
        import msvcrt,printf,'printf',system,'system',exit,'exit',scanf,'scanf'
AsmEnd
That dragitz who forked my repository on GitHub apparently won't cooperate with me any more. But, anyway, I've introduced a few hard-to-fix problems in my code by adding new features. First of all, the AST diagrams that are produced for ternary operators are next to useless. Second, perhaps even more damaging, there is a [url=https://github.com/FlatAssembler/Arithme...r/issues/1]bug[/url] that prevents my implementation of QuickSort to be compiled using the newest version of the compiler (HybridSort and MergeSort are unaffected), and it's not easy to fix. I've chosen a fundamentally wrong method of compiling long ago and it would take very long to think of a better algorithm and implement it.
#36
RE: Arithmetic Expression Compiler
So, yesterday, I made two rather big achievements. First of all, I changed my compiler so that it can also produce assembly code compatible with GNU Assembler. It should now be relatively easy to add even more assemblers, I made the code significantly more modular in the process.
I also finally managed to make a version that can be run in NodeJS, you can download it here.
Here is an example program targeting GNU Assembler, drawing the Sierpinski triangle:
Code:
Syntax GAS
;So, this is an example of how to target GNU Assembler (GAS) using AEC, and how to target Linux.
AsmStart ;What follows is the assembly code produced by CLANG 9.0 on Linux, I don't understand it either, and it's not important.
    .text
    .file   "sierpinski.c"
    .globl  main                    # -- Begin function main
    .p2align    4, 0x90
    .type   main,@function
main:                                   # @main
# %bb.0:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $0, -4(%ebp)
    leal    .L.str, %eax
    movl    %eax, (%esp)
    calll   printf
    leal    .L.str.1, %ecx
    movl    %ecx, (%esp)
    leal    n, %ecx
    movl    %ecx, 4(%esp)
    movl    %eax, -8(%ebp)          # 4-byte Spill
    calll   __isoc99_scanf
AsmEnd ;And here goes a program written in AEC, finally.
i:=0
While i<n ;This loop will fill the array "new_array" with zeros, except the point right in the middle, which will be filled with 1.
    If i=n/2-mod(n/2,1) ;So, if i=floor(n/2), except that I didn't put "floor" in my programming language, so I need to paraphrase it.
        new_array(i):=1
    Else
        new_array(i):=0
    EndIf
    i:=i+1
EndWhile
i:=0
While i<n
    j:=0
    While j<n | j=n ;Printing the current state and the new line.
        If j=n
            AsmStart
                .intel_syntax noprefix #Because I don't know anything about att_syntax assembly.
                mov byte ptr [esp+4],'\n' #'\n' is, of course, the new line character (in FlatAssembler, you would need to write 10, the ASCII of '\n').
                .att_syntax #Not important here (my compiler switches to Intel Syntax before outputting anything except inline Assembly), but added for consistency.
            AsmEnd
        ElseIf new_array(j)=1
            AsmStart
                .intel_syntax noprefix
                mov byte ptr [esp+4],'*'
                .att_syntax
            AsmEnd
        Else
            AsmStart
                .intel_syntax noprefix
                mov byte ptr [esp+4],' '
                .att_syntax
            AsmEnd
        EndIf
        charSign<="%c\0" ;If you write "'%c',0", as you write that in FlatAssembler, GAS complains.
        AsmStart
            .intel_syntax noprefix
            lea ebx,dword ptr [charSign]
            mov dword ptr [esp],ebx
            call printf
            .att_syntax
        AsmEnd
        j:=j+1
    EndWhile
    j:=0
    While j<n ;Copying "new_array" into "old_array".
        old_array(j):=new_array(j)
        j:=j+1
    EndWhile
    j:=0
    While j<n
        If j=0 | j=n-1 ;Edges
            new_array(j):=0
        ElseIf old_array(j-1)=old_array(j+1) ;In other words, each cell in the new line in the Sierpinski triangle is the "xor" of its neighbours in the line above it.
            new_array(j):=0
        Else
            new_array(j):=1
        EndIf
        j:=j+1
    EndWhile
    i:=i+1
EndWhile
AsmStart ;Again, assembly code produced by CLANG 9.0 on Linux, I don't understand it either.
    xorl    %ecx, %ecx
    movl    %eax, -12(%ebp)         # 4-byte Spill
    movl    %ecx, %eax
    addl    $24, %esp
    popl    %ebp
    retl
.Lfunc_end0:
    .size   main, .Lfunc_end0-main
                                        # -- End function
    .type   .L.str,@object          # @.str
    .section    .rodata.str1.1,"aMS",@progbits,1
.L.str:
    .asciz  "Enter the number of rows and columns in the Sierpinski triangle.\n"
    .size   .L.str, 67

    .type   .L.str.1,@object        # @.str.1
.L.str.1:
    .asciz  "%f"
    .size   .L.str.1, 3
    .type   n,@object               # @n
    .comm   n,4,4
    .type   i,@object               # @i
    .comm   i,4,4
    .type   j,@object               # @j
    .comm   j,4,4
    .type   result,@object          # @result
    .comm   result,4,4
    .type   old_array,@object       # @old_array
    .comm   old_array,320,4
    .type   new_array,@object       # @new_array
    .comm   new_array,320,4

    .ident  "clang version 9.0.0 (tags/RELEASE_900/final)"
    .section    ".note.GNU-stack","",@progbits
    .addrsig
    .addrsig_sym printf
    .addrsig_sym __isoc99_scanf
    .addrsig_sym n
AsmEnd
#37
RE: Arithmetic Expression Compiler
I've made another program in my programming language targeting Linux and GNU Assembler, an analog clock. Here it goes:
Code:
Syntax GAS
;This is yet another example of how to target Linux using GNU Assembler.
AsmStart ;What follows is code produced by GCC 9.3.0 on Linux, I don't understand much of it either, and it's not important.
   .file   "analogClock.c"
   .text
   .comm   result,4,4
   .comm   i,4,4
   .comm   x,4,4
   .comm   y,4,4
   .comm   currentSign,4,4
   .comm   centerX,4,4
   .comm   centerY,4,4
   .comm   distance,4,4
   .comm   clockRadius,4,4
   .comm   output,7360,32
   .comm   hour,4,4
   .comm   minute,4,4
   .comm   second,4,4
   .comm   angle,4,4
   .comm   endOfTheHandX,4,4
   .comm   endOfTheHandY,4,4
   .comm   coefficientOfTheDirection,4,4
   .comm   windowWidth,4,4
   .comm   windowHeight,4,4
   .comm   lowerBoundX,4,4
   .comm   upperBoundX,4,4
   .comm   lowerBoundY,4,4
   .comm   upperBoundY,4,4
   .comm   isXWithinBounds,4,4
   .comm   isYWithinBounds,4,4
   .comm   expectedY,4,4
   .comm   expectedX,4,4
   .comm   j,4,4
   .comm   ASCIIofSpaceAsFloat32,4,4
   .globl  main
   .type   main, @function
main:
.LFB0:
   .cfi_startproc
   leal    4(%esp), %ecx
   .cfi_def_cfa 1, 0
   andl    $-16, %esp
   pushl   -4(%ecx)
   pushl   %ebp
   .cfi_escape 0x10,0x5,0x2,0x75,0
   movl    %esp, %ebp
   pushl   %ecx
   .cfi_escape 0xf,0x3,0x75,0x7c,0x6
   subl    $36, %esp
   subl    $12, %esp
   leal    -20(%ebp), %eax
   pushl   %eax
   call    time
   addl    $16, %esp
   subl    $12, %esp
   leal    -20(%ebp), %eax
   pushl   %eax
   call    localtime
   addl    $16, %esp
   movl    %eax, -16(%ebp)
   movl    -16(%ebp), %eax
   movl    8(%eax), %eax
   movl    %eax, -28(%ebp)
   fildl   -28(%ebp)
   fstps   hour
   movl    -16(%ebp), %eax
   movl    4(%eax), %eax
   movl    %eax, -28(%ebp)
   fildl   -28(%ebp)
   fstps   minute
   movl    -16(%ebp), %eax
   movl    (%eax), %eax
   movl    %eax, -28(%ebp)
   fildl   -28(%ebp)
   fstps   second
   #APP
AsmEnd ;And now finally follows a program written in AEC.
windowWidth:=80
windowHeight:=23
ASCIIofSpace<=" \0\0\0" ;As integer. We know we are dealing with a...
ASCIIofNewLine<="\n\0\0\0" ;32-bit low-endian machine.
ASCIIofStar<="*\0\0\0"
i:=0
While i<windowWidth*windowHeight ;First, fill the window with spaces and newlines.
   If mod(i,windowWidth)=windowWidth-1
       AsmStart
           .intel_syntax noprefix
           fild dword ptr ASCIIofNewLine
           fstp dword ptr currentSign
           .att_syntax
       AsmEnd
   Else
       AsmStart
           .intel_syntax noprefix
           fild dword ptr ASCIIofSpace
           fstp dword ptr currentSign
           fld dword ptr currentSign
           fstp dword ptr ASCIIofSpaceAsFloat32
           .att_syntax
       AsmEnd
   EndIf
   output[i]:=currentSign
   i:=i+1
EndWhile
centerX:=windowWidth/2-mod(windowWidth/2,1)
centerY:=windowHeight/2-mod(windowHeight/2,1)
clockRadius:=(centerX<centerY)?(centerX):(centerY)-1
i:=0
While i<windowWidth*windowHeight ;Next, draw the circle which represents the clock.
   y:=i/windowWidth-mod(i/windowWidth,1) ;When I didn't put "floor" into my programming language...
   x:=mod(i,windowWidth)
   distance:=sqrt((x-centerX)*(x-centerX)+(y-centerY)*(y-centerY)) ;Pythagorean Theorem.
   If abs(distance-clockRadius)<3/4
       AsmStart
           .intel_syntax noprefix
           fild dword ptr ASCIIofStar
           fstp dword ptr currentSign
           .att_syntax
       AsmEnd
       output[i]:=currentSign
   EndIf
   i:=i+1
EndWhile
AsmStart
   .intel_syntax noprefix
   jmp ASCIIofDigits$
   ASCIIofDigits:
   .macro writeDigits startingWith=0
       .byte '0'+\startingWith,0,0,0 #".byte" is to GNU Assembler about the same as "db" is to FlatAssembler.
       .if \startingWith < 9
           writeDigits \startingWith+1
       .endif
   .endm
   writeDigits #The goal is to make Assembler output the ASCII of "0\0\0\01\0\0\02\0\0\0...9\0\0\0" inside the executable (if the instruction pointer points to it, it will, of course, be an invalid instruction).
   ASCIIofDigits$:
   .att_syntax
AsmEnd
;Label of "12"...
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+1*4] #The ASCII of '1'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
output[(centerY-clockRadius+1)*windowWidth+centerX]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+2*4] #The ASCII of '2'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
output[(centerY-clockRadius+1)*windowWidth+centerX+1]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+6*4] #The ASCII of '6'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
output[(centerY+clockRadius-1)*windowWidth+centerX]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+3*4] #The ASCII of '3'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
output[centerY*windowWidth+centerX+clockRadius-1]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+9*4] #The ASCII of '9'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
output[centerY*windowWidth+centerX-clockRadius+1]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+1*4] #The ASCII of '1'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(360/12)*(clockRadius-1)]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+2*4] #The ASCII of '2'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(2*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(2*360/12)*(clockRadius-1.5)]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+4*4] #The ASCII of '4'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(4*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(4*360/12)*(clockRadius-1)]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+5*4] #The ASCII of '5'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(5*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(5*360/12)*(clockRadius-1)]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+7*4] #The ASCII of '7'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(7*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(7*360/12)*(clockRadius-1)]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+8*4] #The ASCII of '8'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(8*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(8*360/12)*(clockRadius-1)]:=currentSign
;Label "10"...
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+1*4] #The ASCII of '1'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(10*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(10*360/12)*(clockRadius-1.5)]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+0*4] #The ASCII of '0'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(10*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(10*360/12)*(clockRadius-1.5)+1]:=currentSign
;Label "11"...
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+1*4] #The ASCII of '1'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(11*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(11*360/12)*(clockRadius-1.5)]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigits+1*4] #The ASCII of '1'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(11*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(11*360/12)*(clockRadius-1.5)+1] := currentSign
j:=0
While j<3
   If j=0
       angle:=(mod(hour+minute/60,12))*(360/12)
   ElseIf j=1
       angle:=minute*(360/60)
   Else
       angle:=second*(360/60)
   EndIf
   endOfTheHandX:=centerX+sin(angle)*clockRadius/(j=0?2:j=1?3/2:4/3) ;Hour hand will be the shortest, and the hand that shows the seconds will be the longest.
   endOfTheHandY:=centerY-cos(angle)*clockRadius/(j=0?2:j=1?3/2:4/3)
   coefficientOfTheDirection:=(endOfTheHandY-centerY)/(endOfTheHandX-centerX)
   debugString <= "Drawing line between (%d,%d) and (%d,%d).\n\0"
   AsmStart
       .intel_syntax noprefix
       .ifdef DEBUG #Conditional assembly, this will only be assembled if you tell GNU Assembler (by modifying the file or using command line) that you want to enable debugging.
           fld dword ptr endOfTheHandY
           fistp dword ptr result
           push dword ptr result #This (pushing a "dword" onto the system stack) breaks the compatibility with 64-bit Linux (but you can still enable it by disabling debugging)!
           fld dword ptr endOfTheHandX
           fistp dword ptr result
           push dword ptr result
           fld dword ptr centerY
           fistp dword ptr result
           push dword ptr result
           fld dword ptr centerX
           fistp dword ptr result
           push dword ptr result
           lea ebx,debugString
           push ebx
           call printf
       .endif #End of the conditional assembly.
       .att_syntax
   AsmEnd
   i:=0
   While i<windowWidth*windowHeight
       lowerBoundX:=(endOfTheHandX<centerX)?(endOfTheHandX):(centerX)
       upperBoundX:=(endOfTheHandX>centerX)?(endOfTheHandX):(centerX)
       lowerBoundY:=(endOfTheHandY<centerY)?(endOfTheHandY):(centerY)
       upperBoundY:=(endOfTheHandY>centerY)?(endOfTheHandY):(centerY)
       y:=i/windowWidth-mod(i/windowWidth,1)
       x:=mod(i,windowWidth)
       isXWithinBounds:=(x>lowerBoundX | x=lowerBoundX) & (x<upperBoundX | x=upperBoundX) ;Damn... Now I understand why almost every programming language supports the "<=" and ">=" operators, no matter how much harder they make the language to tokenize.
       isYWithinBounds:=(y>lowerBoundY | y=lowerBoundY) & (y<upperBoundY | y=upperBoundY)
       If isXWithinBounds=1 & isYWithinBounds=1
           expectedY:=(x-centerX)*coefficientOfTheDirection+centerY
           expectedX:=(y-centerY)*(1/coefficientOfTheDirection)+centerX
           debugString1 <= "The point (%d,%d) is within bounds, expectedY is %d and expectedX is %d.\n\0"
           AsmStart
               .intel_syntax noprefix
               .ifdef DEBUG
                   fld dword ptr expectedX
                   fistp dword ptr result
                   push dword ptr result
                   fld dword ptr expectedY
                   fistp dword ptr result
                   push dword ptr result
                   fld dword ptr y
                   fistp dword ptr result
                   push dword ptr result
                   fld dword ptr x
                   fistp dword ptr result
                   push dword ptr result
                   lea ebx,debugString1
                   push ebx
                   call printf
               .endif
               .att_syntax
           AsmEnd
           ASCIIofLetterH<="h\0\0\0"
           ASCIIofLetterM<="m\0\0\0"
           ASCIIofLetterS<="s\0\0\0"
           If j=0
               AsmStart
                   .intel_syntax noprefix
                   fild dword ptr ASCIIofLetterH
                   fstp dword ptr currentSign
                   .att_syntax
               AsmEnd
           ElseIf j=1
               AsmStart
                   .intel_syntax noprefix
                   fild dword ptr ASCIIofLetterM
                   fstp dword ptr currentSign
                   .att_syntax
               AsmEnd
           Else
               AsmStart
                   .intel_syntax noprefix
                   fild dword ptr ASCIIofLetterS
                   fstp dword ptr currentSign
                   .att_syntax
               AsmEnd
           EndIf
           If (upperBoundX=lowerBoundX | upperBoundY=lowerBoundY) & output[i]=ASCIIofSpaceAsFloat32
               output[i]:=currentSign
           EndIf
           If (abs(expectedY-y)<3/4 | abs(expectedX-x)<3/4) & output[i]=ASCIIofSpaceAsFloat32
               output[i]:=currentSign
           EndIf
       EndIf
       i:=i+1
   EndWhile
   j:=j+1
EndWhile
;Draw some ornament...
ASCIIofLetterX<="x\0\0\0"
AsmStart
   .intel_syntax noprefix
   fild dword ptr ASCIIofLetterX
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
i:=0
While i<windowWidth*windowHeight
   y:=i/windowWidth-mod(i/windowWidth,1)
   x:=mod(i,windowWidth)
   If abs(windowHeight-2*ln(1+abs((x-centerX)/2))-y)<1-abs(x-centerX)/(centerX*95/112) & x>1/2*centerX & x<3/2*centerX & output[i]=ASCIIofSpaceAsFloat32 ;The logarithmic curve looks somewhat like a lemma of a flower.
       output[i]:=currentSign
   EndIf
   i:=i+1
EndWhile
AsmStart ;And here goes how, according to GCC 9.3.0, you print the table and finish an Assembly program on 32-bit Linux (I don't understand that either, and it's not important).
#NO_APP
   movl    $0, -12(%ebp)
   jmp .L2
.L3:
   movl    -12(%ebp), %eax
   movss   output(,%eax,4), %xmm0
   cvttss2sil  %xmm0, %eax
   subl    $12, %esp
   pushl   %eax
   call    putchar
   addl    $16, %esp
   addl    $1, -12(%ebp)
.L2:
   cmpl    $1839, -12(%ebp)
   jle .L3
   movl    $0, %eax
   movl    -4(%ebp), %ecx
   .cfi_def_cfa 1, 0
   leave
   .cfi_restore 5
   leal    -4(%ecx), %esp
   .cfi_def_cfa 4, 4
   ret
   .cfi_endproc
.LFE0:
   .size   main, .-main
   .ident  "GCC: (GNU) 9.3.0"
   .section    .note.GNU-stack,"",@progbits
AsmEnd
Here is what it outputs right now (at 19:09):
Code:
                                                                             
                                    *******                                  
                                  ***  12 ***                                
                                ***11      1***                              
                               **             **                              
                               *               *                              
                              **10            2**                            
                              *            m    *                            
                             **           mm    **                            
                             *           mm      *                            
                             *          m        *                            
                             *9        h        3*                            
                             *        hh         *                            
                             *       hh          *                            
                             **     sh          **                            
                              *8   ssh         4*                            
                              **  ss           **                            
                               *               *                              
                    xxx        **  7        5 **        xxx                  
                       xxxxxxxx ***         *** xxxxxxxx                      
                             xxxxx***  6  ***xxxxx                            
                                 xxx*******xxx                                
                                    xxx xxx                                  
It's a bit weird that, in order to effectively work in your own programming language, you need to insert tons of assembly code which you yourself don't fully understand.
#38
RE: Arithmetic Expression Compiler
Is this a personal blog? It seems that something geared toward discussion would have posts by multiple people. I'm confused.
  
“If you are the smartest person in the room, then you are in the wrong room.” — Confucius
                                      
#39
RE: Arithmetic Expression Compiler
arewethereyet Wrote:Is this a personal blog?
Well, it's not supposed to be.
arewethereyet Wrote:It seems that something geared toward discussion would have posts by multiple people.
Well, then, post something related to the topic. You are welcome. For instance, what do you think about the program I described in my last post here? Why do you think it is that, if you program in your own programming language, you need to insert tons of code you yourself don't fully understand? Do you think the same is true for some other advanced parts of informatics, such as operating system development?
arewethereyet Wrote:I'm confused.
Well, I am also somewhat confused where did the tech-savvy guys go and why they don't respond to me any more.
#40
RE: Arithmetic Expression Compiler
Yesterday evening, I made a program in my programming language that can run on DOS. It's an analog clock again, similar to the previous one.
Code:
Syntax GAS
;This is the same program as in the "analogClock.aec" file, just modified to
;run on DOS instead of Linux. It also compiles using GNU Assembler.
;Namely, GCC 9.3.0 and GNU Assembler 2.34, although they are released in
;2019, still feature the ability to compile for DOS. You don't need to
;run them on DOS for that, in fact, I doubt they even can be run on DOS.
;If you manage to compile them to run on DOS, they will probably run out of
;RAM even for the simplest programs (DOS can't use more than 64MB of RAM,
;which is far too little to run a modern compiler). You can run them on
;Linux and they will produce a DOS executable which you then can run in an
;emulator. That's called cross-compiling. Now, it's not possible to do with
;the stripped-down version of GNU Compiler Collection (GCC) you get with
;Linux, you need to build it from source to get all the features (among
;other things, cross-compilation to many OS-es). It's not too hard, but it
;does take hours to compile full version of GCC even on a super-modern
;computer. For some reason that escapes me, this particular executable
;causes DosBox to crash, even though it works on FreeDOS in VirtualBox.
;Now, I hope this goes without saying, but if some modern program runs on
;DOS, that's probably a coincidence, and you can't count on it working
;flawlessly. Developers have long stopped testing whether their app works
;under DOS. So, while the C library that comes with GCC 9.3.0 can compile
;for DOS, attempts to actually link with it lead to countless linker errors.
;GCC will by default attempt to link to the C library, even if your code
;doesn't use any of the functions present in it. So, you need to compile
;the assembly code ArithmeticExpressionCompiler produces with:
;   djgpp-gcc -o analogClockForDOS.exe -ffreestanding -nostdlib analogClockForDOS.s
;For that reason, I wasn't able to compile Duktape to run on DOS.
;Why use GNU Assembler instead of FlatAssembler? Well, first of all, I
;already have tons of inline assembly compatible with GNU Assembler (from
;"analogClock.aec" which runs on Linux). Second, when you work in
;GNU Assembler, you don't have to write the complicated code for putting
;the processor in the 32-bit mode (DOS programs automatically start in
;16-bit mode), GNU Assembler does that for you.
;Now, in order for 32-bit apps to be able to run on DOS, you need to have
;a driver called DPMI (DOS Protected Mode Interface). It comes pre-installed
;in FreeDOS, but not on MS-DOS. It also comes with Windows 3.x.
;FlatAssembler for DOS is also a 32-bit app and it won't run on DOS without
;a DPMI installed and run.
AsmStart ;So, the following code is generated by GCC 9.3.0, plus some inline assembly I put in the C program.
   .file    "analogClock.c"
    .section .text
/APP
    .intel_syntax noprefix
call _main #I hope this goes without saying, but when you are developing...
#...for a system without a C library, there is no guarantee "main" will...
#...be called first (or even at all before your program crashes),
#you need to take care of that yourself.
.att_syntax

/NO_APP
    .globl    _putchar
_putchar:
LFB0:
    .cfi_startproc
    pushl    %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    subl    $4, %esp
    movl    8(%ebp), %eax
    movb    %al, -4(%ebp)
/APP
# 9 "analogClock.c" 1
    movb -4(%ebp),%dl
movb $0x02,%ah
int $0x21

# 0 "" 2
/NO_APP
    nop
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE0:
   .comm   result,4
   .comm   i,4
   .comm   x,4
   .comm   y,4
   .comm   currentSign,4
   .comm   centerX,4
   .comm   centerY,4
   .comm   distance,4
   .comm   clockRadius,4
   .comm   output,7360
   .comm   hour,4
   .comm   minute,4
   .comm   second,4
   .comm   angle,4
   .comm   endOfTheHandX,4
   .comm   endOfTheHandY,4
   .comm   coefficientOfTheDirection,4
   .comm   windowWidth,4
   .comm   windowHeight,4
   .comm   lowerBoundX,4
   .comm   upperBoundX,4
   .comm   lowerBoundY,4
   .comm   upperBoundY,4
   .comm   isXWithinBounds,4
   .comm   isYWithinBounds,4
   .comm   expectedY,4
   .comm   expectedX,4
   .comm   j,4
   .comm   ASCIIofSpaceAsFloat32,4
   .comm   ASCIIofDigit0AsFloat32,4
   .comm   ASCIIofColonAsFloat32,4
   .comm   ASCIIofNewLineAsFloat32,4
    .globl    _main
_main:
LFB1:
    .cfi_startproc
    pushl    %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    subl    $24, %esp
/APP
# 18 "analogClock.c" 1
.ifdef debugForDOS #When you don't have a good debugger (like when working on DOS), you need to find some clever ways to debug. You know, like printing "Hello world!" step by step.
   .intel_syntax noprefix
   mov dl,'H'
   mov ax,0x200
   int 0x21
.att_syntax
.endif
.intel_syntax noprefix #Get current time. As we have no access to the standard C library here, we need to look up a way to do that in DOS API.
mov ax,0x2C00
int 0x21
mov byte ptr hour,ch
fild dword ptr hour
fstp dword ptr hour
mov byte ptr minute,cl
fild dword ptr minute
fstp dword ptr minute
mov byte ptr second,dh
fild dword ptr second
fstp dword ptr second #Eh, now I understand why some assembly-language programmers prefer att_syntax to intel_syntax (no need to write "dword ptr" there).
#Let's also set the graphic card to text-mode, in case it isn't in it (though I don't know if it's possible to invoke my program from some other mode without crashing DOS before my program even begins then).
mov ax,0x0003
int 0x10
.att_syntax
AsmEnd ;And now finally follows a program written in AEC.
windowWidth:=80
windowHeight:=23
ASCIIofSpace<=" \0\0\0" ;As integer. We know we are dealing with a...
ASCIIofNewLine<="\n\0\0\0" ;32-bit little-endian machine.
ASCIIofStar<="*\0\0\0"
i:=0
While i<windowWidth*windowHeight ;First, fill the window with spaces and newlines.
   If mod(i,windowWidth)=windowWidth-1
       AsmStart
           .intel_syntax noprefix
           fild dword ptr ASCIIofSpace #Not need for a new line, DOS will do that automatically.
           fstp dword ptr currentSign
           .att_syntax
       AsmEnd
   Else
       AsmStart
           .intel_syntax noprefix
           fild dword ptr ASCIIofSpace
           fstp dword ptr currentSign
           fld dword ptr currentSign
           fstp dword ptr ASCIIofSpaceAsFloat32
           .att_syntax
       AsmEnd
   EndIf
   output[i]:=currentSign
   i:=i+1
EndWhile
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
   mov dl,'e'
   mov ax,0x200
   int 0x21
.endif
.att_syntax
AsmEnd
centerX:=windowWidth/2-mod(windowWidth/2,1)
centerY:=windowHeight/2-mod(windowHeight/2,1)
clockRadius:=(centerX<centerY)?(centerX):(centerY)-1
i:=0
While i<windowWidth*windowHeight ;Next, draw the circle which represents the clock.
   y:=i/windowWidth-mod(i/windowWidth,1) ;When I didn't put "floor" into my programming language...
   x:=mod(i,windowWidth)
   distance:=sqrt((x-centerX)*(x-centerX)+(y-centerY)*(y-centerY)) ;Pythagorean Theorem.
   If abs(distance-clockRadius)<3/4
       AsmStart
           .intel_syntax noprefix
           fild dword ptr ASCIIofStar
           fstp dword ptr currentSign
           .att_syntax
       AsmEnd
       output[i]:=currentSign
   EndIf
   i:=i+1
EndWhile
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
   mov dl,'l'
   mov ax,0x200
   int 0x21
.endif
.att_syntax
AsmEnd
AsmStart
   .intel_syntax noprefix
   jmp ASCIIofDigitsAsInt32Array$
   ASCIIofDigitsAsInt32Array:
   .macro writeDigits startingWith=0
       .byte '0'+\startingWith,0,0,0 #".byte" is to GNU Assembler about the same as "db" is to FlatAssembler.
       .if \startingWith < 9
           writeDigits \startingWith+1
       .endif
   .endm
   writeDigits #The goal is to make Assembler output the ASCII of "0\0\0\01\0\0\02\0\0\0...9\0\0\0" inside the executable (if the instruction pointer points to it, it will, of course, be an invalid instruction).
   ASCIIofDigitsAsInt32Array$:
   .att_syntax
AsmEnd
;Label of "12"...
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+1*4] #The ASCII of '1'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
output[(centerY-clockRadius+1)*windowWidth+centerX]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+2*4] #The ASCII of '2'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
output[(centerY-clockRadius+1)*windowWidth+centerX+1]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+6*4] #The ASCII of '6'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
output[(centerY+clockRadius-1)*windowWidth+centerX]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+3*4] #The ASCII of '3'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
output[centerY*windowWidth+centerX+clockRadius-1]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+9*4] #The ASCII of '9'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
output[centerY*windowWidth+centerX-clockRadius+1]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+1*4] #The ASCII of '1'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(360/12)*(clockRadius-1)]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+2*4] #The ASCII of '2'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(2*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(2*360/12)*(clockRadius-1.5)]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+4*4] #The ASCII of '4'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(4*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(4*360/12)*(clockRadius-1)]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+5*4] #The ASCII of '5'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(5*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(5*360/12)*(clockRadius-1)]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+7*4] #The ASCII of '7'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(7*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(7*360/12)*(clockRadius-1)]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+8*4] #The ASCII of '8'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1)*cos(8*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(8*360/12)*(clockRadius-1)]:=currentSign
;Label "10"...
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+1*4] #The ASCII of '1'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(10*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(10*360/12)*(clockRadius-1.5)]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+0*4] #The ASCII of '0'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(10*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(10*360/12)*(clockRadius-1.5)+1]:=currentSign
;Label "11"...
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+1*4] #The ASCII of '1'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(11*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(11*360/12)*(clockRadius-1.5)]:=currentSign
AsmStart
   .intel_syntax noprefix
   fild dword ptr [ASCIIofDigitsAsInt32Array+1*4] #The ASCII of '1'.
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
y:=centerY-(clockRadius-1.5)*cos(11*360/12)
y:=y-mod(y,1)
output[y*windowWidth+centerX+sin(11*360/12)*(clockRadius-1.5)+1] := currentSign
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
   mov dl,'o'
   mov ax,0x200
   int 0x21
.endif
.att_syntax
AsmEnd
j:=0
While j<3
   If j=0
       angle:=(mod(hour+minute/60,12))*(360/12)
   ElseIf j=1
       angle:=minute*(360/60)
   Else
       angle:=second*(360/60)
   EndIf
   endOfTheHandX:=centerX+sin(angle)*clockRadius/(j=0?2:j=1?3/2:4/3) ;Hour hand will be the shortest, and the hand that shows the seconds will be the longest.
   endOfTheHandY:=centerY-cos(angle)*clockRadius/(j=0?2:j=1?3/2:4/3)
   coefficientOfTheDirection:=(endOfTheHandY-centerY)/(endOfTheHandX-centerX)
   debugString <= "Drawing line between (%d,%d) and (%d,%d).\n\0"
   AsmStart
       .intel_syntax noprefix
       .ifdef DEBUG #Conditional assembly, this will only be assembled if you tell GNU Assembler (by modifying the file or using command line) that you want to enable debugging.
           fld dword ptr endOfTheHandY
           fistp dword ptr result
           push dword ptr result #This (pushing a "dword" onto the system stack) breaks the compatibility with 64-bit Linux (but you can still enable it by disabling debugging)!
           fld dword ptr endOfTheHandX
           fistp dword ptr result
           push dword ptr result
           fld dword ptr centerY
           fistp dword ptr result
           push dword ptr result
           fld dword ptr centerX
           fistp dword ptr result
           push dword ptr result
           lea ebx,debugString
           push ebx
           call printf #I hope this goes without saying, but, unless you link with a C library, this won't work under DOS.
       .endif #End of the conditional assembly.
       .att_syntax
   AsmEnd
   i:=0
   While i<windowWidth*windowHeight
       lowerBoundX:=(endOfTheHandX<centerX)?(endOfTheHandX):(centerX)
       upperBoundX:=(endOfTheHandX>centerX)?(endOfTheHandX):(centerX)
       lowerBoundY:=(endOfTheHandY<centerY)?(endOfTheHandY):(centerY)
       upperBoundY:=(endOfTheHandY>centerY)?(endOfTheHandY):(centerY)
       y:=i/windowWidth-mod(i/windowWidth,1)
       x:=mod(i,windowWidth)
       isXWithinBounds:=(x>lowerBoundX | x=lowerBoundX) & (x<upperBoundX | x=upperBoundX) ;Damn... Now I understand why almost every programming language supports the "<=" and ">=" operators, no matter how much harder they make the language to tokenize.
       isYWithinBounds:=(y>lowerBoundY | y=lowerBoundY) & (y<upperBoundY | y=upperBoundY)
       If isXWithinBounds=1 & isYWithinBounds=1
           expectedY:=(x-centerX)*coefficientOfTheDirection+centerY
           expectedX:=(y-centerY)*(1/coefficientOfTheDirection)+centerX
           debugString1 <= "The point (%d,%d) is within bounds, expectedY is %d and expectedX is %d.\n\0"
           AsmStart
               .intel_syntax noprefix
               .ifdef DEBUG
                   fld dword ptr expectedX
                   fistp dword ptr result
                   push dword ptr result
                   fld dword ptr expectedY
                   fistp dword ptr result
                   push dword ptr result
                   fld dword ptr y
                   fistp dword ptr result
                   push dword ptr result
                   fld dword ptr x
                   fistp dword ptr result
                   push dword ptr result
                   lea ebx,debugString1
                   push ebx
                   call printf
               .endif
               .att_syntax
           AsmEnd
           ASCIIofLetterH<="h\0\0\0"
           ASCIIofLetterM<="m\0\0\0"
           ASCIIofLetterS<="s\0\0\0"
           If j=0
               AsmStart
                   .intel_syntax noprefix
                   fild dword ptr ASCIIofLetterH
                   fstp dword ptr currentSign
                   .att_syntax
               AsmEnd
           ElseIf j=1
               AsmStart
                   .intel_syntax noprefix
                   fild dword ptr ASCIIofLetterM
                   fstp dword ptr currentSign
                   .att_syntax
               AsmEnd
           Else
               AsmStart
                   .intel_syntax noprefix
                   fild dword ptr ASCIIofLetterS
                   fstp dword ptr currentSign
                   .att_syntax
               AsmEnd
           EndIf
           If (upperBoundX=lowerBoundX | upperBoundY=lowerBoundY) & output[i]=ASCIIofSpaceAsFloat32
               output[i]:=currentSign
           EndIf
           If (abs(expectedY-y)<3/4 | abs(expectedX-x)<3/4) & output[i]=ASCIIofSpaceAsFloat32
               output[i]:=currentSign
           EndIf
       EndIf
       i:=i+1
   EndWhile
   j:=j+1
EndWhile
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
   mov dl,' '
   mov ax,0x200
   int 0x21
.endif
.att_syntax
AsmEnd
;Draw some ornament...
ASCIIofLetterX<="x\0\0\0"
AsmStart
   .intel_syntax noprefix
   fild dword ptr ASCIIofLetterX
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
i:=0
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
   mov dl,'w'
   mov ax,0x200
   int 0x21
.endif
.att_syntax
AsmEnd
While i<windowWidth*windowHeight
   y:=i/windowWidth-mod(i/windowWidth,1)
   x:=mod(i,windowWidth)
   If abs(windowHeight-2*ln(1+abs((x-centerX)/2))-y)<1-abs(x-centerX)/(centerX*95/112) & x>1/2*centerX & x<3/2*centerX & output[i]=ASCIIofSpaceAsFloat32 ;The logarithmic curve looks somewhat like a lemma of a flower.
       output[i]:=currentSign
   EndIf
   i:=i+1
EndWhile
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
   mov dl,'o'
   mov ax,0x200
   int 0x21
.endif
.att_syntax
AsmEnd
AsmStart
   .intel_syntax noprefix
   fild dword ptr ASCIIofLetterX
   fstp dword ptr currentSign
   .att_syntax
AsmEnd
;Let's try to make it look like the bottom of the lemma isn't floating in the air.
j:=0
While j<3
   i:=windowWidth*(windowHeight-1) ;So, move to the beginning of the last line.
   While i<windowWidth*windowHeight
       If j<2 & (output[i-windowWidth]=currentSign & (output[i+1]=currentSign | output[i-1]=currentSign))
           output[i]:=currentSign
       ElseIf j=2 & (output[i+1]=ASCIIofSpaceAsFloat32 & output[i-windowWidth]=currentSign)
           output[i]:=ASCIIofSpaceAsFloat32
       EndIf
       i:=i+1
   EndWhile
   j:=j+1
EndWhile
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
   mov dl,'r'
   mov ax,0x200
   int 0x21
.endif
.att_syntax
AsmEnd
;Let's make a digital clock in the corner...
AsmStart
   .intel_syntax noprefix
   fild dword ptr ASCIIofDigitsAsInt32Array #So, load "0\0\0\0" (the first 32 bits of the array "ASCIIofDigitsAsInt32Array") into the st0 register and convert it to Float32.
   fstp dword ptr ASCIIofDigit0AsFloat32
   .att_syntax
AsmEnd
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
   mov dl,'l'
   mov ax,0x200
   int 0x21
.endif
.att_syntax
AsmEnd
ASCIIofColon<=":\0\0\0"
AsmStart
   .intel_syntax
   fild dword ptr ASCIIofColon
   fstp dword ptr ASCIIofColonAsFloat32
   .att_syntax
AsmEnd
output[windowWidth*windowHeight-2]:=ASCIIofDigit0AsFloat32+mod(second,10)
output[windowWidth*windowHeight-3]:=ASCIIofDigit0AsFloat32+second/10-mod(second/10,1)
output[windowWidth*windowHeight-4]:=ASCIIofColonAsFloat32
output[windowWidth*windowHeight-5]:=ASCIIofDigit0AsFloat32+mod(minute,10)
output[windowWidth*windowHeight-6]:=ASCIIofDigit0AsFloat32+minute/10-mod(minute/10,1)
output[windowWidth*windowHeight-7]:=ASCIIofColonAsFloat32
output[windowWidth*windowHeight-8]:=ASCIIofDigit0AsFloat32+mod(hour,10)
output[windowWidth*windowHeight-9]:=ASCIIofDigit0AsFloat32+hour/10-mod(hour/10,1)
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
   mov dl,'d'
   mov ax,0x200
   int 0x21
.endif
.att_syntax
AsmEnd
signature<="Analog Clock for DOS\nMade in AEC by\nTeo Samarzija\0"
currentSign:=signature[0]
i:=windowWidth*(windowHeight-3)
j:=0
While not(currentSign=0) ;That is, as long as it's not the '\0' sign.
   AsmStart
       .intel_syntax noprefix
       fld dword ptr j
       fistp dword ptr result
       mov ebx, dword ptr result
       movzx eax, byte ptr [signature+ebx] #I hope it goes without saying something like this (using post-Pentium instructions in inline assembly) won't work on a machine with an archaic processor. I am writing this program for a machine with a modern processor which happens to run DOS.
       mov dword ptr result, eax
       fild dword ptr result
       fstp dword ptr currentSign
       fild dword ptr ASCIIofNewLine
       fstp dword ptr ASCIIofNewLineAsFloat32
       .att_syntax
   AsmEnd
   If currentSign=ASCIIofNewLineAsFloat32
       i:=(i/windowWidth-mod(i/windowWidth,1)+1)*windowWidth
   ElseIf not(currentSign=0)
       output[i]:=currentSign
       i:=i+1
   Else
       output[i]:=ASCIIofSpaceAsFloat32
   EndIf
   j:=j+1
EndWhile
AsmStart
.intel_syntax noprefix
.ifdef debugForDOS
   mov dl,'!'
   mov ax,0x200
   int 0x21
.endif
.att_syntax
AsmEnd
AsmStart ;And this is, according to GCC 9.3.0, how you convert a Float32Array with ASCII codes and print it under DOS.
# 0 "" 2
/NO_APP
    movl    $0, -4(%ebp)
    jmp    L3
L4:
    movl    -4(%ebp), %eax
    flds    output(,%eax,4)
    fnstcw    -18(%ebp)
    movw    -18(%ebp), %ax
    orb    $12, %ah
    movw    %ax, -20(%ebp)
    fldcw    -20(%ebp)
    fistps    -22(%ebp)
    fldcw    -18(%ebp)
    movb    -22(%ebp), %al
    movsbl    %al, %eax
    pushl    %eax
    call    _putchar
    addl    $4, %esp
    incl    -4(%ebp)
L3:
    cmpl    $1839, -4(%ebp)
    jle    L4
/APP
# 21 "analogClock.c" 1
    .intel_syntax noprefix
mov al,0 #And I hope this also goes without saying, but when there is...
mov ah,0x4C #...no C library, returning 0 from "main" crashes your program...
int 0x21 #...and you need to use OS-specific code to end it properly.
.att_syntax

# 0 "" 2
/NO_APP
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE1:
    .ident    "GCC: (GNU) 9.3.0"
AsmEnd
Here is what it looks like in QEMU:
[Image: analogClockForDOS.png]



Possibly Related Threads...
Thread Author Replies Views Last Post
  Compiler Theory FlatAssembler 5 988 October 27, 2020 at 10:48 am
Last Post: Angrboda



Users browsing this thread: 1 Guest(s)