Assembly 运行32位DOS应用程序时出现NTVDM系统错误4001h

Assembly 运行32位DOS应用程序时出现NTVDM系统错误4001h,assembly,x86-emulation,ntvdm,Assembly,X86 Emulation,Ntvdm,我用GNU汇编程序交叉编译了一个32位模拟时钟,它是从Linux到DOS的。当在QEMU或VirtualBox中的FreeDOS下运行时(它们是比NTVDM好得多的模拟器,对吗?),它创建了这样一幅画面: 我希望它也能在Windows10上运行。但是,当我尝试在Windows 10上运行它时,会出现以下错误: 这到底是什么意思?我的程序不是16位的,它是32位的,所以为什么有些16位的子系统甚至在运行呢?“错误4001h”是什么意思?Windows应该能够运行32位DOS程序,对吗? 这是我

我用GNU汇编程序交叉编译了一个32位模拟时钟,它是从Linux到DOS的。当在QEMU或VirtualBox中的FreeDOS下运行时(它们是比NTVDM好得多的模拟器,对吗?),它创建了这样一幅画面:

我希望它也能在Windows10上运行。但是,当我尝试在Windows 10上运行它时,会出现以下错误:

这到底是什么意思?我的程序不是16位的,它是32位的,所以为什么有些16位的子系统甚至在运行呢?“错误4001h”是什么意思?Windows应该能够运行32位DOS程序,对吗?
这是我的程序的源代码,如果有点长,很抱歉,但我不知道是什么导致了这个错误(我对系统编程的了解很少):

语法气体 ;这与“analogClock.aec”文件中的程序相同,只是修改为 ;在DOS而不是Linux上运行。它还使用GNU汇编程序进行编译。 ;即GCC 9.3.0和GNU汇编程序2.34,尽管它们是在 ;2019年,仍然具有为DOS编译的功能。你不需要 ;为此,在DOS上运行它们,事实上,我甚至怀疑它们是否可以在DOS上运行。 ;如果您设法将它们编译为在DOS上运行,它们可能会用完 ;RAM即使是最简单的程序(DOS不能使用超过64MB的RAM, ;这对于运行现代编译器来说太小了)。你可以在上面运行它们 ;Linux和它们将生成一个DOS可执行文件,然后您可以在 ;仿真器。这叫做交叉编译。现在,这是不可能的 ;您使用的GNU编译器集合(GCC)的精简版本 ;Linux,您需要从源代码构建它以获得所有功能(包括 ;其他方面,交叉编译到许多操作系统)。这不太难,但很难 ;即使是在超现代版本上,编译完整版本的GCC也需要几个小时 ;电脑。出于某种我无法理解的原因,这个特定的可执行文件 ;导致DosBox崩溃,即使它在VirtualBox中的FreeDOS上工作。 ;现在,我希望这是不言而喻的,但是如果一些现代程序继续运行 ;多斯,这可能是巧合,你不能指望它会起作用 ;完美无瑕。开发人员早已停止测试他们的应用程序是否有效 ;在DOS下。因此,虽然GCC 9.3.0附带的C库可以编译 ;对于DOS,尝试实际链接会导致无数链接器错误。 ;默认情况下,GCC将尝试链接到C库,即使您的代码 ;不使用其中存在的任何函数。所以,您需要编译 ;汇编代码ArtihmeticExpressionCompiler通过以下方式生成: ; djgpp gcc-o analogClockForDOS.exe-ffreestanding-nostlib analogClockForDOS.s ;由于这个原因,我无法编译Duktape以在DOS上运行。 ;为什么使用GNU汇编程序而不是FlatAssembler?首先,我 ;已经有大量与GNU汇编程序兼容的内联汇编程序(从 在Linux上运行的“analogClock.aec”)。第二,当你在公司工作时 ;GNU汇编程序,您不必编写复杂的代码 ;处理器在32位模式(DOS)下自动启动 ;16位模式),GNU汇编程序会为您完成这项工作。 ;现在,为了让32位应用程序能够在DOS上运行,您需要 ;一个名为DPMI(DOS保护模式接口)的驱动程序。它是预装的 ;在FreeDOS中,但不在MS-DOS上。它还附带Windows3.x。 ;FlatAssembler for DOS也是一个32位应用程序,如果没有它,它将无法在DOS上运行 ;DPMI已安装并运行。 AsmStart;因此,下面的代码是由GCC 9.3.0生成的,再加上我放在C程序中的一些内联程序集。 .文件“analogClock.c” .第节.正文 /应用程序 .intel_语法noprefix 打电话给_main#我希望这不用说,但是当你在开发。。。 #…对于没有C库的系统,不能保证“main”将。。。 #…首先被调用(甚至在程序崩溃之前被调用), #你需要自己照顾好。 .att_语法 /无应用程序 .globl_putchar _putchar: LFB0: .cfi_startproc 推力%ebp .cfi_def_cfa_偏移量8 .cfi_偏移量5,-8 移动%esp,%ebp .cfi_def_cfa_寄存器5 次级$4,%esp movl 8(%ebp),%eax movb%al,-4(%ebp) /应用程序 #9“模拟时钟c”1 movb-4(%ebp),%dl movb$0x02,%ah 整型$0x21 # 0 "" 2 /无应用程序 不 离开 .cfi_恢复5 .cfi_def_cfa 4,4 ret .cfi_endproc LFE0: .通信结果,4 .comm i,4 .通信x,4 .社区,4 .通信当前符号,4 .comm centerX,4 .通讯中心,4 .通信距离,4 .通信时钟半径,4 .通信输出,7360 .通讯时间,4 .通讯分钟,4 .通信秒,4 .通信角,4 .comm endOfTheHandX,4 .comm endOfTheHandY,4 .方向的通信系数,4 .通信窗口宽度,4 .通信窗口高度,4 .comm lowerBoundX,4 .comm上限x,4 .comm lowerBoundY,4 .通信上限,4 .comm ISX带边界,4 .comm isYWithinBounds,4 .预期通信量为4 .comm-expectedX,4 .comm j,4 .comm ASCIIofSpaceAsFloat32,4 .comm ASCIIofDigit0AsFloat32,4 .comm ASCIIofColonAsFloat32,4 .comm ASCIIOFNEWLINEAS32,4 .globl_main _主要内容: LFB1: .cfi_startproc 推力%ebp .cfi_def_cfa_偏移量8 .cfi_偏移量5,-8 移动%esp,%ebp .cfi_def_cfa_寄存器5 24美元,特别是 /应用程序 #18“模拟时钟c”1 .ifdef debugForDOS#当你没有一个好的调试器时(比如在DOS上工作时),你需要找到一些聪明的调试方法。你知道,就像一步一步地打印“你好,世界!”。 .intel_语法noprefix mov-dl,'H' mov-ax,0x200 int 0x21 .att_语法 .endif .intel_语法noprefix#获取当前时间。由于我们无法访问这里的标准C库,我们需要在DOSAPI中查找一种方法来实现这一点。 mov ax,0x2C00 int 0x21 mov字节ptr小时,ch 菲尔德德沃德小时酒店 德沃德飞行小时 mov字节ptr分钟,cl 费尔德·德沃德ptr分钟 德沃德ptr分钟 mov字节ptr秒,dh 费尔德·德沃德第二 德沃德第二#
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